root/usr/src/cmd/news/news.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

/*
        news foo        prints /var/news/foo
        news -a         prints all news items, latest first
        news -n         lists names of new items
        news -s         tells count of new items only
        news            prints items changed since last news
*/

#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <setjmp.h>
#include <signal.h>
#include <dirent.h>
#include <pwd.h>
#include <time.h>
#include <locale.h>

#define INDENT 3
#define RD_WR_ALL       (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)

#define DATE_FMT        "%a %b %e %H:%M:%S %Y"
/*
 *      %a      abbreviated weekday name
 *      %b      abbreviated month name
 *      %e      day of month
 *      %H      hour (24-hour clock)
 *      %M      minute
 *      %S      second
 *      %Y      year
 */
/*
        The following items should not be printed.
*/
char    *ignore[] = {
                "core",
                NULL
};

struct n_file {
        long n_time;
        char n_name[MAXNAMLEN];
} *n_list;

char    NEWS[] = "/var/news";   /* directory for news items */

int     aopt = 0;       /* 1 say -a specified */
int     n_count;        /* number of items in NEWS directory */
int     number_read;    /* number of items read */
int     nopt = 0;       /* 1 say -n specified */
int     optsw;          /* for getopt */
int     opt = 0;        /* number of options specified */
int     sopt = 0;       /* 1 says -s specified */
char    stdbuf[BUFSIZ];
char    time_buf[50];   /* holds date and time string */

jmp_buf save_addr;

void all_news(void);
int ck_num(void);
void count(char *);
void initialize(void);
void late_news(void(*)(), int);
void notify(char *);
void print_item(char *);
void read_dir(void);

int
main(int argc, char **argv)
{
        int i;

        (void)setlocale(LC_ALL, "");
        setbuf (stdout, stdbuf);
        initialize();
        read_dir();

        if (argc <= 1) {
                late_news (print_item, 1);
                ck_num();
        }
        else while ((optsw = getopt(argc, argv, "ans")) != EOF)
                switch(optsw) {
                case 'a':
                        aopt++;
                        opt++;
                        break;

                case 'n':
                        nopt++;
                        opt++;
                        break;

                case 's':
                        sopt++;
                        opt++;
                        break;

                default:
                        fprintf (stderr, "usage: news [-a] [-n] [-s] [items]\n");
                        exit (1);
        }

        if (opt > 1) {
                fprintf(stderr, "news: options are mutually exclusive\n");
                exit(1);
        }

        if (opt > 0 && argc > 2) {
                fprintf(stderr, "news: options are not allowed with file names\n");
                exit(1);
        }

        if (aopt) {
                all_news();
                ck_num();
                exit(0);
        }

        if (nopt) {
                late_news (notify, 0);
                ck_num();
                exit(0);
        }

        if (sopt) {
                late_news (count, 0);
                exit(0);
        }

        for (i=1; i<argc; i++) print_item (argv[i]);

        return (0);
}

/*
 *      read_dir: get the file names and modification dates for the
 *      files in /var/news into n_list; sort them in reverse by
 *      modification date. We assume /var/news is the working directory.
 */

void
read_dir(void)
{
        struct dirent *nf, *readdir();
        struct stat sbuf;
        char fname[MAXNAMLEN];
        DIR *dirp;
        int i, j;

        /* Open the current directory */
        if ((dirp = opendir(".")) == NULL) {
                fprintf (stderr, "Cannot open %s\n", NEWS);
                exit (1);
        }

        /* Read the file names into n_list */
        n_count = 0;
        while (nf = readdir(dirp)) {
                strncpy (fname, nf->d_name, (unsigned) strlen(nf->d_name) + 1);
                if (nf->d_ino != (ino_t)0 && stat (fname, &sbuf) >= 0
                 && (sbuf.st_mode & S_IFMT) == S_IFREG) {
                        register char **p;
                        p = ignore;
                        while (*p && strncmp (*p, nf->d_name, MAXNAMLEN))
                                ++p;
                        if (!*p) {
                                if (n_count++ > 0)
                                        n_list = (struct n_file *)
                                                realloc ((char *) n_list,
                                                (unsigned)
                                                (sizeof (struct n_file)
                                                    * n_count));
                                else
                                        n_list = (struct n_file *) malloc
                                                ((unsigned)
                                                (sizeof (struct n_file) *
                                                n_count));
                                if (n_list == NULL) {
                                        fprintf (stderr, "No storage\n");
                                        exit (1);
                                }
                                n_list[n_count-1].n_time = sbuf.st_mtime;
                                strncpy (n_list[n_count-1].n_name,
                                        nf->d_name, MAXNAMLEN);
                        }
                }
        }

        /* Sort the elements of n_list in decreasing time order */
        for (i=1; i<n_count; i++)
                for (j=0; j<i; j++)
                        if (n_list[j].n_time < n_list[i].n_time) {
                                struct n_file temp;
                                temp = n_list[i];
                                n_list[i] = n_list[j];
                                n_list[j] = temp;
                        }

        /* Clean up */
        closedir(dirp);
}

void
initialize(void)
{
        if (signal (SIGQUIT, SIG_IGN) != (void(*)())SIG_IGN)
                signal (SIGQUIT, _exit);
        umask (((~(S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) & S_IAMB));
        if (chdir (NEWS) < 0) {
                fprintf (stderr, "Cannot chdir to %s\n", NEWS);
                exit (1);
        }
}

void
all_news(void)
{
        int i;

        for (i=0; i<n_count; i++)
                print_item (n_list[i].n_name);
}

void
print_item(char *f)
{
        FILE *fd;
        char fname[MAXNAMLEN+1];
        static int firstitem = 1;
        void onintr();
        struct passwd *getpwuid();

        if (f == NULL) {
                return;
        }
        strncpy (fname, f, MAXNAMLEN);
        fname[MAXNAMLEN] = '\0';
        if ((fd = fopen (fname, "r")) == NULL)
                printf ("Cannot open %s/%s\n", NEWS, fname);
        else {
                register int c, ip, op;
                struct stat sbuf;
                struct passwd *pw;

                fstat (fileno (fd), &sbuf);
                if (firstitem) {
                        firstitem = 0;
                        putchar ('\n');
                }
                if (setjmp(save_addr))
                        goto finish;
                if (signal(SIGINT, SIG_IGN) != (void(*)())SIG_IGN)
                        signal(SIGINT, onintr);
                printf ("%s ", fname);
                pw = getpwuid (sbuf.st_uid);
                if (pw)
                        printf ("(%s)", pw->pw_name);
                else
                        printf (".....");
                cftime(time_buf, DATE_FMT, &sbuf.st_mtime);
                printf (" %s\n", time_buf);
                op = 0;
                ip = INDENT;
                while ((c = getc (fd)) != EOF) {
                        switch (c) {

                        case '\r':
                        case '\n':
                                putchar (c);
                                op = 0;
                                ip = INDENT;
                                break;

                        case ' ':
                                ip++;
                                break;

                        case '\b':
                                if (ip > INDENT)
                                        ip--;
                                break;

                        case '\t':
                                ip = ((ip - INDENT + 8) & -8) + INDENT;
                                break;

                        default:
                                while (ip < op) {
                                        putchar ('\b');
                                        op--;
                                }
                                while ((ip & -8) > (op & -8)) {
                                        putchar ('\t');
                                        op = (op + 8) & -8;
                                }
                                while (ip > op) {
                                        putchar (' ');
                                        op++;
                                }
                                putchar (c);
                                ip++;
                                op++;
                                break;
                        }
                }
                fflush (stdout);
finish:
                putchar ('\n');
                fclose (fd);
                number_read++;
                if (signal(SIGINT, SIG_IGN) != (void(*)())SIG_IGN)
                        signal(SIGINT, SIG_DFL);
        }
}

void
late_news(void(*emit)(), int update)
{
        long cutoff;
        int i;
        char fname[50], *cp;
        struct stat newstime;
        int fd;
        struct {
                long actime, modtime;
        } utb;
        extern char *getenv();

        /* Determine the time when last called */
        cp = getenv ("HOME");
        if (cp == NULL) {
                fprintf (stderr, "Cannot find HOME variable\n");
                exit (1);
        }
        strcpy (fname, cp);
        strcat (fname, "/");
        strcat (fname, ".news_time");
        cutoff = stat (fname, &newstime) < 0? 0: newstime.st_mtime;

        /* Print the recent items */
        for (i=0; i<n_count && n_list[i].n_time > cutoff; i++) {
                (*emit) (n_list[i].n_name);
                number_read++;
        }
        (*emit) ((char *) NULL);
        fflush (stdout);

        if (update) {
                /* Re-create the file and refresh the update time */
                if (n_count > 0 && (fd = creat (fname, RD_WR_ALL)) >= 0) {
                        utb.actime = utb.modtime = n_list[0].n_time;
                        close (fd);
                        utime (fname, &utb);
                }
        }
}

void
notify(char *s)
{
        static int first = 1;

        if (s) {
                if (first) {
                        first = 0;
                        printf ("news:", NEWS);
                }
                printf (" %.14s", s);
        } else if (!first)
                putchar ('\n');
}

/*ARGSUSED*/
void
count(char *s)
{
        static int nitems = 0;

        if (s)
                nitems++;
        else {
                if (nitems) {
                        printf ("%d news item", nitems);
                        if (nitems != 1)
                                putchar ('s');
                        printf (".\n");
                }
                else printf("No news.\n");
        }
}

void
onintr()
{
        sleep(2);
        longjmp(save_addr, 1);
}

int
ck_num(void)
{
        if (sopt && !number_read) printf("No news.\n");
        return(0);
}