|  | /* | 
|  | *	news foo	prints /lib/news/foo | 
|  | *	news -a		prints all news items, latest first | 
|  | *	news -n		lists names of new items | 
|  | *	news		prints items changed since last news | 
|  | */ | 
|  |  | 
|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <bio.h> | 
|  |  | 
|  | #define	NINC	50	/* Multiples of directory allocation */ | 
|  | char	*NEWS = "#9/news"; | 
|  | char	TFILE[] = "%s/lib/newstime"; | 
|  |  | 
|  | /* | 
|  | *	The following items should not be printed. | 
|  | */ | 
|  | char*	ignore[] = | 
|  | { | 
|  | "core", | 
|  | "dead.letter", | 
|  | 0 | 
|  | }; | 
|  |  | 
|  | typedef | 
|  | struct | 
|  | { | 
|  | long	time; | 
|  | char	*name; | 
|  | vlong	length; | 
|  | } File; | 
|  | File*	n_list; | 
|  | int	n_count; | 
|  | int	n_items; | 
|  | Biobuf	bout; | 
|  |  | 
|  | int	fcmp(const void *a, const void *b); | 
|  | void	read_dir(int update); | 
|  | void	print_item(char *f); | 
|  | void	eachitem(void (*emit)(char*), int all, int update); | 
|  | void	note(char *s); | 
|  |  | 
|  | void | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | NEWS = unsharp(NEWS); | 
|  |  | 
|  | Binit(&bout, 1, OWRITE); | 
|  | if(argc == 1) { | 
|  | eachitem(print_item, 0, 1); | 
|  | exits(0); | 
|  | } | 
|  | ARGBEGIN{ | 
|  | case 'a':	/* print all */ | 
|  | eachitem(print_item, 1, 0); | 
|  | break; | 
|  |  | 
|  | case 'n':	/* names only */ | 
|  | eachitem(note, 0, 0); | 
|  | if(n_items) | 
|  | Bputc(&bout, '\n'); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | fprint(2, "news: bad option %c\n", ARGC()); | 
|  | exits("usage"); | 
|  | }ARGEND | 
|  | for(i=0; i<argc; i++) | 
|  | print_item(argv[i]); | 
|  | exits(0); | 
|  | } | 
|  |  | 
|  | int | 
|  | fcmp(const void *a, const void *b) | 
|  | { | 
|  | long x; | 
|  |  | 
|  | x = ((File*)b)->time - ((File*)a)->time; | 
|  | if(x < 0) | 
|  | return -1; | 
|  | if(x > 0) | 
|  | return 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	read_dir: get the file names and modification dates for the | 
|  | *	files in /usr/news into n_list; sort them in reverse by | 
|  | *	modification date. | 
|  | */ | 
|  | void | 
|  | read_dir(int update) | 
|  | { | 
|  | Dir *d; | 
|  | char newstime[100], *home; | 
|  | int i, j, n, na, fd; | 
|  |  | 
|  | n_count = 0; | 
|  | n_list = malloc(NINC*sizeof(File)); | 
|  | na = NINC; | 
|  | home = getenv("HOME"); | 
|  | if(home) { | 
|  | sprint(newstime, TFILE, home); | 
|  | d = dirstat(newstime); | 
|  | if(d != nil) { | 
|  | n_list[n_count].name = strdup(""); | 
|  | n_list[n_count].time =d->mtime-1; | 
|  | n_list[n_count].length = 0; | 
|  | n_count++; | 
|  | free(d); | 
|  | } | 
|  | if(update) { | 
|  | fd = create(newstime, OWRITE, 0644); | 
|  | if(fd >= 0) | 
|  | close(fd); | 
|  | } | 
|  | } | 
|  | fd = open(NEWS, OREAD); | 
|  | if(fd < 0) { | 
|  | fprint(2, "news: "); | 
|  | perror(NEWS); | 
|  | exits(NEWS); | 
|  | } | 
|  |  | 
|  | n = dirreadall(fd, &d); | 
|  | for(i=0; i<n; i++) { | 
|  | for(j=0; ignore[j]; j++) | 
|  | if(strcmp(ignore[j], d[i].name) == 0) | 
|  | goto ign; | 
|  | if(na <= n_count) { | 
|  | na += NINC; | 
|  | n_list = realloc(n_list, na*sizeof(File)); | 
|  | } | 
|  | n_list[n_count].name = strdup(d[i].name); | 
|  | n_list[n_count].time = d[i].mtime; | 
|  | n_list[n_count].length = d[i].length; | 
|  | n_count++; | 
|  | ign:; | 
|  | } | 
|  | free(d); | 
|  |  | 
|  | close(fd); | 
|  | qsort(n_list, n_count, sizeof(File), fcmp); | 
|  | } | 
|  |  | 
|  | void | 
|  | print_item(char *file) | 
|  | { | 
|  | char name[4096], *p, *ep; | 
|  | Dir *dbuf; | 
|  | int f, c; | 
|  | int bol, bop; | 
|  |  | 
|  | sprint(name, "%s/%s", NEWS, file); | 
|  | f = open(name, OREAD); | 
|  | if(f < 0) { | 
|  | fprint(2, "news: "); | 
|  | perror(name); | 
|  | return; | 
|  | } | 
|  | strcpy(name, "..."); | 
|  | dbuf = dirfstat(f); | 
|  | if(dbuf == nil) | 
|  | return; | 
|  | Bprint(&bout, "\n%s (%s) %s\n", file, | 
|  | dbuf->muid[0]? dbuf->muid : dbuf->uid, | 
|  | asctime(localtime(dbuf->mtime))); | 
|  | free(dbuf); | 
|  |  | 
|  | bol = 1;	/* beginning of line ...\n */ | 
|  | bop = 1;	/* beginning of page ...\n\n */ | 
|  | for(;;) { | 
|  | c = read(f, name, sizeof(name)); | 
|  | if(c <= 0) | 
|  | break; | 
|  | p = name; | 
|  | ep = p+c; | 
|  | while(p < ep) { | 
|  | c = *p++; | 
|  | if(c == '\n') { | 
|  | if(!bop) { | 
|  | Bputc(&bout, c); | 
|  | if(bol) | 
|  | bop = 1; | 
|  | bol = 1; | 
|  | } | 
|  | continue; | 
|  | } | 
|  | if(bol) { | 
|  | Bputc(&bout, '\t'); | 
|  | bol = 0; | 
|  | bop = 0; | 
|  | } | 
|  | Bputc(&bout, c); | 
|  | } | 
|  | } | 
|  | if(!bol) | 
|  | Bputc(&bout, '\n'); | 
|  | close(f); | 
|  | } | 
|  |  | 
|  | void | 
|  | eachitem(void (*emit)(char*), int all, int update) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | read_dir(update); | 
|  | for(i=0; i<n_count; i++) { | 
|  | if(n_list[i].name[0] == 0) {	/* newstime */ | 
|  | if(all) | 
|  | continue; | 
|  | break; | 
|  | } | 
|  | if(n_list[i].length == 0)		/* in progress */ | 
|  | continue; | 
|  | (*emit)(n_list[i].name); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | note(char *file) | 
|  | { | 
|  |  | 
|  | if(!n_items) | 
|  | Bprint(&bout, "news:"); | 
|  | Bprint(&bout, " %s", file); | 
|  | n_items++; | 
|  | } |