xref: /plan9/sys/src/cmd/news.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
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