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