From 2bad5b5ad30bc24c21cc7423756afac79c61004f Mon Sep 17 00:00:00 2001 From: June McEnroe Date: Wed, 24 Jul 2019 20:31:29 -0400 Subject: Add when When? Now. --- bin/.gitignore | 1 + bin/Makefile | 1 + bin/README | 3 +- bin/bin.7 | 5 +- bin/ddc.y | 213 ------------------------------------------------- bin/man1/when.1 | 76 ++++++++++++++++++ bin/when.y | 243 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 327 insertions(+), 215 deletions(-) delete mode 100644 bin/ddc.y create mode 100644 bin/man1/when.1 create mode 100644 bin/when.y diff --git a/bin/.gitignore b/bin/.gitignore index b9083639..0a1e37aa 100644 --- a/bin/.gitignore +++ b/bin/.gitignore @@ -35,4 +35,5 @@ ttpre up wake wat +when xx diff --git a/bin/Makefile b/bin/Makefile index e03e5093..b246bf28 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -35,6 +35,7 @@ BINS += shotty BINS += ttpre BINS += up BINS += wake +BINS += when BINS += xx LINKS += atch diff --git a/bin/README b/bin/README index c178f342..dc5e9b0d 100644 --- a/bin/README +++ b/bin/README @@ -33,6 +33,7 @@ DESCRIPTION up(1) upload file wake(1) wake-on-LAN wat(1) watch files + when(1) date calculator xx(1) hexdump To build graphical tools, set one of: @@ -41,4 +42,4 @@ DESCRIPTION GFX=fb GFX=x11 -Causal Agency July 9, 2019 Causal Agency +Causal Agency July 24, 2019 Causal Agency diff --git a/bin/bin.7 b/bin/bin.7 index ac34b97c..1503e87f 100644 --- a/bin/bin.7 +++ b/bin/bin.7 @@ -1,4 +1,4 @@ -.Dd July 9, 2019 +.Dd July 24, 2019 .Dt BIN 7 .Os "Causal Agency" . @@ -94,6 +94,9 @@ wake-on-LAN .It Xr wat 1 watch files . +.It Xr when 1 +date calculator +. .It Xr xx 1 hexdump .El diff --git a/bin/ddc.y b/bin/ddc.y deleted file mode 100644 index 35e1b398..00000000 --- a/bin/ddc.y +++ /dev/null @@ -1,213 +0,0 @@ -/* Copyright (C) 2019 June McEnroe - * - * 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 - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -%{ - -#include -#include -#include -#include -#include -#include -#include - -static void yyerror(const char *str); -static int yylex(void); - -#define YYSTYPE struct tm - -static struct tm normalize(struct tm date) { - time_t time = mktime(&date); - struct tm *norm = localtime(&time); - if (!norm) err(EX_OSERR, "localtime"); - return *norm; -} - -static struct tm today(void) { - time_t now = time(NULL); - struct tm *date = localtime(&now); - if (!date) err(EX_OSERR, "localtime"); - date->tm_hour = date->tm_min = date->tm_sec = 0; - return *date; -} - -static struct tm monthDay(int month, int day) { - struct tm date = today(); - date.tm_mon = month; - date.tm_mday = day; - return normalize(date); -} - -static struct tm monthDayYear(int month, int day, int year) { - struct tm date = { - .tm_mon = month, - .tm_mday = day, - .tm_year = year - 1900, - }; - return normalize(date); -} - -static struct tm weekDay(int day) { - struct tm date = today(); - date.tm_mday += day - date.tm_wday; - return normalize(date); -} - -static struct tm scalarAdd(struct tm a, struct tm b) { - a.tm_mday += b.tm_mday; - a.tm_mon += b.tm_mon; - a.tm_year += b.tm_year; - return a; -} - -static struct tm scalarSub(struct tm a, struct tm b) { - a.tm_mday -= b.tm_mday; - a.tm_mon -= b.tm_mon; - a.tm_year -= b.tm_year; - return a; -} - -static struct tm dateAdd(struct tm date, struct tm scalar) { - return normalize(scalarAdd(date, scalar)); -} - -static struct tm dateSub(struct tm date, struct tm scalar) { - return normalize(scalarSub(date, scalar)); -} - -static struct tm dateDiff(struct tm a, struct tm b) { - struct tm diff = { .tm_year = a.tm_year - b.tm_year }; - if (a.tm_mday < b.tm_mday) { - diff.tm_mon = a.tm_mon - b.tm_mon - 1; - while (dateAdd(b, diff).tm_mday != a.tm_mday) diff.tm_mday++; - } else { - diff.tm_mon = a.tm_mon - b.tm_mon; - diff.tm_mday = a.tm_mday - b.tm_mday; - } - return diff; -} - -static const char *Days[7] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", -}; - -static const char *Months[12] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", -}; - -static void printDate(struct tm date) { - printf( - "%s %s %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 (scalar.tm_year) printf("%dy ", scalar.tm_year); - if (scalar.tm_mon) printf("%dm ", scalar.tm_mon); - if (scalar.tm_mday % 7) { - printf("%dd\n", scalar.tm_mday); - } else { - printf("%dw\n", scalar.tm_mday / 7); - } -} - -%} - -%token Number Month Day -%left '+' '-' - -%% - -expr: - date { printDate($1); } - | scalar { printScalar($1); } - ; - -date: - dateSpec - | date '+' scalar { $$ = dateAdd($1, $3); } - | date '-' scalar { $$ = dateSub($1, $3); } - ; - -scalar: - scalarSpec - | scalar '+' scalar { $$ = scalarAdd($1, $3); } - | scalar '-' scalar { $$ = scalarSub($1, $3); } - | date '-' date { $$ = dateDiff($1, $3); } - ; - -dateSpec: - '.' { $$ = today(); } - | Month Number { $$ = monthDay($1.tm_mon, $2.tm_sec); } - | Month Number Number { $$ = monthDayYear($1.tm_mon, $2.tm_sec, $3.tm_sec); } - | Day { $$ = weekDay($1.tm_wday); } - ; - -scalarSpec: - Number 'd' { $$ = (struct tm) { .tm_mday = $1.tm_sec }; } - | Number 'w' { $$ = (struct tm) { .tm_mday = 7 * $1.tm_sec }; } - | Number 'm' { $$ = (struct tm) { .tm_mon = $1.tm_sec }; } - | Number 'y' { $$ = (struct tm) { .tm_year = $1.tm_sec }; } - ; - -%% - -static void yyerror(const char *str) { - warnx("%s", str); -} - -static const char *input; - -static int yylex(void) { - while (isspace(*input)) input++; - if (!*input) return EOF; - - if (isdigit(*input)) { - char *rest; - yylval.tm_sec = strtol(input, &rest, 10); - input = rest; - 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; - } - - for (int i = 0; i < 12; ++i) { - if (strncasecmp(input, Months[i], 3)) continue; - while (isalpha(*input)) input++; - yylval.tm_mon = i; - return Month; - } - - return *input++; -} - -int main(void) { - char *buf = NULL; - size_t cap = 0; - while (0 < getline(&buf, &cap, stdin)) { - input = buf; - yyparse(); - printf("\n"); - } -} diff --git a/bin/man1/when.1 b/bin/man1/when.1 new file mode 100644 index 00000000..0b473573 --- /dev/null +++ b/bin/man1/when.1 @@ -0,0 +1,76 @@ +.Dd July 24, 2019 +.Dt WHEN 1 +.Os +. +.Sh NAME +.Nm when +.Nd date calculator +. +.Sh SYNOPSIS +.Nm +.Op Ar expr +. +.Sh DESCRIPTION +.Nm +is a date calculator. +If no +.Ar expr +is given, +expressions are read +from standard input. +. +.Pp +The grammar is as follows: +.Bl -tag -width Ds +.It Sy \&. +Today's date. +. +.It Ar month Ar date Op Ar year +A full date, +or a date in the current year. +.Ar month +must be at least three letters. +. +.It Ar day +A day of the week +in the current week. +.Ar day +must be at least three letters. +. +.It Sy < Ar date +The date one week before. +. +.It Sy > Ar date +The date one week after. +. +.It Ar date Sy + Ar interval +The date after some interval. +. +.It Ar date Sy - Ar interval +The date before some interval. +. +.It Ar date Sy - Ar date +The interval between two dates. +. +.It Ar num Sy d +A number of days. +. +.It Ar num Sy w +A number of weeks. +. +.It Ar num Sy m +A number of months. +. +.It Ar num Sy y +A number of years. +.El +. +.Sh EXAMPLES +.Bl -tag -width "Dec 25 - ." +.It Ic Dec 25 - \&. +How long until Christmas. +.It Ic >Fri +The date next Friday. +.It Ic \&. + 2w +Your last day at work. +.El diff --git a/bin/when.y b/bin/when.y new file mode 100644 index 00000000..e8531ad3 --- /dev/null +++ b/bin/when.y @@ -0,0 +1,243 @@ +/* Copyright (C) 2019 June McEnroe + * + * 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +%{ + +#include +#include +#include +#include +#include +#include +#include + +static void yyerror(const char *str); +static int yylex(void); + +#define YYSTYPE struct tm + +static const char *Days[7] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", +}; + +static const char *Months[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", +}; + +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"); + return *norm; +} + +static struct tm today(void) { + time_t now = time(NULL); + struct tm *local = localtime(&now); + if (!local) err(EX_OSERR, "localtime"); + struct tm date = { + .tm_year = local->tm_year, + .tm_mon = local->tm_mon, + .tm_mday = local->tm_mday, + }; + return normalize(date); +} + +static struct tm monthDay(int month, int day) { + struct tm date = today(); + date.tm_mon = month; + date.tm_mday = day; + return normalize(date); +} + +static struct tm monthDayYear(int month, int day, int year) { + struct tm date = today(); + date.tm_mon = month; + date.tm_mday = day; + date.tm_year = year - 1900; + return normalize(date); +} + +static struct tm weekDay(int day) { + struct tm date = today(); + date.tm_mday += day - date.tm_wday; + return normalize(date); +} + +static struct tm scalarAdd(struct tm a, struct tm b) { + a.tm_mday += b.tm_mday; + a.tm_mon += b.tm_mon; + a.tm_year += b.tm_year; + return a; +} + +static struct tm scalarSub(struct tm a, struct tm b) { + a.tm_mday -= b.tm_mday; + a.tm_mon -= b.tm_mon; + a.tm_year -= b.tm_year; + return a; +} + +static struct tm dateAdd(struct tm date, struct tm scalar) { + return normalize(scalarAdd(date, scalar)); +} + +static struct tm dateSub(struct tm date, struct tm scalar) { + return normalize(scalarSub(date, scalar)); +} + +static struct tm dateDiff(struct tm a, struct tm b) { + 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) { + diff.tm_year--; + diff.tm_mon += 12; + } + if (a.tm_mday < b.tm_mday) { + diff.tm_mon--; + 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 void printDate(struct tm date) { + printf( + "%s %s %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 (scalar.tm_year) printf("%dy ", scalar.tm_year); + if (scalar.tm_mon) printf("%dm ", scalar.tm_mon); + if (scalar.tm_mday % 7) { + printf("%dd ", scalar.tm_mday); + } else if (scalar.tm_mday) { + printf("%dw ", scalar.tm_mday / 7); + } + if (scalar.tm_yday && scalar.tm_mon) printf("(%dd) ", scalar.tm_yday); + printf("\n"); +} + +%} + +%token Number Month Day +%left '+' '-' +%right '<' '>' + +%% + +expr: + date { printDate($1); } + | scalar { printScalar($1); } + ; + +date: + dateLit + | '(' date ')' { $$ = $2; } + | '<' date { $$ = dateSub($2, Week); } + | '>' date { $$ = dateAdd($2, Week); } + | date '+' scalar { $$ = dateAdd($1, $3); } + | date '-' scalar { $$ = dateSub($1, $3); } + ; + +scalar: + scalarLit + | '(' scalar ')' { $$ = $2; } + | scalar '+' scalar { $$ = scalarAdd($1, $3); } + | scalar '-' scalar { $$ = scalarSub($1, $3); } + | date '-' date { $$ = dateDiff($1, $3); } + ; + +dateLit: + { $$ = today(); } + | '.' { $$ = today(); } + | Month Number { $$ = monthDay($1.tm_mon, $2.tm_sec); } + | Month Number Number { $$ = monthDayYear($1.tm_mon, $2.tm_sec, $3.tm_sec); } + | Day { $$ = weekDay($1.tm_wday); } + ; + +scalarLit: + Number 'd' { $$ = (struct tm) { .tm_mday = $1.tm_sec }; } + | Number 'w' { $$ = (struct tm) { .tm_mday = 7 * $1.tm_sec }; } + | Number 'm' { $$ = (struct tm) { .tm_mon = $1.tm_sec }; } + | Number 'y' { $$ = (struct tm) { .tm_year = $1.tm_sec }; } + ; + +%% + +static void yyerror(const char *str) { + warnx("%s", str); +} + +static const char *input; + +static int yylex(void) { + while (isspace(*input)) input++; + if (!*input) return EOF; + + if (isdigit(*input)) { + char *rest; + yylval.tm_sec = strtol(input, &rest, 10); + input = rest; + 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; + } + + for (int i = 0; i < 12; ++i) { + if (strncasecmp(input, Months[i], 3)) continue; + while (isalpha(*input)) input++; + yylval.tm_mon = i; + return Month; + } + + return *input++; +} + +int main(int argc, char *argv[]) { + if (argc > 1) { + input = argv[1]; + return yyparse(); + } + + printDate(today()); + printf("\n"); + + char *line = NULL; + size_t cap = 0; + while (0 < getline(&line, &cap, stdin)) { + if (line[0] == '\n') continue; + input = line; + yyparse(); + printf("\n"); + } +} -- cgit 1.4.1