1 /*
2 * news foo prints /lib/news/foo
3 * news -a prints all news items, latest first
4 * news -n lists names of new items
5 * news prints items changed since last news
6 */
7
8 #include <u.h>
9 #include <libc.h>
10 #include <bio.h>
11
12 #define NINC 50 /* Multiples of directory allocation */
13 char NEWS[] = "/lib/news";
14 char TFILE[] = "%s/lib/newstime";
15
16 /*
17 * The following items should not be printed.
18 */
19 char* ignore[] =
20 {
21 "core",
22 "dead.letter",
23 0
24 };
25
26 typedef
27 struct
28 {
29 long time;
30 char *name;
31 vlong length;
32 } File;
33 File* n_list;
34 int n_count;
35 int n_items;
36 Biobuf bout;
37
38 int fcmp(void *a, void *b);
39 void read_dir(int update);
40 void print_item(char *f);
41 void eachitem(void (*emit)(char*), int all, int update);
42 void note(char *s);
43
44 void
main(int argc,char * argv[])45 main(int argc, char *argv[])
46 {
47 int i;
48
49 Binit(&bout, 1, OWRITE);
50 if(argc == 1) {
51 eachitem(print_item, 0, 1);
52 exits(0);
53 }
54 ARGBEGIN{
55 case 'a': /* print all */
56 eachitem(print_item, 1, 0);
57 break;
58
59 case 'n': /* names only */
60 eachitem(note, 0, 0);
61 if(n_items)
62 Bputc(&bout, '\n');
63 break;
64
65 default:
66 fprint(2, "news: bad option %c\n", ARGC());
67 exits("usage");
68 }ARGEND
69 for(i=0; i<argc; i++)
70 print_item(argv[i]);
71 exits(0);
72 }
73
74 int
fcmp(void * a,void * b)75 fcmp(void *a, void *b)
76 {
77 long x;
78
79 x = ((File*)b)->time - ((File*)a)->time;
80 if(x < 0)
81 return -1;
82 if(x > 0)
83 return 1;
84 return 0;
85 }
86
87 /*
88 * read_dir: get the file names and modification dates for the
89 * files in /usr/news into n_list; sort them in reverse by
90 * modification date.
91 */
92 void
read_dir(int update)93 read_dir(int update)
94 {
95 Dir *d;
96 char newstime[100], *home;
97 int i, j, n, na, fd;
98
99 n_count = 0;
100 n_list = malloc(NINC*sizeof(File));
101 na = NINC;
102 home = getenv("home");
103 if(home) {
104 sprint(newstime, TFILE, home);
105 d = dirstat(newstime);
106 if(d != nil) {
107 n_list[n_count].name = strdup("");
108 n_list[n_count].time =d->mtime-1;
109 n_list[n_count].length = 0;
110 n_count++;
111 free(d);
112 }
113 if(update) {
114 fd = create(newstime, OWRITE, 0644);
115 if(fd >= 0)
116 close(fd);
117 }
118 }
119 fd = open(NEWS, OREAD);
120 if(fd < 0) {
121 fprint(2, "news: ");
122 perror(NEWS);
123 exits(NEWS);
124 }
125
126 n = dirreadall(fd, &d);
127 for(i=0; i<n; i++) {
128 for(j=0; ignore[j]; j++)
129 if(strcmp(ignore[j], d[i].name) == 0)
130 goto ign;
131 if(na <= n_count) {
132 na += NINC;
133 n_list = realloc(n_list, na*sizeof(File));
134 }
135 n_list[n_count].name = strdup(d[i].name);
136 n_list[n_count].time = d[i].mtime;
137 n_list[n_count].length = d[i].length;
138 n_count++;
139 ign:;
140 }
141 free(d);
142
143 close(fd);
144 qsort(n_list, n_count, sizeof(File), fcmp);
145 }
146
147 void
print_item(char * file)148 print_item(char *file)
149 {
150 char name[4096], *p, *ep;
151 Dir *dbuf;
152 int f, c;
153 int bol, bop;
154
155 sprint(name, "%s/%s", NEWS, file);
156 f = open(name, OREAD);
157 if(f < 0) {
158 fprint(2, "news: ");
159 perror(name);
160 return;
161 }
162 strcpy(name, "...");
163 dbuf = dirfstat(f);
164 if(dbuf == nil)
165 return;
166 Bprint(&bout, "\n%s (%s) %s\n", file,
167 dbuf->muid[0]? dbuf->muid : dbuf->uid,
168 asctime(localtime(dbuf->mtime)));
169 free(dbuf);
170
171 bol = 1; /* beginning of line ...\n */
172 bop = 1; /* beginning of page ...\n\n */
173 for(;;) {
174 c = read(f, name, sizeof(name));
175 if(c <= 0)
176 break;
177 p = name;
178 ep = p+c;
179 while(p < ep) {
180 c = *p++;
181 if(c == '\n') {
182 if(!bop) {
183 Bputc(&bout, c);
184 if(bol)
185 bop = 1;
186 bol = 1;
187 }
188 continue;
189 }
190 if(bol) {
191 Bputc(&bout, '\t');
192 bol = 0;
193 bop = 0;
194 }
195 Bputc(&bout, c);
196 }
197 }
198 if(!bol)
199 Bputc(&bout, '\n');
200 close(f);
201 }
202
203 void
eachitem(void (* emit)(char *),int all,int update)204 eachitem(void (*emit)(char*), int all, int update)
205 {
206 int i;
207
208 read_dir(update);
209 for(i=0; i<n_count; i++) {
210 if(n_list[i].name[0] == 0) { /* newstime */
211 if(all)
212 continue;
213 break;
214 }
215 if(n_list[i].length == 0) /* in progress */
216 continue;
217 (*emit)(n_list[i].name);
218 }
219 }
220
221 void
note(char * file)222 note(char *file)
223 {
224
225 if(!n_items)
226 Bprint(&bout, "news:");
227 Bprint(&bout, " %s", file);
228 n_items++;
229 }
230