bday

diff bday.c @ 16:79d22407a6be

a lot of refactoring
author markus schnalke <meillo@marmaro.de>
date Mon, 24 Feb 2014 21:11:38 +0100
parents 032af48d590b
children d18a3b2b76bd
line diff
     1.1 --- a/bday.c	Mon Feb 24 17:46:57 2014 +0100
     1.2 +++ b/bday.c	Mon Feb 24 21:11:38 2014 +0100
     1.3 @@ -26,21 +26,21 @@
     1.4  Input is read through standard input. For example: bday < ~/.birthdays
     1.5  The input (file) has to have the following format:
     1.6  
     1.7 -text=date flags
     1.8 +date flags text
     1.9  
    1.10  where:
    1.11 -	date is yyyy-mm-dd
    1.12 +	date is YYYY-MM-DD
    1.13  	flags is ONE or ZERO of
    1.14 -		bd  for a birthday (default)
    1.15 -		ann for an anniversary
    1.16 -		ev  for an event
    1.17 +		#ann for an anniversary
    1.18 +		#ev  for an event
    1.19  	and zero or more of
    1.20 -		w <n> to set the warn-in-advance time to n days
    1.21 +		#w<n> to set the warn-in-advance time to n days
    1.22  			(don't include the brackets! :)
    1.23 -		to <date>
    1.24 -		for <days>
    1.25 +		#to<date>
    1.26 +		#for<days>
    1.27  			to specify the length of time taken by an
    1.28 -			event, for example a holiday.
    1.29 +			event, for example a holiday
    1.30 +	separated by spaces.
    1.31  
    1.32  Lines preceeded by # are treated as comments.
    1.33  
    1.34 @@ -69,16 +69,8 @@
    1.35  /* ========== Global constants and data types */
    1.36  
    1.37  
    1.38 -/* month lengths etc */
    1.39 -#define isleapyear(y) ((y)%4==0 && ((y)%100 != 0 || (y)%400 == 0))
    1.40 -const unsigned MLENDAT[];
    1.41 -#define mlen(m,y) (MLENDAT[(m)-1] != -1 ? MLENDAT[(m)-1] : (isleapyear((y)) ? 29 : 28))
    1.42 -#define before(a,b) ((a).month < (b).month || ((a).month == (b).month && (a).day < (b).day))
    1.43 -#define ydelta(a,b) ((int) (b).year - (a).year + before((a),(b)))
    1.44 -
    1.45  /* -------- modifier flags */
    1.46  #define F_MTYPE 0x07
    1.47 -#define F_TBIRTHDAY 1
    1.48  #define F_TANNIVERSARY 2
    1.49  #define F_TEVENT 3
    1.50  
    1.51 @@ -88,8 +80,10 @@
    1.52  #define F_FORDAYS 0x16
    1.53  #define F_TODATE 0x24
    1.54  
    1.55 -struct _ftable {char* txt; unsigned flag;};
    1.56 -
    1.57 +struct _ftable {
    1.58 +	char* txt;
    1.59 +	unsigned flag;
    1.60 +};
    1.61  const struct _ftable FTABLE[];
    1.62  
    1.63  struct date {
    1.64 @@ -105,33 +99,29 @@
    1.65  	int warn;
    1.66  };
    1.67  
    1.68 -typedef int (*prnfunc)(const char *);
    1.69 -
    1.70  /* ========== Global Variables */
    1.71  
    1.72  struct event *readlist(void);
    1.73  void gettoday(void);
    1.74  unsigned delta(struct date *);
    1.75  unsigned ddiff(struct date *D1, struct date *D2);
    1.76 -void liststrings(struct event *evl, prnfunc outf);
    1.77 +void liststrings(struct event *evl);
    1.78  char *tdelta(struct date *d);
    1.79  char *ttime(int yr, int mn, int wk, int dy);
    1.80 -int skptok(int j, char *ptr);
    1.81 +char *skptok(char *ptr);
    1.82  int evcmp(const void *e1, const void *e2);
    1.83  
    1.84  
    1.85  struct date today;
    1.86 -int iDWarn = DEF_WARN;
    1.87 +int def_warn = DEF_WARN;
    1.88  
    1.89 -const unsigned MLENDAT[] = {31,-1,31,30,31,30,31,31,30,31,30,31};
    1.90  
    1.91  const struct _ftable FTABLE[] = {
    1.92 -	{"bd", F_TBIRTHDAY},
    1.93 -	{"ann",F_TANNIVERSARY},
    1.94 -	{"ev", F_TEVENT},
    1.95 -	{"w",  F_WTIME_P},
    1.96 -	{"to", F_TODATE},
    1.97 -	{"for", F_FORDAYS},
    1.98 +	{"#ann",F_TANNIVERSARY},
    1.99 +	{"#ev", F_TEVENT},
   1.100 +	{"#w",  F_WTIME_P},
   1.101 +	{"#to", F_TODATE},
   1.102 +	{"#for", F_FORDAYS},
   1.103  	{NULL, 0}
   1.104  };
   1.105  
   1.106 @@ -174,18 +164,60 @@
   1.107  
   1.108  /*
   1.109  like strcat(), but lets the buffer automagically grow :-)
   1.110 -(needs local variable "size" with the buffer size)
   1.111  */
   1.112 -#define append(where, what) do {                            \
   1.113 -	if (strlen(what) > (size - strlen(where))) {          \
   1.114 -		xrealloc(where, size + 128 + strlen(what));   \
   1.115 -		size += 128 + strlen(what);                   \
   1.116 -	}                                                     \
   1.117 -	strcat(where, what);                                  \
   1.118 -} while(0)
   1.119 +int
   1.120 +append(char *where, int size, char *what)
   1.121 +{
   1.122 +	if (strlen(what) > ((size) - strlen(where))) {
   1.123 +		xrealloc(where, (size) + 128 + strlen(what));
   1.124 +		size += 128 + strlen(what);
   1.125 +	}
   1.126 +	strcat(where, what);
   1.127 +	return size;
   1.128 +}
   1.129  
   1.130  /* ========== */
   1.131  
   1.132 +
   1.133 +int
   1.134 +before(struct date a, struct date b)
   1.135 +{
   1.136 +	if (a.month < b.month) {
   1.137 +		return 1;
   1.138 +	} else if (a.month == b.month && a.day < b.day) {
   1.139 +		return 1;
   1.140 +	} else {
   1.141 +		return 0;
   1.142 +	}
   1.143 +}
   1.144 +
   1.145 +int
   1.146 +ydelta(struct date a, struct date b)
   1.147 +{
   1.148 +	return b.year - a.year + before(a, b);
   1.149 +}
   1.150 +
   1.151 +/*
   1.152 +returns the length of the given month
   1.153 +*/
   1.154 +int
   1.155 +mlen(int month, int year)
   1.156 +{
   1.157 +	unsigned mlendat[] = {31,0,31,30,31,30,31,31,30,31,30,31};
   1.158 +
   1.159 +	if (mlendat[month - 1]) {
   1.160 +		return mlendat[month - 1];
   1.161 +	} else {
   1.162 +		if (year%4==0 && (year%100!=0 || year%400==0)) {
   1.163 +			return 29;
   1.164 +		} else {
   1.165 +			return 28;
   1.166 +		}
   1.167 +	}
   1.168 +}
   1.169 +
   1.170 +
   1.171 +
   1.172  /*
   1.173  returns delta(d) in days, weeks, months, etc
   1.174  the returned buffer is malloc()ed, do not forget to free() it
   1.175 @@ -201,10 +233,10 @@
   1.176  	*buf = 0;
   1.177  	switch (delta(d)) {
   1.178  	case 0:
   1.179 -		append(buf, "today");
   1.180 +		size = append(buf, size, "TODAY");
   1.181  		return buf;
   1.182  	case 1:
   1.183 -		append(buf, "tomorrow");
   1.184 +		size = append(buf, size, "Tomorrow");
   1.185  		return buf;
   1.186  	default:
   1.187  		/* like delta(), we ignore the year */
   1.188 @@ -224,9 +256,9 @@
   1.189  		wk = (dy / 7);
   1.190  		dy %= 7;
   1.191  
   1.192 -		append(buf, "in ");
   1.193 +		size = append(buf, size, "In ");
   1.194  		tmp = ttime(yr, mn, wk, dy);
   1.195 -		append(buf, tmp);
   1.196 +		size = append(buf, size, tmp);
   1.197  		free(tmp);
   1.198  
   1.199  		return buf;
   1.200 @@ -237,66 +269,46 @@
   1.201  
   1.202  
   1.203  
   1.204 -/*
   1.205  void
   1.206 -donum(n,txt)
   1.207 +donum(char *buf, int size, int n, char *txt, int *terms)
   1.208  {
   1.209 -	if (n > 0) {
   1.210 -		snprintf(tmp, sizeof(tmp), "%d", n);
   1.211 -		append(buf, tmp);
   1.212 -		append(buf, " " txt);
   1.213 -		if (n != 1)
   1.214 -			append(buf, "s");
   1.215 -		terms--;
   1.216 -		if (orgterms > 1) {
   1.217 -			if (terms == 1)
   1.218 -				append(buf, " and ");
   1.219 -			else if (terms > 1)
   1.220 -				append(buf, ", ");
   1.221 -		}
   1.222 +	char tmp[128];
   1.223 +
   1.224 +	if (n <= 0) {
   1.225 +		return;
   1.226 +	}
   1.227 +	snprintf(tmp, sizeof(tmp), "%d", n);
   1.228 +	size = append(buf, size, tmp);
   1.229 +	size = append(buf, size, " ");
   1.230 +	size = append(buf, size, txt);
   1.231 +	if (n != 1) {
   1.232 +		size = append(buf, size, "s");
   1.233 +	}
   1.234 +	if (--*terms == 1) {
   1.235 +		size = append(buf, size, " and ");
   1.236 +	} else if (*terms > 1) {
   1.237 +		size = append(buf, size, ", ");
   1.238  	}
   1.239  }
   1.240 -*/
   1.241 -
   1.242 -
   1.243 -#define donum(n,txt) do {                       \
   1.244 -	if (n > 0) {                              \
   1.245 -		snprintf(tmp, sizeof(tmp), "%d", n);   \
   1.246 -		append(buf, tmp);                      \
   1.247 -		append(buf, " " txt);                  \
   1.248 -		if (n != 1)                            \
   1.249 -			append(buf, "s");                   \
   1.250 -		terms--;                               \
   1.251 -		if (orgterms > 1) {                    \
   1.252 -			if (terms == 1)                     \
   1.253 -				append(buf, " and ");            \
   1.254 -			else if (terms > 1)                 \
   1.255 -				append(buf, ", ");               \
   1.256 -		}                                      \
   1.257 -	}                                         \
   1.258 -} while(0)
   1.259  
   1.260  
   1.261  /* returns allocated buffer, don't forget to free() */
   1.262  char *
   1.263  ttime(int yr, int mn, int wk, int dy)
   1.264  {
   1.265 -	char *buf = xmalloc(128);
   1.266  	int size = 128;
   1.267 -	int terms, orgterms;
   1.268 -	char tmp[128];
   1.269 +	char *buf = xmalloc(size);
   1.270 +	int terms = (yr!=0) + (mn!=0) + (wk!=0) + (dy!=0);
   1.271  
   1.272 -	*buf = 0; /* Initialize buffer */
   1.273 -	terms = orgterms = (yr!=0) + (mn!=0) + (wk!=0) + (dy!=0);
   1.274 +	*buf = '\0'; /* Initialize buffer */
   1.275  
   1.276 -	donum(yr, "year");
   1.277 -	donum(mn, "month");
   1.278 -	donum(wk, "week");
   1.279 -	donum(dy, "day");
   1.280 +	donum(buf, size, yr, "year", &terms);
   1.281 +	donum(buf, size, mn, "month", &terms);
   1.282 +	donum(buf, size, wk, "week", &terms);
   1.283 +	donum(buf, size, dy, "day", &terms);
   1.284  
   1.285  	return buf;
   1.286  }
   1.287 -#undef donum
   1.288  
   1.289  
   1.290  
   1.291 @@ -308,58 +320,60 @@
   1.292  the string to a function.
   1.293  */
   1.294  void
   1.295 -liststrings(struct event *evl, prnfunc outf)
   1.296 +liststrings(struct event *evl)
   1.297  {
   1.298  	int i,j;
   1.299  	char *buf, *tmp;
   1.300  	int size;
   1.301  
   1.302 -	for (i = 0; evl[i].text != NULL; i++) {
   1.303 -		buf = xmalloc(128);
   1.304 +	for (i=0; evl[i].text; i++) {
   1.305 +		size = 128;
   1.306 +		buf = xmalloc(size);
   1.307  		*buf = '\0';
   1.308 -		size = 128;
   1.309  
   1.310  		if (evl[i].warn == -1 && delta(&(evl[i].date))==0) {
   1.311 -			 append(buf, evl[i].text);
   1.312 +			 size = append(buf, size, evl[i].text);
   1.313  		} else if (evl[i].enddate.day == 0) {
   1.314 +			
   1.315  			if (delta(&(evl[i].date)) <= evl[i].warn) {
   1.316 -				append(buf, evl[i].text);
   1.317 -				append(buf, " ");
   1.318  				tmp = tdelta(&(evl[i].date));
   1.319 -				append(buf, tmp);
   1.320 +				size = append(buf, size, tmp);
   1.321 +				size = append(buf, size, ":  ");
   1.322 +				size = append(buf, size, evl[i].text);
   1.323  				free(tmp);
   1.324  			}
   1.325  		} else {
   1.326  			if (delta(&(evl[i].date)) <= evl[i].warn) {
   1.327 -				append(buf, evl[i].text);
   1.328 -				append(buf, " for ");
   1.329 -				/* +1 because, if the difference between two dates is one day,
   1.330 -				   then the length of an event on those days is two days */
   1.331 +				size = append(buf, size, evl[i].text);
   1.332 +				size = append(buf, size, " for ");
   1.333 +				/* +1 because, if the difference between
   1.334 +				two dates is one day, then the length of
   1.335 +				an event on those days is two days */
   1.336  				j = ddiff(&(evl[i].date),&(evl[i].enddate)) + 1;
   1.337  				tmp = ttime(0, 0, j/7, j%7);
   1.338 -				append(buf, tmp);
   1.339 +				size = append(buf, size, tmp);
   1.340  				free(tmp);
   1.341 -				append(buf, " ");
   1.342 +				size = append(buf, size, " ");
   1.343  				tmp = tdelta(&(evl[i].date));
   1.344 -				append(buf, tmp);
   1.345 +				size = append(buf, size, tmp);
   1.346  			} else if (delta(&(evl[i].enddate)) <= evl[i].warn) {
   1.347 -				append(buf, evl[i].text);
   1.348 -				append(buf, " ");
   1.349 +				size = append(buf, size, evl[i].text);
   1.350 +				size = append(buf, size, " ");
   1.351  				j = delta(&(evl[i].enddate));
   1.352  				if (j) {
   1.353 -					append(buf, "for ");
   1.354 +					size = append(buf, size, "for ");
   1.355  					tmp = ttime(0, 0, j/7, j%7);
   1.356 -					append(buf, tmp);
   1.357 +					size = append(buf, size, tmp);
   1.358  					free(tmp);
   1.359 -					append(buf, " longer");
   1.360 +					size = append(buf, size, " longer");
   1.361  				} else {
   1.362 -					append(buf, "finishes today");
   1.363 +					size = append(buf, size, "finishes today");
   1.364  				}
   1.365  			}
   1.366  		}
   1.367  		if (*buf) {
   1.368 -			append(buf, ".");
   1.369 -			outf(buf);
   1.370 +			size = append(buf, size, ".");
   1.371 +			puts(buf);
   1.372  		}
   1.373  		free(buf);
   1.374  	}
   1.375 @@ -534,7 +548,7 @@
   1.376  	int i, j, k, l, d;
   1.377  	struct event *evl;
   1.378  	char buf[1024], buf2[1024];
   1.379 -	char *ptr;
   1.380 +	char *ptr, *cp;
   1.381  	unsigned flags;
   1.382  
   1.383  	/* initialise */
   1.384 @@ -550,46 +564,48 @@
   1.385  		}
   1.386  
   1.387  		/* parse string in buf */
   1.388 -		ptr = strrchr(buf, '='); /* allow '=' in text */
   1.389 +
   1.390 +		ptr = strchr(buf, ' ');  /* start of text */
   1.391  
   1.392  		/* not a valid line, so ignore it! Cool, huh? */
   1.393  		/* Attention: only recognizes lines without '=' */
   1.394 -		if (ptr == NULL) {
   1.395 -			fprintf(stderr, "WARNING: Invalid line in input:\n%s", buf);
   1.396 +		if (!ptr) {
   1.397 +			fprintf(stderr, "WARNING: Invalid input line:\n\t%s", buf);
   1.398  			i--;
   1.399  			continue;
   1.400  		}
   1.401  
   1.402 -		*(ptr++) = 0;
   1.403 +		*(ptr++) = '\0';
   1.404 +		ptr[strlen(ptr)-1] = '\0';
   1.405  
   1.406 -		j = sscanf(ptr, "%u-%u-%u", &(evl[i].date.year),
   1.407 +		j = sscanf(buf, "%u-%u-%u", &(evl[i].date.year),
   1.408  				&(evl[i].date.month), &(evl[i].date.day));
   1.409 -		/* ... unless it wasn't read, in which case set it to zero */
   1.410 -		if (j==2) {
   1.411 -			evl[i].date.year = 0;
   1.412 +		if (j != 3) {
   1.413 +			fprintf(stderr, "Error: Invalid date:\t%s\n", buf);
   1.414 +			i--;
   1.415 +			continue;
   1.416  		}
   1.417  
   1.418 -
   1.419  		/* parse flags */
   1.420  
   1.421 -		evl[i].warn = iDWarn;
   1.422 +		evl[i].warn = def_warn;
   1.423  		evl[i].enddate.day = 0;
   1.424  		evl[i].enddate.month = 0;
   1.425  		evl[i].enddate.year = 0;
   1.426  
   1.427  		flags = 0;
   1.428  		j = 0;
   1.429 -
   1.430 -		while(j = skptok(j, ptr), ptr[j] != 0) {
   1.431 -			for (k = 0; FTABLE[k].txt != NULL && strncmp(FTABLE[k].txt, ptr + j, strlen(FTABLE[k].txt)); k++) {
   1.432 +		cp = skptok(ptr);
   1.433 +		for (cp=ptr; *cp && *cp=='#'; cp=skptok(cp)) {
   1.434 +			for (k = 0; FTABLE[k].txt && strncmp(FTABLE[k].txt, cp, strlen(FTABLE[k].txt)); k++) {
   1.435  			}
   1.436  
   1.437  			switch (FTABLE[k].flag) {
   1.438 -			case F_WTIME_P: /* w <n> -- sets warning time */
   1.439 -				sscanf(ptr + j, "w %u", &(evl[i].warn));
   1.440 +			case F_WTIME_P: /* #w<n> -- sets warning time */
   1.441 +				sscanf(cp, "#w%u", &(evl[i].warn));
   1.442  				break;
   1.443 -			case F_FORDAYS: /* for <days> -- sets the duration of the event */
   1.444 -				sscanf(ptr + j, "for %u", &d);
   1.445 +			case F_FORDAYS: /* #for<days> -- sets the duration of the event */
   1.446 +				sscanf(cp, "#for%u", &d);
   1.447  				evl[i].enddate=evl[i].date;
   1.448  				for (l = 1; l < d; l++) {
   1.449  					evl[i].enddate.day++;
   1.450 @@ -603,8 +619,8 @@
   1.451  					}
   1.452  				}
   1.453  				break;
   1.454 -			case F_TODATE: /* to <date> -- sets the end date of the event */
   1.455 -				l = sscanf(ptr + j, "to %u-%u-%u", &(evl[i].enddate.year), &(evl[i].enddate.month), &(evl[i].enddate.day));
   1.456 +			case F_TODATE: /* #to<date> -- sets the end date of the event */
   1.457 +				l = sscanf(cp, "#to%u-%u-%u", &(evl[i].enddate.year), &(evl[i].enddate.month), &(evl[i].enddate.day));
   1.458  				if (l == 2) {
   1.459  					evl[i].enddate.year = 0;
   1.460  				}
   1.461 @@ -621,34 +637,32 @@
   1.462  		/* construct event text */
   1.463  
   1.464  		switch(flags & F_MTYPE) {
   1.465 -			case F_TBIRTHDAY:
   1.466  			default: /* assume it's a birthday */
   1.467 -				if (evl[i].date.year != 0) {
   1.468 -					int tmp_age = ydelta(evl[i].date, today);
   1.469 -					if (tmp_age != 1) {
   1.470 -						sprintf(buf2, "%s is %d years old", buf, tmp_age);
   1.471 -					} else {
   1.472 -						sprintf(buf2, "%s is %d year old", buf, tmp_age);
   1.473 -					}
   1.474 -				} else {
   1.475 -					sprintf(buf2, "%s has a birthday", buf);
   1.476 +				if (!evl[i].date.year) {
   1.477 +					sprintf(buf2, "%s has a birthday", cp);
   1.478 +					break;
   1.479  				}
   1.480 +				int tmp_age = ydelta(evl[i].date, today);
   1.481 +				sprintf(buf2, "%s is %d year%s old",
   1.482 +				  cp, tmp_age, (tmp_age>1)?"s":"");
   1.483  				break;
   1.484  			case F_TANNIVERSARY:
   1.485 -				if (evl[i].date.year != 0) {
   1.486 -					sprintf(buf2, "%s %d years ago", buf, ydelta(evl[i].date, today));
   1.487 +				if (evl[i].date.year) {
   1.488 +					sprintf(buf2, "%s %d years ago",
   1.489 +					  cp, ydelta(evl[i].date, today));
   1.490  				} else {
   1.491 -					strcpy(buf2, buf);
   1.492 +					strcpy(buf2, cp);
   1.493  				}
   1.494  				break;
   1.495  			case F_TEVENT:
   1.496 -				/* if a year was specified, and this warning isn't for it, ignore! */
   1.497 -				if ((evl[i].date.year != 0 && ydelta(evl[i].date, today) != 0)
   1.498 -				    && (evl[i].enddate.year == 0 || ydelta(evl[i].enddate, today) != 0)) {
   1.499 +				/* if a year was specified, and this
   1.500 +				   warning isn't for it, ignore! */
   1.501 +				if ((evl[i].date.year && ydelta(evl[i].date, today))
   1.502 +				    && (!evl[i].enddate.year || ydelta(evl[i].enddate, today))) {
   1.503  					i--;
   1.504  					continue;
   1.505  				}
   1.506 -				strcpy(buf2, buf);
   1.507 +				strcpy(buf2, cp);
   1.508  				break;
   1.509  		}
   1.510  		evl[i].text = strdup(buf2);
   1.511 @@ -671,13 +685,16 @@
   1.512  
   1.513  
   1.514  
   1.515 -int
   1.516 -skptok(int j, char *ptr)
   1.517 +char *
   1.518 +skptok(char *ptr)
   1.519  {
   1.520 -	for (; ptr[j] != 0 &&  ptr[j] != ' ' && ptr[j] != '\t' ; j++);
   1.521 -	for (; ptr[j] != 0 && (ptr[j] == ' ' || ptr[j] == '\t'); j++);
   1.522 -
   1.523 -	return j;
   1.524 +	while (*ptr && (*ptr!=' ' && *ptr!='\t')) {
   1.525 +		ptr++;
   1.526 +	}
   1.527 +	while (*ptr && (*ptr==' ' || *ptr=='\t')) {
   1.528 +		ptr++;
   1.529 +	}
   1.530 +	return ptr;
   1.531  }
   1.532  
   1.533  
   1.534 @@ -688,18 +705,16 @@
   1.535  int
   1.536  main(int argc, char *argv[])
   1.537  {
   1.538 -	while (--argc > 0 && (*++argv)[0] == '-') {
   1.539 +	while (--argc > 0 && **++argv == '-') {
   1.540  		if (strcmp(argv[0], "-W") == 0) {
   1.541  			/* TODO: catch if no value given */
   1.542 -			iDWarn = atoi((++argv)[0]);
   1.543 +			def_warn = atoi((++argv)[0]);
   1.544  			argc--;
   1.545  		} else {
   1.546  			fprintf(stderr, "unknown option %s\n", argv[0]);
   1.547  			exit(1);
   1.548  		}
   1.549  	}
   1.550 -
   1.551 -	liststrings(readlist(), puts);
   1.552 -
   1.553 +	liststrings(readlist());
   1.554  	return 0;
   1.555  }