bday
diff bday.c @ 16:79d22407a6be
a lot of refactoring
author | markus schnalke <meillo@marmaro.de> |
---|---|
date | Mon, 24 Feb 2014 21:11:38 +0100 |
parents | 032af48d590b |
children | d18a3b2b76bd |
line diff
1.1 --- a/bday.c Mon Feb 24 17:46:57 2014 +0100 1.2 +++ b/bday.c Mon Feb 24 21:11:38 2014 +0100 1.3 @@ -26,21 +26,21 @@ 1.4 Input is read through standard input. For example: bday < ~/.birthdays 1.5 The input (file) has to have the following format: 1.6 1.7 -text=date flags 1.8 +date flags text 1.9 1.10 where: 1.11 - date is yyyy-mm-dd 1.12 + date is YYYY-MM-DD 1.13 flags is ONE or ZERO of 1.14 - bd for a birthday (default) 1.15 - ann for an anniversary 1.16 - ev for an event 1.17 + #ann for an anniversary 1.18 + #ev for an event 1.19 and zero or more of 1.20 - w <n> to set the warn-in-advance time to n days 1.21 + #w<n> to set the warn-in-advance time to n days 1.22 (don't include the brackets! :) 1.23 - to <date> 1.24 - for <days> 1.25 + #to<date> 1.26 + #for<days> 1.27 to specify the length of time taken by an 1.28 - event, for example a holiday. 1.29 + event, for example a holiday 1.30 + separated by spaces. 1.31 1.32 Lines preceeded by # are treated as comments. 1.33 1.34 @@ -69,16 +69,8 @@ 1.35 /* ========== Global constants and data types */ 1.36 1.37 1.38 -/* month lengths etc */ 1.39 -#define isleapyear(y) ((y)%4==0 && ((y)%100 != 0 || (y)%400 == 0)) 1.40 -const unsigned MLENDAT[]; 1.41 -#define mlen(m,y) (MLENDAT[(m)-1] != -1 ? MLENDAT[(m)-1] : (isleapyear((y)) ? 29 : 28)) 1.42 -#define before(a,b) ((a).month < (b).month || ((a).month == (b).month && (a).day < (b).day)) 1.43 -#define ydelta(a,b) ((int) (b).year - (a).year + before((a),(b))) 1.44 - 1.45 /* -------- modifier flags */ 1.46 #define F_MTYPE 0x07 1.47 -#define F_TBIRTHDAY 1 1.48 #define F_TANNIVERSARY 2 1.49 #define F_TEVENT 3 1.50 1.51 @@ -88,8 +80,10 @@ 1.52 #define F_FORDAYS 0x16 1.53 #define F_TODATE 0x24 1.54 1.55 -struct _ftable {char* txt; unsigned flag;}; 1.56 - 1.57 +struct _ftable { 1.58 + char* txt; 1.59 + unsigned flag; 1.60 +}; 1.61 const struct _ftable FTABLE[]; 1.62 1.63 struct date { 1.64 @@ -105,33 +99,29 @@ 1.65 int warn; 1.66 }; 1.67 1.68 -typedef int (*prnfunc)(const char *); 1.69 - 1.70 /* ========== Global Variables */ 1.71 1.72 struct event *readlist(void); 1.73 void gettoday(void); 1.74 unsigned delta(struct date *); 1.75 unsigned ddiff(struct date *D1, struct date *D2); 1.76 -void liststrings(struct event *evl, prnfunc outf); 1.77 +void liststrings(struct event *evl); 1.78 char *tdelta(struct date *d); 1.79 char *ttime(int yr, int mn, int wk, int dy); 1.80 -int skptok(int j, char *ptr); 1.81 +char *skptok(char *ptr); 1.82 int evcmp(const void *e1, const void *e2); 1.83 1.84 1.85 struct date today; 1.86 -int iDWarn = DEF_WARN; 1.87 +int def_warn = DEF_WARN; 1.88 1.89 -const unsigned MLENDAT[] = {31,-1,31,30,31,30,31,31,30,31,30,31}; 1.90 1.91 const struct _ftable FTABLE[] = { 1.92 - {"bd", F_TBIRTHDAY}, 1.93 - {"ann",F_TANNIVERSARY}, 1.94 - {"ev", F_TEVENT}, 1.95 - {"w", F_WTIME_P}, 1.96 - {"to", F_TODATE}, 1.97 - {"for", F_FORDAYS}, 1.98 + {"#ann",F_TANNIVERSARY}, 1.99 + {"#ev", F_TEVENT}, 1.100 + {"#w", F_WTIME_P}, 1.101 + {"#to", F_TODATE}, 1.102 + {"#for", F_FORDAYS}, 1.103 {NULL, 0} 1.104 }; 1.105 1.106 @@ -174,18 +164,60 @@ 1.107 1.108 /* 1.109 like strcat(), but lets the buffer automagically grow :-) 1.110 -(needs local variable "size" with the buffer size) 1.111 */ 1.112 -#define append(where, what) do { \ 1.113 - if (strlen(what) > (size - strlen(where))) { \ 1.114 - xrealloc(where, size + 128 + strlen(what)); \ 1.115 - size += 128 + strlen(what); \ 1.116 - } \ 1.117 - strcat(where, what); \ 1.118 -} while(0) 1.119 +int 1.120 +append(char *where, int size, char *what) 1.121 +{ 1.122 + if (strlen(what) > ((size) - strlen(where))) { 1.123 + xrealloc(where, (size) + 128 + strlen(what)); 1.124 + size += 128 + strlen(what); 1.125 + } 1.126 + strcat(where, what); 1.127 + return size; 1.128 +} 1.129 1.130 /* ========== */ 1.131 1.132 + 1.133 +int 1.134 +before(struct date a, struct date b) 1.135 +{ 1.136 + if (a.month < b.month) { 1.137 + return 1; 1.138 + } else if (a.month == b.month && a.day < b.day) { 1.139 + return 1; 1.140 + } else { 1.141 + return 0; 1.142 + } 1.143 +} 1.144 + 1.145 +int 1.146 +ydelta(struct date a, struct date b) 1.147 +{ 1.148 + return b.year - a.year + before(a, b); 1.149 +} 1.150 + 1.151 +/* 1.152 +returns the length of the given month 1.153 +*/ 1.154 +int 1.155 +mlen(int month, int year) 1.156 +{ 1.157 + unsigned mlendat[] = {31,0,31,30,31,30,31,31,30,31,30,31}; 1.158 + 1.159 + if (mlendat[month - 1]) { 1.160 + return mlendat[month - 1]; 1.161 + } else { 1.162 + if (year%4==0 && (year%100!=0 || year%400==0)) { 1.163 + return 29; 1.164 + } else { 1.165 + return 28; 1.166 + } 1.167 + } 1.168 +} 1.169 + 1.170 + 1.171 + 1.172 /* 1.173 returns delta(d) in days, weeks, months, etc 1.174 the returned buffer is malloc()ed, do not forget to free() it 1.175 @@ -201,10 +233,10 @@ 1.176 *buf = 0; 1.177 switch (delta(d)) { 1.178 case 0: 1.179 - append(buf, "today"); 1.180 + size = append(buf, size, "TODAY"); 1.181 return buf; 1.182 case 1: 1.183 - append(buf, "tomorrow"); 1.184 + size = append(buf, size, "Tomorrow"); 1.185 return buf; 1.186 default: 1.187 /* like delta(), we ignore the year */ 1.188 @@ -224,9 +256,9 @@ 1.189 wk = (dy / 7); 1.190 dy %= 7; 1.191 1.192 - append(buf, "in "); 1.193 + size = append(buf, size, "In "); 1.194 tmp = ttime(yr, mn, wk, dy); 1.195 - append(buf, tmp); 1.196 + size = append(buf, size, tmp); 1.197 free(tmp); 1.198 1.199 return buf; 1.200 @@ -237,66 +269,46 @@ 1.201 1.202 1.203 1.204 -/* 1.205 void 1.206 -donum(n,txt) 1.207 +donum(char *buf, int size, int n, char *txt, int *terms) 1.208 { 1.209 - if (n > 0) { 1.210 - snprintf(tmp, sizeof(tmp), "%d", n); 1.211 - append(buf, tmp); 1.212 - append(buf, " " txt); 1.213 - if (n != 1) 1.214 - append(buf, "s"); 1.215 - terms--; 1.216 - if (orgterms > 1) { 1.217 - if (terms == 1) 1.218 - append(buf, " and "); 1.219 - else if (terms > 1) 1.220 - append(buf, ", "); 1.221 - } 1.222 + char tmp[128]; 1.223 + 1.224 + if (n <= 0) { 1.225 + return; 1.226 + } 1.227 + snprintf(tmp, sizeof(tmp), "%d", n); 1.228 + size = append(buf, size, tmp); 1.229 + size = append(buf, size, " "); 1.230 + size = append(buf, size, txt); 1.231 + if (n != 1) { 1.232 + size = append(buf, size, "s"); 1.233 + } 1.234 + if (--*terms == 1) { 1.235 + size = append(buf, size, " and "); 1.236 + } else if (*terms > 1) { 1.237 + size = append(buf, size, ", "); 1.238 } 1.239 } 1.240 -*/ 1.241 - 1.242 - 1.243 -#define donum(n,txt) do { \ 1.244 - if (n > 0) { \ 1.245 - snprintf(tmp, sizeof(tmp), "%d", n); \ 1.246 - append(buf, tmp); \ 1.247 - append(buf, " " txt); \ 1.248 - if (n != 1) \ 1.249 - append(buf, "s"); \ 1.250 - terms--; \ 1.251 - if (orgterms > 1) { \ 1.252 - if (terms == 1) \ 1.253 - append(buf, " and "); \ 1.254 - else if (terms > 1) \ 1.255 - append(buf, ", "); \ 1.256 - } \ 1.257 - } \ 1.258 -} while(0) 1.259 1.260 1.261 /* returns allocated buffer, don't forget to free() */ 1.262 char * 1.263 ttime(int yr, int mn, int wk, int dy) 1.264 { 1.265 - char *buf = xmalloc(128); 1.266 int size = 128; 1.267 - int terms, orgterms; 1.268 - char tmp[128]; 1.269 + char *buf = xmalloc(size); 1.270 + int terms = (yr!=0) + (mn!=0) + (wk!=0) + (dy!=0); 1.271 1.272 - *buf = 0; /* Initialize buffer */ 1.273 - terms = orgterms = (yr!=0) + (mn!=0) + (wk!=0) + (dy!=0); 1.274 + *buf = '\0'; /* Initialize buffer */ 1.275 1.276 - donum(yr, "year"); 1.277 - donum(mn, "month"); 1.278 - donum(wk, "week"); 1.279 - donum(dy, "day"); 1.280 + donum(buf, size, yr, "year", &terms); 1.281 + donum(buf, size, mn, "month", &terms); 1.282 + donum(buf, size, wk, "week", &terms); 1.283 + donum(buf, size, dy, "day", &terms); 1.284 1.285 return buf; 1.286 } 1.287 -#undef donum 1.288 1.289 1.290 1.291 @@ -308,58 +320,60 @@ 1.292 the string to a function. 1.293 */ 1.294 void 1.295 -liststrings(struct event *evl, prnfunc outf) 1.296 +liststrings(struct event *evl) 1.297 { 1.298 int i,j; 1.299 char *buf, *tmp; 1.300 int size; 1.301 1.302 - for (i = 0; evl[i].text != NULL; i++) { 1.303 - buf = xmalloc(128); 1.304 + for (i=0; evl[i].text; i++) { 1.305 + size = 128; 1.306 + buf = xmalloc(size); 1.307 *buf = '\0'; 1.308 - size = 128; 1.309 1.310 if (evl[i].warn == -1 && delta(&(evl[i].date))==0) { 1.311 - append(buf, evl[i].text); 1.312 + size = append(buf, size, evl[i].text); 1.313 } else if (evl[i].enddate.day == 0) { 1.314 + 1.315 if (delta(&(evl[i].date)) <= evl[i].warn) { 1.316 - append(buf, evl[i].text); 1.317 - append(buf, " "); 1.318 tmp = tdelta(&(evl[i].date)); 1.319 - append(buf, tmp); 1.320 + size = append(buf, size, tmp); 1.321 + size = append(buf, size, ": "); 1.322 + size = append(buf, size, evl[i].text); 1.323 free(tmp); 1.324 } 1.325 } else { 1.326 if (delta(&(evl[i].date)) <= evl[i].warn) { 1.327 - append(buf, evl[i].text); 1.328 - append(buf, " for "); 1.329 - /* +1 because, if the difference between two dates is one day, 1.330 - then the length of an event on those days is two days */ 1.331 + size = append(buf, size, evl[i].text); 1.332 + size = append(buf, size, " for "); 1.333 + /* +1 because, if the difference between 1.334 + two dates is one day, then the length of 1.335 + an event on those days is two days */ 1.336 j = ddiff(&(evl[i].date),&(evl[i].enddate)) + 1; 1.337 tmp = ttime(0, 0, j/7, j%7); 1.338 - append(buf, tmp); 1.339 + size = append(buf, size, tmp); 1.340 free(tmp); 1.341 - append(buf, " "); 1.342 + size = append(buf, size, " "); 1.343 tmp = tdelta(&(evl[i].date)); 1.344 - append(buf, tmp); 1.345 + size = append(buf, size, tmp); 1.346 } else if (delta(&(evl[i].enddate)) <= evl[i].warn) { 1.347 - append(buf, evl[i].text); 1.348 - append(buf, " "); 1.349 + size = append(buf, size, evl[i].text); 1.350 + size = append(buf, size, " "); 1.351 j = delta(&(evl[i].enddate)); 1.352 if (j) { 1.353 - append(buf, "for "); 1.354 + size = append(buf, size, "for "); 1.355 tmp = ttime(0, 0, j/7, j%7); 1.356 - append(buf, tmp); 1.357 + size = append(buf, size, tmp); 1.358 free(tmp); 1.359 - append(buf, " longer"); 1.360 + size = append(buf, size, " longer"); 1.361 } else { 1.362 - append(buf, "finishes today"); 1.363 + size = append(buf, size, "finishes today"); 1.364 } 1.365 } 1.366 } 1.367 if (*buf) { 1.368 - append(buf, "."); 1.369 - outf(buf); 1.370 + size = append(buf, size, "."); 1.371 + puts(buf); 1.372 } 1.373 free(buf); 1.374 } 1.375 @@ -534,7 +548,7 @@ 1.376 int i, j, k, l, d; 1.377 struct event *evl; 1.378 char buf[1024], buf2[1024]; 1.379 - char *ptr; 1.380 + char *ptr, *cp; 1.381 unsigned flags; 1.382 1.383 /* initialise */ 1.384 @@ -550,46 +564,48 @@ 1.385 } 1.386 1.387 /* parse string in buf */ 1.388 - ptr = strrchr(buf, '='); /* allow '=' in text */ 1.389 + 1.390 + ptr = strchr(buf, ' '); /* start of text */ 1.391 1.392 /* not a valid line, so ignore it! Cool, huh? */ 1.393 /* Attention: only recognizes lines without '=' */ 1.394 - if (ptr == NULL) { 1.395 - fprintf(stderr, "WARNING: Invalid line in input:\n%s", buf); 1.396 + if (!ptr) { 1.397 + fprintf(stderr, "WARNING: Invalid input line:\n\t%s", buf); 1.398 i--; 1.399 continue; 1.400 } 1.401 1.402 - *(ptr++) = 0; 1.403 + *(ptr++) = '\0'; 1.404 + ptr[strlen(ptr)-1] = '\0'; 1.405 1.406 - j = sscanf(ptr, "%u-%u-%u", &(evl[i].date.year), 1.407 + j = sscanf(buf, "%u-%u-%u", &(evl[i].date.year), 1.408 &(evl[i].date.month), &(evl[i].date.day)); 1.409 - /* ... unless it wasn't read, in which case set it to zero */ 1.410 - if (j==2) { 1.411 - evl[i].date.year = 0; 1.412 + if (j != 3) { 1.413 + fprintf(stderr, "Error: Invalid date:\t%s\n", buf); 1.414 + i--; 1.415 + continue; 1.416 } 1.417 1.418 - 1.419 /* parse flags */ 1.420 1.421 - evl[i].warn = iDWarn; 1.422 + evl[i].warn = def_warn; 1.423 evl[i].enddate.day = 0; 1.424 evl[i].enddate.month = 0; 1.425 evl[i].enddate.year = 0; 1.426 1.427 flags = 0; 1.428 j = 0; 1.429 - 1.430 - while(j = skptok(j, ptr), ptr[j] != 0) { 1.431 - for (k = 0; FTABLE[k].txt != NULL && strncmp(FTABLE[k].txt, ptr + j, strlen(FTABLE[k].txt)); k++) { 1.432 + cp = skptok(ptr); 1.433 + for (cp=ptr; *cp && *cp=='#'; cp=skptok(cp)) { 1.434 + for (k = 0; FTABLE[k].txt && strncmp(FTABLE[k].txt, cp, strlen(FTABLE[k].txt)); k++) { 1.435 } 1.436 1.437 switch (FTABLE[k].flag) { 1.438 - case F_WTIME_P: /* w <n> -- sets warning time */ 1.439 - sscanf(ptr + j, "w %u", &(evl[i].warn)); 1.440 + case F_WTIME_P: /* #w<n> -- sets warning time */ 1.441 + sscanf(cp, "#w%u", &(evl[i].warn)); 1.442 break; 1.443 - case F_FORDAYS: /* for <days> -- sets the duration of the event */ 1.444 - sscanf(ptr + j, "for %u", &d); 1.445 + case F_FORDAYS: /* #for<days> -- sets the duration of the event */ 1.446 + sscanf(cp, "#for%u", &d); 1.447 evl[i].enddate=evl[i].date; 1.448 for (l = 1; l < d; l++) { 1.449 evl[i].enddate.day++; 1.450 @@ -603,8 +619,8 @@ 1.451 } 1.452 } 1.453 break; 1.454 - case F_TODATE: /* to <date> -- sets the end date of the event */ 1.455 - l = sscanf(ptr + j, "to %u-%u-%u", &(evl[i].enddate.year), &(evl[i].enddate.month), &(evl[i].enddate.day)); 1.456 + case F_TODATE: /* #to<date> -- sets the end date of the event */ 1.457 + l = sscanf(cp, "#to%u-%u-%u", &(evl[i].enddate.year), &(evl[i].enddate.month), &(evl[i].enddate.day)); 1.458 if (l == 2) { 1.459 evl[i].enddate.year = 0; 1.460 } 1.461 @@ -621,34 +637,32 @@ 1.462 /* construct event text */ 1.463 1.464 switch(flags & F_MTYPE) { 1.465 - case F_TBIRTHDAY: 1.466 default: /* assume it's a birthday */ 1.467 - if (evl[i].date.year != 0) { 1.468 - int tmp_age = ydelta(evl[i].date, today); 1.469 - if (tmp_age != 1) { 1.470 - sprintf(buf2, "%s is %d years old", buf, tmp_age); 1.471 - } else { 1.472 - sprintf(buf2, "%s is %d year old", buf, tmp_age); 1.473 - } 1.474 - } else { 1.475 - sprintf(buf2, "%s has a birthday", buf); 1.476 + if (!evl[i].date.year) { 1.477 + sprintf(buf2, "%s has a birthday", cp); 1.478 + break; 1.479 } 1.480 + int tmp_age = ydelta(evl[i].date, today); 1.481 + sprintf(buf2, "%s is %d year%s old", 1.482 + cp, tmp_age, (tmp_age>1)?"s":""); 1.483 break; 1.484 case F_TANNIVERSARY: 1.485 - if (evl[i].date.year != 0) { 1.486 - sprintf(buf2, "%s %d years ago", buf, ydelta(evl[i].date, today)); 1.487 + if (evl[i].date.year) { 1.488 + sprintf(buf2, "%s %d years ago", 1.489 + cp, ydelta(evl[i].date, today)); 1.490 } else { 1.491 - strcpy(buf2, buf); 1.492 + strcpy(buf2, cp); 1.493 } 1.494 break; 1.495 case F_TEVENT: 1.496 - /* if a year was specified, and this warning isn't for it, ignore! */ 1.497 - if ((evl[i].date.year != 0 && ydelta(evl[i].date, today) != 0) 1.498 - && (evl[i].enddate.year == 0 || ydelta(evl[i].enddate, today) != 0)) { 1.499 + /* if a year was specified, and this 1.500 + warning isn't for it, ignore! */ 1.501 + if ((evl[i].date.year && ydelta(evl[i].date, today)) 1.502 + && (!evl[i].enddate.year || ydelta(evl[i].enddate, today))) { 1.503 i--; 1.504 continue; 1.505 } 1.506 - strcpy(buf2, buf); 1.507 + strcpy(buf2, cp); 1.508 break; 1.509 } 1.510 evl[i].text = strdup(buf2); 1.511 @@ -671,13 +685,16 @@ 1.512 1.513 1.514 1.515 -int 1.516 -skptok(int j, char *ptr) 1.517 +char * 1.518 +skptok(char *ptr) 1.519 { 1.520 - for (; ptr[j] != 0 && ptr[j] != ' ' && ptr[j] != '\t' ; j++); 1.521 - for (; ptr[j] != 0 && (ptr[j] == ' ' || ptr[j] == '\t'); j++); 1.522 - 1.523 - return j; 1.524 + while (*ptr && (*ptr!=' ' && *ptr!='\t')) { 1.525 + ptr++; 1.526 + } 1.527 + while (*ptr && (*ptr==' ' || *ptr=='\t')) { 1.528 + ptr++; 1.529 + } 1.530 + return ptr; 1.531 } 1.532 1.533 1.534 @@ -688,18 +705,16 @@ 1.535 int 1.536 main(int argc, char *argv[]) 1.537 { 1.538 - while (--argc > 0 && (*++argv)[0] == '-') { 1.539 + while (--argc > 0 && **++argv == '-') { 1.540 if (strcmp(argv[0], "-W") == 0) { 1.541 /* TODO: catch if no value given */ 1.542 - iDWarn = atoi((++argv)[0]); 1.543 + def_warn = atoi((++argv)[0]); 1.544 argc--; 1.545 } else { 1.546 fprintf(stderr, "unknown option %s\n", argv[0]); 1.547 exit(1); 1.548 } 1.549 } 1.550 - 1.551 - liststrings(readlist(), puts); 1.552 - 1.553 + liststrings(readlist()); 1.554 return 0; 1.555 }