commit 3a33fa3c7921c69efbc9beaaa922452ba2eaf271
parent 022d9c173d78c04ec2569a2394e2b04b81d60794
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date: Mon, 15 May 2023 18:28:45 +0200
Makefile fix
Diffstat:
5 files changed, 393 insertions(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
@@ -32,7 +32,7 @@ clean:
dist: clean
mkdir -p sdep-${VERSION}
- cp -R LICENSE Makefile README sdep.1 sdep.c sdep-${VERSION}
+ cp -R LICENSE Makefile README.md sdep.1 sdep.c sdep-${VERSION}
tar -cf sdep-${VERSION}.tar sdep-${VERSION}
gzip sdep-${VERSION}.tar
rm -rf sdep-${VERSION}
diff --git a/sdep-0.2/LICENSE b/sdep-0.2/LICENSE
@@ -0,0 +1,21 @@
+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/Makefile b/sdep-0.2/Makefile
diff --git a/sdep-0.2/sdep.1 b/sdep-0.2/sdep.1
@@ -0,0 +1,94 @@
+.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
@@ -0,0 +1,277 @@
+/* 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;
+}