# HG changeset patch # User markus schnalke # Date 1393272698 -3600 # Node ID 79d22407a6bed05da28c062372923a7b75e343bf # Parent 032af48d590bdb0429d480842572b6e4ef62d3f8 a lot of refactoring diff -r 032af48d590b -r 79d22407a6be Makefile --- a/Makefile Mon Feb 24 17:46:57 2014 +0100 +++ b/Makefile Mon Feb 24 21:11:38 2014 +0100 @@ -1,81 +1,53 @@ # bday by meillo@marmaro.de -NAME=bday -VERSION = 0.1 -NV=${NAME}-${VERSION} +VERSION = 0.2 -DOCS=COPYRIGHT COPYING ChangeLog TODO examples +DOCS=COPYRIGHT COPYING ChangeLog # paths -PREFIX = /usr +PREFIX = /usr/local BINDIR = ${PREFIX}/bin MANDIR = ${PREFIX}/share/man -SRC=bday.c -OBJ=$(SRC:.c=.o) - CFLAGS=-O2 -Wall all: build +build: bday -build: ${NAME} - -${NAME}: $(OBJ) - $(CC) $(LDFLAGS) $(OBJ) -o $@ - -car: clean all +bday: bday.o + $(CC) $(LDFLAGS) bday.o -o $@ dist: build changelog - @mkdir -p ${NV} - @cp -rf ${NAME} ${SRC} ${NAME}.1 Makefile ${DOCS} ${NV} - @tar -czhof ${NV}.tar.gz ${NV} - @rm -rf ${NV} - -deb: dist - @mkdir -p Packages - @cp ${NV}.tar.gz Packages/ - @( \ - cd Packages/ ;\ - tar -xzf ${NV}.tar.gz ;\ - mv ${NV}.tar.gz ${NAME}_${VERSION}.orig.tar.gz ;\ - cd ${NV}/ ;\ - cp -r ../../debian/ . ;\ - debuild -sa ;\ - ) + @mkdir -p bday-${VERSION} + @cp bday bday.c bday.1 Makefile ${DOCS} bday-${VERSION} + @tar -czhf bday-${VERSION}.tar.gz bday-${VERSION} + @rm -rf bday-${VERSION} changelog: @echo generating changelog from mercurial log @hg log -v --style changelog > ChangeLog install: - @echo installing executable file to ${DESTDIR}${BINDIR} - @mkdir -p ${DESTDIR}${BINDIR} - @cp ${NAME} ${DESTDIR}${BINDIR} - @chmod 755 ${DESTDIR}${BINDIR}/${NAME} - @echo installing manual page to ${DESTDIR}${MANDIR}/man1 - @mkdir -p ${DESTDIR}${MANDIR}/man1 - @sed 's/VERSION/${VERSION}/g' < ${NAME}.1 > ${DESTDIR}${MANDIR}/man1/${NAME}.1 - @chmod 644 ${DESTDIR}${MANDIR}/man1/${NAME}.1 + @echo installing executable file to ${BINDIR} + @mkdir -p ${BINDIR} + @cp bday ${BINDIR} + @chmod 755 ${BINDIR}/bday + @echo installing manual page to ${MANDIR}/man1 + @mkdir -p ${MANDIR}/man1 + @sed 's/VERSION/${VERSION}/g' ${MANDIR}/man1/bday.1 + @chmod 644 ${MANDIR}/man1/bday.1 uninstall: - @echo removing executable file from ${DESTDIR}${BINDIR} - @rm -f ${DESTDIR}${BINDIR}/${NAME} - @echo removing manual page from ${DESTDIR}${MANDIR}/man1 - @rm -f ${DESTDIR}${MANDIR}/man1/${NAME}.1 + @echo removing executable file from ${BINDIR} + @rm -f ${BINDIR}/bday + @echo removing manual page from ${MANDIR}/man1 + @rm -f ${MANDIR}/man1/bday.1 clean: rm -f *.o - realclean: clean - rm -f ${NAME} - + rm -f bday distclean: realclean - @rm -f ${NAME}-*.tar.gz ChangeLog - -debclean: distclean - @cd Packages/${NV}/ ; debuild clean ; - - -.PHONY: all dist deb changelog clean distclean debclean build install uninstall + @rm -f bday-*.tar.gz ChangeLog diff -r 032af48d590b -r 79d22407a6be bday.c --- a/bday.c Mon Feb 24 17:46:57 2014 +0100 +++ b/bday.c Mon Feb 24 21:11:38 2014 +0100 @@ -26,21 +26,21 @@ Input is read through standard input. For example: bday < ~/.birthdays The input (file) has to have the following format: -text=date flags +date flags text where: - date is yyyy-mm-dd + date is YYYY-MM-DD flags is ONE or ZERO of - bd for a birthday (default) - ann for an anniversary - ev for an event + #ann for an anniversary + #ev for an event and zero or more of - w to set the warn-in-advance time to n days + #w to set the warn-in-advance time to n days (don't include the brackets! :) - to - for + #to + #for to specify the length of time taken by an - event, for example a holiday. + event, for example a holiday + separated by spaces. Lines preceeded by # are treated as comments. @@ -69,16 +69,8 @@ /* ========== 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 @@ -88,8 +80,10 @@ #define F_FORDAYS 0x16 #define F_TODATE 0x24 -struct _ftable {char* txt; unsigned flag;}; - +struct _ftable { + char* txt; + unsigned flag; +}; const struct _ftable FTABLE[]; struct date { @@ -105,33 +99,29 @@ 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); +void liststrings(struct event *evl); char *tdelta(struct date *d); char *ttime(int yr, int mn, int wk, int dy); -int skptok(int j, char *ptr); +char *skptok(char *ptr); int evcmp(const void *e1, const void *e2); struct date today; -int iDWarn = DEF_WARN; +int def_warn = 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}, + {"#ann",F_TANNIVERSARY}, + {"#ev", F_TEVENT}, + {"#w", F_WTIME_P}, + {"#to", F_TODATE}, + {"#for", F_FORDAYS}, {NULL, 0} }; @@ -174,18 +164,60 @@ /* 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) +int +append(char *where, int size, char *what) +{ + if (strlen(what) > ((size) - strlen(where))) { + xrealloc(where, (size) + 128 + strlen(what)); + size += 128 + strlen(what); + } + strcat(where, what); + return size; +} /* ========== */ + +int +before(struct date a, struct date b) +{ + if (a.month < b.month) { + return 1; + } else if (a.month == b.month && a.day < b.day) { + return 1; + } else { + return 0; + } +} + +int +ydelta(struct date a, struct date b) +{ + return b.year - a.year + before(a, b); +} + +/* +returns the length of the given month +*/ +int +mlen(int month, int year) +{ + unsigned mlendat[] = {31,0,31,30,31,30,31,31,30,31,30,31}; + + if (mlendat[month - 1]) { + return mlendat[month - 1]; + } else { + if (year%4==0 && (year%100!=0 || year%400==0)) { + return 29; + } else { + return 28; + } + } +} + + + /* returns delta(d) in days, weeks, months, etc the returned buffer is malloc()ed, do not forget to free() it @@ -201,10 +233,10 @@ *buf = 0; switch (delta(d)) { case 0: - append(buf, "today"); + size = append(buf, size, "TODAY"); return buf; case 1: - append(buf, "tomorrow"); + size = append(buf, size, "Tomorrow"); return buf; default: /* like delta(), we ignore the year */ @@ -224,9 +256,9 @@ wk = (dy / 7); dy %= 7; - append(buf, "in "); + size = append(buf, size, "In "); tmp = ttime(yr, mn, wk, dy); - append(buf, tmp); + size = append(buf, size, tmp); free(tmp); return buf; @@ -237,66 +269,46 @@ -/* void -donum(n,txt) +donum(char *buf, int size, int n, char *txt, int *terms) { - 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, ", "); - } + char tmp[128]; + + if (n <= 0) { + return; + } + snprintf(tmp, sizeof(tmp), "%d", n); + size = append(buf, size, tmp); + size = append(buf, size, " "); + size = append(buf, size, txt); + if (n != 1) { + size = append(buf, size, "s"); + } + if (--*terms == 1) { + size = append(buf, size, " and "); + } else if (*terms > 1) { + size = append(buf, size, ", "); } } -*/ - - -#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]; + char *buf = xmalloc(size); + int terms = (yr!=0) + (mn!=0) + (wk!=0) + (dy!=0); - *buf = 0; /* Initialize buffer */ - terms = orgterms = (yr!=0) + (mn!=0) + (wk!=0) + (dy!=0); + *buf = '\0'; /* Initialize buffer */ - donum(yr, "year"); - donum(mn, "month"); - donum(wk, "week"); - donum(dy, "day"); + donum(buf, size, yr, "year", &terms); + donum(buf, size, mn, "month", &terms); + donum(buf, size, wk, "week", &terms); + donum(buf, size, dy, "day", &terms); return buf; } -#undef donum @@ -308,58 +320,60 @@ the string to a function. */ void -liststrings(struct event *evl, prnfunc outf) +liststrings(struct event *evl) { int i,j; char *buf, *tmp; int size; - for (i = 0; evl[i].text != NULL; i++) { - buf = xmalloc(128); + for (i=0; evl[i].text; i++) { + size = 128; + buf = xmalloc(size); *buf = '\0'; - size = 128; if (evl[i].warn == -1 && delta(&(evl[i].date))==0) { - append(buf, evl[i].text); + size = append(buf, size, 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); + size = append(buf, size, tmp); + size = append(buf, size, ": "); + size = append(buf, size, evl[i].text); 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 */ + size = append(buf, size, evl[i].text); + size = append(buf, size, " 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); + size = append(buf, size, tmp); free(tmp); - append(buf, " "); + size = append(buf, size, " "); tmp = tdelta(&(evl[i].date)); - append(buf, tmp); + size = append(buf, size, tmp); } else if (delta(&(evl[i].enddate)) <= evl[i].warn) { - append(buf, evl[i].text); - append(buf, " "); + size = append(buf, size, evl[i].text); + size = append(buf, size, " "); j = delta(&(evl[i].enddate)); if (j) { - append(buf, "for "); + size = append(buf, size, "for "); tmp = ttime(0, 0, j/7, j%7); - append(buf, tmp); + size = append(buf, size, tmp); free(tmp); - append(buf, " longer"); + size = append(buf, size, " longer"); } else { - append(buf, "finishes today"); + size = append(buf, size, "finishes today"); } } } if (*buf) { - append(buf, "."); - outf(buf); + size = append(buf, size, "."); + puts(buf); } free(buf); } @@ -534,7 +548,7 @@ int i, j, k, l, d; struct event *evl; char buf[1024], buf2[1024]; - char *ptr; + char *ptr, *cp; unsigned flags; /* initialise */ @@ -550,46 +564,48 @@ } /* parse string in buf */ - ptr = strrchr(buf, '='); /* allow '=' in text */ + + ptr = strchr(buf, ' '); /* start of 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); + if (!ptr) { + fprintf(stderr, "WARNING: Invalid input line:\n\t%s", buf); i--; continue; } - *(ptr++) = 0; + *(ptr++) = '\0'; + ptr[strlen(ptr)-1] = '\0'; - j = sscanf(ptr, "%u-%u-%u", &(evl[i].date.year), + j = sscanf(buf, "%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; + if (j != 3) { + fprintf(stderr, "Error: Invalid date:\t%s\n", buf); + i--; + continue; } - /* parse flags */ - evl[i].warn = iDWarn; + evl[i].warn = def_warn; 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++) { + cp = skptok(ptr); + for (cp=ptr; *cp && *cp=='#'; cp=skptok(cp)) { + for (k = 0; FTABLE[k].txt && strncmp(FTABLE[k].txt, cp, strlen(FTABLE[k].txt)); k++) { } switch (FTABLE[k].flag) { - case F_WTIME_P: /* w -- sets warning time */ - sscanf(ptr + j, "w %u", &(evl[i].warn)); + case F_WTIME_P: /* #w -- sets warning time */ + sscanf(cp, "#w%u", &(evl[i].warn)); break; - case F_FORDAYS: /* for -- sets the duration of the event */ - sscanf(ptr + j, "for %u", &d); + case F_FORDAYS: /* #for -- sets the duration of the event */ + sscanf(cp, "#for%u", &d); evl[i].enddate=evl[i].date; for (l = 1; l < d; l++) { evl[i].enddate.day++; @@ -603,8 +619,8 @@ } } break; - case F_TODATE: /* to -- 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)); + case F_TODATE: /* #to -- sets the end date of the event */ + l = sscanf(cp, "#to%u-%u-%u", &(evl[i].enddate.year), &(evl[i].enddate.month), &(evl[i].enddate.day)); if (l == 2) { evl[i].enddate.year = 0; } @@ -621,34 +637,32 @@ /* 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); + if (!evl[i].date.year) { + sprintf(buf2, "%s has a birthday", cp); + break; } + int tmp_age = ydelta(evl[i].date, today); + sprintf(buf2, "%s is %d year%s old", + cp, tmp_age, (tmp_age>1)?"s":""); break; case F_TANNIVERSARY: - if (evl[i].date.year != 0) { - sprintf(buf2, "%s %d years ago", buf, ydelta(evl[i].date, today)); + if (evl[i].date.year) { + sprintf(buf2, "%s %d years ago", + cp, ydelta(evl[i].date, today)); } else { - strcpy(buf2, buf); + strcpy(buf2, cp); } 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)) { + /* if a year was specified, and this + warning isn't for it, ignore! */ + if ((evl[i].date.year && ydelta(evl[i].date, today)) + && (!evl[i].enddate.year || ydelta(evl[i].enddate, today))) { i--; continue; } - strcpy(buf2, buf); + strcpy(buf2, cp); break; } evl[i].text = strdup(buf2); @@ -671,13 +685,16 @@ -int -skptok(int j, char *ptr) +char * +skptok(char *ptr) { - for (; ptr[j] != 0 && ptr[j] != ' ' && ptr[j] != '\t' ; j++); - for (; ptr[j] != 0 && (ptr[j] == ' ' || ptr[j] == '\t'); j++); - - return j; + while (*ptr && (*ptr!=' ' && *ptr!='\t')) { + ptr++; + } + while (*ptr && (*ptr==' ' || *ptr=='\t')) { + ptr++; + } + return ptr; } @@ -688,18 +705,16 @@ int main(int argc, char *argv[]) { - while (--argc > 0 && (*++argv)[0] == '-') { + while (--argc > 0 && **++argv == '-') { if (strcmp(argv[0], "-W") == 0) { /* TODO: catch if no value given */ - iDWarn = atoi((++argv)[0]); + def_warn = atoi((++argv)[0]); argc--; } else { fprintf(stderr, "unknown option %s\n", argv[0]); exit(1); } } - - liststrings(readlist(), puts); - + liststrings(readlist()); return 0; }