xref: /illumos-gate/usr/src/cmd/news/news.c (revision 0d8b5334)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 /*
33 	news foo	prints /var/news/foo
34 	news -a		prints all news items, latest first
35 	news -n		lists names of new items
36 	news -s		tells count of new items only
37 	news		prints items changed since last news
38 */
39 
40 #include <stdio.h>
41 #include <sys/types.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <sys/stat.h>
45 #include <setjmp.h>
46 #include <signal.h>
47 #include <dirent.h>
48 #include <pwd.h>
49 #include <time.h>
50 #include <locale.h>
51 
52 #define INDENT 3
53 #define	RD_WR_ALL	(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
54 
55 #define	DATE_FMT	"%a %b %e %H:%M:%S %Y"
56 /*
57  *	%a	abbreviated weekday name
58  *	%b	abbreviated month name
59  *	%e	day of month
60  *	%H	hour (24-hour clock)
61  *	%M	minute
62  *	%S	second
63  *	%Y	year
64  */
65 /*
66 	The following items should not be printed.
67 */
68 char	*ignore[] = {
69 		"core",
70 		NULL
71 };
72 
73 struct n_file {
74 	long n_time;
75 	char n_name[MAXNAMLEN];
76 } *n_list;
77 
78 char	NEWS[] = "/var/news";	/* directory for news items */
79 
80 int	aopt = 0;	/* 1 say -a specified */
81 int	n_count;	/* number of items in NEWS directory */
82 int	number_read;	/* number of items read */
83 int	nopt = 0;	/* 1 say -n specified */
84 int	optsw;		/* for getopt */
85 int	opt = 0;	/* number of options specified */
86 int	sopt = 0;	/* 1 says -s specified */
87 char	stdbuf[BUFSIZ];
88 char	time_buf[50];	/* holds date and time string */
89 
90 jmp_buf	save_addr;
91 
92 void all_news(void);
93 int ck_num(void);
94 void count(char *);
95 void initialize(void);
96 void late_news(void(*)(), int);
97 void notify(char *);
98 void print_item(char *);
99 void read_dir(void);
100 
101 int
102 main(int argc, char **argv)
103 {
104 	int i;
105 
106 	(void)setlocale(LC_ALL, "");
107 	setbuf (stdout, stdbuf);
108 	initialize();
109 	read_dir();
110 
111 	if (argc <= 1) {
112 		late_news (print_item, 1);
113 		ck_num();
114 	}
115 	else while ((optsw = getopt(argc, argv, "ans")) != EOF)
116 		switch(optsw) {
117 		case 'a':
118 			aopt++;
119 			opt++;
120 			break;
121 
122 		case 'n':
123 			nopt++;
124 			opt++;
125 			break;
126 
127 		case 's':
128 			sopt++;
129 			opt++;
130 			break;
131 
132 		default:
133 			fprintf (stderr, "usage: news [-a] [-n] [-s] [items]\n");
134 			exit (1);
135 	}
136 
137         if (opt > 1) {
138         	fprintf(stderr, "news: options are mutually exclusive\n");
139         	exit(1);
140 	}
141 
142         if (opt > 0 && argc > 2) {
143         	fprintf(stderr, "news: options are not allowed with file names\n");
144         	exit(1);
145 	}
146 
147 	if (aopt) {
148 		all_news();
149 		ck_num();
150 		exit(0);
151 	}
152 
153 	if (nopt) {
154 		late_news (notify, 0);
155 		ck_num();
156 		exit(0);
157 	}
158 
159 	if (sopt) {
160 		late_news (count, 0);
161 		exit(0);
162 	}
163 
164 	for (i=1; i<argc; i++) print_item (argv[i]);
165 
166 	return (0);
167 }
168 
169 /*
170  *	read_dir: get the file names and modification dates for the
171  *	files in /var/news into n_list; sort them in reverse by
172  *	modification date. We assume /var/news is the working directory.
173  */
174 
175 void
176 read_dir(void)
177 {
178 	struct dirent *nf, *readdir();
179 	struct stat sbuf;
180 	char fname[MAXNAMLEN];
181 	DIR *dirp;
182 	int i, j;
183 
184 	/* Open the current directory */
185 	if ((dirp = opendir(".")) == NULL) {
186 		fprintf (stderr, "Cannot open %s\n", NEWS);
187 		exit (1);
188 	}
189 
190 	/* Read the file names into n_list */
191 	n_count = 0;
192 	while (nf = readdir(dirp)) {
193 		strncpy (fname, nf->d_name, (unsigned) strlen(nf->d_name) + 1);
194 		if (nf->d_ino != (ino_t)0 && stat (fname, &sbuf) >= 0
195 		 && (sbuf.st_mode & S_IFMT) == S_IFREG) {
196 			register char **p;
197 			p = ignore;
198 			while (*p && strncmp (*p, nf->d_name, MAXNAMLEN))
199 				++p;
200 			if (!*p) {
201 				if (n_count++ > 0)
202 					n_list = (struct n_file *)
203 						realloc ((char *) n_list,
204 						(unsigned)
205 						(sizeof (struct n_file)
206 						    * n_count));
207 				else
208 					n_list = (struct n_file *) malloc
209 						((unsigned)
210 						(sizeof (struct n_file) *
211 						n_count));
212 				if (n_list == NULL) {
213 					fprintf (stderr, "No storage\n");
214 					exit (1);
215 				}
216 				n_list[n_count-1].n_time = sbuf.st_mtime;
217 				strncpy (n_list[n_count-1].n_name,
218 					nf->d_name, MAXNAMLEN);
219 			}
220 		}
221 	}
222 
223 	/* Sort the elements of n_list in decreasing time order */
224 	for (i=1; i<n_count; i++)
225 		for (j=0; j<i; j++)
226 			if (n_list[j].n_time < n_list[i].n_time) {
227 				struct n_file temp;
228 				temp = n_list[i];
229 				n_list[i] = n_list[j];
230 				n_list[j] = temp;
231 			}
232 
233 	/* Clean up */
234 	closedir(dirp);
235 }
236 
237 void
238 initialize(void)
239 {
240 	if (signal (SIGQUIT, SIG_IGN) != (void(*)())SIG_IGN)
241 		signal (SIGQUIT, _exit);
242 	umask (((~(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) & S_IAMB));
243 	if (chdir (NEWS) < 0) {
244 		fprintf (stderr, "Cannot chdir to %s\n", NEWS);
245 		exit (1);
246 	}
247 }
248 
249 void
250 all_news(void)
251 {
252 	int i;
253 
254 	for (i=0; i<n_count; i++)
255 		print_item (n_list[i].n_name);
256 }
257 
258 void
259 print_item(char *f)
260 {
261 	FILE *fd;
262 	char fname[MAXNAMLEN+1];
263 	static int firstitem = 1;
264 	void onintr();
265 	struct passwd *getpwuid();
266 
267 	if (f == NULL) {
268 		return;
269 	}
270 	strncpy (fname, f, MAXNAMLEN);
271 	fname[MAXNAMLEN] = '\0';
272 	if ((fd = fopen (fname, "r")) == NULL)
273 		printf ("Cannot open %s/%s\n", NEWS, fname);
274 	else {
275 		register int c, ip, op;
276 		struct stat sbuf;
277 		struct passwd *pw;
278 
279 		fstat (fileno (fd), &sbuf);
280 		if (firstitem) {
281 			firstitem = 0;
282 			putchar ('\n');
283 		}
284 		if (setjmp(save_addr))
285 			goto finish;
286 		if (signal(SIGINT, SIG_IGN) != (void(*)())SIG_IGN)
287 			signal(SIGINT, onintr);
288 		printf ("%s ", fname);
289 		pw = getpwuid (sbuf.st_uid);
290 		if (pw)
291 			printf ("(%s)", pw->pw_name);
292 		else
293 			printf (".....");
294 		cftime(time_buf, DATE_FMT, &sbuf.st_mtime);
295 		printf (" %s\n", time_buf);
296 		op = 0;
297 		ip = INDENT;
298 		while ((c = getc (fd)) != EOF) {
299 			switch (c) {
300 
301 			case '\r':
302 			case '\n':
303 				putchar (c);
304 				op = 0;
305 				ip = INDENT;
306 				break;
307 
308 			case ' ':
309 				ip++;
310 				break;
311 
312 			case '\b':
313 				if (ip > INDENT)
314 					ip--;
315 				break;
316 
317 			case '\t':
318 				ip = ((ip - INDENT + 8) & -8) + INDENT;
319 				break;
320 
321 			default:
322 				while (ip < op) {
323 					putchar ('\b');
324 					op--;
325 				}
326 				while ((ip & -8) > (op & -8)) {
327 					putchar ('\t');
328 					op = (op + 8) & -8;
329 				}
330 				while (ip > op) {
331 					putchar (' ');
332 					op++;
333 				}
334 				putchar (c);
335 				ip++;
336 				op++;
337 				break;
338 			}
339 		}
340 		fflush (stdout);
341 finish:
342 		putchar ('\n');
343 		fclose (fd);
344 		number_read++;
345 		if (signal(SIGINT, SIG_IGN) != (void(*)())SIG_IGN)
346 			signal(SIGINT, SIG_DFL);
347 	}
348 }
349 
350 void
351 late_news(void(*emit)(), int update)
352 {
353 	long cutoff;
354 	int i;
355 	char fname[50], *cp;
356 	struct stat newstime;
357 	int fd;
358 	struct {
359 		long actime, modtime;
360 	} utb;
361 	extern char *getenv();
362 
363 	/* Determine the time when last called */
364 	cp = getenv ("HOME");
365 	if (cp == NULL) {
366 		fprintf (stderr, "Cannot find HOME variable\n");
367 		exit (1);
368 	}
369 	strcpy (fname, cp);
370 	strcat (fname, "/");
371 	strcat (fname, ".news_time");
372 	cutoff = stat (fname, &newstime) < 0? 0: newstime.st_mtime;
373 
374 	/* Print the recent items */
375 	for (i=0; i<n_count && n_list[i].n_time > cutoff; i++) {
376 		(*emit) (n_list[i].n_name);
377 		number_read++;
378 	}
379 	(*emit) ((char *) NULL);
380 	fflush (stdout);
381 
382 	if (update) {
383 		/* Re-create the file and refresh the update time */
384 		if (n_count > 0 && (fd = creat (fname, RD_WR_ALL)) >= 0) {
385 			utb.actime = utb.modtime = n_list[0].n_time;
386 			close (fd);
387 			utime (fname, &utb);
388 		}
389 	}
390 }
391 
392 void
393 notify(char *s)
394 {
395 	static int first = 1;
396 
397 	if (s) {
398 		if (first) {
399 			first = 0;
400 			printf ("news:", NEWS);
401 		}
402 		printf (" %.14s", s);
403 	} else if (!first)
404 		putchar ('\n');
405 }
406 
407 /*ARGSUSED*/
408 void
409 count(char *s)
410 {
411 	static int nitems = 0;
412 
413 	if (s)
414 		nitems++;
415 	else {
416 		if (nitems) {
417 			printf ("%d news item", nitems);
418 			if (nitems != 1)
419 				putchar ('s');
420 			printf (".\n");
421 		}
422 		else printf("No news.\n");
423 	}
424 }
425 
426 void
427 onintr()
428 {
429 	sleep(2);
430 	longjmp(save_addr, 1);
431 }
432 
433 int
434 ck_num(void)
435 {
436 	if (sopt && !number_read) printf("No news.\n");
437 	return(0);
438 }
439