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