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
main(int argc,char ** argv)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
read_dir(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
initialize(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
all_news(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
print_item(char * f)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
late_news(void (* emit)(),int update)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
notify(char * s)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
count(char * s)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
onintr()427 onintr()
428 {
429 sleep(2);
430 longjmp(save_addr, 1);
431 }
432
433 int
ck_num(void)434 ck_num(void)
435 {
436 if (sopt && !number_read) printf("No news.\n");
437 return(0);
438 }
439