changeset 7:b6f4c7fba64a

all sources now in one file
author meillo@marmaro.de
date Tue, 18 Dec 2007 11:59:21 +0100
parents fc6e40f7bd5a
children 19c1ad697022
files Makefile bday.c bdengine.c birthday.c birthday.h
diffstat 5 files changed, 665 insertions(+), 750 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Mon Dec 17 16:31:40 2007 +0100
+++ b/Makefile	Tue Dec 18 11:59:21 2007 +0100
@@ -12,7 +12,7 @@
 BINDIR = ${PREFIX}/bin
 MANDIR = ${PREFIX}/share/man
 
-SRC=birthday.c bdengine.c
+SRC=bday.c
 OBJ=$(SRC:.c=.o)
 
 CFLAGS=-O2 -Wall
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bday.c	Tue Dec 18 11:59:21 2007 +0100
@@ -0,0 +1,664 @@
+/*
+   birthday
+
+   Birthday/Anniversary display on login
+
+   (c) 1996 AS Mortimer
+
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  You may also
+    distribute it under the Artistic License, as comes with Perl.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+    You should also have recieved a copy of the Artistic license with
+    this program.
+
+
+	 We're getting there. At the moment, the file used by default is ~/.birthdays
+	 under UNIX, or C:\PERSONAL\BDAYS.LST under DOS, but this can be overridden on
+	 the command line. The file has the following format:
+
+	 name/event/whatever=date flags
+	 where:
+		 date is dd/mm, dd/mm/yy (assumes 20th century!) or dd/mm/yyyy
+		 flags is ONE or ZERO of
+			 o  bd  for a birthday (default)
+			 o  ann for an anniversary
+			 o  ev  for an event
+		 and zero or more of
+			 o  w <n> to set the warn-in-advance time to n days (don't include the
+	 brackets! :)
+			 o  to <date>
+			 o  for <days>
+							 to specify the length of time taken by an event, for example a
+				 holiday.
+
+		 Comment lines are preceeded by #.
+
+		 Note: If you deviate from this format, I cannot guarantee anything about
+		 it's behaviour. In most cases, it will just quietly ignore the error,
+		 which probably isn't ideal behaviour. Oh, well.
+
+		 2003/05/20: Automatic reallocation of output buffer in listsrings() by
+								 Sebastian Schmidt <yath@yath.eu.org>.
+
+*/
+
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+
+
+/* standard time to warn in advance, when no explicit w flag is given. */
+#define DEF_WARN 14
+
+/* ========== Global constants and data types */
+
+
+/* month lengths etc */
+
+#define isleapyear(y) ((y)%4==0 && ((y)%100 != 0 || (y)%400 == 0))
+const unsigned MLENDAT[];
+#define mlen(m,y) (MLENDAT[(m)-1] != -1 ? MLENDAT[(m)-1] : (isleapyear((y)) ? 29 : 28))
+#define before(a,b) ((a).month < (b).month || ((a).month == (b).month && (a).day < (b).day))
+#define ydelta(a,b) ((int) (b).year - (a).year + before((a),(b)))
+
+/* -------- modifier flags */
+
+#define F_MTYPE 0x07
+#define F_TBIRTHDAY 1
+#define F_TANNIVERSARY 2
+#define F_TEVENT 3
+
+/* flags processed immediately on encountering */
+#define F_MIMMEDIATE 0x24
+#define F_WTIME_P 0x08
+#define F_FORDAYS 0x16
+#define F_TODATE 0x24
+
+struct _ftable {char *txt; unsigned flag;};
+
+const struct _ftable FTABLE[];
+
+struct date {
+  unsigned day;
+  unsigned month;
+  unsigned year;
+};
+
+struct event {
+  char *text;
+  struct date date;
+  struct date enddate;
+  int warn;
+};
+
+typedef int (*prnfunc)(const char *);
+
+/* ========== Global Variables */
+
+struct event* readlist(void);
+void gettoday(void);
+unsigned delta(struct date *);
+unsigned ddiff(struct date *D1, struct date *D2);
+void liststrings(struct event* evl, prnfunc outf);
+char *tdelta(struct date *d);
+char *ttime(int yr, int mn, int wk, int dy);
+
+int skptok(int j, char *ptr);
+int evcmp(const void *e1, const void *e2);
+
+
+struct date today;
+int iDWarn   = DEF_WARN;
+
+const unsigned MLENDAT[]={31,-1,31,30,31,30,31,31,30,31,30,31};
+
+const struct _ftable FTABLE[] = {
+	{"bd", F_TBIRTHDAY},
+	{"ann",F_TANNIVERSARY},
+	{"ev", F_TEVENT},
+	{"w",  F_WTIME_P},
+	{"to", F_TODATE},
+	{"for", F_FORDAYS},
+	{NULL, 0}
+};
+
+
+
+
+
+
+/*
+	xmalloc/xrealloc functions
+	Note: the x* functions are lifted straight from the GNU libc info docs
+	$Id: xmalloc.c,v 1.2 1999/01/16 17:08:59 andy Exp $
+*/
+
+void* xmalloc (size_t size) {
+	register void* value = malloc (size);
+	if (value == 0) {
+		fprintf(stderr, "virtual memory exhausted\n");
+		exit(1);
+	}
+	return value;
+}
+
+
+void* xrealloc (void* ptr, size_t size) {
+	register void* value = realloc (ptr, size);
+	if (value == 0) {
+		fprintf(stderr, "virtual memory exhausted\n");
+		exit(1);
+	}
+	return value;
+}
+
+/* ========== */
+
+
+
+/* like strcat(), but lets the buffer automagically grow :-)
+ * (needs local variable "size" with the buffer size) */
+#define append(where, what) do {                            \
+	if (strlen(what) > (size - strlen(where))) {          \
+		xrealloc(where, size + 128 + strlen(what));   \
+		size += 128 + strlen(what);                   \
+	}                                                     \
+	strcat(where, what);                                  \
+} while(0)
+
+/* ========== */
+
+/* returns delta(d) in days, weeks, months, etc
+ * the returned buffer is malloc()ed, do not forget to free() it */
+char *tdelta(struct date *d) {
+	int dy, wk, mn, yr;
+	char *tmp;
+	char *buf = xmalloc(128);
+	int size = 128;
+	*buf = 0;
+
+	switch (delta(d)) {
+		case 0:
+			append(buf, "today");
+			return buf;
+		case 1:
+			append(buf, "tomorrow");
+			return buf;
+		default:
+			/* like delta(), we ignore the year */
+			yr=-before(*d,today);
+			mn=d->month - today.month;
+			dy=d->day - today.day;
+
+			if (dy < 0) {
+				dy += mlen(today.month, today.year);
+				mn--;
+			}
+			if (mn < 0) {
+				mn += 12;
+				yr++;
+			}
+
+			wk = (dy / 7);
+			dy %= 7;
+
+			append(buf, "in ");
+			tmp = ttime(yr, mn, wk, dy);
+			append(buf, tmp);
+			free(tmp);
+
+			return buf;
+	}
+}
+
+
+
+
+
+/*
+void donum(n,txt) {
+	do {
+		if (n > 0) {
+			snprintf(tmp, sizeof(tmp), "%d", n);
+			append(buf, tmp);
+			append(buf, " " txt);
+			if (n != 1)
+				append(buf, "s");
+			terms--;
+			if (orgterms > 1) {
+				if (terms == 1)
+					append(buf, " and ");
+				else if (terms > 1)
+					append(buf, ", ");
+			}
+		}
+	 } while(0)
+}
+*/
+
+
+#define donum(n,txt) do {                       \
+	if (n > 0) {                              \
+		snprintf(tmp, sizeof(tmp), "%d", n);   \
+		append(buf, tmp);                      \
+		append(buf, " " txt);                  \
+		if (n != 1)                            \
+			append(buf, "s");                   \
+		terms--;                               \
+		if (orgterms > 1) {                    \
+			if (terms == 1)                     \
+				append(buf, " and ");            \
+			else if (terms > 1)                 \
+				append(buf, ", ");               \
+		}                                      \
+	}                                         \
+} while(0)
+
+
+/* returns allocated buffer, don't forget to free() */
+char* ttime(int yr, int mn, int wk, int dy) {
+	char* buf = xmalloc(128);
+	int size = 128;
+	int terms, orgterms;
+	char tmp[128];
+
+	*buf = 0; /* Initialize buffer */
+	terms = orgterms = (yr!=0) + (mn!=0) + (wk!=0) + (dy!=0);
+
+	donum(yr, "year");
+	donum(mn, "month");
+	donum(wk, "week");
+	donum(dy, "day");
+
+	return buf;
+}
+#undef donum
+
+
+
+
+
+
+/* lists the birthdays in their string format, one by one, and passes the string to a function. */
+void liststrings(struct event* evl, prnfunc outf) {
+	int i,j;
+	char *buf, *tmp;
+	int size;
+
+	for (i = 0; evl[i].text != NULL; i++) {
+		buf = xmalloc(128);
+		*buf = '\0';
+		size = 128;
+
+		if (evl[i].warn == -1 && delta(&(evl[i].date))==0) {
+			 append(buf, evl[i].text);
+		} else if (evl[i].enddate.day == 0) {
+			if (delta(&(evl[i].date)) <= evl[i].warn) {
+				append(buf, evl[i].text);
+				append(buf, " ");
+				tmp = tdelta(&(evl[i].date));
+				append(buf, tmp);
+				free(tmp);
+			}
+		} else {
+			if (delta(&(evl[i].date)) <= evl[i].warn) {
+				append(buf, evl[i].text);
+				append(buf, " for ");
+				/* +1 because, if the difference between two dates is one day, then the length of an event on those days is two days */
+				j = ddiff(&(evl[i].date),&(evl[i].enddate)) + 1;
+				tmp = ttime(0, 0, j/7, j%7);
+				append(buf, tmp);
+				free(tmp);
+				append(buf, " ");
+				tmp = tdelta(&(evl[i].date));
+				append(buf, tmp);
+			} else if (delta(&(evl[i].enddate)) <= evl[i].warn) {
+				append(buf, evl[i].text);
+				append(buf, " ");
+				j = delta(&(evl[i].enddate));
+				if (j) {
+					append(buf, "for ");
+					tmp = ttime(0, 0, j/7, j%7);
+					append(buf, tmp);
+					free(tmp);
+					append(buf, " longer");
+				} else {
+					append(buf, "finishes today");
+				}
+			}
+		}
+		if (*buf) {
+			append(buf, ".");
+			outf(buf);
+		}
+		free(buf);
+	}
+}
+
+
+
+
+
+
+
+
+/* sort the events by the time before the next time they come up, putting those
+	 where the start has passed but we are still in the time-period first */
+int evcmp(const void *p1, const void *p2) {
+	struct event *e1=(struct event *)p1;
+	struct event *e2=(struct event *)p2;
+	unsigned d1,d2;
+
+	/* if the delta for the enddate is less than that for the start date, then we
+		have passed the start date but not yet the end date, and so we should
+		display the enddate; otherwise, we should display the start date */
+
+	d1=delta(&(e1->date));
+	if (e1->enddate.day && delta(&(e1->enddate)) < d1)
+		d1=delta(&(e1->enddate));
+
+	d2=delta(&(e2->date));
+	if (e2->enddate.day && delta(&(e2->enddate)) < d2)
+		d2=delta(&(e2->enddate));
+
+	if (d1 < d2) return -1;
+	if (d1 > d2) return  1;
+
+	return strcmp(e1->text, e2->text);
+}
+
+
+
+
+
+
+/* difference in days between two dates */
+/* it is assumed that D1 < D2, and so the result is always positive */
+unsigned ddiff(struct date *D1, struct date *D2) {
+	struct date d1,d2;
+	int dd,m;
+
+	/* make working copies */
+	d1=*D1;
+	d2=*D2;
+
+	/* sort out zero years */
+	if (d1.year == 0 || d2.year==0) {
+		if (d1.year != d2.year) {
+			if (d1.year == 0) {
+	if (before(d1,d2))
+		d1.year=d2.year;
+	else
+		d1.year=d2.year-1;
+			} else {
+	if (before(d1,d2))
+		d2.year=d1.year;
+	else
+		d2.year=d1.year+1;
+			}
+		} else { /* both years zero */
+			if (before(d1,d2))
+	d1.year=d2.year=today.year;
+			else {
+	d1.year=today.year;
+	d2.year=d1.year+1;
+			}
+		}
+	}
+
+	/* now we can actually do the comparison ... */
+	dd=0;
+
+	/* to start with, we work in months */
+	for (m=d1.month; m < d2.month + (d2.year-d1.year)*12; m++)
+		dd += mlen(((m-1)%12)+1, d1.year + m/12);
+
+	/* and then we renormalise for the days within the months */
+	/* the first month was included in our calculations */
+	dd -= d1.day;
+	/* but the last one wasn't */
+	dd += d2.day;
+
+	return dd;
+}
+
+
+
+
+
+
+
+
+/* actually until the next anniversary of ... */
+unsigned delta(struct date *date) {
+	struct date d;
+	unsigned dt, mn;
+
+	memcpy(&d, date, sizeof(struct date));
+
+	/* past the end of the year */
+	if (before(d, today)) {
+		d.year = 1;
+	} else {
+		d.year = 0;
+	}
+
+	for (mn = today.month, dt=0; mn < d.month + 12*d.year; mn++)
+		dt += mlen(((mn-1)%12) + 1,today.year + mn/12);
+
+	dt -= today.day;
+	dt += d.day;
+
+	return dt;
+}
+
+
+
+
+
+
+void gettoday(void) {
+	struct tm *tm;
+	time_t t;
+
+	time(&t);
+	tm = localtime(&t);
+	today.day = tm->tm_mday;
+	today.month = tm->tm_mon + 1; /* 1-12 instead of 0-11 */
+	today.year = tm->tm_year + 1900;
+}
+
+
+
+
+
+
+
+
+
+
+struct event* readlist() {
+	int i, j, k, l, d;
+	struct event *evl;
+	char buf[1024], buf2[1024];
+	char *ptr;
+	unsigned flags;
+
+	/* initialise */
+	gettoday();
+
+	for (i = 0, evl = NULL; fgets(buf, sizeof(buf), stdin) != NULL; i++) {
+		evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1));
+
+		/* ignore comments and empty lines */
+		if (*buf == '#' || *buf == '\n') {
+			i--;
+			continue;
+		}
+
+		/* parse string in buf */
+		ptr = strrchr(buf, '='); /* allow '=' in text */
+
+		/* not a valid line, so ignore it! Cool, huh? */
+		/* Attention: only recognizes lines without '=' */
+		if (ptr == NULL) {
+			fprintf(stderr, "WARNING: Invalid line in input:\n%s", buf);
+			i--;
+			continue;
+		}
+
+		*(ptr++) = 0;
+
+		j = sscanf(ptr, "%u-%u-%u", &(evl[i].date.year), &(evl[i].date.month), &(evl[i].date.day));
+		/* ... unless it wasn't read, in which case set it to zero */
+		if (j==2) {
+			evl[i].date.year = 0;
+		}
+
+
+		/* parse flags */
+
+		evl[i].warn = iDWarn;
+		evl[i].enddate.day = 0;
+		evl[i].enddate.month = 0;
+		evl[i].enddate.year = 0;
+
+		flags = 0;
+		j = 0;
+
+		while(j = skptok(j, ptr), ptr[j] != 0) {
+			for (k = 0; FTABLE[k].txt != NULL && strncmp(FTABLE[k].txt, ptr + j, strlen(FTABLE[k].txt)); k++) {
+			}
+
+			switch (FTABLE[k].flag) {
+				case F_WTIME_P: /* w <n> -- sets warning time */
+					sscanf(ptr + j, "w %u", &(evl[i].warn));
+					break;
+				case F_FORDAYS: /* for <days> -- sets the duration of the event */
+					sscanf(ptr + j, "for %u", &d);
+					evl[i].enddate=evl[i].date;
+					for (l = 1; l < d; l++) {
+						evl[i].enddate.day++;
+						if (evl[i].enddate.day > mlen(evl[i].enddate.month, evl[i].enddate.year)) {
+							evl[i].enddate.month++;
+							evl[i].enddate.day = 1;
+						}
+						if (evl[i].enddate.month > 12) {
+							evl[i].enddate.year++;
+							evl[i].enddate.month = 1;
+						}
+					}
+					break;
+				case F_TODATE: /* to <date> -- sets the end date of the event */
+					l = sscanf(ptr + j, "to %u-%u-%u", &(evl[i].enddate.year), &(evl[i].enddate.month), &(evl[i].enddate.day));
+					if (l == 2) {
+						evl[i].enddate.year = 0;
+					}
+					break;
+				case 0:
+					break;
+				default:
+					flags |= FTABLE[k].flag;
+					break;
+			}
+		}
+
+
+		/* construct event text */
+
+		switch(flags & F_MTYPE) {
+			case F_TBIRTHDAY:
+			default: /* assume it's a birthday */
+				if (evl[i].date.year != 0) {
+					int tmp_age = ydelta(evl[i].date, today);
+					if (tmp_age != 1) {
+						sprintf(buf2, "%s is %d years old", buf, tmp_age);
+					} else {
+						sprintf(buf2, "%s is %d year old", buf, tmp_age);
+					}
+				} else {
+					sprintf(buf2, "%s has a birthday", buf);
+				}
+				break;
+			case F_TANNIVERSARY:
+				if (evl[i].date.year != 0) {
+					sprintf(buf2, "%s %d years ago", buf, ydelta(evl[i].date, today));
+				} else {
+					strcpy(buf2, buf);
+				}
+				break;
+			case F_TEVENT:
+				/* if a year was specified, and this warning isn't for it, ignore! */
+				if ((evl[i].date.year != 0 && ydelta(evl[i].date, today) != 0) && (evl[i].enddate.year == 0 || ydelta(evl[i].enddate, today) != 0)) {
+					i--;
+					continue;
+				}
+				strcpy(buf2, buf);
+				break;
+		}
+		evl[i].text = strdup(buf2);
+	}
+
+	evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1));
+	evl[i].date.day = 0;
+	evl[i].date.month = 0;
+	evl[i].date.year = 0;
+	evl[i].text = (char *) NULL;
+
+	fclose(stdin);
+
+	/* NB uses i from above */
+	qsort(evl, i, sizeof(struct event), evcmp);
+	return evl;
+}
+
+
+
+
+
+int skptok(int j, char *ptr) {
+	for (; ptr[j] != 0 &&  ptr[j] != ' ' && ptr[j] != '\t' ; j++);
+	for (; ptr[j] != 0 && (ptr[j] == ' ' || ptr[j] == '\t'); j++);
+
+	return j;
+}
+
+
+
+
+
+
+int main(int argc, char* argv[]) {
+
+  while (--argc > 0 && (*++argv)[0] == '-') {
+		if (strcmp(argv[0], "-W") == 0) {
+			/* TODO: catch if no value given */
+			iDWarn = atoi((++argv)[0]);
+			argc--;
+		} else {
+			fprintf(stderr, "unknown option %s\n", argv[0]);
+			exit(1);
+		}
+	}
+
+	liststrings(readlist(), puts);
+
+  return 0;
+}
--- a/bdengine.c	Mon Dec 17 16:31:40 2007 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,606 +0,0 @@
-/*
-	 birthday.c
-
-	 Birthday/Anniversary display on login
-
-	 (c) 1996 AS Mortimer
-
-		This program is free software; you can redistribute it and/or
-		modify it under the terms of the GNU General Public License as
-		published by the Free Software Foundation; either version 2 of the
-		License, or (at your option) any later version.  You may also
-		distribute it under the Artistic License, as comes with Perl.
-
-		This program is distributed in the hope that it will be useful,
-		but WITHOUT ANY WARRANTY; without even the implied warranty of
-		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-		You should have received a copy of the GNU General Public License
-		along with this program; if not, write to the Free Software
-		Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-		You should also have recieved a copy of the Artistic license with
-		this program.
-
-	 $Id: bdengine.c,v 1.14 2001/10/21 07:03:49 andy Exp $
-
-	 We're getting there. At the moment, the file used by default is ~/.birthdays
-	 under UNIX, or C:\PERSONAL\BDAYS.LST under DOS, but this can be overridden on
-	 the command line. The file has the following format:
-
-	 name/event/whatever=date flags
-	 where:
-		 date is dd/mm, dd/mm/yy (assumes 20th century!) or dd/mm/yyyy
-		 flags is ONE or ZERO of
-			 o  bd  for a birthday (default)
-			 o  ann for an anniversary
-			 o  ev  for an event
-		 and zero or more of
-			 o  w <n> to set the warn-in-advance time to n days (don't include the
-	 brackets! :)
-			 o  to <date>
-			 o  for <days>
-							 to specify the length of time taken by an event, for example a
-				 holiday.
-
-		 Comment lines are preceeded by #.
-
-		 Note: If you deviate from this format, I cannot guarantee anything about
-		 it's behaviour. In most cases, it will just quietly ignore the error,
-		 which probably isn't ideal behaviour. Oh, well.
-
-		 2003/05/20: Automatic reallocation of output buffer in listsrings() by
-								 Sebastian Schmidt <yath@yath.eu.org>.
-
-*/
-
-
-
-/* ========== */
-
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "birthday.h"
-
-/* ========== */
-
-
-
-/*
-	xmalloc/xrealloc functions
-	Note: the x* functions are lifted straight from the GNU libc info docs
-	$Id: xmalloc.c,v 1.2 1999/01/16 17:08:59 andy Exp $
-*/
-
-void* xmalloc (size_t size) {
-	register void* value = malloc (size);
-	if (value == 0) {
-		fprintf(stderr, "virtual memory exhausted\n");
-		exit(1);
-	}
-	return value;
-}
-
-
-void* xrealloc (void* ptr, size_t size) {
-	register void* value = realloc (ptr, size);
-	if (value == 0) {
-		fprintf(stderr, "virtual memory exhausted\n");
-		exit(1);
-	}
-	return value;
-}
-
-/* ========== */
-
-
-
-
-
-
-int skptok(int j, char *ptr);
-int evcmp(const void *e1, const void *e2);
-
-
-/* ========== Global variables */
-
-struct date today;
-int iDWarn   = DEF_WARN;
-
-const unsigned MLENDAT[]={31,-1,31,30,31,30,31,31,30,31,30,31};
-
-const struct _ftable FTABLE[] = {
-	{"bd", F_TBIRTHDAY},
-	{"ann",F_TANNIVERSARY},
-	{"ev", F_TEVENT},
-	{"w",  F_WTIME_P},
-	{"to", F_TODATE},
-	{"for", F_FORDAYS},
-	{NULL, 0}
-};
-
-
-
-/* ========== */
-
-
-
-/* like strcat(), but lets the buffer automagically grow :-)
- * (needs local variable "size" with the buffer size) */
-#define append(where, what) do {                            \
-	if (strlen(what) > (size - strlen(where))) {          \
-		xrealloc(where, size + 128 + strlen(what));   \
-		size += 128 + strlen(what);                   \
-	}                                                     \
-	strcat(where, what);                                  \
-} while(0)
-
-/* ========== */
-
-/* returns delta(d) in days, weeks, months, etc
- * the returned buffer is malloc()ed, do not forget to free() it */
-char *tdelta(struct date *d) {
-	int dy, wk, mn, yr;
-	char *tmp;
-	char *buf = xmalloc(128);
-	int size = 128;
-	*buf = 0;
-
-	switch (delta(d)) {
-		case 0:
-			append(buf, "today");
-			return buf;
-		case 1:
-			append(buf, "tomorrow");
-			return buf;
-		default:
-			/* like delta(), we ignore the year */
-			yr=-before(*d,today);
-			mn=d->month - today.month;
-			dy=d->day - today.day;
-
-			if (dy < 0) {
-				dy += mlen(today.month, today.year);
-				mn--;
-			}
-			if (mn < 0) {
-				mn += 12;
-				yr++;
-			}
-
-			wk = (dy / 7);
-			dy %= 7;
-
-			append(buf, "in ");
-			tmp = ttime(yr, mn, wk, dy);
-			append(buf, tmp);
-			free(tmp);
-
-			return buf;
-	}
-}
-
-
-
-
-
-/*
-void donum(n,txt) {
-	do {
-		if (n > 0) {
-			snprintf(tmp, sizeof(tmp), "%d", n);
-			append(buf, tmp);
-			append(buf, " " txt);
-			if (n != 1)
-				append(buf, "s");
-			terms--;
-			if (orgterms > 1) {
-				if (terms == 1)
-					append(buf, " and ");
-				else if (terms > 1)
-					append(buf, ", ");
-			}
-		}
-	 } while(0)
-}
-*/
-
-
-#define donum(n,txt) do {                       \
-	if (n > 0) {                              \
-		snprintf(tmp, sizeof(tmp), "%d", n);   \
-		append(buf, tmp);                      \
-		append(buf, " " txt);                  \
-		if (n != 1)                            \
-			append(buf, "s");                   \
-		terms--;                               \
-		if (orgterms > 1) {                    \
-			if (terms == 1)                     \
-				append(buf, " and ");            \
-			else if (terms > 1)                 \
-				append(buf, ", ");               \
-		}                                      \
-	}                                         \
-} while(0)
-
-
-/* returns allocated buffer, don't forget to free() */
-char* ttime(int yr, int mn, int wk, int dy) {
-	char* buf = xmalloc(128);
-	int size = 128;
-	int terms, orgterms;
-	char tmp[128];
-
-	*buf = 0; /* Initialize buffer */
-	terms = orgterms = (yr!=0) + (mn!=0) + (wk!=0) + (dy!=0);
-
-	donum(yr, "year");
-	donum(mn, "month");
-	donum(wk, "week");
-	donum(dy, "day");
-
-	return buf;
-}
-#undef donum
-
-
-
-
-
-
-/* lists the birthdays in their string format, one by one, and passes the string to a function. */
-void liststrings(struct event* evl, prnfunc outf) {
-	int i,j;
-	char *buf, *tmp;
-	int size;
-
-	for (i = 0; evl[i].text != NULL; i++) {
-		buf = xmalloc(128);
-		*buf = '\0';
-		size = 128;
-
-		if (evl[i].warn == -1 && delta(&(evl[i].date))==0) {
-			 append(buf, evl[i].text);
-		} else if (evl[i].enddate.day == 0) {
-			if (delta(&(evl[i].date)) <= evl[i].warn) {
-				append(buf, evl[i].text);
-				append(buf, " ");
-				tmp = tdelta(&(evl[i].date));
-				append(buf, tmp);
-				free(tmp);
-			}
-		} else {
-			if (delta(&(evl[i].date)) <= evl[i].warn) {
-				append(buf, evl[i].text);
-				append(buf, " for ");
-				/* +1 because, if the difference between two dates is one day, then the length of an event on those days is two days */
-				j = ddiff(&(evl[i].date),&(evl[i].enddate)) + 1;
-				tmp = ttime(0, 0, j/7, j%7);
-				append(buf, tmp);
-				free(tmp);
-				append(buf, " ");
-				tmp = tdelta(&(evl[i].date));
-				append(buf, tmp);
-			} else if (delta(&(evl[i].enddate)) <= evl[i].warn) {
-				append(buf, evl[i].text);
-				append(buf, " ");
-				j = delta(&(evl[i].enddate));
-				if (j) {
-					append(buf, "for ");
-					tmp = ttime(0, 0, j/7, j%7);
-					append(buf, tmp);
-					free(tmp);
-					append(buf, " longer");
-				} else {
-					append(buf, "finishes today");
-				}
-			}
-		}
-		if (*buf) {
-			append(buf, ".");
-			outf(buf);
-		}
-		free(buf);
-	}
-}
-
-
-
-
-
-
-
-
-/* sort the events by the time before the next time they come up, putting those
-	 where the start has passed but we are still in the time-period first */
-int evcmp(const void *p1, const void *p2) {
-	struct event *e1=(struct event *)p1;
-	struct event *e2=(struct event *)p2;
-	unsigned d1,d2;
-
-	/* if the delta for the enddate is less than that for the start date, then we
-		have passed the start date but not yet the end date, and so we should
-		display the enddate; otherwise, we should display the start date */
-
-	d1=delta(&(e1->date));
-	if (e1->enddate.day && delta(&(e1->enddate)) < d1)
-		d1=delta(&(e1->enddate));
-
-	d2=delta(&(e2->date));
-	if (e2->enddate.day && delta(&(e2->enddate)) < d2)
-		d2=delta(&(e2->enddate));
-
-	if (d1 < d2) return -1;
-	if (d1 > d2) return  1;
-
-	return strcmp(e1->text, e2->text);
-}
-
-
-
-
-
-
-/* difference in days between two dates */
-/* it is assumed that D1 < D2, and so the result is always positive */
-unsigned ddiff(struct date *D1, struct date *D2) {
-	struct date d1,d2;
-	int dd,m;
-
-	/* make working copies */
-	d1=*D1;
-	d2=*D2;
-
-	/* sort out zero years */
-	if (d1.year == 0 || d2.year==0) {
-		if (d1.year != d2.year) {
-			if (d1.year == 0) {
-	if (before(d1,d2))
-		d1.year=d2.year;
-	else
-		d1.year=d2.year-1;
-			} else {
-	if (before(d1,d2))
-		d2.year=d1.year;
-	else
-		d2.year=d1.year+1;
-			}
-		} else { /* both years zero */
-			if (before(d1,d2))
-	d1.year=d2.year=today.year;
-			else {
-	d1.year=today.year;
-	d2.year=d1.year+1;
-			}
-		}
-	}
-
-	/* now we can actually do the comparison ... */
-	dd=0;
-
-	/* to start with, we work in months */
-	for (m=d1.month; m < d2.month + (d2.year-d1.year)*12; m++)
-		dd += mlen(((m-1)%12)+1, d1.year + m/12);
-
-	/* and then we renormalise for the days within the months */
-	/* the first month was included in our calculations */
-	dd -= d1.day;
-	/* but the last one wasn't */
-	dd += d2.day;
-
-	return dd;
-}
-
-
-
-
-
-
-
-
-/* actually until the next anniversary of ... */
-unsigned delta(struct date *date) {
-	struct date d;
-	unsigned dt, mn;
-
-	memcpy(&d, date, sizeof(struct date));
-
-	/* past the end of the year */
-	if (before(d, today)) {
-		d.year = 1;
-	} else {
-		d.year = 0;
-	}
-
-	for (mn = today.month, dt=0; mn < d.month + 12*d.year; mn++)
-		dt += mlen(((mn-1)%12) + 1,today.year + mn/12);
-
-	dt -= today.day;
-	dt += d.day;
-
-	return dt;
-}
-
-
-
-
-
-
-void gettoday(void) {
-	struct tm *tm;
-	time_t t;
-
-	time(&t);
-	tm = localtime(&t);
-	today.day = tm->tm_mday;
-	today.month = tm->tm_mon + 1; /* 1-12 instead of 0-11 */
-	today.year = tm->tm_year + 1900;
-}
-
-
-
-
-
-
-
-
-
-
-struct event* readlist() {
-	int i, j, k, l, d;
-	struct event *evl;
-	char buf[1024], buf2[1024];
-	char *ptr;
-	unsigned flags;
-
-	/* initialise */
-	gettoday();
-
-	for (i = 0, evl = NULL; fgets(buf, sizeof(buf), stdin) != NULL; i++) {
-		evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1));
-
-		/* ignore comments and empty lines */
-		if (*buf == '#' || *buf == '\n') {
-			i--;
-			continue;
-		}
-
-		/* parse string in buf */
-		ptr = strrchr(buf, '='); /* allow '=' in text */
-
-		/* not a valid line, so ignore it! Cool, huh? */
-		/* Attention: only recognizes lines without '=' */
-		if (ptr == NULL) {
-			fprintf(stderr, "WARNING: Invalid line in input:\n%s", buf);
-			i--;
-			continue;
-		}
-
-		*(ptr++) = 0;
-
-		j = sscanf(ptr, "%u-%u-%u", &(evl[i].date.year), &(evl[i].date.month), &(evl[i].date.day));
-		/* ... unless it wasn't read, in which case set it to zero */
-		if (j==2) {
-			evl[i].date.year = 0;
-		}
-
-
-		/* parse flags */
-
-		evl[i].warn = iDWarn;
-		evl[i].enddate.day = 0;
-		evl[i].enddate.month = 0;
-		evl[i].enddate.year = 0;
-
-		flags = 0;
-		j = 0;
-
-		while(j = skptok(j, ptr), ptr[j] != 0) {
-			for (k = 0; FTABLE[k].txt != NULL && strncmp(FTABLE[k].txt, ptr + j, strlen(FTABLE[k].txt)); k++) {
-			}
-
-			switch (FTABLE[k].flag) {
-				case F_WTIME_P: /* w <n> -- sets warning time */
-					sscanf(ptr + j, "w %u", &(evl[i].warn));
-					break;
-				case F_FORDAYS: /* for <days> -- sets the duration of the event */
-					sscanf(ptr + j, "for %u", &d);
-					evl[i].enddate=evl[i].date;
-					for (l = 1; l < d; l++) {
-						evl[i].enddate.day++;
-						if (evl[i].enddate.day > mlen(evl[i].enddate.month, evl[i].enddate.year)) {
-							evl[i].enddate.month++;
-							evl[i].enddate.day = 1;
-						}
-						if (evl[i].enddate.month > 12) {
-							evl[i].enddate.year++;
-							evl[i].enddate.month = 1;
-						}
-					}
-					break;
-				case F_TODATE: /* to <date> -- sets the end date of the event */
-					l = sscanf(ptr + j, "to %u-%u-%u", &(evl[i].enddate.year), &(evl[i].enddate.month), &(evl[i].enddate.day));
-					if (l == 2) {
-						evl[i].enddate.year = 0;
-					}
-					break;
-				case 0:
-					break;
-				default:
-					flags |= FTABLE[k].flag;
-					break;
-			}
-		}
-
-
-		/* construct event text */
-
-		switch(flags & F_MTYPE) {
-			case F_TBIRTHDAY:
-			default: /* assume it's a birthday */
-				if (evl[i].date.year != 0) {
-					int tmp_age = ydelta(evl[i].date, today);
-					if (tmp_age != 1) {
-						sprintf(buf2, "%s is %d years old", buf, tmp_age);
-					} else {
-						sprintf(buf2, "%s is %d year old", buf, tmp_age);
-					}
-				} else {
-					sprintf(buf2, "%s has a birthday", buf);
-				}
-				break;
-			case F_TANNIVERSARY:
-				if (evl[i].date.year != 0) {
-					sprintf(buf2, "%s %d years ago", buf, ydelta(evl[i].date, today));
-				} else {
-					strcpy(buf2, buf);
-				}
-				break;
-			case F_TEVENT:
-				/* if a year was specified, and this warning isn't for it, ignore! */
-				if ((evl[i].date.year != 0 && ydelta(evl[i].date, today) != 0) && (evl[i].enddate.year == 0 || ydelta(evl[i].enddate, today) != 0)) {
-					i--;
-					continue;
-				}
-				strcpy(buf2, buf);
-				break;
-		}
-		evl[i].text = strdup(buf2);
-	}
-
-	evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1));
-	evl[i].date.day = 0;
-	evl[i].date.month = 0;
-	evl[i].date.year = 0;
-	evl[i].text = (char *) NULL;
-
-	fclose(stdin);
-
-	/* NB uses i from above */
-	qsort(evl, i, sizeof(struct event), evcmp);
-	return evl;
-}
-
-
-
-
-
-
-
-
-int skptok(int j, char *ptr) {
-	for (; ptr[j] != 0 &&  ptr[j] != ' ' && ptr[j] != '\t' ; j++);
-	for (; ptr[j] != 0 && (ptr[j] == ' ' || ptr[j] == '\t'); j++);
-
-	return j;
-}
-
-
--- a/birthday.c	Mon Dec 17 16:31:40 2007 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/*
-   birthday
-
-   Birthday/Anniversary display on login
-
-   (c) 1996 AS Mortimer
-
-    This program is free software; you can redistribute it and/or
-    modify it under the terms of the GNU General Public License as
-    published by the Free Software Foundation; either version 2 of the
-    License, or (at your option) any later version.  You may also
-    distribute it under the Artistic License, as comes with Perl.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-    You should also have recieved a copy of the Artistic license with
-    this program.
-
-   $Id: birthday.c,v 1.5 1999/04/25 14:01:29 andy Exp $
-*/
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "birthday.h"
-
-
-int main(int argc, char* argv[]) {
-
-  while (--argc > 0 && (*++argv)[0] == '-') {
-		if (strcmp(argv[0], "-W") == 0) {
-			/* TODO: catch if no value given */
-			iDWarn = atoi((++argv)[0]);
-			argc--;
-		} else {
-			fprintf(stderr, "unknown option %s\n", argv[0]);
-			exit(1);
-		}
-	}
-
-	liststrings(readlist(), puts);
-
-  return 0;
-}
--- a/birthday.h	Mon Dec 17 16:31:40 2007 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,92 +0,0 @@
-/*
-   birthday
-
-   Birthday/Anniversary display on login
-
-   (c) 1996 AS Mortimer
-
-    This program is free software; you can redistribute it and/or
-    modify it under the terms of the GNU General Public License as
-    published by the Free Software Foundation; either version 2 of the
-    License, or (at your option) any later version.  You may also
-    distribute it under the Artistic License, as comes with Perl.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-    You should also have recieved a copy of the Artistic license with
-    this program.
-
-   $Id: birthday.h,v 1.6 1999/04/25 14:01:29 andy Exp $
-*/
-
-#include <stdio.h>
-
-/* ========== Configuration section */
-
-/* standard time to warn in advance, when no explicit w flag is given. */
-#define DEF_WARN 14
-
-/* ========== Global constants and data types */
-
-
-/* month lengths etc */
-
-#define isleapyear(y) ((y)%4==0 && ((y)%100 != 0 || (y)%400 == 0))
-extern const unsigned MLENDAT[];
-#define mlen(m,y) (MLENDAT[(m)-1] != -1 ? MLENDAT[(m)-1] : (isleapyear((y)) ? 29 : 28))
-#define before(a,b) ((a).month < (b).month || ((a).month == (b).month && (a).day < (b).day))
-#define ydelta(a,b) ((int) (b).year - (a).year + before((a),(b)))
-
-/* -------- modifier flags */
-
-#define F_MTYPE 0x07
-#define F_TBIRTHDAY 1
-#define F_TANNIVERSARY 2
-#define F_TEVENT 3
-
-/* flags processed immediately on encountering */
-#define F_MIMMEDIATE 0x24
-#define F_WTIME_P 0x08
-#define F_FORDAYS 0x16
-#define F_TODATE 0x24
-
-struct _ftable {char *txt; unsigned flag;};
-
-extern const struct _ftable FTABLE[];
-
-struct date {
-  unsigned day;
-  unsigned month;
-  unsigned year;
-};
-
-struct event {
-  char *text;
-  struct date date;
-  struct date enddate;
-  int warn;
-};
-
-typedef int (*prnfunc)(const char *);
-
-/* ========== */
-
-struct event* readlist(void);
-void gettoday(void);
-unsigned delta(struct date *);
-unsigned ddiff(struct date *D1, struct date *D2);
-void liststrings(struct event* evl, prnfunc outf);
-char *tdelta(struct date *d);
-char *ttime(int yr, int mn, int wk, int dy);
-
-/* ========== Global Variables */
-
-extern struct date today;
-extern int iDWarn;
-