diff options
Diffstat (limited to 'bin/when.y')
-rw-r--r-- | bin/when.y | 152 |
1 files changed, 125 insertions, 27 deletions
diff --git a/bin/when.y b/bin/when.y index d647d193..1d3795ad 100644 --- a/bin/when.y +++ b/bin/when.y @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 C. McEnroe <june@causal.agency> +/* Copyright (C) 2019, 2022 June McEnroe <june@causal.agency> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -18,10 +18,12 @@ #include <ctype.h> #include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <strings.h> -#include <sysexits.h> #include <time.h> static void yyerror(const char *str); @@ -30,12 +32,13 @@ static int yylex(void); #define YYSTYPE struct tm static const char *Days[7] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday", }; static const char *Months[12] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December", }; static const struct tm Week = { .tm_mday = 7 }; @@ -43,14 +46,14 @@ static const struct tm Week = { .tm_mday = 7 }; static struct tm normalize(struct tm date) { time_t time = timegm(&date); struct tm *norm = gmtime(&time); - if (!norm) err(EX_OSERR, "gmtime"); + if (!norm) err(1, "gmtime"); return *norm; } static struct tm today(void) { time_t now = time(NULL); struct tm *local = localtime(&now); - if (!local) err(EX_OSERR, "localtime"); + if (!local) err(1, "localtime"); struct tm date = { .tm_year = local->tm_year, .tm_mon = local->tm_mon, @@ -103,12 +106,24 @@ static struct tm dateSub(struct tm date, struct tm scalar) { } static struct tm dateDiff(struct tm a, struct tm b) { + time_t atime = timegm(&a), btime = timegm(&b); + if (atime < btime) { + struct tm x = a; + a = b; + b = x; + time_t xtime = atime; + atime = btime; + btime = xtime; + } struct tm diff = { .tm_year = a.tm_year - b.tm_year, .tm_mon = a.tm_mon - b.tm_mon, .tm_mday = a.tm_mday - b.tm_mday, }; - if (a.tm_mon < b.tm_mon) { + if ( + a.tm_mon < b.tm_mon || + (a.tm_mon == b.tm_mon && a.tm_mday < b.tm_mday) + ) { diff.tm_year--; diff.tm_mon += 12; } @@ -117,20 +132,54 @@ static struct tm dateDiff(struct tm a, struct tm b) { diff.tm_mday = 0; while (dateAdd(b, diff).tm_mday != a.tm_mday) diff.tm_mday++; } - time_t atime = timegm(&a), btime = timegm(&b); diff.tm_yday = (atime - btime) / 24 / 60 / 60; return diff; } +static struct { + size_t cap, len; + struct tm *ptr; +} dates; + +static struct tm getDate(const char *name) { + for (size_t i = 0; i < dates.len; ++i) { + if (!strcmp(dates.ptr[i].tm_zone, name)) return dates.ptr[i]; + } + return (struct tm) {0}; +} + +static void setDate(const char *name, struct tm date) { + for (size_t i = 0; i < dates.len; ++i) { + if (strcmp(dates.ptr[i].tm_zone, name)) continue; + char *tm_zone = dates.ptr[i].tm_zone; + dates.ptr[i] = date; + dates.ptr[i].tm_zone = tm_zone; + return; + } + if (dates.len == dates.cap) { + dates.cap = (dates.cap ? dates.cap * 2 : 8); + dates.ptr = realloc(dates.ptr, sizeof(*dates.ptr) * dates.cap); + if (!dates.ptr) err(1, "realloc"); + } + dates.ptr[dates.len] = date; + dates.ptr[dates.len].tm_zone = strdup(name); + if (!dates.ptr[dates.len].tm_zone) err(1, "strdup"); + dates.len++; +} + +static bool silent; + static void printDate(struct tm date) { + if (silent) return; printf( - "%s %s %d %d\n", + "%.3s %.3s %d %d\n", Days[date.tm_wday], Months[date.tm_mon], date.tm_mday, 1900 + date.tm_year ); } static void printScalar(struct tm scalar) { + if (silent) return; if (scalar.tm_year) printf("%dy ", scalar.tm_year); if (scalar.tm_mon) printf("%dm ", scalar.tm_mon); if (scalar.tm_mday % 7) { @@ -139,8 +188,12 @@ static void printScalar(struct tm scalar) { printf("%dw ", scalar.tm_mday / 7); } if (scalar.tm_yday && scalar.tm_mon) { - if (scalar.tm_yday % 7 == 0) { - printf("(%dw) ", scalar.tm_yday / 7); + if (scalar.tm_yday >= 7) { + printf("(%dw", scalar.tm_yday / 7); + if (scalar.tm_yday % 7) { + printf(" %dd", scalar.tm_yday % 7); + } + printf(") "); } printf("(%dd) ", scalar.tm_yday); } @@ -149,7 +202,8 @@ static void printScalar(struct tm scalar) { %} -%token Number Month Day +%token Name Number Month Day +%right '=' %left '+' '-' %right '<' '>' @@ -162,6 +216,8 @@ expr: date: dateLit + | Name { $$ = getDate($1.tm_zone); free($1.tm_zone); } + | Name '=' date { setDate($1.tm_zone, $3); free($1.tm_zone); $$ = $3; } | '(' date ')' { $$ = $2; } | '<' date { $$ = dateSub($2, Week); } | '>' date { $$ = dateAdd($2, Week); } @@ -211,35 +267,77 @@ static int yylex(void) { return Number; } - for (int i = 0; i < 7; ++i) { - if (strncasecmp(input, Days[i], 3)) continue; - while (isalpha(*input)) input++; - yylval.tm_wday = i; - return Day; + size_t len; + for (len = 0; isalnum(input[len]) || input[len] == '_'; ++len); + + if (len >= 3) { + for (int i = 0; i < 7; ++i) { + if (strncasecmp(input, Days[i], len)) continue; + yylval.tm_wday = i; + input += len; + return Day; + } + + for (int i = 0; i < 12; ++i) { + if (strncasecmp(input, Months[i], len)) continue; + yylval.tm_mon = i; + input += len; + return Month; + } } - for (int i = 0; i < 12; ++i) { - if (strncasecmp(input, Months[i], 3)) continue; - while (isalpha(*input)) input++; - yylval.tm_mon = i; - return Month; + if (len && (len != 1 || !strchr("dwmy", *input))) { + yylval.tm_zone = strndup(input, len); + if (!yylval.tm_zone) err(1, "strndup"); + input += len; + return Name; } return *input++; } int main(int argc, char *argv[]) { + size_t cap = 0; + char *line = NULL; + + char path[PATH_MAX]; + const char *configHome = getenv("XDG_CONFIG_HOME"); + if (configHome) { + snprintf(path, sizeof(path), "%s/when/dates", configHome); + } else { + snprintf(path, sizeof(path), "%s/.config/when/dates", getenv("HOME")); + } + + FILE *file = fopen(path, "r"); + if (file) { + silent = true; + while (0 < getline(&line, &cap, file)) { + input = line; + yyparse(); + } + fclose(file); + silent = false; + } else if (errno != ENOENT) { + err(1, "%s", path); + } + if (argc > 1) { - input = argv[1]; - return yyparse(); + if (strcmp(argv[1], "-")) { + input = argv[1]; + return yyparse(); + } else { + for (size_t i = 0; i < dates.len; ++i) { + printf("%s: ", dates.ptr[i].tm_zone); + printScalar(dateDiff(today(), dates.ptr[i])); + } + return 0; + } } struct tm date = today(); printDate(date); printf("\n"); - char *line = NULL; - size_t cap = 0; while (0 < getline(&line, &cap, stdin)) { if (line[0] == '\n') continue; |