bday

annotate bday.c @ 17:d18a3b2b76bd

updated man page; s/-W/-w/
author markus schnalke <meillo@marmaro.de>
date Mon, 24 Feb 2014 21:22:21 +0100
parents 79d22407a6be
children c1cd1d444353
rev   line source
meillo@0 1 /*
meillo@15 2 bday -- Birthday/Anniversary reminder
meillo@0 3
meillo@15 4 (c) 2007,2014 markus schnalke <meillo@marmaro.de>
meillo@15 5 (c) 1994-1999 AS Mortimer
meillo@0 6
meillo@15 7 This program is free software; you can redistribute it and/or
meillo@15 8 modify it under the terms of the GNU General Public License as
meillo@15 9 published by the Free Software Foundation; either version 2 of the
meillo@15 10 License, or (at your option) any later version. You may also
meillo@15 11 distribute it under the Artistic License, as comes with Perl.
meillo@0 12
meillo@15 13 This program is distributed in the hope that it will be useful,
meillo@15 14 but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@15 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
meillo@0 16
meillo@15 17 You should have received a copy of the GNU General Public License
meillo@15 18 along with this program; if not, write to the Free Software
meillo@15 19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
meillo@0 20
meillo@15 21 You should also have recieved a copy of the Artistic license with
meillo@15 22 this program.
meillo@0 23
meillo@15 24 =====================================================================
meillo@9 25
meillo@15 26 Input is read through standard input. For example: bday < ~/.birthdays
meillo@15 27 The input (file) has to have the following format:
meillo@0 28
meillo@16 29 date flags text
meillo@7 30
meillo@15 31 where:
meillo@16 32 date is YYYY-MM-DD
meillo@15 33 flags is ONE or ZERO of
meillo@16 34 #ann for an anniversary
meillo@16 35 #ev for an event
meillo@15 36 and zero or more of
meillo@16 37 #w<n> to set the warn-in-advance time to n days
meillo@15 38 (don't include the brackets! :)
meillo@16 39 #to<date>
meillo@16 40 #for<days>
meillo@15 41 to specify the length of time taken by an
meillo@16 42 event, for example a holiday
meillo@16 43 separated by spaces.
meillo@7 44
meillo@15 45 Lines preceeded by # are treated as comments.
meillo@7 46
meillo@15 47 Note: If you deviate from this format, I cannot guarantee anything about
meillo@15 48 it's behaviour. In most cases, it will just quietly ignore the
meillo@15 49 error, which probably isn't ideal behaviour. Oh, well.
meillo@7 50
meillo@15 51 =====================================================================
meillo@8 52 */
meillo@7 53
meillo@8 54
meillo@8 55 /* standard time to warn in advance, when no explicit w flag is given. */
meillo@8 56 #define DEF_WARN 14
meillo@0 57
meillo@7 58
meillo@7 59 #include <stdarg.h>
meillo@0 60 #include <stdio.h>
meillo@0 61 #include <stdlib.h>
meillo@7 62 #include <string.h>
meillo@7 63 #include <sys/types.h>
meillo@7 64 #include <time.h>
meillo@7 65 #include <unistd.h>
meillo@0 66
meillo@7 67
meillo@7 68
meillo@7 69 /* ========== Global constants and data types */
meillo@7 70
meillo@7 71
meillo@7 72 /* -------- modifier flags */
meillo@7 73 #define F_MTYPE 0x07
meillo@7 74 #define F_TANNIVERSARY 2
meillo@7 75 #define F_TEVENT 3
meillo@7 76
meillo@7 77 /* flags processed immediately on encountering */
meillo@7 78 #define F_MIMMEDIATE 0x24
meillo@7 79 #define F_WTIME_P 0x08
meillo@7 80 #define F_FORDAYS 0x16
meillo@7 81 #define F_TODATE 0x24
meillo@7 82
meillo@16 83 struct _ftable {
meillo@16 84 char* txt;
meillo@16 85 unsigned flag;
meillo@16 86 };
meillo@7 87 const struct _ftable FTABLE[];
meillo@7 88
meillo@7 89 struct date {
meillo@15 90 unsigned day;
meillo@15 91 unsigned month;
meillo@15 92 unsigned year;
meillo@7 93 };
meillo@7 94
meillo@7 95 struct event {
meillo@15 96 char* text;
meillo@15 97 struct date date;
meillo@15 98 struct date enddate;
meillo@15 99 int warn;
meillo@7 100 };
meillo@7 101
meillo@7 102 /* ========== Global Variables */
meillo@7 103
meillo@15 104 struct event *readlist(void);
meillo@7 105 void gettoday(void);
meillo@15 106 unsigned delta(struct date *);
meillo@15 107 unsigned ddiff(struct date *D1, struct date *D2);
meillo@16 108 void liststrings(struct event *evl);
meillo@15 109 char *tdelta(struct date *d);
meillo@15 110 char *ttime(int yr, int mn, int wk, int dy);
meillo@16 111 char *skptok(char *ptr);
meillo@15 112 int evcmp(const void *e1, const void *e2);
meillo@7 113
meillo@7 114
meillo@7 115 struct date today;
meillo@16 116 int def_warn = DEF_WARN;
meillo@7 117
meillo@7 118
meillo@7 119 const struct _ftable FTABLE[] = {
meillo@16 120 {"#ann",F_TANNIVERSARY},
meillo@16 121 {"#ev", F_TEVENT},
meillo@16 122 {"#w", F_WTIME_P},
meillo@16 123 {"#to", F_TODATE},
meillo@16 124 {"#for", F_FORDAYS},
meillo@7 125 {NULL, 0}
meillo@7 126 };
meillo@7 127
meillo@7 128
meillo@7 129
meillo@7 130
meillo@7 131
meillo@7 132
meillo@7 133 /*
meillo@15 134 xmalloc/xrealloc functions
meillo@15 135 Note: the x* functions are lifted straight from the GNU libc info docs
meillo@15 136 $Id: xmalloc.c,v 1.2 1999/01/16 17:08:59 andy Exp $
meillo@7 137 */
meillo@7 138
meillo@15 139 void *
meillo@15 140 xmalloc(size_t size)
meillo@15 141 {
meillo@15 142 register void *value = malloc (size);
meillo@7 143 if (value == 0) {
meillo@7 144 fprintf(stderr, "virtual memory exhausted\n");
meillo@7 145 exit(1);
meillo@7 146 }
meillo@7 147 return value;
meillo@7 148 }
meillo@7 149
meillo@15 150 void *
meillo@15 151 xrealloc(void *ptr, size_t size)
meillo@15 152 {
meillo@15 153 register void *value = realloc (ptr, size);
meillo@7 154 if (value == 0) {
meillo@7 155 fprintf(stderr, "virtual memory exhausted\n");
meillo@7 156 exit(1);
meillo@7 157 }
meillo@7 158 return value;
meillo@7 159 }
meillo@7 160
meillo@8 161
meillo@7 162 /* ========== */
meillo@7 163
meillo@7 164
meillo@15 165 /*
meillo@15 166 like strcat(), but lets the buffer automagically grow :-)
meillo@15 167 */
meillo@16 168 int
meillo@16 169 append(char *where, int size, char *what)
meillo@16 170 {
meillo@16 171 if (strlen(what) > ((size) - strlen(where))) {
meillo@16 172 xrealloc(where, (size) + 128 + strlen(what));
meillo@16 173 size += 128 + strlen(what);
meillo@16 174 }
meillo@16 175 strcat(where, what);
meillo@16 176 return size;
meillo@16 177 }
meillo@7 178
meillo@7 179 /* ========== */
meillo@7 180
meillo@16 181
meillo@16 182 int
meillo@16 183 before(struct date a, struct date b)
meillo@16 184 {
meillo@16 185 if (a.month < b.month) {
meillo@16 186 return 1;
meillo@16 187 } else if (a.month == b.month && a.day < b.day) {
meillo@16 188 return 1;
meillo@16 189 } else {
meillo@16 190 return 0;
meillo@16 191 }
meillo@16 192 }
meillo@16 193
meillo@16 194 int
meillo@16 195 ydelta(struct date a, struct date b)
meillo@16 196 {
meillo@16 197 return b.year - a.year + before(a, b);
meillo@16 198 }
meillo@16 199
meillo@16 200 /*
meillo@16 201 returns the length of the given month
meillo@16 202 */
meillo@16 203 int
meillo@16 204 mlen(int month, int year)
meillo@16 205 {
meillo@16 206 unsigned mlendat[] = {31,0,31,30,31,30,31,31,30,31,30,31};
meillo@16 207
meillo@16 208 if (mlendat[month - 1]) {
meillo@16 209 return mlendat[month - 1];
meillo@16 210 } else {
meillo@16 211 if (year%4==0 && (year%100!=0 || year%400==0)) {
meillo@16 212 return 29;
meillo@16 213 } else {
meillo@16 214 return 28;
meillo@16 215 }
meillo@16 216 }
meillo@16 217 }
meillo@16 218
meillo@16 219
meillo@16 220
meillo@15 221 /*
meillo@15 222 returns delta(d) in days, weeks, months, etc
meillo@15 223 the returned buffer is malloc()ed, do not forget to free() it
meillo@15 224 */
meillo@15 225 char *
meillo@15 226 tdelta(struct date *d)
meillo@15 227 {
meillo@7 228 int dy, wk, mn, yr;
meillo@15 229 char *tmp;
meillo@15 230 char *buf = xmalloc(128);
meillo@7 231 int size = 128;
meillo@15 232
meillo@7 233 *buf = 0;
meillo@15 234 switch (delta(d)) {
meillo@15 235 case 0:
meillo@16 236 size = append(buf, size, "TODAY");
meillo@15 237 return buf;
meillo@15 238 case 1:
meillo@16 239 size = append(buf, size, "Tomorrow");
meillo@15 240 return buf;
meillo@15 241 default:
meillo@15 242 /* like delta(), we ignore the year */
meillo@15 243 yr = -before(*d, today);
meillo@15 244 mn = d->month - today.month;
meillo@15 245 dy = d->day - today.day;
meillo@7 246
meillo@15 247 if (dy < 0) {
meillo@15 248 dy += mlen(today.month, today.year);
meillo@15 249 mn--;
meillo@15 250 }
meillo@15 251 if (mn < 0) {
meillo@15 252 mn += 12;
meillo@15 253 yr++;
meillo@15 254 }
meillo@7 255
meillo@15 256 wk = (dy / 7);
meillo@15 257 dy %= 7;
meillo@7 258
meillo@16 259 size = append(buf, size, "In ");
meillo@15 260 tmp = ttime(yr, mn, wk, dy);
meillo@16 261 size = append(buf, size, tmp);
meillo@15 262 free(tmp);
meillo@7 263
meillo@15 264 return buf;
meillo@7 265 }
meillo@7 266 }
meillo@7 267
meillo@7 268
meillo@7 269
meillo@7 270
meillo@7 271
meillo@15 272 void
meillo@16 273 donum(char *buf, int size, int n, char *txt, int *terms)
meillo@15 274 {
meillo@16 275 char tmp[128];
meillo@16 276
meillo@16 277 if (n <= 0) {
meillo@16 278 return;
meillo@16 279 }
meillo@16 280 snprintf(tmp, sizeof(tmp), "%d", n);
meillo@16 281 size = append(buf, size, tmp);
meillo@16 282 size = append(buf, size, " ");
meillo@16 283 size = append(buf, size, txt);
meillo@16 284 if (n != 1) {
meillo@16 285 size = append(buf, size, "s");
meillo@16 286 }
meillo@16 287 if (--*terms == 1) {
meillo@16 288 size = append(buf, size, " and ");
meillo@16 289 } else if (*terms > 1) {
meillo@16 290 size = append(buf, size, ", ");
meillo@15 291 }
meillo@7 292 }
meillo@7 293
meillo@7 294
meillo@7 295 /* returns allocated buffer, don't forget to free() */
meillo@15 296 char *
meillo@15 297 ttime(int yr, int mn, int wk, int dy)
meillo@15 298 {
meillo@7 299 int size = 128;
meillo@16 300 char *buf = xmalloc(size);
meillo@16 301 int terms = (yr!=0) + (mn!=0) + (wk!=0) + (dy!=0);
meillo@7 302
meillo@16 303 *buf = '\0'; /* Initialize buffer */
meillo@7 304
meillo@16 305 donum(buf, size, yr, "year", &terms);
meillo@16 306 donum(buf, size, mn, "month", &terms);
meillo@16 307 donum(buf, size, wk, "week", &terms);
meillo@16 308 donum(buf, size, dy, "day", &terms);
meillo@7 309
meillo@7 310 return buf;
meillo@7 311 }
meillo@7 312
meillo@7 313
meillo@7 314
meillo@7 315
meillo@7 316
meillo@7 317
meillo@15 318 /*
meillo@15 319 lists the birthdays in their string format, one by one, and passes
meillo@15 320 the string to a function.
meillo@15 321 */
meillo@15 322 void
meillo@16 323 liststrings(struct event *evl)
meillo@15 324 {
meillo@7 325 int i,j;
meillo@7 326 char *buf, *tmp;
meillo@7 327 int size;
meillo@7 328
meillo@16 329 for (i=0; evl[i].text; i++) {
meillo@16 330 size = 128;
meillo@16 331 buf = xmalloc(size);
meillo@7 332 *buf = '\0';
meillo@7 333
meillo@7 334 if (evl[i].warn == -1 && delta(&(evl[i].date))==0) {
meillo@16 335 size = append(buf, size, evl[i].text);
meillo@7 336 } else if (evl[i].enddate.day == 0) {
meillo@16 337
meillo@7 338 if (delta(&(evl[i].date)) <= evl[i].warn) {
meillo@7 339 tmp = tdelta(&(evl[i].date));
meillo@16 340 size = append(buf, size, tmp);
meillo@16 341 size = append(buf, size, ": ");
meillo@16 342 size = append(buf, size, evl[i].text);
meillo@7 343 free(tmp);
meillo@7 344 }
meillo@7 345 } else {
meillo@7 346 if (delta(&(evl[i].date)) <= evl[i].warn) {
meillo@16 347 size = append(buf, size, evl[i].text);
meillo@16 348 size = append(buf, size, " for ");
meillo@16 349 /* +1 because, if the difference between
meillo@16 350 two dates is one day, then the length of
meillo@16 351 an event on those days is two days */
meillo@7 352 j = ddiff(&(evl[i].date),&(evl[i].enddate)) + 1;
meillo@7 353 tmp = ttime(0, 0, j/7, j%7);
meillo@16 354 size = append(buf, size, tmp);
meillo@7 355 free(tmp);
meillo@16 356 size = append(buf, size, " ");
meillo@7 357 tmp = tdelta(&(evl[i].date));
meillo@16 358 size = append(buf, size, tmp);
meillo@7 359 } else if (delta(&(evl[i].enddate)) <= evl[i].warn) {
meillo@16 360 size = append(buf, size, evl[i].text);
meillo@16 361 size = append(buf, size, " ");
meillo@7 362 j = delta(&(evl[i].enddate));
meillo@7 363 if (j) {
meillo@16 364 size = append(buf, size, "for ");
meillo@7 365 tmp = ttime(0, 0, j/7, j%7);
meillo@16 366 size = append(buf, size, tmp);
meillo@7 367 free(tmp);
meillo@16 368 size = append(buf, size, " longer");
meillo@7 369 } else {
meillo@16 370 size = append(buf, size, "finishes today");
meillo@7 371 }
meillo@7 372 }
meillo@7 373 }
meillo@7 374 if (*buf) {
meillo@16 375 size = append(buf, size, ".");
meillo@16 376 puts(buf);
meillo@7 377 }
meillo@7 378 free(buf);
meillo@7 379 }
meillo@7 380 }
meillo@7 381
meillo@7 382
meillo@7 383
meillo@7 384
meillo@7 385
meillo@7 386
meillo@7 387
meillo@7 388
meillo@15 389 /*
meillo@15 390 sort the events by the time before the next time they come up,
meillo@15 391 putting those where the start has passed but we are still in the
meillo@15 392 time-period first
meillo@15 393 */
meillo@15 394 int
meillo@15 395 evcmp(const void *p1, const void *p2)
meillo@15 396 {
meillo@15 397 struct event *e1=(struct event *) p1;
meillo@15 398 struct event *e2=(struct event *) p2;
meillo@8 399 unsigned d1, d2;
meillo@7 400
meillo@15 401 /*
meillo@15 402 if the delta for the enddate is less than that for the start
meillo@15 403 date, then we have passed the start date but not yet the end
meillo@15 404 date, and so we should display the enddate; otherwise, we
meillo@15 405 should display the start date
meillo@15 406 */
meillo@7 407
meillo@7 408 d1=delta(&(e1->date));
meillo@7 409 if (e1->enddate.day && delta(&(e1->enddate)) < d1)
meillo@7 410 d1=delta(&(e1->enddate));
meillo@7 411
meillo@7 412 d2=delta(&(e2->date));
meillo@7 413 if (e2->enddate.day && delta(&(e2->enddate)) < d2)
meillo@7 414 d2=delta(&(e2->enddate));
meillo@7 415
meillo@7 416 if (d1 < d2) return -1;
meillo@8 417 if (d1 > d2) return 1;
meillo@7 418
meillo@7 419 return strcmp(e1->text, e2->text);
meillo@7 420 }
meillo@7 421
meillo@7 422
meillo@7 423
meillo@7 424
meillo@7 425
meillo@7 426
meillo@15 427 /*
meillo@15 428 difference in days between two dates
meillo@15 429 it is assumed that D1 < D2, and so the result is always positive
meillo@15 430 */
meillo@15 431 unsigned
meillo@15 432 ddiff(struct date *D1, struct date *D2)
meillo@15 433 {
meillo@8 434 struct date d1, d2;
meillo@8 435 int dd, m;
meillo@7 436
meillo@7 437 /* make working copies */
meillo@8 438 d1 = *D1;
meillo@8 439 d2 = *D2;
meillo@7 440
meillo@7 441 /* sort out zero years */
meillo@7 442 if (d1.year == 0 || d2.year==0) {
meillo@7 443 if (d1.year != d2.year) {
meillo@7 444 if (d1.year == 0) {
meillo@8 445 if (before(d1,d2))
meillo@8 446 d1.year = d2.year;
meillo@8 447 else
meillo@8 448 d1.year = d2.year - 1;
meillo@8 449 } else {
meillo@8 450 if (before(d1, d2))
meillo@8 451 d2.year = d1.year;
meillo@8 452 else
meillo@8 453 d2.year = d1.year + 1;
meillo@7 454 }
meillo@7 455 } else { /* both years zero */
meillo@8 456 if (before(d1, d2))
meillo@8 457 d1.year = d2.year = today.year;
meillo@7 458 else {
meillo@8 459 d1.year = today.year;
meillo@8 460 d2.year = d1.year + 1;
meillo@7 461 }
meillo@7 462 }
meillo@7 463 }
meillo@7 464
meillo@7 465 /* now we can actually do the comparison ... */
meillo@8 466 dd = 0;
meillo@7 467
meillo@7 468 /* to start with, we work in months */
meillo@7 469 for (m=d1.month; m < d2.month + (d2.year-d1.year)*12; m++)
meillo@7 470 dd += mlen(((m-1)%12)+1, d1.year + m/12);
meillo@7 471
meillo@15 472 /*
meillo@15 473 and then we renormalise for the days within the months
meillo@15 474 the first month was included in our calculations
meillo@15 475 */
meillo@7 476 dd -= d1.day;
meillo@7 477 /* but the last one wasn't */
meillo@7 478 dd += d2.day;
meillo@7 479
meillo@7 480 return dd;
meillo@7 481 }
meillo@7 482
meillo@7 483
meillo@7 484
meillo@7 485
meillo@7 486
meillo@7 487
meillo@7 488
meillo@7 489
meillo@15 490 /*
meillo@15 491 actually until the next anniversary of ...
meillo@15 492 */
meillo@15 493 unsigned
meillo@15 494 delta(struct date *date)
meillo@15 495 {
meillo@7 496 struct date d;
meillo@7 497 unsigned dt, mn;
meillo@7 498
meillo@7 499 memcpy(&d, date, sizeof(struct date));
meillo@7 500
meillo@7 501 /* past the end of the year */
meillo@7 502 if (before(d, today)) {
meillo@7 503 d.year = 1;
meillo@7 504 } else {
meillo@7 505 d.year = 0;
meillo@7 506 }
meillo@7 507
meillo@8 508 for (mn = today.month, dt=0; mn < d.month + 12*d.year; mn++) {
meillo@7 509 dt += mlen(((mn-1)%12) + 1,today.year + mn/12);
meillo@8 510 }
meillo@7 511
meillo@7 512 dt -= today.day;
meillo@7 513 dt += d.day;
meillo@7 514
meillo@7 515 return dt;
meillo@7 516 }
meillo@7 517
meillo@7 518
meillo@7 519
meillo@7 520
meillo@7 521
meillo@7 522
meillo@15 523 void
meillo@15 524 gettoday(void)
meillo@15 525 {
meillo@7 526 struct tm *tm;
meillo@7 527 time_t t;
meillo@7 528
meillo@7 529 time(&t);
meillo@7 530 tm = localtime(&t);
meillo@7 531 today.day = tm->tm_mday;
meillo@7 532 today.month = tm->tm_mon + 1; /* 1-12 instead of 0-11 */
meillo@7 533 today.year = tm->tm_year + 1900;
meillo@7 534 }
meillo@7 535
meillo@7 536
meillo@7 537
meillo@7 538
meillo@7 539
meillo@7 540
meillo@7 541
meillo@7 542
meillo@7 543
meillo@7 544
meillo@15 545 struct event *
meillo@15 546 readlist()
meillo@15 547 {
meillo@7 548 int i, j, k, l, d;
meillo@7 549 struct event *evl;
meillo@7 550 char buf[1024], buf2[1024];
meillo@16 551 char *ptr, *cp;
meillo@7 552 unsigned flags;
meillo@7 553
meillo@7 554 /* initialise */
meillo@7 555 gettoday();
meillo@7 556
meillo@7 557 for (i = 0, evl = NULL; fgets(buf, sizeof(buf), stdin) != NULL; i++) {
meillo@7 558 evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1));
meillo@7 559
meillo@7 560 /* ignore comments and empty lines */
meillo@7 561 if (*buf == '#' || *buf == '\n') {
meillo@7 562 i--;
meillo@7 563 continue;
meillo@7 564 }
meillo@7 565
meillo@7 566 /* parse string in buf */
meillo@16 567
meillo@16 568 ptr = strchr(buf, ' '); /* start of text */
meillo@7 569
meillo@7 570 /* not a valid line, so ignore it! Cool, huh? */
meillo@7 571 /* Attention: only recognizes lines without '=' */
meillo@16 572 if (!ptr) {
meillo@16 573 fprintf(stderr, "WARNING: Invalid input line:\n\t%s", buf);
meillo@7 574 i--;
meillo@7 575 continue;
meillo@7 576 }
meillo@7 577
meillo@16 578 *(ptr++) = '\0';
meillo@16 579 ptr[strlen(ptr)-1] = '\0';
meillo@7 580
meillo@16 581 j = sscanf(buf, "%u-%u-%u", &(evl[i].date.year),
meillo@15 582 &(evl[i].date.month), &(evl[i].date.day));
meillo@16 583 if (j != 3) {
meillo@16 584 fprintf(stderr, "Error: Invalid date:\t%s\n", buf);
meillo@16 585 i--;
meillo@16 586 continue;
meillo@7 587 }
meillo@7 588
meillo@7 589 /* parse flags */
meillo@7 590
meillo@16 591 evl[i].warn = def_warn;
meillo@7 592 evl[i].enddate.day = 0;
meillo@7 593 evl[i].enddate.month = 0;
meillo@7 594 evl[i].enddate.year = 0;
meillo@7 595
meillo@7 596 flags = 0;
meillo@7 597 j = 0;
meillo@16 598 cp = skptok(ptr);
meillo@16 599 for (cp=ptr; *cp && *cp=='#'; cp=skptok(cp)) {
meillo@16 600 for (k = 0; FTABLE[k].txt && strncmp(FTABLE[k].txt, cp, strlen(FTABLE[k].txt)); k++) {
meillo@7 601 }
meillo@7 602
meillo@7 603 switch (FTABLE[k].flag) {
meillo@16 604 case F_WTIME_P: /* #w<n> -- sets warning time */
meillo@16 605 sscanf(cp, "#w%u", &(evl[i].warn));
meillo@15 606 break;
meillo@16 607 case F_FORDAYS: /* #for<days> -- sets the duration of the event */
meillo@16 608 sscanf(cp, "#for%u", &d);
meillo@15 609 evl[i].enddate=evl[i].date;
meillo@15 610 for (l = 1; l < d; l++) {
meillo@15 611 evl[i].enddate.day++;
meillo@15 612 if (evl[i].enddate.day > mlen(evl[i].enddate.month, evl[i].enddate.year)) {
meillo@15 613 evl[i].enddate.month++;
meillo@15 614 evl[i].enddate.day = 1;
meillo@7 615 }
meillo@15 616 if (evl[i].enddate.month > 12) {
meillo@15 617 evl[i].enddate.year++;
meillo@15 618 evl[i].enddate.month = 1;
meillo@7 619 }
meillo@15 620 }
meillo@15 621 break;
meillo@16 622 case F_TODATE: /* #to<date> -- sets the end date of the event */
meillo@16 623 l = sscanf(cp, "#to%u-%u-%u", &(evl[i].enddate.year), &(evl[i].enddate.month), &(evl[i].enddate.day));
meillo@15 624 if (l == 2) {
meillo@15 625 evl[i].enddate.year = 0;
meillo@15 626 }
meillo@15 627 break;
meillo@15 628 case 0:
meillo@15 629 break;
meillo@15 630 default:
meillo@15 631 flags |= FTABLE[k].flag;
meillo@15 632 break;
meillo@7 633 }
meillo@7 634 }
meillo@7 635
meillo@7 636
meillo@7 637 /* construct event text */
meillo@7 638
meillo@7 639 switch(flags & F_MTYPE) {
meillo@7 640 default: /* assume it's a birthday */
meillo@16 641 if (!evl[i].date.year) {
meillo@16 642 sprintf(buf2, "%s has a birthday", cp);
meillo@16 643 break;
meillo@7 644 }
meillo@16 645 int tmp_age = ydelta(evl[i].date, today);
meillo@16 646 sprintf(buf2, "%s is %d year%s old",
meillo@16 647 cp, tmp_age, (tmp_age>1)?"s":"");
meillo@7 648 break;
meillo@7 649 case F_TANNIVERSARY:
meillo@16 650 if (evl[i].date.year) {
meillo@16 651 sprintf(buf2, "%s %d years ago",
meillo@16 652 cp, ydelta(evl[i].date, today));
meillo@7 653 } else {
meillo@16 654 strcpy(buf2, cp);
meillo@7 655 }
meillo@7 656 break;
meillo@7 657 case F_TEVENT:
meillo@16 658 /* if a year was specified, and this
meillo@16 659 warning isn't for it, ignore! */
meillo@16 660 if ((evl[i].date.year && ydelta(evl[i].date, today))
meillo@16 661 && (!evl[i].enddate.year || ydelta(evl[i].enddate, today))) {
meillo@7 662 i--;
meillo@7 663 continue;
meillo@7 664 }
meillo@16 665 strcpy(buf2, cp);
meillo@7 666 break;
meillo@7 667 }
meillo@7 668 evl[i].text = strdup(buf2);
meillo@7 669 }
meillo@7 670
meillo@7 671 evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1));
meillo@7 672 evl[i].date.day = 0;
meillo@7 673 evl[i].date.month = 0;
meillo@7 674 evl[i].date.year = 0;
meillo@7 675 evl[i].text = (char *) NULL;
meillo@7 676
meillo@7 677 fclose(stdin);
meillo@7 678
meillo@7 679 /* NB uses i from above */
meillo@7 680 qsort(evl, i, sizeof(struct event), evcmp);
meillo@7 681 return evl;
meillo@7 682 }
meillo@7 683
meillo@7 684
meillo@7 685
meillo@7 686
meillo@7 687
meillo@16 688 char *
meillo@16 689 skptok(char *ptr)
meillo@15 690 {
meillo@16 691 while (*ptr && (*ptr!=' ' && *ptr!='\t')) {
meillo@16 692 ptr++;
meillo@16 693 }
meillo@16 694 while (*ptr && (*ptr==' ' || *ptr=='\t')) {
meillo@16 695 ptr++;
meillo@16 696 }
meillo@16 697 return ptr;
meillo@7 698 }
meillo@7 699
meillo@7 700
meillo@7 701
meillo@7 702
meillo@0 703
meillo@0 704
meillo@15 705 int
meillo@15 706 main(int argc, char *argv[])
meillo@15 707 {
meillo@16 708 while (--argc > 0 && **++argv == '-') {
meillo@17 709 if (strcmp(argv[0], "-w") == 0) {
meillo@6 710 /* TODO: catch if no value given */
meillo@16 711 def_warn = atoi((++argv)[0]);
meillo@0 712 argc--;
meillo@0 713 } else {
meillo@3 714 fprintf(stderr, "unknown option %s\n", argv[0]);
meillo@0 715 exit(1);
meillo@0 716 }
meillo@0 717 }
meillo@16 718 liststrings(readlist());
meillo@15 719 return 0;
meillo@0 720 }