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