sdep

A simple "date+event" line parser
git clone https://git.tronto.net/sdep
Download | Log | Files | Refs | README | LICENSE

commit 2b4be20507fee93fc165a550cd0cb8f81dcad05d
parent 3a33fa3c7921c69efbc9beaaa922452ba2eaf271
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Mon, 15 May 2023 18:31:12 +0200

Updated .gitignore, README formatting

Diffstat:
M.gitignore | 2+-
MREADME.md | 60+++++++++++++++++++++++++++++++++---------------------------
Dsdep-0.2/LICENSE | 21---------------------
Dsdep-0.2/Makefile | 61-------------------------------------------------------------
Dsdep-0.2/sdep.1 | 94-------------------------------------------------------------------------------
Dsdep-0.2/sdep.c | 277-------------------------------------------------------------------------------
6 files changed, 34 insertions(+), 481 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,2 +1,2 @@ sdep -sdep-*.tar.gz +sdep-* diff --git a/README.md b/README.md @@ -1,26 +1,30 @@ # sdep + A simple "date+event" line parser. -sdep follows the UNIX philosphy (do one thing well, use stdin and stdout) and -is heavily inspired by [suckless](https://suckless.org) utilities such as -[dmenu](https://tools.suckless.org/dmenu/). +sdep follows the UNIX philosphy (do one thing well, use stdin and stdout) +and is heavily inspired by [suckless](https://suckless.org) utilities +such as [dmenu](https://tools.suckless.org/dmenu/). -You can wrap it around shell scripts to turn it into a no-nonsense calendar -system. +You can wrap it around shell scripts to turn it into a no-nonsense +calendar system. ## Description -sdep reads lines of the form `date text` from stdin and writes to stdout those -lines such that `date` is between the two dates specified with the `-f` and -`-t` options, both of which default to the current minute. +sdep reads lines of the form `date text` from stdin and writes to stdout +those lines such that `date` is between the two dates specified with the +`-f` and `-t` options, both of which default to the current minute. -The format for `date` can be specified with the same syntax as for date(1). -The dates should correspond to a unique minute in time. +The format for `date` can be specified with the same syntax as for +date(1). The dates should correspond to a unique minute in time. ## Installation -Edit the Makefile to match your local configuration and type `make install`. + +Edit the Makefile to match your local configuration and type `make +install`. ## Examples + If `events.txt` contains lines formatted as `date text` then ``` @@ -39,25 +43,26 @@ will print all the lines whose date is in the past, while sdep -t -w "%A" <events.txt ``` -will print all lines whose date is in the future, showing only the day of the -week and the text. +will print all lines whose date is in the future, showing only the day +of the week and the text. ``` sdep -f "1999-01-01 00:00" -t "1999-12-31 23:59" -w "" <events.txt ``` -will show only the `text` of all lines with a date in 1999. You can specify a -different format for the dates, for example +will show only the `text` of all lines with a date in 1999. You can +specify a different format for the dates, for example ``` sdep +"%m/%d/%Y %I:%M%p" -t "12/31/2020 11:59pm" -w "" <events.txt ``` will match all dates from December 31st, 2020, one minute before midnight -(included). Note: this only works if your locale has an am/pm format, see -date(1). +(included). Note: this only works if your locale has an am/pm format, +see date(1). ## A stupidly simple calendar app + If you keep your events and reminders in a simple plain text file (say events.txt), you can run @@ -65,12 +70,12 @@ events.txt), you can run sdep -w "" -s "" | while read text; do notify-send "$text"; done < events.txt ``` -every minute, for example using cron(8), to get a notification every time -an events is happens. +every minute, for example using cron(8), to get a notification every +time an events is happens. You can use `sdep -f -t < events.txt` to list all your events, or -`sdep -t < events.txt` to list only the future ones. You can specify any date -range. Running +`sdep -t < events.txt` +to list only the future ones. You can specify any date range. Running ``` temp = $(mktemp) @@ -80,18 +85,19 @@ mv "$temp" events.txt will remove all old events from your file. -You can edit your events using any text editor and you can keep them synced -between multiple devices using something like +You can edit your events using any text editor and you can +keep them synced between multiple devices using something like [rsync](https://rsync.samba.org/). ## Scripts + The `scripts` folder contains the few scripts that I use. They are basically just a more elaborate version of the calendar system described above, with support for recurring events (e.g. weekly, daily). You can install them with `sudo SD=/path/to/scripts/folder make scripts`, -where `SD` specifies the path where the directory where you want your sdep -files to be saved; for example it can be `/home/username/.sdep`. +where `SD` specifies the path where the directory where you want your +sdep files to be saved; for example it can be `/home/username/.sdep`. For example check that the folder SDEPDATA in Makefile suits you. -Most of the scripts rely on the `-d` option of the GNU date utility, so you -should change that too if you are on a BSD system or on MacOS. +Most of the scripts rely on the `-d` option of the GNU date utility, +so you should change that too if you are on a BSD system or on MacOS. diff --git a/sdep-0.2/LICENSE b/sdep-0.2/LICENSE @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021-2023 Sebastiano Tronto - -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/sdep-0.2/Makefile b/sdep-0.2/Makefile @@ -1,61 +0,0 @@ -# See LICENSE file for copyright and license details. - -VERSION = 0.2 - -PREFIX = /usr/local -MANPREFIX = ${PREFIX}/share/man -SCRIPTS = sdep-add sdep-checknow sdep-checkpast sdep-clear sdep-edit sdep-list - -CPPFLAGS = -D_XOPEN_SOURCE=700 -DVERSION=\"${VERSION}\" -CFLAGS = -pedantic -Wall -Os ${CPPFLAGS} -DBGFLAGS = -pedantic -Wall -Wextra \ - -fsanitize=address -fsanitize=undefined ${CPPFLAGS} - -CC = cc - - -all: options sdep - -options: - @echo sdep build options: - @echo "CFLAGS = ${CFLAGS}" - @echo "CC = ${CC}" - -sdep: - ${CC} ${CFLAGS} -o sdep sdep.c - -debug: - ${CC} ${CFLAGS} -o sdep sdep.c - -clean: - rm -rf sdep sdep-${VERSION}.tar.gz - -dist: clean - mkdir -p sdep-${VERSION} - cp -R LICENSE Makefile README sdep.1 sdep.c sdep-${VERSION} - tar -cf sdep-${VERSION}.tar sdep-${VERSION} - gzip sdep-${VERSION}.tar - rm -rf sdep-${VERSION} - -install: all - mkdir -p ${DESTDIR}${PREFIX}/bin - cp -f sdep ${DESTDIR}${PREFIX}/bin/sdep - chmod 755 ${DESTDIR}${PREFIX}/bin/sdep - mkdir -p ${DESTDIR}${MANPREFIX}/man1 - sed "s/VERSION/${VERSION}/g" < sdep.1 \ - > ${DESTDIR}${MANPREFIX}/man1/sdep.1 - chmod 644 ${DESTDIR}${MANPREFIX}/man1/sdep.1 - -scripts: - for s in ${SCRIPTS}; do\ - sed "s|SDEPDATA|${SD}|g" < scripts/$$s > \ - ${DESTDIR}${PREFIX}/bin/$$s ; \ - chmod 755 ${DESTDIR}${PREFIX}/bin/$$s ;\ - done - -uninstall: - rm -rf ${DESTDIR}${PREFIX}/bin/sdep ${DESTDIR}${MANPREFIX}/man1/sdep.1 - for s in ${SCRIPTS}; do rm -rf ${DESTDIR}${PREFIX}/bin/$$s; done - -.PHONY: all options debug clean dist install scripts uninstall - diff --git a/sdep-0.2/sdep.1 b/sdep-0.2/sdep.1 @@ -1,94 +0,0 @@ -.Dd May 13, 2021 -.Dt SDEP 1 -.Os -.Sh NAME -.Nm sdep -.Nd a simple "date+event" line parser - -.Sh SYNOPSIS -.Nm -.Op Fl dv -.Op Ar +format -.Op Fl f Op Ar date -.Op Fl t Op Ar date -.Op Fl w Ar format -.Op Fl s Ar string - -.Sh DESCRIPTION -.Nm -reads lines of the form - -.Dl Ar "date text" - -from stdin and writes to stdout those lines such that -.Ar date -is between the dates specified by the -.Fl f -and -.Fl t -options, both defaulting to the current minute. -The dates should correspond to a unique minute in time. The format for -.Ar date -can be specified with the same syntax as for -.Xr date 1 . - -The options are as follows: -.Bl -tag -width Ds -.It Fl d -print the default date format and exit. -.It Fl f Op Ar date -initial date for the range (default: current minute). If -.Ar date -is not specified there will be no lower bound for the dates. -.It Fl s Ar string -change the string that separates the date from the text in the output -lines (deafult: "\t"). -.It Fl t Op Ar date -final date for the range (default: current minute). If -.Ar date -is not specified then there will be no upper bound for the dates. -.It Fl v -print version information and exit. -.It Fl w Ar format -change the format in which the date is written in the output lines. - -.Sh EXAMPLES -If -.Ar events.txt -contains lines formatted as -.Ar "date text" -then - -.Dl sdep -f <events.txt - -will print all the lines whose date is in the past, while - -.Dl sdep -t -w "%A" <events.txt - -will print all lines whose date is in the future, showing only the day of the -week and the text. - -.Dl sdep -f "1999-01-01 00:00" -t "1999-12-31 23:59" -w "" <events.txt - -will show only the -.Ar text -of all lines with a date in 1999. You can specify a different format for the -dates, for example - -.Dl sdep +"%m/%d/%Y %I:%M%p" -t "12/31/2020 11:59pm" -w "" <events.txt - -will match all dates from December 31st, 2020, one minute before midnight -(included). Note: this only works if your locale has an am/pm format, see -.Xr date 1 . - -.Sh AUTHORS -.An Sebastiano Tronto Aq Mt sebastiano.tronto@gmail.com - -.Sh SOURCE CODE -Source code is available at -.Lk https://github.com/sebastianotronto/sdep - -.Sh SEE ALSO -.Xr date 1 , -.Xr strftime 3 , -.Xr strptime 3 diff --git a/sdep-0.2/sdep.c b/sdep-0.2/sdep.c @@ -1,277 +0,0 @@ -/* See LICENSE file for copyright and license details. */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -/* - * Maximum number of characters in a line. The rest will be truncated. - * Change this if you need very long lines. - */ -#define MAXLEN 10000 - -/* - * Default date format. Anything that strftime(3) understands works, but - * it should determine a date completely up to the minute. - */ -static char *default_format = "%Y-%m-%d %H:%M"; - - -typedef struct Event Event; -typedef struct EventList EventList; -typedef struct EventNode EventNode; -typedef struct Options Options; - -struct Event{ - struct tm time; - char *text; -}; - -struct EventList { - EventNode *first; - EventNode *last; - int count; -}; - -struct EventNode { - Event ev; - EventNode *next; -}; - -struct Options { - struct tm from; - struct tm to; - char *format_in; - char *format_out; - char *separator; -}; - -static void add_event(struct tm, char *, EventList *); -static int compare_tm(const void *, const void *); -static int compare_event(const void *, const void *); -static Options default_op(void); -static int events_in_range(EventList *, Options, Event *); -static char *format_line(Event, Options, char *); -static int is_space(char); -static void read_input(Options, EventList *); -static Options read_op(int, char *[]); -static void str_copy(char *, char *, int); -static char *str_trim(char *); -static void write_output(Options, Event *, int); - - -static void -add_event(struct tm t, char *text, EventList *evlist) -{ - size_t l = strlen(text)+1; - EventNode *next = malloc(sizeof(EventNode)); - - next->ev.time = t; - next->ev.text = malloc(l); - str_copy(next->ev.text, text, l); - next->ev.text = str_trim(next->ev.text); - next->next = NULL; - - if (++evlist->count == 1) { - evlist->first = next; - evlist->last = next; - } else { - evlist->last->next = next; - evlist->last = next; - } -} - -static int -compare_tm(const void *v1, const void *v2) -{ - const struct tm *t1 = v1; - const struct tm *t2 = v2; - - if (t1->tm_year != t2->tm_year) - return t1->tm_year - t2->tm_year; - if (t1->tm_mon != t2->tm_mon) - return t1->tm_mon - t2->tm_mon; - if (t1->tm_mday != t2->tm_mday) - return t1->tm_mday - t2->tm_mday; - if (t1->tm_hour != t2->tm_hour) - return t1->tm_hour - t2->tm_hour; - return t1->tm_min - t2->tm_min; -} - -static int -compare_event(const void *v1, const void *v2) -{ - const Event *ev1 = v1; - const Event *ev2 = v2; - - return compare_tm(&ev1->time, &ev2->time); -} - -static Options -default_op(void) -{ - Options op; - time_t t_now = time(NULL); - struct tm *now = localtime(&t_now); - - op.format_in = malloc(MAXLEN); - op.format_out = malloc(MAXLEN); - op.separator = malloc(MAXLEN); - strcpy(op.format_in, default_format); - strcpy(op.format_out, default_format); - strcpy(op.separator, "\t"); - op.from = *now; - op.to = *now; - - return op; -} - -/* - * Saves the events in ev[] that happen between op->from and op->to in sel[] - * sorted by date and returns their number. - */ -static int -events_in_range(EventList *evlist, Options op, Event *sel) -{ - EventNode *i; - int n = 0; - - for (i = evlist->first; i != NULL; i = i->next) - if (compare_tm(&i->ev.time, &op.from) >= 0 && - compare_tm(&i->ev.time, &op.to) <= 0) - sel[n++] = i->ev; - - qsort(sel, n, sizeof(Event), compare_event); - - return n; -} - -static char * -format_line(Event ev, Options op, char *out) -{ - size_t l; - - strftime(out, MAXLEN, op.format_out, &ev.time); - l = strlen(out); - - str_copy(out+l, op.separator, MAXLEN - l); - l = strlen(out); - - str_copy(out+l, ev.text, MAXLEN - l); - - return out; -} - -static int -is_space(char c) -{ - return c == ' ' || c == '\t'; -} - -static void -read_input(Options op, EventList *evlist) -{ - struct tm t; - char line[MAXLEN], *text_ptr; - - while (fgets(line, MAXLEN, stdin) != NULL) - if ((text_ptr = strptime(line, op.format_in, &t)) != NULL) - add_event(t, text_ptr, evlist); -} - -static Options -read_op(int argc, char *argv[]) -{ - Options op = default_op(); - int i; - - /* Check for format specification. - * This changes the way other options are read */ - for (i = 1; i < argc; i++) { - if (argv[i][0] == '+') { - str_copy(op.format_in, &argv[i][1], MAXLEN); - str_copy(op.format_out, &argv[i][1], MAXLEN); - } - } - - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-v")) { - puts("sdep-"VERSION); - exit(0); - } else if (!strcmp(argv[i], "-d")) { - puts(default_format); - exit(0); - } else if (!strcmp(argv[i], "-s")) { - str_copy(op.separator, argv[++i], MAXLEN); - } else if (!strcmp(argv[i], "-f")) { - if (i+1 >= argc || - strptime(argv[i+1], op.format_in, &op.from) == NULL) - op.from.tm_year = -1000000000; /* Very large number */ - else - i++; - } else if (!strcmp(argv[i], "-t")) { - if (i+1 >= argc || - strptime(argv[i+1], op.format_in, &op.to) == NULL) - op.to.tm_year = 1000000000; /* Very small number */ - else - i++; - } else if (!strcmp(argv[i], "-w")) { - op.format_out = argv[++i]; - } else if (argv[i][0] != '+' && strlen(argv[i]) != 0) { - fprintf(stderr, "usage: sdep [-dv]"); - fprintf(stderr, " [+format] [-f [date]] [-t [date]]"); - fprintf(stderr, " [-w format] [-s string]\n"); - exit(1); - } - } - - return op; -} - -/* - * Copy up to n characters of the string str to dst and append '\0'. - * Suggested by NRK. - */ -static void -str_copy(char *dst, char *src, int n) -{ - if (memccpy(dst, src, '\0', n) == NULL) - dst[n-1] = '\0'; -} - -static char * -str_trim(char *t) -{ - char *s; - - for (s = &t[strlen(t)-1]; s != t && is_space(*s); *s = '\0', s--); - for (; *t != '\0' && is_space(*t); t++); - - return t; -} - -static void -write_output(Options op, Event *ev, int n) -{ - char outline[MAXLEN]; - int i; - - for (i = 0; i < n; i++) - printf("%s\n", format_line(ev[i], op, outline)); -} - -int -main(int argc, char *argv[]) -{ - Options op; - EventList evlist = {0}; - Event *selected; - - op = read_op(argc, argv); - read_input(op, &evlist); - selected = malloc(sizeof(Event) * evlist.count); - write_output(op, selected, events_in_range(&evlist, op, selected)); - - return 0; -}