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

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

[ this is the last part - cat them all together and feed to 'sh' ]
        if (i = index (buf, '\t'))            /* tabs separate name from info */
            printf ("%d-%s\r\n", INFO_HELP, ++i);
        found++;
    }

    if (found)
        printf ("%d End of HELP info for \"%s\"\r\n", INFO_HELP, subj);
    else 
        printf ("%d HELP topic \"%s\" unknown\r\n", ERR_NOHELP, subj);
    fflush (stdout);
    return (found != 0);
}
!Funky!Stuff!
if test 1708 -ne "`wc -c < 'help.c'`"
then
	echo shar: error transmitting "'help.c'" '(should have been 1708 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'find.c'" '(3500 characters)'
if test -f 'find.c'
then
	echo shar: will not over-write existing file "'find.c'"
else
cat << \!Funky!Stuff! > 'find.c'
#ifndef lint
static char RCSid[] = "$Header: find.c,v 1.4 85/08/27 15:16:36 broome Exp $";
#endif

/*
 * $Log:    find.c,v $
 * Revision 1.4  85/08/27  15:16:36  broome
 * Last cleanup before release.
 * 
 * Revision 1.3  85/08/04  16:32:15  broome
 * Increased efficiency by checking for directory existence before stat'ing
 * each possible file name.
 * 
 * Revision 1.2  85/07/06  16:56:18  broome
 * 
 * Revision 1.1  85/07/05  18:19:13  broome
 * Initial revision
 */

#include <sys/file.h>
#include "defs.h"

/*
 *  Take the name/section argument and try to 
 *  find the corresponding files.
 */

struct where *
find (argc, argv)
int   argc;
char  *argv[];
{
    static struct where wp;
    SEC    *sec;
    DIR    *dir;
    int    d;

    bzero ((char *)&wp, sizeof (wp));

    if (argc == 2) {   /* section specified */
        wp.section = strsave (argv[0]);
        wp.name    = strsave (argv[1]);

        if (strlen (*argv) == 2)              /* subsection kludge */
            wp.subsec = (*argv)[1];

        if (sec = find_section (*argv++)) {
            for (d = 0; sec->dirs[d]; d++)    /* check each dir pointed to */
                if (checkpath (sec->dirs[d], *argv, &wp))
                    break;
        }
    } else {
        wp.name = strsave (*argv);

        /*
         *   Default action is to check all known
         *   directories and suffixes.
         */

        for (dir = dirs; dir; dir = dir->next)
            if (checkpath (dir, *argv, &wp))
                break;
    }
    return (&wp);
}


/*
 *  Return a pointer to the section structure for the named section,
 *  Null pointer if not found.
 */

SEC *
find_section (name)
char *name;
{
    register SEC *sec;
    register int len = strlen (name);

    for (sec = sections; sec; sec = sec->next) {
        if (eq (name, sec->name))
            return (sec);
        if (len == 2 && strlen (sec->name) == 1)  /* kludge for `man 3x foo' */
            if (*name == *sec->name)
                return (sec);
    }
    return ((SEC *) 0);
}


/*
 *  Given a filename and DIR pointer, see if a file exists whose name
 *  is the concatenation of the dir, name, and suffix.
 */

checkpath (dir, name, wp)
DIR  *dir;
char *name;
struct where *wp;
{
    char  *suff;
    int   i;

    if (wp->subsec) {            /* forced subsection, don't search list */
        char suf[10];

        sprintf (suf, ".%s", wp->section);
        return (docheck (dir, name, suf, wp));
    }

    if (access (dir->man, 0) == -1) {   /* test for dir existence */
        if (debug)
            printf ("%s: No such directory.\n", dir->man);
        return (0);
    }

    for (i = 0; suff = dir->suff[i]; i++)      /* test each possible suffix */
        if (docheck (dir, name, suff, wp))
            return (1);
    return (0);
}

/*
 *  Common code to check on one file.
 */

static 
docheck (dir, name, suff, wp)
DIR  *dir;
char *name;
char *suff;
struct where *wp;
{
    char buf[256];

    sprintf (buf, "%s/%s%s", dir->man, name, suff);   /* full name of file */

    if (debug)     /* show file we are checking */
        printf ("Checking %s\t", buf);

    if (access (buf, R_OK) == -1) {         /* doesn't exist */
        if (debug)
            printf ("not found.\n");
        return (0);
    }

    if (debug)
        printf ("found.\n");

    wp->man = strsave (buf);
    sprintf (buf, "%s/%s%s", dir->cat, name, suff);   /* name of cat file */
    wp->cat = strsave (buf);
    wp->found = 1;
    return (1);
}
!Funky!Stuff!
if test 3500 -ne "`wc -c < 'find.c'`"
then
	echo shar: error transmitting "'find.c'" '(should have been 3500 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'mand.cf'" '(3633 characters)'
if test -f 'mand.cf'
then
	echo shar: will not over-write existing file "'mand.cf'"
else
cat << \!Funky!Stuff! > 'mand.cf'
#   Anatomy of a directory entry:
#  
#   1	/usr/man/man1	/usr/man/cat1	.1,.1m,.1c .1g .1p .1r .1v
#
#  "1"             --  Section name to be used by users (ie. "man 1 ls")
#  "/usr/man/man1" --  Directory to search for unformatted pages
#  "/usr/man/cat1" --  Directory to look/place formatted pages in
#  ".1,.1m,.1c"    --- Suffixes to add to name, ie. "/usr/man/man1/ls.1"
#
#  List all the sections by name, then the directories and suffixes
#  continuation lines must begin with a *tab* character, suffixes
#  may be separated by white space or commas, long lines may be continued
#  by ending with a backslash character.
#
#  If you want multiple names to apply to one directory or set of directories,
#  you need to have multiple lines with (at least) the section name and the 
#  man directory.  The configuration routine will assign both section names
#  to each directory.
#
#  Note that if you want to have several sections use the same man directory 
#  with *different* suffixes (ie /usr/man/man1 w/ ".1 .1c" and ".1 .1f"), then
#  you need to use different *names* for the two directories --- like
#  "/usr/man//man1" for one of them will do.
# 
#name	man dir		cat dir		suffixes

1	/usr/man/man1	/usr/man/cat1	.1,.1m,.1c .1g .1p .1r .1v
	/usr/man/mann	/usr/man/catn	.n
	/usr/man/manl	/usr/man/catl	.l
	/usr/man/mano	/usr/man/cato	.o

n	/usr/man/mann	/usr/man/catn	.n
new	/usr/man/mann	/usr/man/catn	.n

l	/usr/man/manl	/usr/man/catl	.l
local	/usr/man/manl	/usr/man/catl	.l

6	/usr/man/man6	/usr/man/cat6	.6

8	/usr/man/man8	/usr/man/cat8	.8 .8v .8c

2	/usr/man/man2	/usr/man/cat2	.2 .2v

3	/usr/man/man3	/usr/man/cat3	.3 .3j .3x .3m .3s .3n .3v .3c .3f

4	/usr/man/man4	/usr/man/cat4	.4 .4p .4f .4v .4s .4n

5	/usr/man/man5	/usr/man/cat5	.5

7	/usr/man/man7	/usr/man/cat7	.7

p	/usr/man/manp	/usr/man/catp	.p
public	/usr/man/manp	/usr/man/catp	.p

o	/usr/man/mano	/usr/man/cato	.o
old	/usr/man/mano	/usr/man/cato	.o

#  Switch type to "cad" - this will override all other sections
#  when cpu type is "cad" --> Note that Cad users have no access 
#  to sections 2, 3, 5, old, or public  (Can use this to save
#  users from themselves!)

TYPE cad

1	/usr/man/man1	/usr/man/cat1	.1,.1m,.1c .1g .1p .1r .1v
	/usr/man/mann	/usr/man/catn	.n
	/usr/man/manl	/usr/man/catl	.l
	/usr/man/mano	/usr/man/cato	.o

n	/usr/man/mann	/usr/man/catn	.n
new	/usr/man/mann	/usr/man/catn	.n

l	/usr/man/manl	/usr/man/catl	.l
local	/usr/man/manl	/usr/man/catl	.l

6	/usr/man/man6	/usr/man/cat6	.6

8	/usr/man/man8	/usr/man/cat8	.8 .8v .8c

c	/a/guest/hprg/cad/man/man1	/a/guest/hprg/cad/man/cat1	.1
cad	/a/guest/hprg/cad/man/man1	/a/guest/hprg/cad/man/cat1	.1


TYPE cs8  # these guys don't get to read too much!

1	/c/cs8/doc/man	/c/cs8/doc/cat 	.1 .out
	/usr/man/man1	/usr/man/cat1	.1 .1 .1c .1p .1r .1v


TYPE sun

1	/sun/man/man1	/sun/man/cat1	.1,.1m,.1c .1g .1p .1r .1v
	/usr/man/man1	/usr/man/cat1	.1,.1m,.1c .1g .1p .1r .1v
	/sun/man/mann	/sun/man/catn	.n
	/usr/man/mann	/usr/man/catn	.n
	/sun/man/manl	/sun/man/catl	.l
	/usr/man/manl	/usr/man/catl	.l
	/sun/man/mano	/sun/man/cato	.o

n	/sun/man/mann	/sun/man/catn	.n
new	/sun/man/mann	/sun/man/catn	.n

l	/sun/man/manl	/sun/man/catl	.l
l	/usr/man/manl	/usr/man/catl	.l

local	/sun/man/manl	/sun/man/catl	.l
local	/usr/man/manl	/usr/man/catl	.l

6	/sun/man/man6	/sun/man/cat6	.6

8	/sun/man/man8	/sun/man/cat8	.8 .8v .8c

2	/sun/man/man2	/sun/man/cat2	.2 .2v

3	/sun/man/man3	/sun/man/cat3	.3 .3j .3x .3m .3s .3n .3v .3c .3f

4	/sun/man/man4	/sun/man/cat4	.4 .4p .4f .4v .4s .4n

5	/sun/man/man5	/sun/man/cat5	.5

7	/sun/man/man7	/sun/man/cat7	.7

sun	/sun/man/mans	/sun/man/cats	.s
!Funky!Stuff!
if test 3633 -ne "`wc -c < 'mand.cf'`"
then
	echo shar: error transmitting "'mand.cf'" '(should have been 3633 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'parse.c'" '(2147 characters)'
if test -f 'parse.c'
then
	echo shar: will not over-write existing file "'parse.c'"
else
cat << \!Funky!Stuff! > 'parse.c'
#ifndef lint
static char *RCSid = "$Header: parse.c,v 1.3 85/08/04 16:35:30 broome Exp $";
#endif

/*
 * $Log:    parse.c,v $
 * Revision 1.3  85/08/04  16:35:30  broome
 * Cleaned up a little, added comma as delimiter so that config file can
 * be more freely formatted.
 * 
 * Revision 1.2  85/07/06  16:56:04  broome
 * 
 * Revision 1.1  85/06/25  11:23:41  broome
 * Initial revision
 */

#define iswhite(c)   (c==' '||c=='\t'||c=='\n'||c=='\r'||c=='\0'||c==',')
/*
 *  Turn a line buffer into a pointer to a set 
 *  of strings, just like ``argv[]'', and return argc.
 */

parse (buf, array)
char  *buf;
char ***array;
{
    char **argv;
    char *s;
    char word[132];
    int  argc = 0;
    int  i = 0, j;

    argv = *array;
    if (argv != (char **) 0) {   /* have to free up space taken by old array */
        do 
            free (argv[i]);                  /* free up each element */
        while (argv[i++] != (char *) 0);
        free (argv);                         /* and then free argv itslef */
    }

    /*
     *  Count and null-terminate each word.
     */
    for (s = buf; *s; s++) 
        if (!iswhite (*s) && iswhite(*(s+1)))   /* the end of each word */
            argc++;
        else if (iswhite (*s))
            *s = '\0';
    
    /*
     *  Now malloc up space for the strings plus the null at the end.
     */
    if ((argv = (char **) malloc ((argc+1) * sizeof(char *))) == (char **) 0) {
        perror ("parse: cannot malloc space for argv[]");
        exit (1);
    }

    /*
     *  And copy the contents in.
     */

    i = 0;
    for (s = buf, j = 0; j < argc; s++) {
        if (!iswhite (*s) && iswhite(*(s+1))) {  /* the end of each word */
            word[i++] = *s;
            word[i] = '\0';
            if ((argv[j] = (char *)malloc (i+1)) == (char *) 0) {
                perror ("parse: cannot malloc mem for word");
                exit (1);
            }
            strcpy (argv[j++], word);
            i = 0;
        } else if (!iswhite (*s))
            word[i++] = *s;
    }
    argv[j] = (char *) 0;   /* and a null at the end of it all */
    *array = argv;

    return (argc);
}
!Funky!Stuff!
if test 2147 -ne "`wc -c < 'parse.c'`"
then
	echo shar: error transmitting "'parse.c'" '(should have been 2147 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'mand.hf'" '(2838 characters)'
if test -f 'mand.hf'
then
	echo shar: will not over-write existing file "'mand.hf'"
else
cat << \!Funky!Stuff! > 'mand.hf'
@(#)	mand.hf  Last revised 26/08/85.
	edit this file with tabstops set to 8.

mand	Commands available in ``mand'' are:
mand	 
mand		APROPOS	CAT	DEBUG	FIND	HELP
mand		LIST	PATH	QUIT	RAW	SHOW
mand		SECS	STAT	TYPE	VER	WHATIS
mand	 
mand	For help with a specific command, use "HELP topic".
mand	Send questions or bug reports to broome@ucb-vax.berkeley.edu
mand	This is an experimental version.

apropos	Usage: apropos topic
apropos		Searches through the permuted index for lines containing the
apropos		topic substring.  Very verbose.

cat	Usage: cat <section> page
cat		This is the main command used - it searches for the named
cat		page and retrieves it, formatting it if needed.

debug	Usage: debug
debug		Enables tracing of search and configuration routines.
debug		Not especially interesting.

find	Usage: find <section> name
find		Searches for the named page, returns the location
find		of the source (unformatted) page or error if not found.

fmt	Usage: fmt <section> name
fmt		Takes the unformatted page and formats it, sending the
fmt		output directly to the connection socket.
fmt		NOT YET IMPLEMENTED ....

help	Usage: help <command>
help		Shows the help page for the named command

list	Usage: list
list		Lists all known directories and sections in the order
list		that they will be searched (ie. when the section is 
list		left unspecified in a CAT command.)

path	Usage: path [section]
path		Lists the named section by name, showing each corresponding
path		directory and suffix list in the order searched. When given
path		no arguments, shows the information for all sections.

quit	Usage: quit
quit		Prints a goodbye message and closes down the connection.

raw	Usage: raw <section> page
raw		Sends the raw (unformatted) page if found, ERR otherwise.

secs	Usage: secs
secs		Returns a list of all currently valid section names,
secs		preceded by the number of sections to be sent.
secs		Can be used by a client to verify section names.
secs		(No pun intended!)

show	Usage: show page [ page [ page ] ... ]
show		Shows all the known files for topic `page' in 
show		the order that they are selected.

stat	Usage: stat <section> page 
stat		Returns OK status if a formatted copy of the named
stat		page is found (and is of non-zero length), ERR otherwise

type	Usage: type <cpu_type>
type		If given an argument, will cause ``mand'' to reconfigure 
type		itself for ``cpu_type'', elsee will show the currently
type		selected cpu type.  Used to allow mand to specially
type		cater to any of several	machine types, such as Suns and 
type		System V workstations with special man pages.

ver	Usage: ver
ver		Shows the date this daemon was last compiled and 
ver		the current version number.

whatis	Usage: whatis topic
whatis		Searches for a line in the permuted manual index
whatis		corresponding to the named topic and returns it.
!Funky!Stuff!
if test 2838 -ne "`wc -c < 'mand.hf'`"
then
	echo shar: error transmitting "'mand.hf'" '(should have been 2838 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'misc.c'" '(2712 characters)'
if test -f 'misc.c'
then
	echo shar: will not over-write existing file "'misc.c'"
else
cat << \!Funky!Stuff! > 'misc.c'
#ifndef lint
static char *RCSid = "$Header: misc.c,v 1.6 85/08/27 15:17:00 broome Exp $";
#endif

/*
 * $Log:    misc.c,v $
 * Revision 1.6  85/08/27  15:17:00  broome
 * Last cleanup before release.
 * 
 * Revision 1.5  85/08/04  16:56:04  broome
 * Added new "any" routine.
 * 
 * Revision 1.4  85/07/06  16:56:01  broome
 * 
 * Revision 1.3  85/07/03  17:34:34  broome
 * 
 * Revision 1.2  85/07/02  21:05:56  broome
 * 
 * Revision 1.1  85/06/25  11:23:39  broome
 * Initial revision
 */

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

/*
 *  Search for and print the path name to the topic and 
 *  section requested.  Duplicates the action used by 
 *  `cat()' and `raw()' without actually sending the file.
 */

dofind (argc, argv)
int    argc;
char  *argv[];
{
    struct where *wp;

    argv++, argc--;
    wp = find (argc, argv);
    if (wp->found)
        printf ("%d %s\r\n", OK_STAT, wp->man);
    else 
        notfound (wp);
}


/*
 *  Toggle debugging information.
 */

dbug ()
{
    debug = !debug;
    printf ("100 Debugging is now %s\n", debug ? "ON" : "OFF");
}


/*
 *  Show a list of all known directories.
 */

list ()
{
    DIR  *dir;
    char *suff;
    int  s;

    printf ("121-List of all known directories:\r\n");
    for (dir = dirs; dir; dir = dir->next) {
        printf ("120-Man: %s\n120-Cat: %s\r\n120-\tSuffixes: ", 
                dir->man, dir->cat);
        for (s = 0; suff = dir->suff[s]; s++)
            printf ("%s ", suff);
        puts ("\r");
    }
    printf ("122 That's all.\r\n");
}


/*
 *  Report that the requested file was not found.
 */

notfound (wp)
struct where *wp;
{
    printf ("%d No entry for %s in ", ERR_NOENT, wp->name);
    if (wp->section)
        printf ("section %s of ", wp->section);
    printf ("the manual.\r\n");
}


/*
 *   Allocate enough memory for the given string, then copy it in.
 */

char *
strsave (str)
char *str;
{
    char *s;
    char *malloc();

    s = malloc (strlen (str) + 1);
    strcpy (s, str);
    return (s);
}


/*
 *  Return 1 if ch is contained in str, else 0.
 */

any (ch, str)
register char ch;
register char *str;
{
    while (*str)
        if (ch == *str++)
            return (1);
    return (0);
}



/*
 *  Return the lower case version of a given character.
 *  Can't use a macro when arg is of the form `*foo++'.
 */

tolower (c)
register c;
{
    if ('A' <= c && c <= 'Z')
        return (c - 'A' + 'a');
    return (c);
}



/*
 *  Case insensitive version of strcmp(), returns 0 if equal, 1 if not.
 */

streql (a, b)
register char *a, *b;
{
    while (tolower (*a) == tolower (*b)) {
        if (*a == '\0')
            return (0);
        a++;
        b++;
    }
    return (1);
}
!Funky!Stuff!
if test 2712 -ne "`wc -c < 'misc.c'`"
then
	echo shar: error transmitting "'misc.c'" '(should have been 2712 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'sections.c'" '(899 characters)'
if test -f 'sections.c'
then
	echo shar: will not over-write existing file "'sections.c'"
else
cat << \!Funky!Stuff! > 'sections.c'
#ifndef lint
static char RCSid[] = "$Header: sections.c,v 1.1 85/08/04 16:37:18 broome Exp $";
#endif

/*
 * $Log:    sections.c,v $
 * Revision 1.1  85/08/04  16:37:18  broome
 * Initial revision
 * 
 */

#include "defs.h"

/*
 *  Show the list of all valid sections that the user can ask for.
 *  This can be used by a client program to download a list of *current*
 *  sections, instead of compiling them in. For this reason, we count
 *  the number of sections first, so that the client can allocate enough
 *  memory to save all the sections...
 */

dosections ()
{
    SEC  *sec;
    int  num = 0;

    for (sec = sections; sec; sec = sec->next)    /* count number of sections */
        num++;

    printf ("121-%d sections follow:\r\n", num);
    for (sec = sections; sec; sec = sec->next)
        printf ("120-%s\r\n", sec->name);
    printf ("122 That's all.\r\n");
    fflush (stdout);
}
!Funky!Stuff!
if test 899 -ne "`wc -c < 'sections.c'`"
then
	echo shar: error transmitting "'sections.c'" '(should have been 899 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'format.c'" '(2230 characters)'
if test -f 'format.c'
then
	echo shar: will not over-write existing file "'format.c'"
else
cat << \!Funky!Stuff! > 'format.c'
#ifndef lint
static char *RCSid = "$Header: format.c,v 1.6 85/08/27 15:16:41 broome Exp $";
#endif

/*
 * $Log:    format.c,v $
 * Revision 1.6  85/08/27  15:16:41  broome
 * Last cleanup before release.
 * 
 * Revision 1.5  85/08/06  11:43:56  broome
 * Added check for exit status,
 * remove core if it dumped ...
 * 
 * Revision 1.4  85/07/06  16:55:50  broome
 * 
 * Revision 1.3  85/07/03  17:34:18  broome
 * 
 * Revision 1.2  85/07/02  21:05:43  broome
 * 
 * Revision 1.1  85/06/25  11:23:33  broome
 * Initial revision
 */

#include <stdio.h>
#include <sys/file.h>
#include <sys/wait.h>
#include "response.h"
#define  NROFF "/usr/bin/nroff"

/*
 *  Format the named file into catable form, placing the output into `dest'.
 */

format (src, dest)
char   *src, *dest;
{
    union  wait status;
    int    pid, fd;
    extern int  errno;

    /*  tell them what's going on  */
    printf ("%d-formatting file %s ==> %s ....\r\n", INFO_FMT, src, dest);
    (void) fflush (stdout);

    if ((fd = creat (dest, 0644)) == -1) {   /* can't create file */
        printf ("%d Cannot create output file %s (%d)\r\n", 
                ERR_OUTPUT, dest, errno);
        return (1);
    }

    switch (pid = fork()) {
        case -1: return (1);     /* can't fork */

        case 0: if (fd != 1) {   /* child */
                    dup2 (fd, 1);
                    close (fd);
                }
                execl (NROFF, "nroff", "-man", src, 0);
                perror ("exec");
                exit (1);
        default: close (fd);     /* parent */
                while (wait (&status) != pid)
                    ;
                if (status.w_coredump) { /* shouldn't core dump */
                    (void) unlink ("core");   /* clean up */
                    printf ("%d Format encountered core dump!\r\n", ERR_CORE);
                    return (1);
                }
                if (status.w_retcode) {  /* non-zero exit status */
                    printf ("%d Format returned non-zero exit code.\r\n", 
                            ERR_EXIT);
                    return (2);
                }
                printf ("%d-done.\r\n", INFO_DONE);
                return (0);              /* all was ok */
    }
}
!Funky!Stuff!
if test 2230 -ne "`wc -c < 'format.c'`"
then
	echo shar: error transmitting "'format.c'" '(should have been 2230 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'ping.c'" '(2592 characters)'
if test -f 'ping.c'
then
	echo shar: will not over-write existing file "'ping.c'"
else
cat << \!Funky!Stuff! > 'ping.c'
#ifndef lint
static char RCSid[] = "$Header: ping.c,v 1.5 85/08/27 15:17:08 broome Exp $";
#endif

/*
 * $Log:    ping.c,v $
 * Revision 1.5  85/08/27  15:17:08  broome
 * Last cleanup before release.
 * 
 * Revision 1.4  85/08/04  16:36:11  broome
 * Added load cutoff, such that if 1 minute load is above this, we
 * won't respond to pings from clients.
 * 
 * Revision 1.3  85/07/24  10:39:03  broome
 * 
 * Revision 1.2  85/07/18  12:05:20  broome
 * 
 * Revision 1.1  85/07/16  11:10:26  broome
 * Initial revision
 */

/*
 *  Routines to handle `ping' calls on a dgram socket.
 */

#include <syslog.h>
#include <nlist.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

struct  nlist nl[] = {
    { "_avenrun" },
    { 0 },
};

int     sock;            /* the datagram socket descriptor */
int     kmem;            /* and memory file descriptor     */
double  cutoff = -1.0;   /* don't respond if 1min load is above this */

/*
 *  Initialize everything ...
 */

open_ping (port)
int   port;
{
    struct  sockaddr_in sin;

    if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
        syslog (LOG_ERR, "cannot open ping (datagram) socket: %m");
        return (-1);
    }

    bzero ((char *)&sin, sizeof (sin));
    sin.sin_port = port;         /* should probably do a `getservice' */
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;

    if (bind (sock, (char *)&sin, sizeof (sin)) < 0) {
        syslog (LOG_ERR, "cannot bind ping socket: %m");
        return (-1);
    }

    if ((kmem = open ("/dev/kmem", 0, 0)) < 0) {
        syslog (LOG_ERR, "No kmem: %m");
        return (-1);
    }

    nlist ("/vmunix", nl);
    if (nl[0].n_type == 0) {
        syslog (LOG_ERR, "No namelist: %m");
        return (-1);
    }
    return (sock);
}

/*
 *  Routine to actually handle `ping' packets sent to the datagram socket.
 *  Finds the load average and sends it back to the sending socket.
 */

ping ()
{
    struct  sockaddr_in from;
    double  avenrun[3];
    char    buf[20];

    /* grab the load average */
    lseek (kmem, (long) nl[0].n_value, 0);
    read (kmem, avenrun, sizeof (avenrun));

    if (cutoff != -1.0 && avenrun[0] > cutoff)  /* load's too high */
        return;

    /* don't want to see what they sent, just need their address */
    if (recvfrom (sock, buf, 20, 0, &from, sizeof (from)) < 1)
        return;

    sprintf (buf,"%.2f %.2f %.2f\n", avenrun[0], avenrun[1], avenrun[2]);

    /* and send it on over */
    if (sendto (sock, buf, strlen (buf), 0, &from, sizeof (from)) < 0)
        perror ("sendto");
}
!Funky!Stuff!
if test 2592 -ne "`wc -c < 'ping.c'`"
then
	echo shar: error transmitting "'ping.c'" '(should have been 2592 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'showpath.c'" '(1162 characters)'
if test -f 'showpath.c'
then
	echo shar: will not over-write existing file "'showpath.c'"
else
cat << \!Funky!Stuff! > 'showpath.c'
#ifndef lint
static char RCSid[] = "$Header: showpath.c,v 1.2 85/07/16 11:12:04 broome Exp $";
#endif

/*
 * $Log:    showpath.c,v $
 * Revision 1.2  85/07/16  11:12:04  broome
 * Revised output format, allows path for a given section to be shown.
 * 
 * 
 */

#include "defs.h"

/*
 *  Print out the entire search path used.
 */

path (argc, argv)
int  argc;
char **argv;
{
    SEC  *sec;
    DIR  *dir;
    char *suff;
    int  d, s;

    if (argc == 2) 
        printf ("121-search path for section %s:\r\n", *++argv);
    else
        printf ("121-All sections searched (in order):\r\n");

    for (sec = sections; sec; sec = sec->next) {
        if (argc == 2 && strcmp (sec->name, *argv))
            continue;
        printf ("120-Section: %s\n", sec->name);
        for (d = 0; dir = sec->dirs[d]; d++) {
            printf ("120-\tMan dir: %s\r\n120-\tCat dir: %s\r\n",
                        dir->man, dir->cat);
            printf ("120-\t\tSuffixes: ");
            for (s = 0; suff = dir->suff[s]; s++)
                printf ("%s ", suff);
            puts ("\r");
        }
    }
    printf ("122 That's all.\r\n");
    (void) fflush (stdout);
}
!Funky!Stuff!
if test 1162 -ne "`wc -c < 'showpath.c'`"
then
	echo shar: error transmitting "'showpath.c'" '(should have been 1162 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'raw.c'" '(767 characters)'
if test -f 'raw.c'
then
	echo shar: will not over-write existing file "'raw.c'"
else
cat << \!Funky!Stuff! > 'raw.c'
#ifndef lint
static char RCSid[] = "$Header: raw.c,v 1.4 85/08/27 15:17:12 broome Exp $";
#endif

/*
 * $Log:    raw.c,v $
 * Revision 1.4  85/08/27  15:17:12  broome
 * Last cleanup before release.
 * 
 * Revision 1.3  85/07/06  16:56:07  broome
 * 
 * Revision 1.2  85/07/04  20:35:55  broome
 * Got the rfc protocol right, added rcs keywords.
 */

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

/*
 *  Find and send the raw (unformatted) man page.
 */

raw (argc, argv)
int  argc;
char **argv;
{
    struct where *wp;

    /* find the file */
    wp = find (--argc, ++argv);
    if (wp->found) {
        printf ("%d Raw file %s on the way.\r\n", OK_COMING, wp->man);
        soelim (wp->man);
        puts (".\r");
    } else
        notfound (wp);
}
!Funky!Stuff!
if test 767 -ne "`wc -c < 'raw.c'`"
then
	echo shar: error transmitting "'raw.c'" '(should have been 767 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'show.c'" '(1014 characters)'
if test -f 'show.c'
then
	echo shar: will not over-write existing file "'show.c'"
else
cat << \!Funky!Stuff! > 'show.c'
#ifndef lint
static char RCSid[] = "$Header: show.c,v 1.3 85/08/27 15:17:21 broome Exp $";
#endif

/*
 * $Log:    show.c,v $
 * Revision 1.3  85/08/27  15:17:21  broome
 * Last cleanup before release.
 * 
 * Revision 1.2  85/07/24  10:39:09  broome
 * 
 * Revision 1.1  85/07/06  16:56:28  broome
 * Initial revision
 */

#include "defs.h"
#include <sys/file.h>

/*
 *  Find all occurrences of pages for the given name.
 */

/*ARGSUSED*/
show (argc, argv)
int   argc;
char  *argv[];
{
    DIR    *dir;
    int    s;
    char   buf[256];

    while (*++argv) {
        printf ("101-Searching for %s.\r\n", *argv);
        for (dir = dirs; dir; dir = dir->next)
            for (s = 0; dir->suff[s]; s++) {
                sprintf (buf, "%s/%s%s", dir->man, *argv, dir->suff[s]);
                if (access (buf, R_OK) == 0) {
                    printf ("100-%s\r\n", buf);
                    (void) fflush (stdout);
                }
            }
        printf ("102 That's all for \"%s\"\r\n", *argv);
    }
}
!Funky!Stuff!
if test 1014 -ne "`wc -c < 'show.c'`"
then
	echo shar: error transmitting "'show.c'" '(should have been 1014 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'whatis.c'" '(2159 characters)'
if test -f 'whatis.c'
then
	echo shar: will not over-write existing file "'whatis.c'"
else
cat << \!Funky!Stuff! > 'whatis.c'
#ifndef lint
static char RCSid[] = "$Header: whatis.c,v 1.4 85/07/24 10:39:13 broome Exp $";
#endif

/*
 * $Log:    whatis.c,v $
 * Revision 1.4  85/07/24  10:39:13  broome
 * 
 * 
 * Revision 1.3  85/07/06  16:56:13  broome
 * 
 * Revision 1.2  85/07/03  17:34:41  broome
 */

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

#define LIST    "/usr/lib/whatis"
#define DELIMS  " \t\n\r,\"\'-()"      /* word delimiters */

int key;

/*ARGSUSED*/
whatis (argc, argv)
int     argc;
char    **argv;
{
    extern   int debug;
    register FILE *fp;
    register char *b;
    char     buf[512];
    char     *nextword();
    char     word[80];
    int      found = 0;

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

    while (fgets (buf, 512, fp)) {
        key = 0;
        b = buf;
        while (b = nextword (b, word)) {
            if (debug)
                printf ("|%s|", word);
            if (strcmp (word, *argv) == 0) {  /* word matches arg */
                if (found == 0)
                    printf ("%d information for \"%s\" on the way.\r\n",
                                OK_COMING, *argv);
                printf ("%s", buf);
                found++;
                break;
            }
            if (key == 1)               /* went through all the keywords */
                break;
        }
    }
    if (!found) 
        printf ("%d %s: not found.\n", ERR_NOENT, *argv);
    else
        puts (".\r");
    (void) fflush (stdout);
}

/*
 *  Get the next word from the line and put it into `word', return
 *  the advanced line pointer (NULL if at end of the line).
 */

char *nextword (buf, word)
char *buf, *word;
{
    register char *i = word;

    if (!buf || !*buf)
        return ((char *) 0);

    while (*buf && any (*buf, DELIMS) == 0)      /* get this word */
        *i++ = *buf++;
    *i = '\0';

    while (*buf && any (*buf, DELIMS)) {    /* skip over to next word */
        if (*buf == '(')   /*)*/              /* at end of keyword list */
            key = 1;
        buf++;
    }

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

/*
 * $Log:    so.c,v $
 * Revision 1.3  85/08/27  15:17:24  broome
 * Last cleanup before release.
 * 
 * Revision 1.2  85/07/06  16:56:09  broome
 * 
 * Revision 1.1  85/07/03  13:07:22  broome
 * Initial revision
 */

#include <stdio.h>

/*
 *  A nice simple form of "soelim" to replace `.so' 
 *  directives with the named file inline.
 */

soelim (name)
char *name;
{
    FILE *fp;
    char line[1024];
    char so[512];

    if ((fp = fopen (name, "r")) == NULL) {
        sprintf (line, "/usr/man/%s", name);     /* kludge for old files */
        if ((fp = fopen (line, "r")) == NULL) {
            perror (name);
            return;
        }
    }
    while (fgets (line, 1024, fp)) {
        if (strncmp (line, ".so", 3) == 0) {   /* include directive */
            sscanf (line, ".so%s", so);
            soelim (so);
        } else 
            fputs (line, stdout);
    }
    fclose (fp);
}
!Funky!Stuff!
if test 1003 -ne "`wc -c < 'so.c'`"
then
	echo shar: error transmitting "'so.c'" '(should have been 1003 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'stat.c'" '(1001 characters)'
if test -f 'stat.c'
then
	echo shar: will not over-write existing file "'stat.c'"
else
cat << \!Funky!Stuff! > 'stat.c'
#ifndef lint
static char *RCSid = "$Header: stat.c,v 1.2 85/08/27 15:17:30 broome Exp $";
#endif

/*
 * $Log:    stat.c,v $
 * Revision 1.2  85/08/27  15:17:30  broome
 * Last cleanup before release.
 * 
 * Revision 1.1  85/08/03  18:36:20  broome
 * Initial revision
 */

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

dostat (argc, argv)
int   argc;
char *argv[];
{
    struct  stat statb;
    struct  where *wp;
    long    date;

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

        if (stat (wp->cat, &statb) < 0 || statb.st_size == 0 || 
           statb.st_mtime < date)    /* have to create/update it */
            printf ("%d-No formatted file found for %s.\r\n", ERR_STAT, wp->name);
        else
            printf ("%d-Have formatted page for %s.\r\n", OK_STAT, wp->name);
    } else
        notfound (wp);
}
!Funky!Stuff!
if test 1001 -ne "`wc -c < 'stat.c'`"
then
	echo shar: error transmitting "'stat.c'" '(should have been 1001 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'type.c'" '(1752 characters)'
if test -f 'type.c'
then
	echo shar: will not over-write existing file "'type.c'"
else
cat << \!Funky!Stuff! > 'type.c'
#include "defs.h"
#include "response.h"

static int t_index = 0;    /*  index of current type  */
static int n_types = 0;    /*  number of types known  */

/*
 *  Switch to an alternate type.  Returns 0 if ok, 1 on error.
 */

switchtype (name)
char *name;
{
    int  i;

    for (i = 0; i < n_types; i++)
        if (eq (name, types[i].name)) {
            t_index  = i;                    /* save the index */
            dirs     = types[i].dir;         /* and set the pointers */
            sections = types[i].sec;
            return (0);
        }
    return (1);
}


/*
 *  Interface to the "switchtype" command.  If no args, 
 *  prints current cpu type.
 */

dotype (argc, argv)
int     argc;
char   *argv[];
{
    if (argc == 1) {
        printf ("%d cpu type is \"%s\"\r\n", INFO_TYPE, types[t_index].name);
    } else {
        if (switchtype (argv[1]))
            printf ("%d unknown cpu type \"%s\"\r\n", ERR_TYPE, argv[1]);
        else
            printf ("%d new cpu type is \"%s\"\r\n", OK_TYPE, argv[1]);
    }
    fflush (stdout);
}


/*
 *  Add another cpu type to the list. Used in config(), 
 *  but uses static data here.  Note that "name" is the
 *  name of the _next_ type, not this one just ending.
 */

addtype (name)
char *name; 
{
    static char *this_type = (char *) 0;
    static int  this_index = 0;
        
    n_types++;

    types[this_index].dir = dirs;       /* save pointer to list */
    dirs = (DIR *) 0;                   /* and set it to zero for next list */
    types[this_index].sec = sections;
    sections = (SEC *) 0;

    types[this_index].name = this_type ? this_type : "generic";  /* save name */
    this_index++;

    this_type = name ? strsave (name) : (char *) 0;   /* this is next name */
}
!Funky!Stuff!
if test 1752 -ne "`wc -c < 'type.c'`"
then
	echo shar: error transmitting "'type.c'" '(should have been 1752 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'mand.8'" '(2928 characters)'
if test -f 'mand.8'
then
	echo shar: will not over-write existing file "'mand.8'"
else
cat << \!Funky!Stuff! > 'mand.8'
.TH MAND 8 "August 3 1985"
.UC 4
.ad
.SH NAME
mand \- manual page server daemon
.SH SYNOPSIS
\fB/etc/mand\fP [ -p port ] [ -f config_file ] [ -l load ] [ -s ]
.SH DESCRIPTION
\fIMand\fP is a manual page server normally invoked at boot time from the
\fI/etc/rc\fP or \fI/etc/rc.local\fP file.
It is used in conjunction with \fIrman\fP to allow network access 
to manual pages from remote machines, typically workstations or other
machines short on disk space.
.SH OPTIONS
The \fI\-p\fP option can be used to run \fImand\fP on a port other than the
one defined in \fI/etc/services\fP, typically used to offer a secondary
set of pages to alternate machine types.
.PP
The \fI\-f\fP option can be used to cause \fImand\fP to read a 
different configuration file.
.PP 
The \fI\-l\fP option can be used to specify a load cutoff limit, such 
that if the load is exceeded \fImand\fP will not respond to `pings' from
clients, though it will still accept stream connections.
.PP
The \fI\-s\fP ("secure") switch is used to toggle the identity checking 
routine.  If the secure flag is set, the server will deny access
to any client not identified in the host file. (The default setting
for this flag is system dependent.)
.SH CONFIGURATION
.PP
The configuration file specifies the directories and suffixes that
\fImand\fP will search when looking for a set of pages, indicating
the section name (ie. "cad"), the directory containing the unformatted
manual pages, the directory for formatted pages, and a list of file
suffixes to search.  \fIMand\fP uses this information
to construct filenames by concatenating the directory, the topic
name, and the extension; it will try each extension in the order listed.
Multiple directories can be given for each section by
indenting all but the first line with a space or tab character.
.PP
Alternate machine \fItypes\fP are accomodated in the
configuration file by preceding the list of sections 
for that type with a line of the form
.nf
.br
.sp
	type \fImachine_type\fP
.sp
.br
When a client connects, \fImand\fP will check the host file for 
a line indicating the client's type and will reconfigure itself to
use the list of sections and directories specified for that type.
If a client wishes to override the assigned type, it may issue a
command of the form "type \fItype\fP" upon opening a connection.
.SH PROTOCOL
\fIMand\fP and \fIrman\fP use a protocol similar to that of most internet
servers (e.g. the smtp server \fIsendmail\fP), with English commands and
three-digit response codes.
.SH FUTURE ADDITIONS
\fIMand\fP should use some type of multi-keyed hashing scheme to speed up
searching.
.SH FILES
.nf
.ta \w'/usr/lib/mand.hosts       'u
/etc/services		list of service port numbers
/usr/lib/mand.cf	configuration file
/usr/lib/mand.hf	help file
/usr/lib/mand.hosts	host name/type file
.fi
.SH "SEE ALSO"
rman(1), sendmail(8)
.SH AUTHOR
Jonathan C. Broome (broome@ucb-vax.berkeley.edu)
!Funky!Stuff!
if test 2928 -ne "`wc -c < 'mand.8'`"
then
	echo shar: error transmitting "'mand.8'" '(should have been 2928 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'getline.c'" '(986 characters)'
if test -f 'getline.c'
then
	echo shar: will not over-write existing file "'getline.c'"
else
cat << \!Funky!Stuff! > 'getline.c'
#ifndef lint
static char RCSid[] = "$Header: getline.c,v 1.1 85/08/04 13:59:54 broome Exp $";
#endif

/*
 * $Log:    getline.c,v $
 * Revision 1.1  85/08/04  13:59:54  broome
 * Initial revision
 * 
 */

#include <stdio.h>

/*
 *  Read at most _maxlen_ characters into the buffer pointed to by line.
 *  Strips newlines, allows escaped newlines, turns tabs to spaces, and
 *  eats comments beginning with a `#' sign.
 */

getline (line, maxlen, fp)
char *line; 
int  maxlen;
FILE *fp;
{
    register int ch, len = maxlen, comment = 0;

    while (len && (ch = getc (fp)) != EOF) {
        if (ch == '\n')
            break;
        if (ch == '#')
            comment = 1;
        if (comment)
            continue;
        if (ch == '\\')
            if ((ch = getc (fp)) == '\n')
                continue;
        if (ch == '\t')
            ch = ' ';
        *line++ = ch;
        len--;
    }
    if (ch == EOF)   /* at EOF */
        return (0);
    *line = '\0';
    return (1);
}
!Funky!Stuff!
if test 986 -ne "`wc -c < 'getline.c'`"
then
	echo shar: error transmitting "'getline.c'" '(should have been 986 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'.version'" '(3 characters)'
if test -f '.version'
then
	echo shar: will not over-write existing file "'.version'"
else
cat << \!Funky!Stuff! > '.version'
12
!Funky!Stuff!
if test 3 -ne "`wc -c < '.version'`"
then
	echo shar: error transmitting "'.version'" '(should have been 3 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'newver.csh'" '(231 characters)'
if test -f 'newver.csh'
then
	echo shar: will not over-write existing file "'newver.csh'"
else
cat << \!Funky!Stuff! > 'newver.csh'
#! /bin/csh -f
set date = `date`

if (-e .version) then
	set version = ` awk '{ print $1 + 1 }' .version `
else
	set version = 1
endif
echo $version >! .version

sed -e "s;DATE;$date;;" -e "s;VERSION;$version;;" ver.c >! version.c
!Funky!Stuff!
if test 231 -ne "`wc -c < 'newver.csh'`"
then
	echo shar: error transmitting "'newver.csh'" '(should have been 231 characters)'
fi
chmod +x 'newver.csh'
fi # end of overwriting check
echo shar: extracting "'mand.hosts'" '(721 characters)'
if test -f 'mand.hosts'
then
	echo shar: will not over-write existing file "'mand.hosts'"
else
cat << \!Funky!Stuff! > 'mand.hosts'
#  Recognized host addresses and their machine type.
#
#  For those of you wondering why on earth we use
#  addresses in this file rather than names, the
#  gethostbyaddr() call takes forever!!!

128.32.149.3	generic		# ucbseymour
128.32.149.5	generic		# ucbbuddy
128.32.137.1	generic		# ucbcory
128.32.149.4	generic		# ucbholden
128.32.137.2	pdp		# ucbholden-il
128.32.4	generic		# ucbarpa arpa
128.32.5	cad		# ucbcad cad
128.32.6	generic		# ucbernie ernie
128.32.9	generic		# ucbesvax esvax
128.32.10	generic		# ucbvax vax
128.32.12	generic		# ucbcalder calder
128.32.24	ucf		# ucbmiro miro
128.32.132.1	cad		# ucbic ic
128.32.132.2	cad		# ucbicw icw
128.32.132.3	cad		# ucbcad-ec cad-ec
128.32.132.4	cad		# ucbsim sim
!Funky!Stuff!
if test 721 -ne "`wc -c < 'mand.hosts'`"
then
	echo shar: error transmitting "'mand.hosts'" '(should have been 721 characters)'
fi
fi # end of overwriting check
echo shar: done with directory "'daemon'"
cd ..
#	End of shell archive
exit 0