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 }