changeset 0:22b6e71de68e

initial commit; codebase from birthday; just the needed stuff; substituted getopt by own code
author meillo@marmaro.de
date Sun, 16 Dec 2007 22:26:48 +0100
parents
children 8534f0e3a0db
files .hgignore Makefile bdengine.c birthday.c birthday.h
diffstat 5 files changed, 924 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Sun Dec 16 22:26:48 2007 +0100
@@ -0,0 +1,7 @@
+syntax: glob
+
+*~
+*.swp
+
+*.o
+bday
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Sun Dec 16 22:26:48 2007 +0100
@@ -0,0 +1,31 @@
+######################################################################
+# birthday. Reminder of birthdays and other events in the near future.
+# $Id: Makefile.in,v 1.3 2000/01/02 19:17:33 andy Exp $
+
+
+CFLAGS=-O2 -Wall -Wstrict-prototypes
+
+
+SRC=birthday.c bdengine.c
+OBJ=$(SRC:.c=.o)
+EXE=bday
+
+all: ${EXE}
+
+${EXE}: $(OBJ)
+	$(CC) $(LDFLAGS) $(OBJ) -o $@
+
+
+install: birthday birthday.man
+	mkdir -p $(DESTDIR)/usr/bin $(DESTDIR)/usr/share/man/man1
+	cp ${EXE} $(DESTDIR)/usr/bin/${EXE}
+	chmod 0755 $(DESTDIR)/usr/bin/${EXE}
+	cp ${EXE}.man $(DESTDIR)/usr/share/man/man1/${EXE}.1
+	chmod 0644 $(DESTDIR)/usr/share/man/man1/${EXE}.1
+
+
+clean:
+	rm -f *.o
+
+realclean: clean
+	rm -f ${EXE}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bdengine.c	Sun Dec 16 22:26:48 2007 +0100
@@ -0,0 +1,717 @@
+/*
+	 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  bir for a birthday (exactly equivalent to `bd')
+			 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 <pwd.h>
+
+#include "birthday.h"
+
+/* ========== */
+
+
+
+/*
+	xmalloc/xrealloc functions, and fatal exit function
+	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);
+char *deffname(void);
+
+struct event *dir_include(char *dir, char *parm);
+
+/* ========== Global variables */
+
+struct date today;
+int iDWarn   = DEF_WARN;
+int iMaxWarn = MAX_WARN;
+int iMinWarn = MIN_WARN;
+
+const unsigned MLENDAT[]={31,-1,31,30,31,30,31,31,30,31,30,31};
+
+const struct _ftable FTABLE[] = {
+	{"bir",F_TBIRTHDAY},
+	{"bd", F_TBIRTHDAY},
+	{"ann",F_TANNIVERSARY},
+	{"ev", F_TEVENT},
+	{"mes", F_TMESSAGE},
+	{"w",  F_WTIME_P},
+	{"to", F_TODATE},
+	{"for", F_FORDAYS},
+	{NULL, 0}
+};
+
+
+/* list of directives. These are entered in the file prefixed by '&'. The function should be declared as
+	 	struct event *dir_func(char *directive, char *parameters);
+	 and should return a pointer to a NULL-terminated list of extra records, or NULL if there are none.
+	 This structure will allow us to dynamically add directives, if we wish to do so in the future.
+	 */
+
+struct {
+	const char *text;
+	int len;
+	struct event *(*func)(char*,char*);
+} directlist[] = {
+	{ "include", 0, dir_include },
+	{ NULL, 0, NULL }
+};
+
+/* ========== */
+
+
+
+
+/* compare the first strlen(a) characters of a and b */
+#define strbegcmp(a,b) strncmp(a,b,strlen(a))
+
+/* 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);
+
+		if (*(buf + strlen(buf) - 1) == 's')
+			append(buf, "'");
+		else
+			append(buf, "'s");
+
+		append(buf, " time");
+
+		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)) <= warnperiod(evl[i])) {
+				append(buf, evl[i].text);
+				append(buf, " ");
+				tmp = tdelta(&(evl[i].date));
+				append(buf, tmp);
+				free(tmp);
+			}
+		} else {
+			if (delta(&(evl[i].date)) <= warnperiod(evl[i])) {
+				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)) <= warnperiod(evl[i])) {
+				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);
+	}
+}
+
+
+
+
+
+
+
+char* deffname(void) {
+char buf[256];
+
+	strcpy(buf,getpwuid(getuid())->pw_dir);
+	strcat(buf, "/" DEFAULT_FILE);
+
+	return strdup(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;
+	today.year += 1900;
+}
+
+
+
+
+
+struct event *readlist(char *fname) {
+	FILE *file;
+	int i,j,k,l,d;
+	struct event *evl;
+	char buf[1024], buf2[1024];
+	char *ptr;
+	unsigned flags;
+	struct event *nevl;
+	/* initialise */
+	if (fname==NULL) {
+		fname=deffname();
+	}
+
+	gettoday();
+
+	if (fname[0] == '-' && fname[1] == 0) {
+		/* read from stdin */
+		file=stdin;
+	} else {
+		/* now read it */
+		if((file=fopen(fname, "rt"))==NULL) {
+			fprintf(stderr, "Unable to open file \"%s\"\n", fname);
+			exit(1);
+		}
+	}
+
+
+	for (i = 0, evl=NULL; fgets(buf, sizeof(buf), file) != NULL; i++) {
+		evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1));
+
+		if (*buf == '#' || *buf == '\n')
+			{
+	i--;
+	continue;
+			}
+		if (*buf == '&') {
+			buf[strlen(buf)-1] = 0;
+			if ((ptr=strchr(buf+1,' ')) == NULL && (ptr=strchr((buf+1),'\t')) == NULL) {
+				ptr=(buf+1)+strlen((buf+1));
+			}
+
+			*ptr++=0;
+			nevl=NULL;
+			k=0;
+			for (j=0; directlist[j].text != NULL; j++) {
+				if (directlist[j].len == 0) directlist[j].len = strlen(directlist[j].text);
+				/*
+					fprintf(stderr, "Checking against directive #%d, %s, which has length %d, against \"%s\" with length %d\n",
+					j, directlist[j].text, directlist[j].len, buf+1, ptr-(buf+1)-1);
+					*/
+				if (directlist[j].len == ptr-(buf+1)-1 && !strncmp(directlist[j].text,(buf+1),directlist[j].len)) {
+					nevl = directlist[j].func((buf+1),ptr);
+					k=1;
+					break;
+				}
+			}
+
+			if (nevl != NULL) {
+				for (j=0; nevl[j].text != NULL; j++);
+				i+=j-1;
+				evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1));
+				/* in fact, this loop reverses the order of the elements, but we're going to sort them later anyway. */
+				for (j=0; nevl[j].text != NULL; j++)
+					evl[i-j]=nevl[j];
+			}
+			if (!k) {
+				fprintf(stderr, "Warning: unrecognised directive \"%s\" ignored.\n", (buf+1));
+				i--;
+			}
+			continue;
+		}
+
+		/* parse string in buf */
+		ptr = strrchr(buf, '='); /* allow '=' in text */
+		if(ptr==NULL) /* not a valid line, so ignore it! Cool, huh? */
+			{
+				fprintf(stderr, "WARNING: Invalid line in input file:\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));
+		/* if our year is only two digits, add 1900 to it ... */
+		if(evl[i].date.year < 100) evl[i].date.year+=1900;
+		/* ... 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=evl[i].enddate.month=evl[i].enddate.year=0;
+
+		flags=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 %d", &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:
+					l = sscanf(ptr + j, "to %u-%u-%u", &(evl[i].enddate.year), &(evl[i].enddate.month), &(evl[i].enddate.day));
+					if (evl[i].enddate.year < 100) evl[i].enddate.year+=1900;
+					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;
+		case F_TMESSAGE:
+			/* Like an event, except that it only comes up on the given date, and no text at all is appended */
+			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);
+			evl[i].warn=-1; /* special code! */
+			break;
+		}
+		evl[i].text = strdup(buf2);
+	}
+
+	evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1));
+	evl[i].date.day=evl[i].date.month=evl[i].date.year=0;
+	evl[i].text = (char *) NULL;
+
+	fclose(file);
+	free(fname);
+
+	/* 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;
+}
+
+
+
+
+
+
+/* ---------------------------------------------------------------------- */
+/* Functions to parse input file directives */
+struct event *dir_include(char *dir, char *parm) {
+	struct event *evl;
+
+	evl=readlist(strdup(parm));
+
+	return evl;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/birthday.c	Sun Dec 16 22:26:48 2007 +0100
@@ -0,0 +1,65 @@
+/*
+   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[])
+{
+  char* fname = NULL;
+  struct event *evl; /* evl => event list */
+  char* opt = NULL;
+
+  while (--argc > 0 && (*++argv)[0] == '-') {
+    if (strcmp(argv[0], "-f") == 0) {
+			fname = strdup((++argv)[0]);
+			argc--;
+		} else if (strcmp(argv[0], "-W") == 0) {
+			iDWarn = atoi((++argv)[0]);
+			argc--;
+		} else if (strcmp(argv[0], "-M") == 0) {
+			iMaxWarn = atoi((++argv)[0]);
+			argc--;
+		} else if (strcmp(argv[0], "-m") == 0) {
+			iMinWarn = atoi((++argv)[0]);
+			argc--;
+		} else {
+			fprintf(stderr, "unknown option %s\n", opt);
+			exit(1);
+		}
+	}
+
+  evl = readlist(fname); /* read and format entries */
+
+	liststrings(evl, puts);
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/birthday.h	Sun Dec 16 22:26:48 2007 +0100
@@ -0,0 +1,104 @@
+/*
+   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 $
+*/
+
+/* ========== Configuration section */
+
+#define DEFAULT_FILE ".birthdays"
+
+/* standard time to warn in advance, when no explicit w flag is given. */
+#define DEF_WARN 21
+/* maximum time to warn in advance when no M flag is given */
+#define MAX_WARN 500 /* ie, a year */
+/* minimum time to warn in advance when no m flag */
+#define MIN_WARN 0
+
+/* ========== Required includes */
+
+#include <stdio.h>
+
+/* ========== 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)))
+#define warnperiod(ev) ((ev).warn<iMinWarn?iMinWarn:((ev).warn>iMaxWarn?iMaxWarn:(ev).warn))
+
+/* -------- modifier flags */
+
+#define F_MTYPE 0x07
+#define F_TBIRTHDAY 1
+#define F_TANNIVERSARY 2
+#define F_TEVENT 3
+#define F_TMESSAGE 4
+
+/* 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(char *fname);
+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;
+extern int iMaxWarn;
+extern int iMinWarn;
+