bday

view bdengine.c @ 6:fc6e40f7bd5a

minor stuff
author meillo@marmaro.de
date Mon, 17 Dec 2007 16:31:40 +0100
parents 5af6bf2cb271
children
line source
1 /*
2 birthday.c
4 Birthday/Anniversary display on login
6 (c) 1996 AS Mortimer
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version. You may also
12 distribute it under the Artistic License, as comes with Perl.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 You should also have recieved a copy of the Artistic license with
23 this program.
25 $Id: bdengine.c,v 1.14 2001/10/21 07:03:49 andy Exp $
27 We're getting there. At the moment, the file used by default is ~/.birthdays
28 under UNIX, or C:\PERSONAL\BDAYS.LST under DOS, but this can be overridden on
29 the command line. The file has the following format:
31 name/event/whatever=date flags
32 where:
33 date is dd/mm, dd/mm/yy (assumes 20th century!) or dd/mm/yyyy
34 flags is ONE or ZERO of
35 o bd for a birthday (default)
36 o ann for an anniversary
37 o ev for an event
38 and zero or more of
39 o w <n> to set the warn-in-advance time to n days (don't include the
40 brackets! :)
41 o to <date>
42 o for <days>
43 to specify the length of time taken by an event, for example a
44 holiday.
46 Comment lines are preceeded by #.
48 Note: If you deviate from this format, I cannot guarantee anything about
49 it's behaviour. In most cases, it will just quietly ignore the error,
50 which probably isn't ideal behaviour. Oh, well.
52 2003/05/20: Automatic reallocation of output buffer in listsrings() by
53 Sebastian Schmidt <yath@yath.eu.org>.
55 */
59 /* ========== */
62 #include <stdio.h>
63 #include <stdarg.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <time.h>
68 #include <sys/types.h>
69 #include <unistd.h>
71 #include "birthday.h"
73 /* ========== */
77 /*
78 xmalloc/xrealloc functions
79 Note: the x* functions are lifted straight from the GNU libc info docs
80 $Id: xmalloc.c,v 1.2 1999/01/16 17:08:59 andy Exp $
81 */
83 void* xmalloc (size_t size) {
84 register void* value = malloc (size);
85 if (value == 0) {
86 fprintf(stderr, "virtual memory exhausted\n");
87 exit(1);
88 }
89 return value;
90 }
93 void* xrealloc (void* ptr, size_t size) {
94 register void* value = realloc (ptr, size);
95 if (value == 0) {
96 fprintf(stderr, "virtual memory exhausted\n");
97 exit(1);
98 }
99 return value;
100 }
102 /* ========== */
109 int skptok(int j, char *ptr);
110 int evcmp(const void *e1, const void *e2);
113 /* ========== Global variables */
115 struct date today;
116 int iDWarn = DEF_WARN;
118 const unsigned MLENDAT[]={31,-1,31,30,31,30,31,31,30,31,30,31};
120 const struct _ftable FTABLE[] = {
121 {"bd", F_TBIRTHDAY},
122 {"ann",F_TANNIVERSARY},
123 {"ev", F_TEVENT},
124 {"w", F_WTIME_P},
125 {"to", F_TODATE},
126 {"for", F_FORDAYS},
127 {NULL, 0}
128 };
132 /* ========== */
136 /* like strcat(), but lets the buffer automagically grow :-)
137 * (needs local variable "size" with the buffer size) */
138 #define append(where, what) do { \
139 if (strlen(what) > (size - strlen(where))) { \
140 xrealloc(where, size + 128 + strlen(what)); \
141 size += 128 + strlen(what); \
142 } \
143 strcat(where, what); \
144 } while(0)
146 /* ========== */
148 /* returns delta(d) in days, weeks, months, etc
149 * the returned buffer is malloc()ed, do not forget to free() it */
150 char *tdelta(struct date *d) {
151 int dy, wk, mn, yr;
152 char *tmp;
153 char *buf = xmalloc(128);
154 int size = 128;
155 *buf = 0;
157 switch (delta(d)) {
158 case 0:
159 append(buf, "today");
160 return buf;
161 case 1:
162 append(buf, "tomorrow");
163 return buf;
164 default:
165 /* like delta(), we ignore the year */
166 yr=-before(*d,today);
167 mn=d->month - today.month;
168 dy=d->day - today.day;
170 if (dy < 0) {
171 dy += mlen(today.month, today.year);
172 mn--;
173 }
174 if (mn < 0) {
175 mn += 12;
176 yr++;
177 }
179 wk = (dy / 7);
180 dy %= 7;
182 append(buf, "in ");
183 tmp = ttime(yr, mn, wk, dy);
184 append(buf, tmp);
185 free(tmp);
187 return buf;
188 }
189 }
195 /*
196 void donum(n,txt) {
197 do {
198 if (n > 0) {
199 snprintf(tmp, sizeof(tmp), "%d", n);
200 append(buf, tmp);
201 append(buf, " " txt);
202 if (n != 1)
203 append(buf, "s");
204 terms--;
205 if (orgterms > 1) {
206 if (terms == 1)
207 append(buf, " and ");
208 else if (terms > 1)
209 append(buf, ", ");
210 }
211 }
212 } while(0)
213 }
214 */
217 #define donum(n,txt) do { \
218 if (n > 0) { \
219 snprintf(tmp, sizeof(tmp), "%d", n); \
220 append(buf, tmp); \
221 append(buf, " " txt); \
222 if (n != 1) \
223 append(buf, "s"); \
224 terms--; \
225 if (orgterms > 1) { \
226 if (terms == 1) \
227 append(buf, " and "); \
228 else if (terms > 1) \
229 append(buf, ", "); \
230 } \
231 } \
232 } while(0)
235 /* returns allocated buffer, don't forget to free() */
236 char* ttime(int yr, int mn, int wk, int dy) {
237 char* buf = xmalloc(128);
238 int size = 128;
239 int terms, orgterms;
240 char tmp[128];
242 *buf = 0; /* Initialize buffer */
243 terms = orgterms = (yr!=0) + (mn!=0) + (wk!=0) + (dy!=0);
245 donum(yr, "year");
246 donum(mn, "month");
247 donum(wk, "week");
248 donum(dy, "day");
250 return buf;
251 }
252 #undef donum
259 /* lists the birthdays in their string format, one by one, and passes the string to a function. */
260 void liststrings(struct event* evl, prnfunc outf) {
261 int i,j;
262 char *buf, *tmp;
263 int size;
265 for (i = 0; evl[i].text != NULL; i++) {
266 buf = xmalloc(128);
267 *buf = '\0';
268 size = 128;
270 if (evl[i].warn == -1 && delta(&(evl[i].date))==0) {
271 append(buf, evl[i].text);
272 } else if (evl[i].enddate.day == 0) {
273 if (delta(&(evl[i].date)) <= evl[i].warn) {
274 append(buf, evl[i].text);
275 append(buf, " ");
276 tmp = tdelta(&(evl[i].date));
277 append(buf, tmp);
278 free(tmp);
279 }
280 } else {
281 if (delta(&(evl[i].date)) <= evl[i].warn) {
282 append(buf, evl[i].text);
283 append(buf, " for ");
284 /* +1 because, if the difference between two dates is one day, then the length of an event on those days is two days */
285 j = ddiff(&(evl[i].date),&(evl[i].enddate)) + 1;
286 tmp = ttime(0, 0, j/7, j%7);
287 append(buf, tmp);
288 free(tmp);
289 append(buf, " ");
290 tmp = tdelta(&(evl[i].date));
291 append(buf, tmp);
292 } else if (delta(&(evl[i].enddate)) <= evl[i].warn) {
293 append(buf, evl[i].text);
294 append(buf, " ");
295 j = delta(&(evl[i].enddate));
296 if (j) {
297 append(buf, "for ");
298 tmp = ttime(0, 0, j/7, j%7);
299 append(buf, tmp);
300 free(tmp);
301 append(buf, " longer");
302 } else {
303 append(buf, "finishes today");
304 }
305 }
306 }
307 if (*buf) {
308 append(buf, ".");
309 outf(buf);
310 }
311 free(buf);
312 }
313 }
322 /* sort the events by the time before the next time they come up, putting those
323 where the start has passed but we are still in the time-period first */
324 int evcmp(const void *p1, const void *p2) {
325 struct event *e1=(struct event *)p1;
326 struct event *e2=(struct event *)p2;
327 unsigned d1,d2;
329 /* if the delta for the enddate is less than that for the start date, then we
330 have passed the start date but not yet the end date, and so we should
331 display the enddate; otherwise, we should display the start date */
333 d1=delta(&(e1->date));
334 if (e1->enddate.day && delta(&(e1->enddate)) < d1)
335 d1=delta(&(e1->enddate));
337 d2=delta(&(e2->date));
338 if (e2->enddate.day && delta(&(e2->enddate)) < d2)
339 d2=delta(&(e2->enddate));
341 if (d1 < d2) return -1;
342 if (d1 > d2) return 1;
344 return strcmp(e1->text, e2->text);
345 }
352 /* difference in days between two dates */
353 /* it is assumed that D1 < D2, and so the result is always positive */
354 unsigned ddiff(struct date *D1, struct date *D2) {
355 struct date d1,d2;
356 int dd,m;
358 /* make working copies */
359 d1=*D1;
360 d2=*D2;
362 /* sort out zero years */
363 if (d1.year == 0 || d2.year==0) {
364 if (d1.year != d2.year) {
365 if (d1.year == 0) {
366 if (before(d1,d2))
367 d1.year=d2.year;
368 else
369 d1.year=d2.year-1;
370 } else {
371 if (before(d1,d2))
372 d2.year=d1.year;
373 else
374 d2.year=d1.year+1;
375 }
376 } else { /* both years zero */
377 if (before(d1,d2))
378 d1.year=d2.year=today.year;
379 else {
380 d1.year=today.year;
381 d2.year=d1.year+1;
382 }
383 }
384 }
386 /* now we can actually do the comparison ... */
387 dd=0;
389 /* to start with, we work in months */
390 for (m=d1.month; m < d2.month + (d2.year-d1.year)*12; m++)
391 dd += mlen(((m-1)%12)+1, d1.year + m/12);
393 /* and then we renormalise for the days within the months */
394 /* the first month was included in our calculations */
395 dd -= d1.day;
396 /* but the last one wasn't */
397 dd += d2.day;
399 return dd;
400 }
409 /* actually until the next anniversary of ... */
410 unsigned delta(struct date *date) {
411 struct date d;
412 unsigned dt, mn;
414 memcpy(&d, date, sizeof(struct date));
416 /* past the end of the year */
417 if (before(d, today)) {
418 d.year = 1;
419 } else {
420 d.year = 0;
421 }
423 for (mn = today.month, dt=0; mn < d.month + 12*d.year; mn++)
424 dt += mlen(((mn-1)%12) + 1,today.year + mn/12);
426 dt -= today.day;
427 dt += d.day;
429 return dt;
430 }
437 void gettoday(void) {
438 struct tm *tm;
439 time_t t;
441 time(&t);
442 tm = localtime(&t);
443 today.day = tm->tm_mday;
444 today.month = tm->tm_mon + 1; /* 1-12 instead of 0-11 */
445 today.year = tm->tm_year + 1900;
446 }
457 struct event* readlist() {
458 int i, j, k, l, d;
459 struct event *evl;
460 char buf[1024], buf2[1024];
461 char *ptr;
462 unsigned flags;
464 /* initialise */
465 gettoday();
467 for (i = 0, evl = NULL; fgets(buf, sizeof(buf), stdin) != NULL; i++) {
468 evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1));
470 /* ignore comments and empty lines */
471 if (*buf == '#' || *buf == '\n') {
472 i--;
473 continue;
474 }
476 /* parse string in buf */
477 ptr = strrchr(buf, '='); /* allow '=' in text */
479 /* not a valid line, so ignore it! Cool, huh? */
480 /* Attention: only recognizes lines without '=' */
481 if (ptr == NULL) {
482 fprintf(stderr, "WARNING: Invalid line in input:\n%s", buf);
483 i--;
484 continue;
485 }
487 *(ptr++) = 0;
489 j = sscanf(ptr, "%u-%u-%u", &(evl[i].date.year), &(evl[i].date.month), &(evl[i].date.day));
490 /* ... unless it wasn't read, in which case set it to zero */
491 if (j==2) {
492 evl[i].date.year = 0;
493 }
496 /* parse flags */
498 evl[i].warn = iDWarn;
499 evl[i].enddate.day = 0;
500 evl[i].enddate.month = 0;
501 evl[i].enddate.year = 0;
503 flags = 0;
504 j = 0;
506 while(j = skptok(j, ptr), ptr[j] != 0) {
507 for (k = 0; FTABLE[k].txt != NULL && strncmp(FTABLE[k].txt, ptr + j, strlen(FTABLE[k].txt)); k++) {
508 }
510 switch (FTABLE[k].flag) {
511 case F_WTIME_P: /* w <n> -- sets warning time */
512 sscanf(ptr + j, "w %u", &(evl[i].warn));
513 break;
514 case F_FORDAYS: /* for <days> -- sets the duration of the event */
515 sscanf(ptr + j, "for %u", &d);
516 evl[i].enddate=evl[i].date;
517 for (l = 1; l < d; l++) {
518 evl[i].enddate.day++;
519 if (evl[i].enddate.day > mlen(evl[i].enddate.month, evl[i].enddate.year)) {
520 evl[i].enddate.month++;
521 evl[i].enddate.day = 1;
522 }
523 if (evl[i].enddate.month > 12) {
524 evl[i].enddate.year++;
525 evl[i].enddate.month = 1;
526 }
527 }
528 break;
529 case F_TODATE: /* to <date> -- sets the end date of the event */
530 l = sscanf(ptr + j, "to %u-%u-%u", &(evl[i].enddate.year), &(evl[i].enddate.month), &(evl[i].enddate.day));
531 if (l == 2) {
532 evl[i].enddate.year = 0;
533 }
534 break;
535 case 0:
536 break;
537 default:
538 flags |= FTABLE[k].flag;
539 break;
540 }
541 }
544 /* construct event text */
546 switch(flags & F_MTYPE) {
547 case F_TBIRTHDAY:
548 default: /* assume it's a birthday */
549 if (evl[i].date.year != 0) {
550 int tmp_age = ydelta(evl[i].date, today);
551 if (tmp_age != 1) {
552 sprintf(buf2, "%s is %d years old", buf, tmp_age);
553 } else {
554 sprintf(buf2, "%s is %d year old", buf, tmp_age);
555 }
556 } else {
557 sprintf(buf2, "%s has a birthday", buf);
558 }
559 break;
560 case F_TANNIVERSARY:
561 if (evl[i].date.year != 0) {
562 sprintf(buf2, "%s %d years ago", buf, ydelta(evl[i].date, today));
563 } else {
564 strcpy(buf2, buf);
565 }
566 break;
567 case F_TEVENT:
568 /* if a year was specified, and this warning isn't for it, ignore! */
569 if ((evl[i].date.year != 0 && ydelta(evl[i].date, today) != 0) && (evl[i].enddate.year == 0 || ydelta(evl[i].enddate, today) != 0)) {
570 i--;
571 continue;
572 }
573 strcpy(buf2, buf);
574 break;
575 }
576 evl[i].text = strdup(buf2);
577 }
579 evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1));
580 evl[i].date.day = 0;
581 evl[i].date.month = 0;
582 evl[i].date.year = 0;
583 evl[i].text = (char *) NULL;
585 fclose(stdin);
587 /* NB uses i from above */
588 qsort(evl, i, sizeof(struct event), evcmp);
589 return evl;
590 }
599 int skptok(int j, char *ptr) {
600 for (; ptr[j] != 0 && ptr[j] != ' ' && ptr[j] != '\t' ; j++);
601 for (; ptr[j] != 0 && (ptr[j] == ' ' || ptr[j] == '\t'); j++);
603 return j;
604 }