sdep

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

sdep.c (6022B)


      1 /* See LICENSE file for copyright and license details. */
      2 
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 #include <time.h>
      7 
      8 /*
      9  * Maximum number of characters in a line. The rest will be truncated.
     10  * Change this if you need very long lines.
     11  */
     12 #define MAXLEN 10000
     13 
     14 /*
     15  * Default date format. Anything that strftime(3) understands works, but 
     16  * it should determine a date completely up to the minute.
     17  */
     18 static char *default_format = "%Y-%m-%d %H:%M";
     19 
     20 
     21 typedef struct Event Event;
     22 typedef struct EventList EventList;
     23 typedef struct EventNode EventNode;
     24 typedef struct Options Options;
     25 
     26 struct Event{
     27 	struct tm time;
     28 	char *text;
     29 };
     30 
     31 struct EventList {
     32 	EventNode *first;
     33 	EventNode *last;
     34 	int count;
     35 };
     36 
     37 struct EventNode {
     38 	Event ev;
     39 	EventNode *next;
     40 };
     41 
     42 struct Options {
     43 	struct tm from;
     44 	struct tm to;
     45 	char *format_in;
     46 	char *format_out;
     47 	char *separator;
     48 };
     49 
     50 static void add_event(struct tm, char *, EventList *);
     51 static int compare_tm(const void *, const void *);
     52 static int compare_event(const void *, const void *);
     53 static Options default_op(void);
     54 static int events_in_range(EventList *, Options, Event *);
     55 static char *format_line(Event, Options, char *);
     56 static int is_space(char);
     57 static void read_input(Options, EventList *);
     58 static Options read_op(int, char *[]);
     59 static void str_copy(char *, char *, int);
     60 static char *str_trim(char *);
     61 static void write_output(Options, Event *, int);
     62 
     63 
     64 static void
     65 add_event(struct tm t, char *text, EventList *evlist)
     66 {
     67 	size_t l = strlen(text)+1;
     68 	EventNode *next = malloc(sizeof(EventNode));
     69 
     70 	next->ev.time = t;
     71 	next->ev.text = malloc(l);
     72 	str_copy(next->ev.text, text, l);
     73 	next->ev.text = str_trim(next->ev.text);
     74 	next->next = NULL;
     75 
     76 	if (++evlist->count == 1) {
     77 		evlist->first = next;
     78 		evlist->last  = next;
     79 	} else {
     80 		evlist->last->next = next;
     81 		evlist->last       = next;
     82 	}
     83 }
     84 
     85 static int
     86 compare_tm(const void *v1, const void *v2)
     87 {
     88 	const struct tm *t1 = v1;
     89 	const struct tm *t2 = v2;
     90 
     91 	if (t1->tm_year != t2->tm_year)
     92 		return t1->tm_year - t2->tm_year;
     93 	if (t1->tm_mon != t2->tm_mon)
     94 		return t1->tm_mon - t2->tm_mon;
     95 	if (t1->tm_mday != t2->tm_mday)
     96 		return t1->tm_mday - t2->tm_mday;
     97 	if (t1->tm_hour != t2->tm_hour)
     98 		return t1->tm_hour - t2->tm_hour;
     99 	return t1->tm_min - t2->tm_min;
    100 }
    101 
    102 static int
    103 compare_event(const void *v1, const void *v2)
    104 {
    105 	const Event *ev1 = v1;
    106 	const Event *ev2 = v2;
    107 
    108 	return compare_tm(&ev1->time, &ev2->time);
    109 }
    110 
    111 static Options
    112 default_op(void)
    113 {
    114 	Options op;
    115 	time_t t_now = time(NULL);
    116 	struct tm *now = localtime(&t_now);
    117 
    118 	op.format_in  = malloc(MAXLEN);
    119 	op.format_out = malloc(MAXLEN);
    120 	op.separator  = malloc(MAXLEN);
    121 	strcpy(op.format_in,  default_format);
    122 	strcpy(op.format_out, default_format);
    123 	strcpy(op.separator,  "\t");
    124 	op.from = *now;
    125 	op.to   = *now;
    126 
    127 	return op;
    128 }
    129 
    130 /*
    131  * Saves the events in ev[] that happen between op->from and op->to in sel[]
    132  * sorted by date and returns their number.
    133  */
    134 static int
    135 events_in_range(EventList *evlist, Options op, Event *sel)
    136 {
    137 	EventNode *i;
    138 	int n = 0;
    139 
    140 	for (i = evlist->first; i != NULL; i = i->next)
    141 		if (compare_tm(&i->ev.time, &op.from) >= 0 &&
    142 		    compare_tm(&i->ev.time, &op.to) <= 0)
    143 			sel[n++] = i->ev;
    144 
    145 	qsort(sel, n, sizeof(Event), compare_event);
    146 
    147 	return n;
    148 }
    149 
    150 static char *
    151 format_line(Event ev, Options op, char *out)
    152 {
    153 	size_t l;
    154 
    155 	strftime(out, MAXLEN, op.format_out, &ev.time);
    156 	l = strlen(out);
    157 
    158 	str_copy(out+l, op.separator, MAXLEN - l);
    159 	l = strlen(out);
    160 
    161 	str_copy(out+l, ev.text, MAXLEN - l);
    162 
    163 	return out;
    164 }
    165 
    166 static int
    167 is_space(char c)
    168 {
    169 	return c == ' '  || c == '\f' || c == '\n' ||
    170 	       c == '\r' || c == '\t' || c == '\v';
    171 }
    172 
    173 static void
    174 read_input(Options op, EventList *evlist)
    175 {
    176 	struct tm t;
    177 	char line[MAXLEN], *text_ptr;
    178 
    179 	while (fgets(line, MAXLEN, stdin) != NULL)
    180 		if ((text_ptr = strptime(line, op.format_in, &t)) != NULL)
    181 			add_event(t, text_ptr, evlist);
    182 }
    183 
    184 static Options
    185 read_op(int argc, char *argv[])
    186 {
    187 	Options op = default_op();
    188 	int i;
    189 
    190 	/* Check for format specification.
    191 	 * This changes the way other options are read */
    192 	for (i = 1; i < argc; i++) {
    193 		if (argv[i][0] == '+') {
    194 			str_copy(op.format_in,  &argv[i][1], MAXLEN);
    195 			str_copy(op.format_out, &argv[i][1], MAXLEN);
    196 		}
    197 	}
    198 
    199 	for (i = 1; i < argc; i++) {
    200 		if        (!strcmp(argv[i], "-v")) {
    201 			puts("sdep-"VERSION);
    202 			exit(0);
    203 		} else if (!strcmp(argv[i], "-d")) {
    204 			puts(default_format);
    205 			exit(0);
    206 		} else if (!strcmp(argv[i], "-s")) {
    207 			str_copy(op.separator, argv[++i], MAXLEN);
    208 		} else if (!strcmp(argv[i], "-f")) {
    209 			if (i+1 >= argc ||
    210 			    strptime(argv[i+1], op.format_in, &op.from) == NULL)
    211 				op.from.tm_year = -1000000000; /* Very large number */
    212 			else
    213 				i++;
    214 		} else if (!strcmp(argv[i], "-t")) {
    215 			if (i+1 >= argc ||
    216 			    strptime(argv[i+1], op.format_in, &op.to) == NULL)
    217 				op.to.tm_year = 1000000000; /* Very small number */
    218 			else
    219 				i++;
    220 		} else if (!strcmp(argv[i], "-w")) {
    221 			op.format_out = argv[++i];
    222 		} else if (argv[i][0] != '+' && strlen(argv[i]) != 0) {
    223 			fprintf(stderr, "usage: sdep [-dv]");
    224 			fprintf(stderr, " [+format] [-f [date]] [-t [date]]");
    225 			fprintf(stderr, " [-w format] [-s string]\n");
    226 			exit(1);
    227 		}
    228 	}
    229 
    230 	return op;
    231 }
    232 
    233 /*
    234  * Copy up to n characters of the string str to dst and append '\0'.
    235  * Suggested by NRK.
    236  */
    237 static void
    238 str_copy(char *dst, char *src, int n)
    239 {
    240 	if (memccpy(dst, src, '\0', n) == NULL)
    241 		dst[n-1] = '\0';
    242 }
    243 
    244 static char *
    245 str_trim(char *t)
    246 {
    247 	char *s;
    248 
    249 	for (s = &t[strlen(t)-1]; s != t && is_space(*s); *s = '\0', s--);
    250 	for (; *t != '\0' && is_space(*t); t++);
    251 
    252 	return t;
    253 }
    254 
    255 static void
    256 write_output(Options op, Event *ev, int n)
    257 {
    258 	char outline[MAXLEN];
    259 	int i;
    260 
    261 	for (i = 0; i < n; i++)
    262 		printf("%s\n", format_line(ev[i], op, outline));
    263 }
    264 
    265 int
    266 main(int argc, char *argv[])
    267 {
    268 	Options op;
    269 	EventList evlist = {0};
    270 	Event *selected;
    271 
    272 	op = read_op(argc, argv);
    273 	read_input(op, &evlist);
    274 	selected = malloc(sizeof(Event) * evlist.count);
    275 	write_output(op, selected, events_in_range(&evlist, op, selected));
    276 
    277 	return 0;
    278 }