xref: /titanic_52/usr/src/cmd/news/news.c (revision b7d62af5b42f0da2eb668e8d33d24d2f4fdd98a8)
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