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