bday
diff bday.c @ 7:b6f4c7fba64a
all sources now in one file
author | meillo@marmaro.de |
---|---|
date | Tue, 18 Dec 2007 11:59:21 +0100 |
parents | birthday.c@fc6e40f7bd5a |
children | 19c1ad697022 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/bday.c Tue Dec 18 11:59:21 2007 +0100 1.3 @@ -0,0 +1,664 @@ 1.4 +/* 1.5 + birthday 1.6 + 1.7 + Birthday/Anniversary display on login 1.8 + 1.9 + (c) 1996 AS Mortimer 1.10 + 1.11 + This program is free software; you can redistribute it and/or 1.12 + modify it under the terms of the GNU General Public License as 1.13 + published by the Free Software Foundation; either version 2 of the 1.14 + License, or (at your option) any later version. You may also 1.15 + distribute it under the Artistic License, as comes with Perl. 1.16 + 1.17 + This program is distributed in the hope that it will be useful, 1.18 + but WITHOUT ANY WARRANTY; without even the implied warranty of 1.19 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 1.20 + 1.21 + You should have received a copy of the GNU General Public License 1.22 + along with this program; if not, write to the Free Software 1.23 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 1.24 + 1.25 + You should also have recieved a copy of the Artistic license with 1.26 + this program. 1.27 + 1.28 + 1.29 + We're getting there. At the moment, the file used by default is ~/.birthdays 1.30 + under UNIX, or C:\PERSONAL\BDAYS.LST under DOS, but this can be overridden on 1.31 + the command line. The file has the following format: 1.32 + 1.33 + name/event/whatever=date flags 1.34 + where: 1.35 + date is dd/mm, dd/mm/yy (assumes 20th century!) or dd/mm/yyyy 1.36 + flags is ONE or ZERO of 1.37 + o bd for a birthday (default) 1.38 + o ann for an anniversary 1.39 + o ev for an event 1.40 + and zero or more of 1.41 + o w <n> to set the warn-in-advance time to n days (don't include the 1.42 + brackets! :) 1.43 + o to <date> 1.44 + o for <days> 1.45 + to specify the length of time taken by an event, for example a 1.46 + holiday. 1.47 + 1.48 + Comment lines are preceeded by #. 1.49 + 1.50 + Note: If you deviate from this format, I cannot guarantee anything about 1.51 + it's behaviour. In most cases, it will just quietly ignore the error, 1.52 + which probably isn't ideal behaviour. Oh, well. 1.53 + 1.54 + 2003/05/20: Automatic reallocation of output buffer in listsrings() by 1.55 + Sebastian Schmidt <yath@yath.eu.org>. 1.56 + 1.57 +*/ 1.58 + 1.59 + 1.60 +#include <stdarg.h> 1.61 +#include <stdio.h> 1.62 +#include <stdlib.h> 1.63 +#include <string.h> 1.64 +#include <sys/types.h> 1.65 +#include <time.h> 1.66 +#include <unistd.h> 1.67 + 1.68 + 1.69 + 1.70 +/* standard time to warn in advance, when no explicit w flag is given. */ 1.71 +#define DEF_WARN 14 1.72 + 1.73 +/* ========== Global constants and data types */ 1.74 + 1.75 + 1.76 +/* month lengths etc */ 1.77 + 1.78 +#define isleapyear(y) ((y)%4==0 && ((y)%100 != 0 || (y)%400 == 0)) 1.79 +const unsigned MLENDAT[]; 1.80 +#define mlen(m,y) (MLENDAT[(m)-1] != -1 ? MLENDAT[(m)-1] : (isleapyear((y)) ? 29 : 28)) 1.81 +#define before(a,b) ((a).month < (b).month || ((a).month == (b).month && (a).day < (b).day)) 1.82 +#define ydelta(a,b) ((int) (b).year - (a).year + before((a),(b))) 1.83 + 1.84 +/* -------- modifier flags */ 1.85 + 1.86 +#define F_MTYPE 0x07 1.87 +#define F_TBIRTHDAY 1 1.88 +#define F_TANNIVERSARY 2 1.89 +#define F_TEVENT 3 1.90 + 1.91 +/* flags processed immediately on encountering */ 1.92 +#define F_MIMMEDIATE 0x24 1.93 +#define F_WTIME_P 0x08 1.94 +#define F_FORDAYS 0x16 1.95 +#define F_TODATE 0x24 1.96 + 1.97 +struct _ftable {char *txt; unsigned flag;}; 1.98 + 1.99 +const struct _ftable FTABLE[]; 1.100 + 1.101 +struct date { 1.102 + unsigned day; 1.103 + unsigned month; 1.104 + unsigned year; 1.105 +}; 1.106 + 1.107 +struct event { 1.108 + char *text; 1.109 + struct date date; 1.110 + struct date enddate; 1.111 + int warn; 1.112 +}; 1.113 + 1.114 +typedef int (*prnfunc)(const char *); 1.115 + 1.116 +/* ========== Global Variables */ 1.117 + 1.118 +struct event* readlist(void); 1.119 +void gettoday(void); 1.120 +unsigned delta(struct date *); 1.121 +unsigned ddiff(struct date *D1, struct date *D2); 1.122 +void liststrings(struct event* evl, prnfunc outf); 1.123 +char *tdelta(struct date *d); 1.124 +char *ttime(int yr, int mn, int wk, int dy); 1.125 + 1.126 +int skptok(int j, char *ptr); 1.127 +int evcmp(const void *e1, const void *e2); 1.128 + 1.129 + 1.130 +struct date today; 1.131 +int iDWarn = DEF_WARN; 1.132 + 1.133 +const unsigned MLENDAT[]={31,-1,31,30,31,30,31,31,30,31,30,31}; 1.134 + 1.135 +const struct _ftable FTABLE[] = { 1.136 + {"bd", F_TBIRTHDAY}, 1.137 + {"ann",F_TANNIVERSARY}, 1.138 + {"ev", F_TEVENT}, 1.139 + {"w", F_WTIME_P}, 1.140 + {"to", F_TODATE}, 1.141 + {"for", F_FORDAYS}, 1.142 + {NULL, 0} 1.143 +}; 1.144 + 1.145 + 1.146 + 1.147 + 1.148 + 1.149 + 1.150 +/* 1.151 + xmalloc/xrealloc functions 1.152 + Note: the x* functions are lifted straight from the GNU libc info docs 1.153 + $Id: xmalloc.c,v 1.2 1999/01/16 17:08:59 andy Exp $ 1.154 +*/ 1.155 + 1.156 +void* xmalloc (size_t size) { 1.157 + register void* value = malloc (size); 1.158 + if (value == 0) { 1.159 + fprintf(stderr, "virtual memory exhausted\n"); 1.160 + exit(1); 1.161 + } 1.162 + return value; 1.163 +} 1.164 + 1.165 + 1.166 +void* xrealloc (void* ptr, size_t size) { 1.167 + register void* value = realloc (ptr, size); 1.168 + if (value == 0) { 1.169 + fprintf(stderr, "virtual memory exhausted\n"); 1.170 + exit(1); 1.171 + } 1.172 + return value; 1.173 +} 1.174 + 1.175 +/* ========== */ 1.176 + 1.177 + 1.178 + 1.179 +/* like strcat(), but lets the buffer automagically grow :-) 1.180 + * (needs local variable "size" with the buffer size) */ 1.181 +#define append(where, what) do { \ 1.182 + if (strlen(what) > (size - strlen(where))) { \ 1.183 + xrealloc(where, size + 128 + strlen(what)); \ 1.184 + size += 128 + strlen(what); \ 1.185 + } \ 1.186 + strcat(where, what); \ 1.187 +} while(0) 1.188 + 1.189 +/* ========== */ 1.190 + 1.191 +/* returns delta(d) in days, weeks, months, etc 1.192 + * the returned buffer is malloc()ed, do not forget to free() it */ 1.193 +char *tdelta(struct date *d) { 1.194 + int dy, wk, mn, yr; 1.195 + char *tmp; 1.196 + char *buf = xmalloc(128); 1.197 + int size = 128; 1.198 + *buf = 0; 1.199 + 1.200 + switch (delta(d)) { 1.201 + case 0: 1.202 + append(buf, "today"); 1.203 + return buf; 1.204 + case 1: 1.205 + append(buf, "tomorrow"); 1.206 + return buf; 1.207 + default: 1.208 + /* like delta(), we ignore the year */ 1.209 + yr=-before(*d,today); 1.210 + mn=d->month - today.month; 1.211 + dy=d->day - today.day; 1.212 + 1.213 + if (dy < 0) { 1.214 + dy += mlen(today.month, today.year); 1.215 + mn--; 1.216 + } 1.217 + if (mn < 0) { 1.218 + mn += 12; 1.219 + yr++; 1.220 + } 1.221 + 1.222 + wk = (dy / 7); 1.223 + dy %= 7; 1.224 + 1.225 + append(buf, "in "); 1.226 + tmp = ttime(yr, mn, wk, dy); 1.227 + append(buf, tmp); 1.228 + free(tmp); 1.229 + 1.230 + return buf; 1.231 + } 1.232 +} 1.233 + 1.234 + 1.235 + 1.236 + 1.237 + 1.238 +/* 1.239 +void donum(n,txt) { 1.240 + do { 1.241 + if (n > 0) { 1.242 + snprintf(tmp, sizeof(tmp), "%d", n); 1.243 + append(buf, tmp); 1.244 + append(buf, " " txt); 1.245 + if (n != 1) 1.246 + append(buf, "s"); 1.247 + terms--; 1.248 + if (orgterms > 1) { 1.249 + if (terms == 1) 1.250 + append(buf, " and "); 1.251 + else if (terms > 1) 1.252 + append(buf, ", "); 1.253 + } 1.254 + } 1.255 + } while(0) 1.256 +} 1.257 +*/ 1.258 + 1.259 + 1.260 +#define donum(n,txt) do { \ 1.261 + if (n > 0) { \ 1.262 + snprintf(tmp, sizeof(tmp), "%d", n); \ 1.263 + append(buf, tmp); \ 1.264 + append(buf, " " txt); \ 1.265 + if (n != 1) \ 1.266 + append(buf, "s"); \ 1.267 + terms--; \ 1.268 + if (orgterms > 1) { \ 1.269 + if (terms == 1) \ 1.270 + append(buf, " and "); \ 1.271 + else if (terms > 1) \ 1.272 + append(buf, ", "); \ 1.273 + } \ 1.274 + } \ 1.275 +} while(0) 1.276 + 1.277 + 1.278 +/* returns allocated buffer, don't forget to free() */ 1.279 +char* ttime(int yr, int mn, int wk, int dy) { 1.280 + char* buf = xmalloc(128); 1.281 + int size = 128; 1.282 + int terms, orgterms; 1.283 + char tmp[128]; 1.284 + 1.285 + *buf = 0; /* Initialize buffer */ 1.286 + terms = orgterms = (yr!=0) + (mn!=0) + (wk!=0) + (dy!=0); 1.287 + 1.288 + donum(yr, "year"); 1.289 + donum(mn, "month"); 1.290 + donum(wk, "week"); 1.291 + donum(dy, "day"); 1.292 + 1.293 + return buf; 1.294 +} 1.295 +#undef donum 1.296 + 1.297 + 1.298 + 1.299 + 1.300 + 1.301 + 1.302 +/* lists the birthdays in their string format, one by one, and passes the string to a function. */ 1.303 +void liststrings(struct event* evl, prnfunc outf) { 1.304 + int i,j; 1.305 + char *buf, *tmp; 1.306 + int size; 1.307 + 1.308 + for (i = 0; evl[i].text != NULL; i++) { 1.309 + buf = xmalloc(128); 1.310 + *buf = '\0'; 1.311 + size = 128; 1.312 + 1.313 + if (evl[i].warn == -1 && delta(&(evl[i].date))==0) { 1.314 + append(buf, evl[i].text); 1.315 + } else if (evl[i].enddate.day == 0) { 1.316 + if (delta(&(evl[i].date)) <= evl[i].warn) { 1.317 + append(buf, evl[i].text); 1.318 + append(buf, " "); 1.319 + tmp = tdelta(&(evl[i].date)); 1.320 + append(buf, tmp); 1.321 + free(tmp); 1.322 + } 1.323 + } else { 1.324 + if (delta(&(evl[i].date)) <= evl[i].warn) { 1.325 + append(buf, evl[i].text); 1.326 + append(buf, " for "); 1.327 + /* +1 because, if the difference between two dates is one day, then the length of an event on those days is two days */ 1.328 + j = ddiff(&(evl[i].date),&(evl[i].enddate)) + 1; 1.329 + tmp = ttime(0, 0, j/7, j%7); 1.330 + append(buf, tmp); 1.331 + free(tmp); 1.332 + append(buf, " "); 1.333 + tmp = tdelta(&(evl[i].date)); 1.334 + append(buf, tmp); 1.335 + } else if (delta(&(evl[i].enddate)) <= evl[i].warn) { 1.336 + append(buf, evl[i].text); 1.337 + append(buf, " "); 1.338 + j = delta(&(evl[i].enddate)); 1.339 + if (j) { 1.340 + append(buf, "for "); 1.341 + tmp = ttime(0, 0, j/7, j%7); 1.342 + append(buf, tmp); 1.343 + free(tmp); 1.344 + append(buf, " longer"); 1.345 + } else { 1.346 + append(buf, "finishes today"); 1.347 + } 1.348 + } 1.349 + } 1.350 + if (*buf) { 1.351 + append(buf, "."); 1.352 + outf(buf); 1.353 + } 1.354 + free(buf); 1.355 + } 1.356 +} 1.357 + 1.358 + 1.359 + 1.360 + 1.361 + 1.362 + 1.363 + 1.364 + 1.365 +/* sort the events by the time before the next time they come up, putting those 1.366 + where the start has passed but we are still in the time-period first */ 1.367 +int evcmp(const void *p1, const void *p2) { 1.368 + struct event *e1=(struct event *)p1; 1.369 + struct event *e2=(struct event *)p2; 1.370 + unsigned d1,d2; 1.371 + 1.372 + /* if the delta for the enddate is less than that for the start date, then we 1.373 + have passed the start date but not yet the end date, and so we should 1.374 + display the enddate; otherwise, we should display the start date */ 1.375 + 1.376 + d1=delta(&(e1->date)); 1.377 + if (e1->enddate.day && delta(&(e1->enddate)) < d1) 1.378 + d1=delta(&(e1->enddate)); 1.379 + 1.380 + d2=delta(&(e2->date)); 1.381 + if (e2->enddate.day && delta(&(e2->enddate)) < d2) 1.382 + d2=delta(&(e2->enddate)); 1.383 + 1.384 + if (d1 < d2) return -1; 1.385 + if (d1 > d2) return 1; 1.386 + 1.387 + return strcmp(e1->text, e2->text); 1.388 +} 1.389 + 1.390 + 1.391 + 1.392 + 1.393 + 1.394 + 1.395 +/* difference in days between two dates */ 1.396 +/* it is assumed that D1 < D2, and so the result is always positive */ 1.397 +unsigned ddiff(struct date *D1, struct date *D2) { 1.398 + struct date d1,d2; 1.399 + int dd,m; 1.400 + 1.401 + /* make working copies */ 1.402 + d1=*D1; 1.403 + d2=*D2; 1.404 + 1.405 + /* sort out zero years */ 1.406 + if (d1.year == 0 || d2.year==0) { 1.407 + if (d1.year != d2.year) { 1.408 + if (d1.year == 0) { 1.409 + if (before(d1,d2)) 1.410 + d1.year=d2.year; 1.411 + else 1.412 + d1.year=d2.year-1; 1.413 + } else { 1.414 + if (before(d1,d2)) 1.415 + d2.year=d1.year; 1.416 + else 1.417 + d2.year=d1.year+1; 1.418 + } 1.419 + } else { /* both years zero */ 1.420 + if (before(d1,d2)) 1.421 + d1.year=d2.year=today.year; 1.422 + else { 1.423 + d1.year=today.year; 1.424 + d2.year=d1.year+1; 1.425 + } 1.426 + } 1.427 + } 1.428 + 1.429 + /* now we can actually do the comparison ... */ 1.430 + dd=0; 1.431 + 1.432 + /* to start with, we work in months */ 1.433 + for (m=d1.month; m < d2.month + (d2.year-d1.year)*12; m++) 1.434 + dd += mlen(((m-1)%12)+1, d1.year + m/12); 1.435 + 1.436 + /* and then we renormalise for the days within the months */ 1.437 + /* the first month was included in our calculations */ 1.438 + dd -= d1.day; 1.439 + /* but the last one wasn't */ 1.440 + dd += d2.day; 1.441 + 1.442 + return dd; 1.443 +} 1.444 + 1.445 + 1.446 + 1.447 + 1.448 + 1.449 + 1.450 + 1.451 + 1.452 +/* actually until the next anniversary of ... */ 1.453 +unsigned delta(struct date *date) { 1.454 + struct date d; 1.455 + unsigned dt, mn; 1.456 + 1.457 + memcpy(&d, date, sizeof(struct date)); 1.458 + 1.459 + /* past the end of the year */ 1.460 + if (before(d, today)) { 1.461 + d.year = 1; 1.462 + } else { 1.463 + d.year = 0; 1.464 + } 1.465 + 1.466 + for (mn = today.month, dt=0; mn < d.month + 12*d.year; mn++) 1.467 + dt += mlen(((mn-1)%12) + 1,today.year + mn/12); 1.468 + 1.469 + dt -= today.day; 1.470 + dt += d.day; 1.471 + 1.472 + return dt; 1.473 +} 1.474 + 1.475 + 1.476 + 1.477 + 1.478 + 1.479 + 1.480 +void gettoday(void) { 1.481 + struct tm *tm; 1.482 + time_t t; 1.483 + 1.484 + time(&t); 1.485 + tm = localtime(&t); 1.486 + today.day = tm->tm_mday; 1.487 + today.month = tm->tm_mon + 1; /* 1-12 instead of 0-11 */ 1.488 + today.year = tm->tm_year + 1900; 1.489 +} 1.490 + 1.491 + 1.492 + 1.493 + 1.494 + 1.495 + 1.496 + 1.497 + 1.498 + 1.499 + 1.500 +struct event* readlist() { 1.501 + int i, j, k, l, d; 1.502 + struct event *evl; 1.503 + char buf[1024], buf2[1024]; 1.504 + char *ptr; 1.505 + unsigned flags; 1.506 + 1.507 + /* initialise */ 1.508 + gettoday(); 1.509 + 1.510 + for (i = 0, evl = NULL; fgets(buf, sizeof(buf), stdin) != NULL; i++) { 1.511 + evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1)); 1.512 + 1.513 + /* ignore comments and empty lines */ 1.514 + if (*buf == '#' || *buf == '\n') { 1.515 + i--; 1.516 + continue; 1.517 + } 1.518 + 1.519 + /* parse string in buf */ 1.520 + ptr = strrchr(buf, '='); /* allow '=' in text */ 1.521 + 1.522 + /* not a valid line, so ignore it! Cool, huh? */ 1.523 + /* Attention: only recognizes lines without '=' */ 1.524 + if (ptr == NULL) { 1.525 + fprintf(stderr, "WARNING: Invalid line in input:\n%s", buf); 1.526 + i--; 1.527 + continue; 1.528 + } 1.529 + 1.530 + *(ptr++) = 0; 1.531 + 1.532 + j = sscanf(ptr, "%u-%u-%u", &(evl[i].date.year), &(evl[i].date.month), &(evl[i].date.day)); 1.533 + /* ... unless it wasn't read, in which case set it to zero */ 1.534 + if (j==2) { 1.535 + evl[i].date.year = 0; 1.536 + } 1.537 + 1.538 + 1.539 + /* parse flags */ 1.540 + 1.541 + evl[i].warn = iDWarn; 1.542 + evl[i].enddate.day = 0; 1.543 + evl[i].enddate.month = 0; 1.544 + evl[i].enddate.year = 0; 1.545 + 1.546 + flags = 0; 1.547 + j = 0; 1.548 + 1.549 + while(j = skptok(j, ptr), ptr[j] != 0) { 1.550 + for (k = 0; FTABLE[k].txt != NULL && strncmp(FTABLE[k].txt, ptr + j, strlen(FTABLE[k].txt)); k++) { 1.551 + } 1.552 + 1.553 + switch (FTABLE[k].flag) { 1.554 + case F_WTIME_P: /* w <n> -- sets warning time */ 1.555 + sscanf(ptr + j, "w %u", &(evl[i].warn)); 1.556 + break; 1.557 + case F_FORDAYS: /* for <days> -- sets the duration of the event */ 1.558 + sscanf(ptr + j, "for %u", &d); 1.559 + evl[i].enddate=evl[i].date; 1.560 + for (l = 1; l < d; l++) { 1.561 + evl[i].enddate.day++; 1.562 + if (evl[i].enddate.day > mlen(evl[i].enddate.month, evl[i].enddate.year)) { 1.563 + evl[i].enddate.month++; 1.564 + evl[i].enddate.day = 1; 1.565 + } 1.566 + if (evl[i].enddate.month > 12) { 1.567 + evl[i].enddate.year++; 1.568 + evl[i].enddate.month = 1; 1.569 + } 1.570 + } 1.571 + break; 1.572 + case F_TODATE: /* to <date> -- sets the end date of the event */ 1.573 + l = sscanf(ptr + j, "to %u-%u-%u", &(evl[i].enddate.year), &(evl[i].enddate.month), &(evl[i].enddate.day)); 1.574 + if (l == 2) { 1.575 + evl[i].enddate.year = 0; 1.576 + } 1.577 + break; 1.578 + case 0: 1.579 + break; 1.580 + default: 1.581 + flags |= FTABLE[k].flag; 1.582 + break; 1.583 + } 1.584 + } 1.585 + 1.586 + 1.587 + /* construct event text */ 1.588 + 1.589 + switch(flags & F_MTYPE) { 1.590 + case F_TBIRTHDAY: 1.591 + default: /* assume it's a birthday */ 1.592 + if (evl[i].date.year != 0) { 1.593 + int tmp_age = ydelta(evl[i].date, today); 1.594 + if (tmp_age != 1) { 1.595 + sprintf(buf2, "%s is %d years old", buf, tmp_age); 1.596 + } else { 1.597 + sprintf(buf2, "%s is %d year old", buf, tmp_age); 1.598 + } 1.599 + } else { 1.600 + sprintf(buf2, "%s has a birthday", buf); 1.601 + } 1.602 + break; 1.603 + case F_TANNIVERSARY: 1.604 + if (evl[i].date.year != 0) { 1.605 + sprintf(buf2, "%s %d years ago", buf, ydelta(evl[i].date, today)); 1.606 + } else { 1.607 + strcpy(buf2, buf); 1.608 + } 1.609 + break; 1.610 + case F_TEVENT: 1.611 + /* if a year was specified, and this warning isn't for it, ignore! */ 1.612 + if ((evl[i].date.year != 0 && ydelta(evl[i].date, today) != 0) && (evl[i].enddate.year == 0 || ydelta(evl[i].enddate, today) != 0)) { 1.613 + i--; 1.614 + continue; 1.615 + } 1.616 + strcpy(buf2, buf); 1.617 + break; 1.618 + } 1.619 + evl[i].text = strdup(buf2); 1.620 + } 1.621 + 1.622 + evl = (struct event *) xrealloc(evl, sizeof(struct event) * (i + 1)); 1.623 + evl[i].date.day = 0; 1.624 + evl[i].date.month = 0; 1.625 + evl[i].date.year = 0; 1.626 + evl[i].text = (char *) NULL; 1.627 + 1.628 + fclose(stdin); 1.629 + 1.630 + /* NB uses i from above */ 1.631 + qsort(evl, i, sizeof(struct event), evcmp); 1.632 + return evl; 1.633 +} 1.634 + 1.635 + 1.636 + 1.637 + 1.638 + 1.639 +int skptok(int j, char *ptr) { 1.640 + for (; ptr[j] != 0 && ptr[j] != ' ' && ptr[j] != '\t' ; j++); 1.641 + for (; ptr[j] != 0 && (ptr[j] == ' ' || ptr[j] == '\t'); j++); 1.642 + 1.643 + return j; 1.644 +} 1.645 + 1.646 + 1.647 + 1.648 + 1.649 + 1.650 + 1.651 +int main(int argc, char* argv[]) { 1.652 + 1.653 + while (--argc > 0 && (*++argv)[0] == '-') { 1.654 + if (strcmp(argv[0], "-W") == 0) { 1.655 + /* TODO: catch if no value given */ 1.656 + iDWarn = atoi((++argv)[0]); 1.657 + argc--; 1.658 + } else { 1.659 + fprintf(stderr, "unknown option %s\n", argv[0]); 1.660 + exit(1); 1.661 + } 1.662 + } 1.663 + 1.664 + liststrings(readlist(), puts); 1.665 + 1.666 + return 0; 1.667 +}