From 9f2884c0dcbd7762c8a6a067174c3ca1f304d9c0 Mon Sep 17 00:00:00 2001 From: Krow Savcik Date: Sat, 26 Jul 2025 12:50:28 +0300 Subject: initial commit --- LICENSE | 21 +++++++ Makefile | 21 +++++++ config.mk | 3 + vtime.c | 60 +++++++++++++++++++ vtime.h | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 299 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 config.mk create mode 100644 vtime.c create mode 100644 vtime.h 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 +#include +#include +#include +#include + +#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 -- cgit v1.2.3