summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE21
-rw-r--r--Makefile21
-rw-r--r--config.mk3
-rw-r--r--vtime.c60
-rw-r--r--vtime.h194
5 files changed, 299 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ca8fe02
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 krowsavcik
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a4d6d42
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,21 @@
+include config.mk
+
+vtime: vtime.c vtime.h
+ ${CC} vtime.c -o vtime
+
+install: vtime
+ mkdir -p $(PREFIX)/bin
+ mkdir -p $(PREFIX)/include
+ cp -f vtime $(PREFIX)/bin
+ cp -f vtime.h $(PREFIX)/include
+ chmod 755 $(PREFIX)/bin/vtime
+ chmod 644 $(PREFIX)/include/vtime.h
+
+uninstall:
+ rm -f ${PREFIX}/bin/vtime\
+ ${PREFIX}/include/vtime.h
+
+clean:
+ rm -f vtime
+
+.PHONY: install uninstall clean
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..e758d6b
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,3 @@
+PREFIX = ~/.local
+
+CC = gcc
diff --git a/vtime.c b/vtime.c
new file mode 100644
index 0000000..782f171
--- /dev/null
+++ b/vtime.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+
+#define VTIME_IMPL
+#include "vtime.h"
+
+void
+help()
+{
+ fprintf(stdout, "vtime [FORMAT]\n\
+\t[YYYY/yy].[mm].dd Sets the date. Can set only the day/month.\n\
+\t Instead of dot(.) coult be any non-numeric character.\n\
+\t+/-[k](d/w/m/y) Add/subtract k units from the date. k is implicitly 1\n\
+\tt[his]/n[ext] Wday\n\
+\tl[ast]/p[rev] Wday Go to next/previous week day. Wday is the week day prefix.\n\
+\tb[egining]\n\ts[tart]\n\tf[irst]\n\
+\te[nd] w[eek]/m[onth]/y[ear] Go to first/last day in week/month/year.\n\
+");
+}
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ time_t now = time(NULL);
+ struct tm out = *(localtime(&now));
+ if (argc == 1) {
+ help();
+ return 0;
+ }
+
+ char *fmt, *pnt;
+ size_t len = 0;
+ for (i = 1; i < argc; i++) {
+ len += 1 + strlen(argv[i]);
+ }
+
+ fmt = calloc(len, sizeof(*fmt));
+ if (fmt == NULL) {
+
+ fprintf(stderr, "Failed malloc\n");
+ return 1;
+ }
+
+ for (i = 1, pnt = fmt; i < argc; i++) {
+ pnt = stpcpy(pnt, argv[i]);
+ if (i != argc-1)
+ *(pnt++) = ' ';
+ }
+
+ if (vtime(&out, fmt, 0)) {
+ fprintf(stderr, "Wrong format\n");
+ return 1;
+ }
+ fprintf(stdout, "%s", asctime(&out));
+ return 0;
+}
diff --git a/vtime.h b/vtime.h
new file mode 100644
index 0000000..011241f
--- /dev/null
+++ b/vtime.h
@@ -0,0 +1,194 @@
+int vtime(struct tm *, const char *, int);
+
+enum vtime_flags {
+ VTIME_START_SUNDAY = 1
+};
+
+#ifdef VTIME_IMPL
+static const char * vtime_next_word(const char *);
+static const char * vtime_relative3(struct tm *, const char *, const int);
+static const char * vtime_relative2(struct tm *, const char *);
+
+static const char* vtime_day_names[] = {
+ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"
+};
+
+int
+vtime(struct tm *tm, const char *fmt, int flags)
+{
+ int i, j, k;
+ const char *pnt;
+ for (; *fmt; fmt++) {
+ if (isblank(*fmt))
+ continue;
+
+ /* Do relative 1 */
+ if (*fmt == '+' || *fmt == '-') {
+ i = isdigit(fmt[1]) ? 0 : 1;
+ pnt = fmt+1;
+ while (isdigit(*pnt))
+ i = i * 10 + (*(pnt++) - '0');
+ if (fmt[0] == '-')
+ i = -i;
+
+ switch (tolower(*pnt)) {
+ case 'w':
+ i *= 7;
+ /* FALLTHROUGH */
+ case 'd':
+ tm->tm_mday += i;
+ break;
+ case 'm':
+ tm->tm_mon += i;
+ break;
+ case 'y':
+ tm->tm_year += i;
+ break;
+ default:
+ return 1;
+ }
+
+ fmt = pnt;
+ /* No day-time savings. We want the same relative hh:mm */
+ tm->tm_isdst = -1;
+ mktime(tm);
+ continue;
+ }
+
+ /* Absolute 1 */
+ if (isdigit(fmt[0])) {
+ i = j = k = -1;
+vtime_abs:
+ k = j;
+ j = i;
+ for (i = 0; isdigit(*fmt); fmt++)
+ i = i * 10 + (*fmt - '0');
+
+ if (!isblank(*fmt) && fmt[0]) {
+ fmt++;
+ goto vtime_abs;
+ }
+
+ if (k != -1)
+ tm->tm_year = k + (k < 100 ? (tm->tm_year / 100 * 100) : -1900);
+
+ tm->tm_mon = (j == -1) ? tm->tm_mon : j-1;
+ tm->tm_mday = i;
+
+ /* No day-time savings. We want the same relative hh:mm */
+ tm->tm_isdst = -1;
+ mktime(tm);
+ continue;
+ }
+
+ switch (tolower(fmt[0])) {
+ case 'f':
+ case 's':
+ case 'b':
+ case 'e':
+ fmt = vtime_relative3(tm, fmt, flags & VTIME_START_SUNDAY);
+ if (fmt == NULL)
+ return 1;
+ break;
+ case 'p':
+ case 'l':
+ case 'n':
+ case 't':
+ fmt = vtime_relative2(tm, fmt);
+ if (fmt == NULL)
+ return 1;
+ break;
+ default:
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static const char *
+vtime_relative2(struct tm *tm, const char *s)
+{
+ size_t len;
+ int i, next = (s[0] == 'n') || (s[0] == 't');
+ const char *day = vtime_next_word(s);
+ if (!day[0])
+ return NULL;
+
+ for (len = 0; day[len] && !isblank(day[len]); len++);
+
+ for (i = 0; i < 7; i++) {
+ if (!strncasecmp(day, vtime_day_names[i], len))
+ break;
+ }
+
+ /* TODO: Could change to words like month/week/year/day */
+ if (i == 7)
+ return NULL;
+
+ if (next)
+ i = ((i + 13 - tm->tm_wday) % 7) + 1;
+ else
+ i = - (((tm->tm_wday - i + 13) % 7) + 1);
+
+ tm->tm_mday += i;
+ /* No day-time savings. We want the same relative hh:mm */
+ tm->tm_isdst = -1;
+ mktime(tm);
+
+
+ for (s = day; s[1] && !isblank(s[1]); s++);
+ return s;
+}
+
+static const char *
+vtime_relative3(struct tm *tm, const char *s, const int startSun)
+{
+ int i, toend = tolower(s[0]) == 'e';
+ const char *type = vtime_next_word(s);
+ switch (tolower(*type)) {
+ case 'w':
+ i = tm->tm_wday;
+ if (!startSun)
+ i = (i + 6) % 7;
+ tm->tm_mday += - i + 6 * toend;
+ break;
+ case 'm':
+ tm->tm_mday = 1;
+ if (toend) {
+ tm->tm_mon++;
+ tm->tm_mday = 0;
+ }
+ break;
+ case 'y':
+ tm->tm_mday = 1;
+ tm->tm_mon = 0;
+ if (toend) {
+ tm->tm_year++;
+ tm->tm_mday = 0;
+ }
+ break;
+ default:
+ return NULL;
+ }
+ /* No day-time savings. We want the same relative hh:mm */
+ tm->tm_isdst = -1;
+ mktime(tm);
+
+
+ for (s = type; s[1] && !isblank(s[1]); s++);
+ return s;
+}
+
+static const char *
+vtime_next_word(const char *s)
+{
+ while (*s && !isblank(*s))
+ s++;
+
+ while (isblank(*s))
+ s++;
+
+ return s;
+}
+#endif