[net.sources] remote manual server/client source available - part 3 of 4

broome@ucbvax.ARPA (Jonathan C. Broome) (08/28/85)

[ cat this file onto the end of the previous file ]
        suffs -= 2;
        sec->dirs[d++] = dir;

        if (dirs == (DIR *) 0)
            dirs = dir;
        else
            dirlast->next = dir;
        dir->next = (DIR *) 0;
        dirlast = dir;

        dir->suff = (char **) malloc (sizeof (char **) * (suffs + 1));
        for (s = 0; *argv; s++)
            dir->suff[s] = strsave (*argv++);
        dir->suff[s] = (char *) 0;
    }
    (void) fclose (fp);

    if (dirs || sections)         /* save last type */
        addtype ((char *) 0);
    
    (void) parse ("", &argv);     /* free up argv memory */
}


/*
 *  Check to see if an entry for this directory already exists.
 *  If so, return a pointer to it, else return (DIR *) 0.
 */

DIR *
isdup (path)
char *path;
{
    register DIR *dir;

    for (dir = dirs; dir; dir = dir->next)
        if (eq (dir->man, path))
            return (dir);
    return ((DIR *) 0);
}

!Funky!Stuff!
if test 3908 -ne "`wc -c < 'config.c'`"
then
	echo shar: error transmitting "'config.c'" '(should have been 3908 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'defs.h'" '(2025 characters)'
if test -f 'defs.h'
then
	echo shar: will not over-write existing file "'defs.h'"
else
cat << \!Funky!Stuff! > 'defs.h'
/*
 * $Header: defs.h,v 1.3 85/08/27 15:16:31 broome Exp $
 */

/*
 * $Log:    defs.h,v $
 * Revision 1.3  85/08/27  15:16:31  broome
 * Last cleanup before release.
 * 
 * Revision 1.2  85/07/16  11:16:36  broome
 * Added global "debug" variable.
 * 
 * Revision 1.1  85/07/05  18:19:11  broome
 * Initial revision
 */

#include <stdio.h>

#define DIRS   11    /* one name can reference DIRS-1 directories */
#define TYPES  10    /* and at most TYPES cpu types               */

struct section {
    char    *name;            /* name of section */
    struct  dir *dirs[DIRS];  /* array of pointers to directories */
    struct  section *next;    /* next one in chain */
};

struct dir {
    char   *man;              /* full pathname of unformatted directory */
    char   *cat;              /* full pathname containing formatted files */
    char   **suff;            /* array of file suffixes */
    struct dir *next;         /* next one in list */
};

struct where {                /* structure returned by find() */
    char   *name;             /* name of file topic asked for */
    char   *section;          /* section asked for (if any)   */
    char   subsec;            /* subsection char, if any      */
    int    found;             /* 1 if file found, 0 if not    */
    char   *man;              /* path to unformatted form     */
    char   *cat;              /* full path to formatted form  */
};

struct type {
    char    *name;            /* cpu type name              */
    struct  dir *dir;         /* pointer to top of dir list */
    struct  section *sec;     /* top of section list        */
};

struct type    types[TYPES];  /* all known cpu types */
struct section *sections;     /* top of list of sections */
struct dir     *dirs;         /* top of list of directories */
int    debug;

typedef struct dir      DIR;
typedef struct section  SEC;

char    *getword();
char    *malloc();
char    *strsave();
SEC     *find_section();
struct  where *find();

#define eq(a,b)     (strcmp (a, b) == 0)
!Funky!Stuff!
if test 2025 -ne "`wc -c < 'defs.h'`"
then
	echo shar: error transmitting "'defs.h'" '(should have been 2025 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'cat.c'" '(1822 characters)'
if test -f 'cat.c'
then
	echo shar: will not over-write existing file "'cat.c'"
else
cat << \!Funky!Stuff! > 'cat.c'
#ifndef lint
static char *RCSid = "$Header: cat.c,v 1.6 85/08/27 15:16:11 broome Exp $";
#endif

/*
 * $Log:    cat.c,v $
 * Revision 1.6  85/08/27  15:16:11  broome
 * Last cleanup before release.
 * 
 * Revision 1.5  85/07/06  16:55:41  broome
 * 
 * Revision 1.4  85/07/03  17:34:11  broome
 * 
 * Revision 1.3  85/07/03  15:41:37  broome
 * Safety check before major revisions...
 * 
 * Revision 1.2  85/07/02  21:05:36  broome
 * 
 * Revision 1.1  85/06/25  11:23:29  broome
 * Initial revision
 */

#include "defs.h"
#include "response.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>

cat (argc, argv)
int   argc;
char *argv[];
{
    extern  int errno;
    struct  stat statb;
    struct  where *wp;
    char    line[256];
    long    date;
    FILE    *fp;

    wp = find (--argc, ++argv);
    if (wp->found) {
        (void) stat (wp->man, &statb);  /* stat the unformatted form */
        date = statb.st_mtime;   /* last modification date */

        if (stat (wp->cat, &statb) < 0 ||   /* doesn't exist */
            statb.st_size == 0 ||           /* zero length   */
            statb.st_mtime < date)          /* out of date   */
                if (format (wp->man, wp->cat))
                    return;

        if ((fp = fopen (wp->cat, "r")) == NULL)  /* should have it ! */
                printf ("%d Cannot open %s: errno %d.\r\n", ERR_NOFILE,
                            wp->cat, errno);
        else {
            printf ("%d Here comes the man page for %s! (%s)\r\n", OK_COMING,
                    wp->name, wp->cat);

            while (fgets (line, 256, fp))  /* don't bother with \n => \r\n */
                fputs (line, stdout);

            puts (".\r");
            fclose (fp);
        }
    } else     /* path not found */
        notfound (wp);
}
!Funky!Stuff!
if test 1822 -ne "`wc -c < 'cat.c'`"
then
	echo shar: error transmitting "'cat.c'" '(should have been 1822 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'response.h'" '(2714 characters)'
if test -f 'response.h'
then
	echo shar: will not over-write existing file "'response.h'"
else
cat << \!Funky!Stuff! > 'response.h'

/*
 * $Header: response.h,v 1.5 85/08/27 15:17:16 broome Exp $
 */

/*
 * $Log:    response.h,v $
 * Revision 1.5  85/08/27  15:17:16  broome
 * Last cleanup before release.
 * 
 * Revision 1.4  85/08/04  16:36:54  broome
 * Added new responses OK_TYPE and ERR_TYPE for "type" command.
 * 
 * Revision 1.3  85/08/03  18:40:24  broome
 * Fixed several codes for consistency ...
 * 
 * Revision 1.2  85/07/02  21:05:32  broome
 * 
 * Revision 1.1  85/06/25  11:23:27  broome
 * Initial revision
 */

#define INFO_HELP   100     /* in front of help message                     */
#define INFO_TYPE   123     /* showing currently selected cpu type          */
#define INFO_FMT    141     /* nroffing file now                            */
#define INFO_DONE   142     /* finished formatting                          */

#define OK_HELLO    210     /* opening banner                               */
#define OK_GOODBYE  211     /* closing banner                               */
#define OK_COMING   221     /* man page on its way                          */
#define OK_STAT     222     /* named page exists in formatted form          */
#define OK_TYPE     223     /* reconfiguring for specified cpu type         */

#define ERR_TIMEOUT 412     /* connection timed out after idle time         */
#define ERR_NOSUBJ  405     /* specified section but no subject             */
#define ERR_NOTSEC  421     /* no entry for subject in this section         */
#define ERR_STAT    422     /* don't have file in *formatted* form          */
#define ERR_TYPE    423     /* no config file for specified cpu type        */
#define ERR_HLPFILE 424     /* cannot open help file                        */
#define ERR_PERM    425     /* cannot open file - permission denied         */
#define ERR_OUTPUT  461     /* cannot create output file for formatting     */
#define ERR_EXEC    462     /* exec failed in format routine                */
#define ERR_CORE    463     /* nroff dumped core                            */
#define ERR_EXIT    465     /* nroff returned non-zero exit status          */
#define ERR_FMT     465     /* unknown formatting error                     */

#define ERR_COMMAND 501     /* command not recognized or command not found  */
#define ERR_SYNTAX  502     /* syntax error - argument count                */
#define ERR_NOHELP  504     /* no help available for this command           */
#define ERR_CNI     506     /* command not implemented yet                  */
#define ERR_HELLO   510     /* your host is denied access                   */
#define ERR_NOENT   521     /* no entry for subject                         */
#define ERR_NOFILE  522     /* cannot open the named file                   */
!Funky!Stuff!
if test 2714 -ne "`wc -c < 'response.h'`"
then
	echo shar: error transmitting "'response.h'" '(should have been 2714 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'main.c'" '(7761 characters)'
if test -f 'main.c'
then
	echo shar: will not over-write existing file "'main.c'"
else
cat << \!Funky!Stuff! > 'main.c'
/*
 *  Copyright (c) 1985  Jonathan C. Broome and The Regents of the 
 *  University of California.
 *
 *  This software may be freely redistributed without licensing
 *  provided that this copyright notice remains intact and no profit
 *  is gained through any redistribution.
 *
 *  Please report any bug fixes or modifications to the author at:
 *
 *        broome@ucb-vax.berkeley.edu
 *   or:
 *        ...!ucbvax!broome
 *
 *  The author and the Regents assume no liability for any damages 
 *  or loss of revenue caused directly or indirectly by the use of 
 *  this software.
 */

#ifndef lint
static char *RCSid = "$Header: main.c,v 1.9 85/08/27 18:45:47 broome Exp $";
#endif

/*
 * $Log:    main.c,v $
 * Revision 1.9  85/08/27  15:16:47  broome
 * Last cleanup before release.
 * 
 * Revision 1.8  85/08/05  08:53:38  broome
 * Fixed missing argument to error printf ...
 * 
 * Revision 1.7  85/08/04  16:33:06  broome
 * Added new command line options:
 *   -f  -  to specify a configuration file besides the default.
 *   -l  -  sets load cutoff so that we won't answer pings if load is higher.
 *   -p  -  specify alternate port number to listen on.
 * 
 * Revision 1.6  85/07/24  10:38:57  broome
 * 
 * Revision 1.5  85/07/16  11:09:56  broome
 * Tuned the new "ping" stuff a bit.
 * 
 * Revision 1.4  85/07/06  16:55:57  broome
 * Added new ping routines to help improve response times,
 * also makes it easier to cope with dead hosts.
 * 
 * Revision 1.3  85/07/03  17:34:30  broome
 * [ RCS screwed up again! ]
 * 
 * Revision 1.2  85/07/02  21:05:52  broome
 * 
 * Revision 1.1  85/06/25  11:23:38  broome
 * Initial revision
 */

#include "response.h"
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#ifdef SERVICES
#include <netdb.h>
#endif SERVICES

#ifndef STRICT         /* hang up on unknown hosts ??? */
#define STRICT 0
#endif
#ifndef TIMEOUT        /* timeout interval on a connection */
#define TIMEOUT 2
#endif

extern int errno;
char   hostname[32];

main (ac, av)
int  ac;
char **av;
{
#ifdef SERVICES
    struct servent *sp, *getservbyname();
#endif
    char   **argv;
    char   *ctime();
    char   *config_file = CONFIG;
    long   now;
    char   line[128];
    int    argc, len, psock, ssock, new, mask, tty;
    int    reaper(), hup(), timeout();
    int    port = 0;
    int    secure = STRICT;    /* hang up on unidentified hosts??? */
    extern double cutoff;
    struct sockaddr_in sin;

/*
 *  usage: mand [ -s ] [ -f config_file ] [ -p service_port ] [ -l load_cutoff ]
 */

    while (*++av) {
        if (**av != '-')
            usage ();
        switch (*++*av) {
            case 'f': if (*++av)   /* next arg is configuration file */
                        config_file = *av;
                      else
                        usage ();
                      break;
            case 'p': if (*++av)  /* next arg is service port to use */
                        port = htons (atoi (*av));
                      else
                        usage ();
                      break;
            case 'l': if (*++av)  /* next arg is load cutoff point */
                        cutoff = (double) atoi (*av);
                      else
                        usage ();
                      break;
            case 's': secure = !secure;
                      printf ("Secure = O%s\n", secure ? "N" : "FF");
                      break;
            default:  usage ();
        }
    }

    if (port == 0) {
#ifndef SERVICES
        fprintf (stderr, "Have to specify a service port!\n");
        usage ();
#else
        if ((sp = getservbyname ("man", "tcp")) == NULL) {
            fprintf (stderr, "mand: \"man/tcp\": unknown service.\n");
            exit (1);
        }
        port = sp->s_port;
#endif SERVICES
    }

    if ((ssock = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
        perror ("socket");
        exit (-1);
    }

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = port;

    if (bind (ssock, (char *)&sin, sizeof (sin)) < 0) {
        perror ("bind");
        exit (-2);
    }

    (void) listen (ssock, 5);

    if ((psock = open_ping (port)) < 0)      /* start up datagram socket */
        exit (1);

    if (fork ())                       /* fork off into background */
        exit (0);
    
    signal (SIGCHLD, reaper);
    signal (SIGPIPE, SIG_IGN);
    signal (SIGHUP,  hup);

    tty = open ("/dev/tty", 0, 0);     /* disassociate from terminal */
    if (tty != -1) {
        ioctl (tty, TIOCNOTTY, (struct sgttyb *)0);
        (void) close (tty);
    } else
        if (setpgrp (0, 0))
            perror ("setpgrp");

    (void) chdir ("/usr/man");
    (void) umask (0);                  /* may not want this when root */
    config (config_file);
    gethostname (hostname, 32);

    for ( ;; ) {
        mask = (1 << psock) | (1 << ssock);
        if (select (20, &mask, 0, 0, 0) <= 0)
            continue;

        if (mask & (1 << psock))           /* `ping' */
            ping ();
        
        if (mask & (1 << ssock)) {
            len = sizeof (sin);
            if ((new = accept (ssock, &sin, &len)) < 0) {
                if (errno != EINTR) {
                    perror ("accept");
                    exit (-1);
                } else
                    continue;
            }
            if (fork ()) {         /* parent */
                close (new);
            } else {               /* child */
                close (ssock);
                close (psock);
                signal (SIGCHLD, SIG_IGN);
                signal (SIGALRM, timeout);
                alarm  (TIMEOUT * 60);   /* close conn after TIMEOUT mins */
                if (new != 0)            /* set up stdin, stdout, stderr */
                    dup2 (new, 0);
                if (new != 1) 
                    dup2 (new, 1);
                if (new != 2) 
                    dup2 (new, 2);

                if (identify (sin) && secure) {
        printf ("%d %s manual server: your host does not have man access\r\n", 
                        ERR_HELLO, hostname);
                    exit (1);
                }

                time (&now);
                printf ("%d %s manual page server ready at %19.19s\r\n",
                        OK_HELLO, hostname, ctime (&now));
                (void) fflush (stdout);
    
                while (gets (line)) {                /* read a line    */
                    argc = parse (line, &argv);      /* parse it       */
                    if (argc == 0)                   /* blank line --- */
                        continue;                    /* just ignore it */
                    funcall (argc, argv);            /* do the command */
                }
                quit();
            }
        }
    }
}


/*
 *  Take care of all the dead children, avoiding zombie processes.
 */

reaper()
{
    union wait status;

    while (wait3 (&status, WNOHANG, 0) > 0)
        ;
}


/*
 *  Bye bye ...
 */

quit ()
{
    long now;

    time (&now);
    printf ("%d %s closing connection at %19.19s\r\n", OK_GOODBYE,
                hostname, ctime (&now));
    exit (0);
}


/*
 *  Exit nicely on SIGHUP.
 */

hup ()
{
    exit (0);
}


/*
 *  Timed out after TIMEOUT minutes --- close down the connection.
 */

timeout ()
{
    printf ("%d %s manual server: connection timed out after %d minutes.\r\n", 
            ERR_TIMEOUT, hostname, TIMEOUT);
    exit (1);
}


/*
 *  Print a usage message and go away.
 */

usage ()
{
    fprintf (stderr, 
    "Usage: mand [ -s ] [ -f config_file ] [ -p port ] [ -l load_cutoff ]\n");
    exit (1);
}
!Funky!Stuff!
if test 7761 -ne "`wc -c < 'main.c'`"
then
	echo shar: error transmitting "'main.c'" '(should have been 7761 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'cmd.h'" '(2489 characters)'
if test -f 'cmd.h'
then
	echo shar: will not over-write existing file "'cmd.h'"
else
cat << \!Funky!Stuff! > 'cmd.h'
/*
 * $Header: cmd.h,v 1.9 85/08/27 15:16:22 broome Exp $
 */

/*
 * $Log:    cmd.h,v $
 * Revision 1.9  85/08/27  15:16:22  broome
 * Last cleanup before release.
 * 
 * Revision 1.8  85/08/06  11:43:09  broome
 * Another new command - "ver" (version). Good for seeing if each site
 * is running the new stuff ....
 * 
 * Revision 1.7  85/08/04  16:30:18  broome
 * Added new "type" command - allows a client to reconfigure mand to
 * use a configuration file with machine-specific pages.
 * 
 * Revision 1.6  85/08/03  18:39:46  broome
 * Added new `stat' command - tests to see if the formatted version is
 * readily available.
 * 
 * Revision 1.5  85/07/16  11:07:33  broome
 * Added new commands "path" and "list".
 * 
 * Revision 1.4  85/07/06  16:55:35  broome
 * 
 * Revision 1.3  85/07/03  17:34:02  broome
 * 
 * Revision 1.2  85/07/02  21:05:29  broome
 * 
 * Revision 1.1  85/06/25  11:23:26  broome
 * Initial revision
 */

/*
 * Have to declare all the functions before we can use them in the command table
 */

int   apropos(), cat(), dbug(), dofind(), dosections(), dostat(), help(),
    list(), quit(), raw(), path(), show(), dotype(), version(), whatis();

struct cmd {
    char *name;       /* command name           */
    int  (*func)();   /* function to handle it  */
    int  min;         /* minimum number of args, including command name */
    int  max;         /* maximum number of args */
    char *usage;      /* and usage msg string   */
} cmdtab[] = {
    { "apropos",apropos,    2,  2,  "apropos topic"         },
    { "cat",    cat,        2,  3,  "cat <section> topic"   },
    { "debug",  dbug,       1,  1,  "debug"                 },
    { "find",   dofind,     2,  3,  "find <section> topic"  },
    { "help",   help,       1,  2,  "help <command>"        },
    { "list",   list,       1,  1,  "list"                  },
    { "path",   path,       1,  2,  "path <section>"        },
    { "quit",   quit,       1,  1,  "quit"                  },
    { "raw",    raw,        2,  3,  "raw <section> topic"   },
    { "show",   show,       2,  99, "show file <file>"      },
    { "secs",   dosections, 1,  1,  "secs"                  },
    { "stat",   dostat,     2,  3,  "stat <section> topic"  },
    { "type",   dotype,     1,  2,  "type <cpu_type>"       },
    { "ver",    version,    1,  1,  "ver"                   },
    { "whatis", whatis,     2,  2,  "whatis topic"          }
};

#define cmdtabsize (sizeof (cmdtab) / sizeof (struct cmd))
!Funky!Stuff!
if test 2489 -ne "`wc -c < 'cmd.h'`"
then
	echo shar: error transmitting "'cmd.h'" '(should have been 2489 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'cmd.c'" '(1273 characters)'
if test -f 'cmd.c'
then
	echo shar: will not over-write existing file "'cmd.c'"
else
cat << \!Funky!Stuff! > 'cmd.c'
#ifndef lint
static char *RCSid = "$Header: cmd.c,v 1.6 85/08/27 15:16:18 broome Exp $";
#endif

/*
 * $Log:    cmd.c,v $
 * Revision 1.6  85/08/27  15:16:18  broome
 * Last cleanup before release.
 * 
 * Revision 1.5  85/07/16  11:06:58  broome
 * Added new "path" and "list" commands, makes debugging easier.
 * 
 * Revision 1.4  85/07/06  16:55:44  broome
 * 
 * Revision 1.3  85/07/03  17:34:15  broome
 * 
 * Revision 1.2  85/07/02  21:05:40  broome
 * 
 * Revision 1.1  85/06/25  11:23:31  broome
 * Initial revision
 */

#include "response.h"
#include "cmd.h"
#include <stdio.h>

/*
 *  Do the actual function calls here.
 */

funcall (argc, argv)
int      argc;
char   **argv;
{
    register struct cmd *cmd;

    for (cmd = cmdtab; cmd < cmdtab + cmdtabsize; cmd++) {
        if (streql (cmd->name, *argv) == 0) {          /* names match */
            if (argc >= cmd->min && argc <= cmd->max)  /* right # args */
                (*cmd->func)(argc, argv);
            else                                       /* bad arg count */
                printf ("%d Usage: %s\r\n", ERR_SYNTAX, cmd->usage);
            (void) fflush (stdout);
            return;
        }
    }
    printf ("%d Command not recognized.\r\n", ERR_COMMAND);
    (void) fflush (stdout);
}

!Funky!Stuff!
if test 1273 -ne "`wc -c < 'cmd.c'`"
then
	echo shar: error transmitting "'cmd.c'" '(should have been 1273 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'identify.c'" '(1677 characters)'
if test -f 'identify.c'
then
	echo shar: will not over-write existing file "'identify.c'"
else
cat << \!Funky!Stuff! > 'identify.c'
/*
 *  Copyright (c) 1985  Jonathan C. Broome and The Regents of the 
 *  University of California.
 *
 *  This software may be freely redistributed without licensing
 *  provided that this copyright notice remains intact and no profit
 *  is gained through any redistribution.
 *
 *  Please report any bug fixes or modifications to the author at:
 *
 *        broome@ucb-vax.berkeley.edu
 *   or:
 *        ...!ucbvax!broome
 *
 *  The author and the Regents assume no liability for any damages 
 *  or loss of revenue caused directly or indirectly by the use of 
 *  this software.
 */

#include "defs.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#ifndef HOSTFILE
#define HOSTFILE "/usr/lib/mand.hosts"
#endif

/*
 *  Determine the calling host's cpu type if possible, then initialize it.
 *  If the host is unknown, set to "generic".
 *  Returns 1 if host is unknown - used if identity checking is strict.
 */

identify (sin)
struct sockaddr_in sin;
{
    char     **argv = (char **) 0;
    char     *addr;
    char     *inet_ntoa();
    char     line[256];
    int      found = 0;
    FILE     *fp;

    addr = inet_ntoa (sin.sin_addr.s_addr);

    if (fp = fopen (HOSTFILE, "r")) {
        while (getline (line, 256, fp)) {
            if (parse (line, &argv) == 0)
                continue;
            if (eq (argv[0], addr)) {
                found = 1;
                break;
            }
        }
        (void) fclose (fp);
    }

    if (switchtype (found ? argv[1] : "generic") && found) 
        switchtype ("generic");

    parse ("", &argv);     /* free up memory */

    return (!found);
}
!Funky!Stuff!
if test 1677 -ne "`wc -c < 'identify.c'`"
then
	echo shar: error transmitting "'identify.c'" '(should have been 1677 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'apropos.c'" '(1769 characters)'
if test -f 'apropos.c'
then
	echo shar: will not over-write existing file "'apropos.c'"
else
cat << \!Funky!Stuff! > 'apropos.c'
#ifndef lint
static char RCSid[] = "$Header: apropos.c,v 1.5 85/08/27 15:16:08 broome Exp $";
#endif

/*
 * $Log:    apropos.c,v $
 * Revision 1.5  85/08/27  15:16:08  broome
 * Last cleanup before release.
 * 
 * Revision 1.4  85/07/24  10:38:26  broome
 * Ready to ship out ... (?)
 * 
 * Revision 1.3  85/07/04  20:26:25  broome
 * Fixed error messages to conform to the rfc.
 */


#include "response.h"
#include <stdio.h>

#define LIST "/usr/lib/whatis"

/*ARGSUSED*/
apropos (argc, argv)
int     argc;
char    **argv;
{
    register FILE *fp;
    char     buf[512];
    int      found = 0;

    argv++;
    if ((fp = fopen (LIST, "r")) == NULL) {
        printf ("%d cannot open %s\r\n", ERR_NOFILE, LIST);
        return;
    }

    while (fgets (buf, 512, fp)) {
        if (substr (buf, *argv)) {
            if (found == 0)
                printf ("%d information on %s coming.\r\n", OK_COMING, *argv);
            printf ("%s", buf);
            (void) fflush (stdout);    /* so it doesn't seem as slow to them */
            found++;
        }
    }
    if (!found) 
        printf ("%d %s: nothing appropriate.\n", ERR_NOENT, *argv);
    else
        puts (".\r");
}


/*
 *  Returns 1 if `sub' is contained in `str'.
 */

substr (buf, str)
register char *buf, *str;
{
    register char *i;

    i = str;

    while (*buf) {
        if (tolower (*buf) == *i) {   /* first char matches */
            while (*buf) {            /* does rest match?   */
                if (tolower (*buf) != *i)
                    break;
                buf++, i++;
            }
            if (!*i)                  /* ran out of substring */
                return (1);
            i = str;                  /* reset i */
        }
        buf++;
    }
    return (0);
}

!Funky!Stuff!
if test 1769 -ne "`wc -c < 'apropos.c'`"
then
	echo shar: error transmitting "'apropos.c'" '(should have been 1769 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'ver.c'" '(1106 characters)'
if test -f 'ver.c'
then
	echo shar: will not over-write existing file "'ver.c'"
else
cat << \!Funky!Stuff! > 'ver.c'
/*
 *  Copyright (c) 1985  Jonathan C. Broome and The Regents of the 
 *  University of California.
 *
 *  This software may be freely redistributed without licensing
 *  provided that this copyright notice remains intact and no profit
 *  is gained through any redistribution.
 *
 *  Please report any bug fixes or modifications to the author at:
 *
 *        broome@ucb-vax.berkeley.edu
 *   or:
 *        ...!ucbvax!broome
 *
 *  The author and the Regents assume no liability for any damages 
 *  or loss of revenue caused directly or indirectly by the use of 
 *  this software.
 */

#ifndef lint
static char RCSid[] = "$Header: ver.c,v 1.2 85/08/27 18:45:53 broome Exp $";
#endif

/*
 * $Log:    ver.c,v $
 * Revision 1.2  85/08/25  17:20:58  broome
 * 
 * Revision 1.1  85/08/06  11:22:54  broome
 * Initial revision
 */

#include <stdio.h>

/*
 *  Show the date this version was last recompiled.
 *  The "V ERSION" and "D ATE" stuff will be filled in
 *  by the "newver.csh" script using sed.
 */

version ()
{
    printf ("101 Version VERSION, compiled DATE\r\n");
    (void) fflush (stdout);
}
!Funky!Stuff!
if test 1106 -ne "`wc -c < 'ver.c'`"
then
	echo shar: error transmitting "'ver.c'" '(should have been 1106 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'help.c'" '(1708 characters)'
if test -f 'help.c'
then
	echo shar: will not over-write existing file "'help.c'"
else
cat << \!Funky!Stuff! > 'help.c'
#ifndef lint
static char *RCSid = "$Header: help.c,v 1.3 85/07/16 11:08:41 broome Exp $";
#endif

/*
 * $Log:    help.c,v $
 * Revision 1.3  85/07/16  11:08:41  broome
 * Fixed to print error message when can't open help file.
 * 
 * Revision 1.2  85/07/06  16:55:53  broome
 * 
 * 
 * Revision 1.1  85/06/25  11:23:35  broome
 * Initial revision
 */

#include <stdio.h>
#include "response.h"

#ifndef HELPFILE
#define HELPFILE  "/usr/lib/mand.hf"
#endif

static FILE *hp = (FILE *) NULL;   /* help file pointer */

help (argc, argv)
int   argc;
char *argv[];
{
    char buf[80];
    int  slen, found = 0;
    char *subj;
    char *i, *index();
    
    if (argc == 1)      /* no args, use "mand" */
        subj = "mand";
    else
        subj = argv[1];
    slen = strlen (subj);

    if (hp == (FILE *) NULL)   /* haven't opened help file yet */
        if ((hp = fopen (HELPFILE, "r")) == (FILE *) NULL) {
            printf ("%d Cannot open help file (%s)\r\n", ERR_HLPFILE, HELPFILE);
            (void) fflush (stdout);
            return (ERR_HLPFILE);
        }

    fseek (hp, 0L, 0);                        /* go to top of file */
    while (fgets (buf, 80, hp)) {
        if (strncmp (buf, subj, slen))        /* subject doesn't match */
            continue;
        buf[strlen(buf)-1] = '\0';            /* zap the '\n' */