| /* |
| * 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++; |
| } |