Annotation of 43BSDTahoe/new/help/help.c, revision 1.1.1.1

1.1       root        1: /* Copyright (c) 1983 Regents of the University of California */
                      2: 
                      3: #ifndef lint
                      4: static char sccsid[] = "@(#)help.c     1.2     (Berkeley)      8/14/85";
                      5: #endif not lint
                      6: 
                      7: /*
                      8:  * help - an easy way to find and use information
                      9:  *
                     10:  * Usage:  see definition of error()
                     11:  *
                     12:  * Files:  /usr/lib/help               root help subsystem
                     13:  *        /usr/lib/help/log            log of system activity
                     14:  *        /usr/lib/help/maint          help maintenance scripts
                     15:  *        /usr/lib/help/config         defines the help system network
                     16:  *        /usr/lib/help/src            nroff sources for /usr/lib/help/cat
                     17:  *        /usr/lib/help/cat            root of system help topic files
                     18:  *        "/index_{help,man,doc}       topics created by mkhelpindex
                     19:  *        "/general                    description of 'help'
                     20:  *        "/*                          all other files are 'help' text files
                     21:  *
                     22:  * Author:  John Kunze, UCB (sorting routines based on David Wasley's originals)
                     23:  */
                     24: 
                     25: #include <sys/types.h>
                     26: #include <sys/stat.h>
                     27: #include <sys/dir.h>
                     28: #include <setjmp.h>
                     29: #include <signal.h>
                     30: /* #include <whoami.h> */      /* this would have defined BSD4_2 */
                     31: #define BSD4_2 1
                     32: #include <ctype.h>
                     33: #include <stdio.h>
                     34: 
                     35:                                        /* user-instruction return codes */
                     36: 
                     37: #define LIST_I 1
                     38: #define PASS_I 2
                     39: #define SAVE_I 3
                     40: #define TYPE_I 4
                     41: #define JUNK_I 5
                     42: #define BACK_I 6
                     43: #define LPRT_I 7
                     44: #define HELP_I 8
                     45: #define ROOT_I 9
                     46: #define YELL_I 10
                     47: #define QUIT_I 11
                     48: #define FIND_I 12
                     49: #define FLAG_I 13
                     50: #define NOOP_I 14
                     51: #define GOT_ONE        15
                     52: 
                     53:                                        /* symbols and macros */
                     54: 
                     55: #define HDBSIZE                256
                     56: #define HVSIZE         10
                     57: #define HELPROOT       "/usr/lib/help/cat"
                     58: #define TOPICINDEX     "index_help"
                     59: #define HELPMAINT      "../maint/do."
                     60: #ifdef notdef
                     61: #define MANINDEX       "/usr/lib/whatis"
                     62: #else
                     63: #define MANINDEX       { "/usr/lib/whatis", "/usr/man/whatis", NULL }
                     64: #endif
                     65: #define DOCINDEX       "/usr/lib/help/cat/index_doc"
                     66: #define HELPSAVE       "helpsave"
                     67: #ifdef notdef
                     68: #define MAINTAINER     "help@ucbopal"
                     69: #else
                     70: #define MAINTAINER     "help"
                     71: #endif
                     72: #define HELPLOG                "/usr/lib/help/log"
                     73: #define DEFSHELL       "/bin/csh"
                     74: #define PERROR         { perror("help"); exit(1); }
                     75: #define PUTNL          { putchar('\n'); fflush(stdout); }
                     76: #define EXISTS(s)      (access(s, 0) == 0)
                     77: #define EXECABLE(s)    (access(s, 1) == 0)
                     78: #define WRITABLE(s)    (access(s, 2) == 0)
                     79: #define READABLE(s)    (access(s, 4) == 0)
                     80: #define DOT(s)         ((s)[0]=='.'&&(s)[1]==0 ? 1 : 0)
                     81: #define DOTDOT(s)      ((s)[0]=='.'&&(s)[1]=='.'&&(s)[2]==0 ? 1 : 0)
                     82: #define ROOT(s)                ((s)[0]=='/'&&(s)[1]==0 ? 1 : 0)
                     83: #define isspecial(a)   (a == '\0' || a == '+' || a == '>' || a == '|')
                     84: #define lcase(a)       (isupper(a) ? tolower(a) : a)
                     85: 
                     86:                                        /* signal handling interface */
                     87: 
                     88: #if BSD4_2
                     89: struct sigvec vec;
                     90: #define GET_SIGPIPE    { vec.sv_handler = onintr; sigvec(SIGPIPE, &vec, 0); }
                     91: #define SET_SIGPIPE    { vec.sv_handler = SIG_DFL; sigvec(SIGPIPE, &vec, 0); }
                     92: #define NO_RUPTS       { vec.sv_handler = SIG_IGN; sigvec(SIGINT, &vec, 0); }
                     93: #define OK_RUPTS       { vec.sv_handler = SIG_DFL; sigvec(SIGINT, &vec, 0); }
                     94: #else
                     95: #define GET_SIGPIPE    signal(SIGPIPE, onintr)
                     96: #define SET_SIGPIPE    signal(SIGPIPE, SIG_DFL)
                     97: #define NO_RUPTS       signal(SIGINT, SIG_IGN)
                     98: #define OK_RUPTS       signal(SIGINT, SIG_DFL)
                     99: #ifndef MAXNAMLEN
                    100: #define MAXNAMLEN      255
                    101: #endif
                    102: #endif
                    103: 
                    104:                                        /* miscellaneous globals */
                    105: 
                    106: char hdbuf[HDBSIZE];           /* names of top level directores */
                    107: char *hvec[HVSIZE];            /* pointers to top level directories */
                    108: char **argp;                   /* pointer to topic arguments */
                    109: char *dirlist;                 /* unparsed list of root directories */
                    110: char cwd[MAXNAMLEN];           /* current directory */
                    111: char *dot, *dotdot;            /* tail parts of current and parent dirs */
                    112: char *subdir;                  /* current subdirectory names */
                    113: short dirlevel = 0;            /* depth from root of current directory */
                    114: short keeppag;                 /* for the >& command to keep pagination */
                    115: char *shell, shellprompt;      /* shell and its prompt */
                    116: char helpprompt[100];          /* help prompt string */
                    117: char indexprompt[100];         /* help-index prompt string */
                    118: 
                    119: /*
                    120:  * Topic names at the top (zero-th) directory level are stored permanently
                    121:  * as null terminated strings in the first segment of topicbuf, each of which
                    122:  * is pointed to by a pointer in the first segment of tptrs.  When a subtopic
                    123:  * at any directory level is under inspection, the second segment of topicbuf,
                    124:  * beginning with topicbuf[rtlen], contains the subtopic names, and the second
                    125:  * segment of tptrs, beginning with tptrs[subt], contains pointers to them.
                    126:  * At all times, tptrs[nt] contains zero to mark the end of the active segment.
                    127:  */
                    128: 
                    129: char topicbuf[4096];           /* null-terminated topic names */
                    130: char *tptrs[256];              /* pointers to topic names */
                    131: char **topics;                 /* points to topics or subtopics */
                    132: int nt = 0, tlen = 0;          /* number and total length of topics */
                    133: int subt;                      /* subtopic index in tptrs */
                    134: int rtlen;                     /* length of root topics names */
                    135: int nhits = 0, hit = -1;       /* number and index of matched topics */
                    136: 
                    137: /*
                    138:  * Index references are stored in indexbuf, those for "help" preceding those
                    139:  * for "man", which start at iptrs[mansegment] and precede those for off-line
                    140:  * references starting at iptrs[docsegment].  Each iptrs[i] points to a pair
                    141:  * of null-terminated strings containing the first and second halves of a line.
                    142:  */
                    143: 
                    144: char *indexbuf;                        /* names of index references */
                    145: char **iptrs;                  /* pointers to index references */
                    146: int ni = 0, ilen = 0;          /* number and length of index refs */
                    147: int inhits = 0, ihit = -1;     /* number and index of matched index refs */
                    148: char *isrc, *idst;             /* partial match of index entry */
                    149: int mansegment;                        /* beginning of UPM refs segment */
                    150: int docsegment;                        /* beginning of off-line refs segment */
                    151: 
                    152: char line[BUFSIZ];             /* raw user instruction */
                    153: char *src, *dst, *dstarg;      /* source and dst parts of an instruction */
                    154: char fname[BUFSIZ];            /* full path name(s) of topic file */
                    155: short fnamect;                 /* number of files in fname */
                    156: short interactive, iflag;      /* interactive session flag */
                    157: short number, quiet;           /* numbers accepted/printed, terse prompt */
                    158: char *more_d;                  /* pointer to value of MORE env. variable */
                    159: char *progname;                        /* name (argv[0]) of invoking program */
                    160: char *maintkey;                        /* help maintenance key */
                    161: 
                    162:                                        /* miscellaneous routines */
                    163: 
                    164: char *getenv(), *strcpy(), *malloc(), *index(), *rindex();
                    165: FILE *outpipe();
                    166: 
                    167: main(argc,argv)
                    168: int argc;
                    169: char **argv;
                    170: {
                    171:        register int ins;               /* current user instruction */
                    172:        register int junkcount = 0;     /* how many times in a row bad ins. */
                    173: 
                    174:        setbuf(stdout, malloc(BUFSIZ)); /* speed up standard output */
                    175:        setbuf(stderr, malloc(BUFSIZ)); /* speed up error output */
                    176:        getoptions(argc, argv);         /* parse options */
                    177:        setgetenv();                    /* make directory list, environment */
                    178: 
                    179:        /*
                    180:         * main loop:  get instruction, execute
                    181:         */
                    182:        for (ins = startup(); ins != QUIT_I; ins = nextins()) {
                    183:                if (ins != JUNK_I)
                    184:                        junkcount = 0;
                    185:                switch (ins) {
                    186:                case LIST_I:
                    187:                        list();
                    188:                        break;
                    189:                case TYPE_I:
                    190:                        if (isadir(fname)) {
                    191:                                chwd(src);
                    192:                                list();
                    193:                        }
                    194:                        else
                    195:                                page();
                    196:                        log('=');
                    197:                        break;
                    198:                case BACK_I:
                    199:                        chwd("..");
                    200:                        list();
                    201:                        break;
                    202:                case ROOT_I:
                    203:                        chwd("/");
                    204:                        list();
                    205:                        break;
                    206:                case SAVE_I:
                    207:                        save();
                    208:                        log('>');
                    209:                        break;
                    210:                case LPRT_I:
                    211:                        lprt();
                    212:                        log('|');
                    213:                        break;
                    214:                case PASS_I:
                    215:                        fflush(stdout);
                    216:                        pass(src);
                    217:                        break;
                    218:                case FLAG_I:
                    219:                        flag(src, dst);
                    220:                        break;
                    221:                case JUNK_I:
                    222:                        printf("\nI%sdon't understand.\n",
                    223:                                (junkcount++ ? " still " : " "));
                    224:                        if (junkcount == 2)
                    225:                                list();
                    226:                        else if (junkcount > 2)
                    227:                                comlist();
                    228:                        break;
                    229:                case HELP_I:
                    230:                        comlist();
                    231:                        break;
                    232:                case YELL_I:
                    233:                        yell();
                    234:                        break;
                    235:                case FIND_I:
                    236:                        find(src);
                    237:                        log('+');
                    238:                        break;
                    239:                case NOOP_I:
                    240:                        break;
                    241:                default:
                    242:                        puts("Unknown instruction - please report this.");
                    243:                        exit(0);
                    244:                        break;
                    245:                }
                    246:        }
                    247:        puts("Bye.");
                    248: }
                    249: 
                    250: save()                 /* save a help file "src" in user file "dst" */
                    251: {
                    252:        register char *p;
                    253:        register FILE *destfile;
                    254:        register int lcount;
                    255:        char c;
                    256: 
                    257:        p = (EXISTS(dst) ? "appended" : "new file");
                    258:        if ((destfile = fopen(dst, "a")) == NULL)
                    259:                perror(dst);
                    260:        lcount = putfiles(NULL, destfile);
                    261:        fclose(destfile);
                    262:        printf("\nTopic \"%s\" is saved in \"%s\" (%s:  %d lines).\n",
                    263:                topics[hit], dst, p, lcount);
                    264: }
                    265: 
                    266: lprt()                 /* lineprint (dst) all args in fname */
                    267: {
                    268:        register int i = 0;
                    269:        register char *fn;
                    270:        char *ap[HVSIZE];               /* arg pointers */
                    271: 
                    272:        ap[i++] = dst;
                    273:        if (strcmp(dst, "ipr") == 0)    /* kludge to force -p with ipr */
                    274:                ap[i++] = "-p";
                    275:        if (dstarg)             /* kludge to allow an option to lpr */
                    276:                ap[i++] = dstarg;
                    277:        fn = fname;
                    278:        while (fnamect--) {
                    279:                ap[i++] = fn;
                    280:                fn += strlen(fn) + 1;
                    281:        }
                    282:        ap[i] = 0;
                    283:        if (!fork()) {
                    284:                fputs("\n>> Executing [", stdout);
                    285:                for (i = 0; ap[i]; fputs(ap[i++], stdout))
                    286:                        putchar(' ');
                    287:                puts(" ] ...");
                    288:                fflush(stdout);
                    289:                execvp(dst, ap);
                    290:                PERROR;
                    291:        }
                    292:        NO_RUPTS;
                    293:        wait(0);
                    294:        puts(">> Done.");
                    295:        OK_RUPTS;
                    296: }
                    297: 
                    298: yell()         /* send complaints or other input to the MAINTAINER of help */
                    299: {
                    300:        if (!fork()) {
                    301:                printf("\n%s\n%s\n%s\n",
                    302:        "Please enter your remarks.  The only way I will know you're done is if",
                    303:        "you enter a . (period) or control-d on a line by itself.  Don't forget!");
                    304:                fflush(stdout);
                    305:                execlp("mail", "mail", MAINTAINER, 0);
                    306:                PERROR;
                    307:        }
                    308:        NO_RUPTS;
                    309:        wait(0);
                    310:        puts("\nDuly noted.");
                    311:        OK_RUPTS;
                    312: }
                    313: 
                    314: log(insc)              /* to turn logging off, deny write access of HELPLOG */
                    315: char insc;                     /* instruction code character to be written */
                    316: {
                    317:        long logtime;
                    318:        char *ctime();
                    319:        FILE *lp;
                    320: 
                    321:        if ((lp = fopen(HELPLOG, "a")) == NULL)
                    322:                return;
                    323:        time(&logtime);
                    324:        fprintf(lp, "%.12s  %c %s\n", ctime(&logtime) + 4, insc, src);
                    325:        fclose(lp);
                    326: }
                    327: 
                    328: getoptions(ac, av)                     /* get command-line options */
                    329: int ac;                        /* spaces need not separate -[dpm] from next arg */
                    330: register char **av;
                    331: {
                    332:        register char *p;
                    333: 
                    334:        interactive = isatty(1);
                    335:        progname = *av;
                    336:        for (p = progname; *p; p++);
                    337:        for (p--; p >= progname && *p != '/'; p--);
                    338:        progname = ++p;                 /* set progname to its tail part */
                    339:        while (**++av == '-')
                    340:                switch (*(p = *av + 1)) {
                    341:                case 'd':
                    342:                        if (*++p || *++av)
                    343:                                dirlist = (*p ? p : *av);
                    344:                        else
                    345:                                error("Directory list must follow -d.");
                    346:                        break;
                    347:                case 'p':
                    348:                        if (*++p || *++av)
                    349:                                progname = (*p ? p : *av);
                    350:                        else
                    351:                                error("Prompt string must follow -p.");
                    352:                        break;
                    353:                case 'm':
                    354:                        if (*++p || *++av)
                    355:                                maintkey = (*p ? p : *av);
                    356:                        else {
                    357:                                maintkey = "default";
                    358:                                av--;
                    359:                        }
                    360:                        break;
                    361:                case 'i':
                    362:                        iflag = interactive = 1;
                    363:                        break;
                    364:                case 'n':
                    365:                        number = 1;
                    366:                        break;
                    367:                case 'q':
                    368:                        quiet = 1;
                    369:                        break;
                    370:                default:
                    371:                        printf("Unknown option -%c.\n", *p);
                    372:                        break;
                    373:                }
                    374:        argp = av;
                    375: }
                    376: 
                    377: error(msg)             /* print a message for command-line errors */
                    378: char *msg;
                    379: {
                    380:        if (*msg)
                    381:                fprintf(stderr, "%s\n", msg);
                    382:        fprintf(stderr, "Usage:  help [ options ] [ topic [ subtopic [ subsubtopic [...] ] ] ]\n");
                    383:        fprintf(stderr, "Options are:\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n",
                    384:                "-d dirlist     override the default pool of help files",
                    385:                "-m key         do the help maintenance function given by key",
                    386:                "-p prompt      override the default prompt",
                    387:                "-i             force help to be interactive",
                    388:                "-n             use numbers in topic and index listings",
                    389:                "-q             suppress the instruction line before prompting");
                    390:        fprintf(stderr, "To get started just type \"help\".\n");
                    391:        exit(1);
                    392: }
                    393: 
                    394: helpmaint(key, dir, av)                        /* invoke maintenance script */
                    395: char *key;                             /* key specifying action */
                    396: char *dir;                             /* first writable dir in HELPPOOL */
                    397: char **av;                             /* topics, if any */
                    398: {
                    399:        char s[BUFSIZ];
                    400:        char *argv[BUFSIZ];
                    401:        register char **vp = argv;
                    402: 
                    403:        sprintf(s, "%s/%s%s", dir, HELPMAINT, key);
                    404:        if (!READABLE(s)) {
                    405:                printf("I don't know how to do \"%s\".\n", key);
                    406:                fflush(stdout);
                    407:                sprintf(s, "%s/%s%s", dir, HELPMAINT, "default");
                    408:        }
                    409:        *vp++ = "csh";
                    410:        *vp++ = "-f";
                    411:        *vp++ = s;
                    412:        *vp++ = dir;
                    413:        while (*av)
                    414:                *vp++ = *av++;
                    415:        *vp = 0;
                    416:        execv("/bin/csh", argv);
                    417:        PERROR;
                    418: }
                    419: 
                    420: setgetenv()    /* get directory list and shell, and set more -d for man */
                    421: {
                    422:        register char *p = dirlist, **vp;
                    423:        register int i = 0;
                    424:        char **myenv; char *t; int moredef = 0;
                    425:        extern char **environ;
                    426: 
                    427:        for (vp = environ; *vp; vp++)
                    428:                if (strncmp(*vp, "HELPPOOL=", 9) == 0 && !p)
                    429:                        p = *vp + 9;
                    430:                else if (strncmp(*vp, "SHELL=", 6) == 0)
                    431:                        shell = *vp + 6;
                    432:                else if (strncmp(*vp, "MORE=", 5) == 0)
                    433:                        moredef++;
                    434:        if (p) {
                    435:                t = &hdbuf[0];
                    436:                while (*p) {
                    437:                        hvec[i++] = t;
                    438:                        while (*p == ':' || isspace(*p))
                    439:                                p++;
                    440:                        while (*p && *p != ':' && !isspace(*p))
                    441:                                *t++ = *p++;
                    442:                        *t++ = 0;
                    443:                }
                    444:        }
                    445:        if (!dirlist)
                    446:                hvec[i++] = HELPROOT;
                    447:        hvec[i] = 0;
                    448:        if (!shell)
                    449:                shell = DEFSHELL;
                    450:        shellprompt = (strcmp(shell, DEFSHELL) == 0 ? '%' : '$');
                    451:        if (number)
                    452:                sprintf(helpprompt,
                    453:                "\nTo see a topic, type its name or number, and RETURN; '%c' to quit, '?' for help.", shellprompt);
                    454:        else
                    455:                sprintf(helpprompt,
                    456:                "\nTo display a topic, type its name, and RETURN; type '%c' to quit, '?' for help.", shellprompt);
                    457:        if (number)
                    458:                sprintf(indexprompt,
                    459:                "\nTo display a subject, type its name or number, and RETURN; type '?' for help.", shellprompt);
                    460:        else
                    461:                sprintf(indexprompt,
                    462:                "\nTo display a subject, type its name, and RETURN; type '?' for help.", shellprompt);
                    463:        if (moredef)
                    464:                return;
                    465:        myenv = (char **) malloc((vp - environ + 2) * sizeof (char *));
                    466:        if (!myenv)
                    467:                PERROR;
                    468:        *myenv = (quiet ? "MORE=  " : "MORE=-d");
                    469:        more_d = *myenv + 5;            /* points to "-d" or "  " */
                    470:        for (i = 0, vp = myenv + 1; environ[i]; i++, vp++)
                    471:                *vp = environ[i];
                    472:        environ = myenv;
                    473: }
                    474: 
                    475: startup()      /* get topic named by args, return first instruction */
                    476: {
                    477:        register int i, ins;
                    478:        char **t, **u;
                    479: 
                    480:        if (maintkey) {
                    481:                for (i = 0; hvec[i]; i++)
                    482:                        if (WRITABLE(hvec[i]) && isadir(hvec[i]))
                    483:                                break;
                    484:                if (!hvec[i]) {
                    485:                        fprintf(stderr, "You need write permission in at least one directory in HELPPOOL.\n");
                    486:                        exit(1);
                    487:                }
                    488:                helpmaint(maintkey, hvec[i], argp);     /* no return */
                    489:        }
                    490:        for (i = 0; hvec[i]; i++)       /* collect first level (root) */
                    491:                getfiles(hvec[i]);      /* topics to be kept permanently */
                    492:        rtlen = tlen;                   /* save root topic sizes */
                    493:        topics = tptrs;                 /* active topic segment */
                    494:        vsort(tptrs);                   /* sort -- replace dups with zero */
                    495:        t = u = tptrs;                  /* kill off zeros */
                    496:        while (t < tptrs + nt)
                    497:                if (!*t)
                    498:                        t++;
                    499:                else
                    500:                        *u++ = *t++;
                    501:        *u = 0;                         /* mark new end of first segment */
                    502:        nt = u - tptrs;                 /* so tptrs[nt-1] is nil */
                    503:        subt = nt + 1;                  /* mark start of subtopic segment */
                    504:        for (; *argp; argp++) {         /* go through topic arguments */
                    505:                if (!match(*argp))      /* if no match, try something else */
                    506:                        if ((ins = whatnext(*argp)) != GOT_ONE)
                    507:                                return ins;     /* user can escape this way */
                    508:                if (!chwd(topics[hit])) /* if match, assume it's a directory */
                    509:                        break;          /* not a directory, must be a file */
                    510:        }
                    511:        if (!*argp)
                    512:                return LIST_I;
                    513:        src = topics[hit];
                    514:        makefname(dirlevel, topics[hit]);
                    515:        page();
                    516:        if (!iflag)
                    517:                exit(0);
                    518:        interactive = 1;
                    519:        return NOOP_I;
                    520: }
                    521: 
                    522: whatnext(s)            /* match s with a file or find out what to do */
                    523: char *s;                       /* if success, global src set from s */
                    524: {
                    525:        static char word[MAXNAMLEN];
                    526:        char rbuf[10];
                    527:        int wlen;
                    528: 
                    529:        strcpy(word, s);
                    530:        src = word;
                    531:        do {
                    532:                if (!interactive) {
                    533:                        printf("\nThere is no topic \"%s\".  I'm looking in the index.\n", word);
                    534:                        fflush(stdout);
                    535:                        return FIND_I;
                    536:                }
                    537:                else if (nhits > 1) {
                    538:                        printf("\nNot precise enough.  Enter more letters, or RETURN:  %s", word);
                    539:                        fflush(stdout);
                    540:                        wlen = strlen(word);
                    541:                        if (gets(word + wlen) == NULL)
                    542:                                return QUIT_I;
                    543:                        if (strlen(word) <= wlen)       /* no new letters */
                    544:                                return NOOP_I;
                    545:                }
                    546:                else {
                    547:                        printf("\nThere is no topic \"%s\".  Shall I look in the index?  ", word);
                    548:                        fflush(stdout);
                    549:                        if (gets(rbuf) == NULL)
                    550:                                return QUIT_I;
                    551:                        if (*rbuf == 'y')
                    552:                                return FIND_I;
                    553:                        return NOOP_I;
                    554:                }
                    555:        } while (!match(word));
                    556:        src = topics[hit];
                    557:        return GOT_ONE;
                    558: }
                    559: 
                    560: char *cwdend = cwd;            /* end of current directory string */
                    561: 
                    562: chwd(s)                                /* change directory routine */
                    563: register char *s;
                    564: {
                    565:        register char *p;
                    566:        register int i;
                    567: 
                    568:        if (DOT(s))
                    569:                return 1;
                    570:        if (DOTDOT(s)) {
                    571:                if (dirlevel == 0)
                    572:                        return !printf("\nYou're at the top level already.\n");
                    573:                while (*--cwdend != '/');
                    574:                *cwdend = 0;
                    575:                dirlevel--;
                    576:        }
                    577:        else if (ROOT(s)) {
                    578:                if (dirlevel == 0)
                    579:                        return !printf("\nYou're at the top level already.\n");
                    580:                dirlevel = 0;
                    581:        }
                    582:        else if (dirlevel > 0) {
                    583:                strcat(strcat(cwdend, "/"), s);
                    584:                if (!EXECABLE(cwd) || !isadir(cwd))
                    585:                        return *cwdend = 0;
                    586:                while (*++cwdend);
                    587:                dirlevel++;
                    588:        }
                    589:        else {
                    590:                for (i = 0; hvec[i]; i++) {
                    591:                        cwdend = strcpy(cwd, hvec[i]);
                    592:                        while (*++cwdend);
                    593:                        subdir = cwdend;
                    594:                        strcat(strcat(cwdend, "/"), s);
                    595:                        if (EXECABLE(cwd) && isadir(cwd))
                    596:                                break;
                    597:                        *cwdend = 0;
                    598:                        subdir = 0;
                    599:                }
                    600:                if (!hvec[i])
                    601:                        return 0;
                    602:                while (*++cwdend);
                    603:                dirlevel++;
                    604:        }
                    605: 
                    606:        /*
                    607:         * reclaim subtopic storage, get new topics
                    608:         */
                    609:        nhits = 0; hit = -1; tlen = rtlen;
                    610:        if (dirlevel > 0) {
                    611:                nt = subt;
                    612:                topics = tptrs + subt;
                    613:                getfiles(cwd);
                    614:                vsort(topics);
                    615:        }
                    616:        else {
                    617:                nt = subt - 1;
                    618:                topics = tptrs;
                    619:                subdir = 0;
                    620:        }
                    621:        return 1;
                    622: }
                    623: 
                    624: nextins()               /* sets up globals:  src, dst, and fname */
                    625: {
                    626:        register char *p, *s;
                    627:        register int ins, got_one = 0;
                    628:        char c = 0;
                    629: 
                    630:        /*
                    631:         * initialize fname, src, dst, and keeppag; get instruction
                    632:         */
                    633:        if (!interactive)
                    634:                return QUIT_I;
                    635:        fname[0] = 0;
                    636:        src = dst = dstarg = 0;
                    637:        keeppag = 0;
                    638:        prompt();
                    639:        if (gets(line) == NULL)
                    640:                return QUIT_I;
                    641: 
                    642:        /*
                    643:         * trim blanks from end and beginning of line
                    644:         */
                    645:        for (p = line+strlen(line)-1; isspace(*p) && p >= line; p--);
                    646:        if (p < line)
                    647:                return NOOP_I;
                    648:        *++p = 0;
                    649:        for (p = line; isspace(*p); p++);
                    650: 
                    651:        /*
                    652:         * parse zero operand instructions
                    653:         */
                    654:        if (*p == '?')
                    655:                return HELP_I;
                    656:        if (*p == '/')
                    657:                return ROOT_I;
                    658:        if (DOT(p))
                    659:                return LIST_I;
                    660:        if (DOTDOT(p))
                    661:                return BACK_I;
                    662:        if (*p == '%' || *p == '$')
                    663:                return QUIT_I;
                    664:        if (*p == '<')
                    665:                return YELL_I;
                    666: 
                    667:        /*
                    668:         * other instructions
                    669:         */
                    670:        if (*p == '!') {
                    671:                src = ++p;
                    672:                return PASS_I;
                    673:        }
                    674:        if (*p == '*') {
                    675:                for (p++; isspace(*p); p++);
                    676:                if (*p)
                    677:                        src = p;
                    678:                for (; *p && !isspace(*p); p++);
                    679:                if (*p)
                    680:                        *p++ = 0;
                    681:                for (; *p && isspace(*p); p++);
                    682:                if (*p)
                    683:                        dst = p;
                    684:                return FLAG_I;
                    685:        }
                    686:        if (*p == '=') {                        /* = as topic */
                    687:                p++;
                    688:                if (hit < 0)
                    689:                        return JUNK_I;
                    690:        }
                    691:        else if (number && isdigit(*p)) {
                    692:                for (s = p; *p; p++)
                    693:                        if (!isdigit(*p))
                    694:                                break;
                    695:                hit = atoi(s) - 1;
                    696:                if (hit < 0 || hit >= (dirlevel == 0 ? nt : nt - subt)) {
                    697:                        printf("\nThere is no topic numbered %d.\n", atoi(s));
                    698:                        return NOOP_I;
                    699:                }
                    700:                got_one++;
                    701:                src = topics[hit];
                    702:                makefname(dirlevel, topics[hit]);
                    703:                for (; isspace(*p); p++);
                    704:        }
                    705:        else if (isalpha(*p) || *p == '.' || *p == '-') {       /* put topic name in src */
                    706:                src = p;
                    707:                for (; !isspecial(*p) && !isspace(*p); p++);
                    708:                for (; isspace(*p); p++)
                    709:                        *p = 0;                 /* make sure it ends */
                    710:        }
                    711:        c = *p;
                    712:        *p++ = 0;
                    713:        if (!src) {                     /* no topic, see if default exists */
                    714:                if (hit < 0) {
                    715:                        printf("\nYou need to give a topic name for that.");
                    716:                        return JUNK_I;
                    717:                }
                    718:                src = topics[hit];
                    719:        }
                    720:        if (c == '>' || c == '|') {     /* more args allowed */
                    721:                for (; isspace(*p); p++);
                    722:                if (*p == '&') {
                    723:                        keeppag = 1;
                    724:                        for (p++; isspace(*p); p++);
                    725:                }
                    726:                if (!*p)
                    727:                        strcat(p, (c == '>' ? HELPSAVE : "lpr"));
                    728:                dst = p;
                    729:                for (; *p && !isspace(*p); p++);
                    730:                for (; *p && isspace(*p); p++)
                    731:                        *p = 0;         /* terminate dst */
                    732:                if (*p) {
                    733:                        dstarg = p;
                    734:                        for (; *p && !isspace(*p); p++);
                    735:                        *p = 0;                 /* terminate dstarg */
                    736:                }
                    737:        }
                    738: 
                    739:        /*
                    740:         * instructions requiring src
                    741:         */
                    742:        if (c == '+')
                    743:                return FIND_I;
                    744:        if (!got_one) {
                    745:                if (!match(src) && (ins = whatnext(src)) != GOT_ONE)
                    746:                        return ins;
                    747:                src = topics[hit];
                    748:                makefname(dirlevel, topics[hit]);
                    749:        }
                    750:        if (!c)
                    751:                return TYPE_I;
                    752:        if (c == '|')
                    753:                return LPRT_I;
                    754:        if (c == '>')
                    755:                return SAVE_I;
                    756:        return JUNK_I;
                    757: }
                    758: 
                    759: prompt()                                       /* prompt user */
                    760: {
                    761:        register char *p;
                    762: 
                    763:        if (!quiet)
                    764:                fputs(helpprompt, stdout);
                    765:        fputs("\n(", stdout);
                    766:        fputs(progname, stdout);
                    767:        if (subdir)
                    768:                for (p = subdir; *p; p++)
                    769:                        if (*p == '/')
                    770:                                putchar(' ');
                    771:                        else
                    772:                                putchar(*p);
                    773:        fputs(") ", stdout);
                    774:        fflush(stdout);
                    775: }
                    776: 
                    777: substr(s, abbr)                        /* returns 1 if abbr abbreviates s */
                    778: register char *s, *abbr;
                    779: {
                    780:        for (; *s == *abbr && *abbr; s++, abbr++);
                    781:        return !*abbr;
                    782: }
                    783: 
                    784: fsubstr(s, abbr)               /* returns 1 if abbr abbreviates lcased s */
                    785: register char *s, *abbr;
                    786: {
                    787:        for (; lcase(*s) == *abbr && *abbr; s++, abbr++);
                    788:        return !*abbr;
                    789: }
                    790: 
                    791: getfiles(dname)                        /* fill topicbuf and tptrs */
                    792: char *dname;
                    793: {
                    794:        struct direct dbuf;
                    795:        register struct direct *ep = &dbuf;     /* directory entry pointer */
                    796:        register int i;
                    797: #if BSD4_2
                    798:        DIR *dp;
                    799: #define OPENDIR(s)     ((dp = opendir(s)) != NULL)
                    800: #define DIRLOOP(s)     for (s = readdir(dp); s != NULL; s = readdir(dp))
                    801: #define PATHSIZE 256
                    802: #define PATHSIZE 256
                    803: #define MAXDLEN                ep->d_namlen
                    804: #define CLOSEDIR       closedir(dp)
                    805: #else
                    806:        int fd;
                    807: #define OPENDIR(s)     ((fd = open(s, 0)) >= 0)
                    808: #define DIRLOOP(s)     while (read(fd, s, sizeof *s) == sizeof *s)
                    809: #define MAXDLEN                DIRSIZ
                    810: #define CLOSEDIR       close(fd)
                    811: #endif
                    812: 
                    813:        if (!OPENDIR(dname))
                    814:                return perror(dname);
                    815:        tptrs[nt] = &topicbuf[tlen];
                    816:        DIRLOOP(ep) {
                    817:                if (ep->d_name[0] == NULL || ep->d_ino == 0
                    818:                        || DOT(ep->d_name) || DOTDOT(ep->d_name))
                    819:                                continue;
                    820:                tptrs[nt++] = &topicbuf[tlen];
                    821:                for (i = 0; i < MAXDLEN && ep->d_name[i]; tlen++, i++)
                    822:                        topicbuf[tlen] = ep->d_name[i];
                    823:                topicbuf[tlen++] = 0;
                    824:        }
                    825:        tptrs[nt] = 0;
                    826:        CLOSEDIR;
                    827: }
                    828: 
                    829: isadir(name)
                    830: char *name;
                    831: {
                    832:        struct stat buf;
                    833: 
                    834:        stat(name, &buf);
                    835:        return buf.st_mode & S_IFDIR;
                    836: }
                    837: 
                    838: jmp_buf jmpenv;
                    839: 
                    840: onintr()                       /* catch broken pipe signals */
                    841: {
                    842:        NO_RUPTS;
                    843:        SET_SIGPIPE;
                    844:        longjmp(jmpenv, 1);
                    845: }
                    846: 
                    847: wrapup(fp)                     /* close a file pointer and wait for child */
                    848: FILE *fp;
                    849: {
                    850:        fclose(fp);
                    851:        wait(0);
                    852:        OK_RUPTS;
                    853: }
                    854: 
                    855: int firstime = 1;              /* for first topic listing */
                    856: 
                    857: list()                         /* list topics in 4 columns */
                    858: {
                    859:        register int col, row, i, last, nrows;
                    860:        FILE *more;
                    861: 
                    862:        fflush(stdout);
                    863:        NO_RUPTS;
                    864:        if ((more = outpipe()) == NULL)
                    865:                PERROR;
                    866:        if (setjmp(jmpenv)) {
                    867:                wrapup(more);
                    868:                return;
                    869:        }
                    870:        GET_SIGPIPE;
                    871:        if (firstime) {
                    872:                fprintf(more, "\n%s\n%s\n",
                    873:                "Here is a list of topics I know about.",
                    874:                "If you don't see the topic you want, I can look for it in the index.");
                    875:                if (match("general"))
                    876:                        fprintf(more, "For a general introduction, please see \"general\" below.\n");
                    877:                firstime = 0;
                    878:        }
                    879:        fputc('\n', more);
                    880:        last = (dirlevel > 0 ? nt - subt : nt);
                    881:        nrows = last / 4 + (last % 4 != 0 ? 1 : 0);
                    882:        for (row = 0; row < nrows; row++)
                    883:                for (i = row, col = 0; col < 4; i += nrows, col++) {
                    884:                        if (i >= last) {
                    885:                                fputc('\n', more);
                    886:                                col = 3;
                    887:                        }
                    888:                        else if (number)
                    889:                                fprintf(more, "%3d%c%-14.14s %c",
                    890:                                        i + 1,
                    891:                                        (hit == i ? '=' : ' '),
                    892:                                        topics[i], (col == 3 ? '\n' : ' '));
                    893:                        else
                    894:                                fprintf(more, "%c%-17.17s %c",
                    895:                                        (hit == i ? '=' : ' '),
                    896:                                        topics[i], (col == 3 ? '\n' : ' '));
                    897:                }
                    898:        wrapup(more);
                    899: }
                    900: 
                    901: #define IBSIZE 16384
                    902: #define IPSIZE 512
                    903: 
                    904: find(s)
                    905: char *s;
                    906: {
                    907:        register char *p;
                    908:        register int i;
                    909:        FILE *fp, *popen();
                    910:        char sbuf[BUFSIZ];
                    911: 
                    912:        if (!iptrs) {                   /* malloc storage once and for all */
                    913:                indexbuf = (char *) malloc(IBSIZE + BUFSIZ);
                    914:                iptrs = (char **) malloc((IPSIZE + 32) * sizeof(char *));
                    915:                if (iptrs == 0 || indexbuf == 0) {
                    916:                        fprintf(stderr, "No index space.\n");
                    917:                        exit(1);
                    918:                }
                    919:        }
                    920:        ni = 0; ilen = 0;
                    921:        for (i = 0; hvec[i]; i++) {
                    922:                sprintf(sbuf, "%s/%s", hvec[i], TOPICINDEX);
                    923:                if ((fp = fopen(sbuf, "r")) == NULL) {
                    924:                        if (strcmp(hvec[i], HELPROOT) == 0) {
                    925:                                perror(sbuf);
                    926:                                exit(1);
                    927:                        }
                    928:                        continue;
                    929:                }
                    930:                getrefs(fp, 0);
                    931:                fclose(fp);
                    932:        }
                    933: #ifdef notdef
                    934:        if ((fp = fopen(MANINDEX, "r")) == NULL) {
                    935:                perror(MANINDEX);
                    936:                exit(1);
                    937:        }
                    938:        mansegment = ni;
                    939:        getrefs(fp, 1);
                    940:        fclose(fp);
                    941: #else
                    942:        {
                    943:                static char *manindex[] = MANINDEX;
                    944:                register char **mi;
                    945: 
                    946:                mansegment = ni;
                    947: 
                    948:                for (mi = manindex; *mi != NULL; mi++)
                    949:                        if ((fp = fopen(*mi, "r")) != NULL) {
                    950:                                getrefs(fp, 1);
                    951:                                fclose(fp);
                    952:                                break;
                    953:                        }
                    954:        }
                    955: #endif
                    956:        docsegment = ni;
                    957:        if ((fp = fopen(DOCINDEX, "r")) != NULL) {
                    958:                getrefs(fp, 0);
                    959:                fclose(fp);
                    960:        }
                    961:        if (ni == 0)
                    962:                return printf("\nNo relevant material; your request has been logged.\n");
                    963:        putrefs();
                    964:        if (!interactive)
                    965:                exit(0);
                    966:        while ((i = selectref()) != QUIT_I)
                    967:                switch (i) {
                    968:                case LIST_I:
                    969:                        putrefs();
                    970:                        break;
                    971:                case HELP_I:
                    972:                        icomlist();
                    973:                        break;
                    974:                case ROOT_I:
                    975:                        chwd("/");
                    976:                case BACK_I:
                    977:                        list();
                    978:                        return;
                    979:                case YELL_I:
                    980:                        yell();
                    981:                        break;
                    982:                case PASS_I:
                    983:                        fflush(stdout);
                    984:                        pass(isrc);
                    985:                        break;
                    986:                case FLAG_I:
                    987:                        flag(isrc, idst);
                    988:                        break;
                    989:                default:
                    990:                        break;
                    991:                }
                    992:        puts("Bye.");
                    993:        exit(0);
                    994: }
                    995: 
                    996: icomlist()
                    997: {
                    998:        if (number)
                    999:                puts("\nTo see a subject, type its name, a unique abbreviation, or its number.");
                   1000:        else
                   1001:                puts("\nTo see a subject, type its name or a unique abbreviation.");
                   1002:        puts("Other commands are:");
                   1003:        printf("  %c             quit from help and return to the shell (control-d works also)\n", shellprompt);
                   1004:        printf("  subject       display a \"subject\", whose name%syou supply\n",
                   1005:                (number ? " or number " : " "));
                   1006:        puts("  ?             display this command list");
                   1007:        puts("  .             list subject references found");
                   1008:        puts("  ..            go back to the previous list of help topics");
                   1009:        puts("  /             back up to and list the top level of topics");
                   1010:        puts("  <             send comments or other input to the maintainer of help");
                   1011:        puts("  !command      do a Unix command and then return to help");
                   1012:        puts("  * flag on/off set a \"flag\" on or off to adjust the behavior of help");
                   1013:        puts("                (type * by itself for a list of flags you can use)");
                   1014:        puts("The Unix command in brackets below each subject will display the same");
                   1015:        puts("information that I do.  Sometimes information exists only off-line and I");
                   1016:        puts("have nothing to show you; try the local distributor of printed documentation.");
                   1017: }
                   1018: 
                   1019: getrefs(fp, upm)               /* get references to src from indexes qq.v. */
                   1020: FILE *fp;
                   1021: int upm;                       /* whether looking at upm database "whatis" */
                   1022: {
                   1023:        /*
                   1024:         * indexbuf     str0\0str1\0str2\0str3\0 ... str(ni-1)\0
                   1025:         * iptrs        ^     ^     ^     ^      ... ^          0
                   1026:         */
                   1027:        register char *p, *ref;
                   1028:        char s[MAXNAMLEN];      /* lower case version of src */
                   1029:        char t[BUFSIZ];         /* temporary line buffer */
                   1030:        int preamble = !upm;
                   1031: 
                   1032:        if (ilen > IBSIZE || ni > IPSIZE)
                   1033:                return puts("Index space full.");
                   1034:        for (p = src, ref = s; *p; p++, ref++)  /* ref becomes lower case */
                   1035:                *ref = lcase(*p);                       /* version of src */
                   1036:        *ref = 0;
                   1037:        ref = s;
                   1038:        iptrs[ni] = &indexbuf[ilen];
                   1039:        while (fgets(t, BUFSIZ, fp) != NULL) {
                   1040:                if (preamble) {         /* indexes all have preamble to skip */
                   1041:                        if ((p = index(t, '-')) && fsubstr(p, "------"))
                   1042:                                preamble = 0;           /* preamble over */
                   1043:                        continue;
                   1044:                }
                   1045:                for (p = t; *p; p++)
                   1046:                        if (lcase(*p) == *ref && fsubstr(p, ref))
                   1047:                                break;
                   1048:                if (!*p)
                   1049:                        continue;
                   1050:                iptrs[ni++] = &indexbuf[ilen];
                   1051:                for (p = t; *p && isspace(*p); p++);
                   1052:                for (; *p && *p != ' '; ilen++, p++)
                   1053:                        indexbuf[ilen] = *p;
                   1054:                if (upm)
                   1055:                        for (; *p && *p != '\t'; ilen++, p++)
                   1056:                                if (*p == '-' && *(p + 1) == ' ')
                   1057:                                        break;  /* cover glitches in MANINDEX */
                   1058:                                else
                   1059:                                        indexbuf[ilen] = *p;
                   1060:                indexbuf[ilen++] = 0;
                   1061:                for (; *p && isspace(*p); p++);
                   1062:                if (upm && *p == '-' && *(p + 1) == ' ')
                   1063:                        p += 2;
                   1064:                iptrs[ni++] = &indexbuf[ilen];
                   1065:                for (; *p; ilen++, p++)
                   1066:                        indexbuf[ilen] = *p;
                   1067:                indexbuf[ilen++] = 0;
                   1068:        }
                   1069:        iptrs[ni] = 0;
                   1070:        fclose(fp);
                   1071: }
                   1072: 
                   1073: putrefs()                      /* list references stored in iptrs */
                   1074: {
                   1075:        register int i;
                   1076:        register char *p, *format;
                   1077:        FILE *more;
                   1078: 
                   1079:        NO_RUPTS;
                   1080:        if ((more = outpipe()) == NULL)
                   1081:                PERROR;
                   1082:        if (setjmp(jmpenv)) {
                   1083:                wrapup(more);
                   1084:                return;
                   1085:        }
                   1086:        GET_SIGPIPE;
                   1087:        format = (number ? "%3d  %s\t\t[ " : "%s\t\t[ ");
                   1088:        fprintf(more, "\nThese subjects appear to be related to \"%s\".\n\n", src);
                   1089:        for (i = 0; i < ni; i += 2) {
                   1090:                if (number)
                   1091:                        fprintf(more, format, (i / 2 + 1), iptrs[i + 1]);
                   1092:                else
                   1093:                        fprintf(more, format, iptrs[i + 1]);
                   1094:                if (i < mansegment) {
                   1095:                        fputs("help ", more);
                   1096:                        for (p = iptrs[i]; *p; p++)
                   1097:                                putc((*p == '/' ? ' ' : *p), more);
                   1098:                }
                   1099:                else if (i >= docsegment)
                   1100:                        fprintf(more, "Off-line only document:  %s", iptrs[i]);
                   1101:                else {
                   1102:                        fputs("man ", more);
                   1103:                        for (p = iptrs[i]; *p && *p != '('; p++);
                   1104:                        for (p++; *p != ')'; p++)
                   1105:                                putc(lcase(*p), more);
                   1106:                        putc(' ', more);
                   1107:                        for (p = iptrs[i]; *p != ',' && *p != ' '; p++)
                   1108:                                putc(*p, more);
                   1109:                }
                   1110:                fputs(" ]\n", more);
                   1111:        }
                   1112:        wrapup(more);
                   1113: }
                   1114: 
                   1115: FILE *
                   1116: outpipe()              /* return a file descriptor pointing to "more" */
                   1117: {
                   1118:        int fildes[2];
                   1119:        FILE *fp, *fdopen();
                   1120: 
                   1121:        if (pipe(fildes) == -1) 
                   1122:                PERROR;
                   1123:        if (!fork()) {
                   1124:                OK_RUPTS;
                   1125:                close(fildes[1]);
                   1126:                fclose(stdin);
                   1127:                if (dup(fildes[0]) == -1)
                   1128:                        PERROR;
                   1129:                close(fildes[0]);
                   1130:                execlp("more", "more", "-s", 0);
                   1131:                PERROR;
                   1132:        }
                   1133:        close(fildes[0]);
                   1134:        return fdopen(fildes[1], "w");
                   1135: }
                   1136: 
                   1137: iprompt()                              /* prompt user - index version */
                   1138: {
                   1139:        if (!quiet)
                   1140:                fputs(indexprompt, stdout);
                   1141:        printf("\n(%s-index %s) ", progname, src);
                   1142:        fflush(stdout);
                   1143: }
                   1144: 
                   1145: selectref()                    /* read user instruction for indexing */
                   1146: {
                   1147:        register char *p, *s;
                   1148:        register int ins;
                   1149:        char sbuf[BUFSIZ];
                   1150: 
                   1151:        isrc = idst = 0;
                   1152:        iprompt();
                   1153:        if (gets(sbuf) == NULL)
                   1154:                exit(0);
                   1155:        for (p = sbuf+strlen(sbuf)-1; isspace(*p) && p >= sbuf; p--);
                   1156:        if (p < sbuf)
                   1157:                return NOOP_I;
                   1158:        *++p = 0;                               /* blanks now trimmed */
                   1159:        for (p = sbuf; isspace(*p); p++);
                   1160:        if (*p == '%' || *p == '$')
                   1161:                return QUIT_I;
                   1162:        if (DOT(p))
                   1163:                return LIST_I;
                   1164:        if (DOTDOT(p))
                   1165:                return BACK_I;
                   1166:        if (*p == '?')
                   1167:                return HELP_I;
                   1168:        if (*p == '/')
                   1169:                return ROOT_I;
                   1170:        if (*p == '<')
                   1171:                return YELL_I;
                   1172:        if (*p == '!') {
                   1173:                isrc = ++p;
                   1174:                return PASS_I;
                   1175:        }
                   1176:        if (*p == '*') {
                   1177:                for (p++; isspace(*p); p++);
                   1178:                if (*p)
                   1179:                        isrc = p;
                   1180:                for (; *p && !isspace(*p); p++);
                   1181:                if (*p)
                   1182:                        *p++ = 0;
                   1183:                for (; *p && isspace(*p); p++);
                   1184:                if (*p)
                   1185:                        idst = p;
                   1186:                return FLAG_I;
                   1187:        }
                   1188:        for (s = p; *s; s++)
                   1189:                if (!isdigit(*s))
                   1190:                        break;
                   1191:        if (!*s && number) {
                   1192:                ihit = 2 * atoi(p) - 1;
                   1193:                if (ihit < 1 || ihit > ni) {
                   1194:                        printf("\nThere is no subject numbered %d.\n", atoi(p));
                   1195:                        return NOOP_I;
                   1196:                }
                   1197:        }
                   1198:        else
                   1199:                if ((ins = iwhatnext(p)) != GOT_ONE)
                   1200:                        return ins;
                   1201:        if (ihit < mansegment) {
                   1202:                makefname(0, iptrs[ihit - 1]);
                   1203:                page();
                   1204:                return NOOP_I;
                   1205:        }
                   1206:        if (ihit >= docsegment && docsegment > 0) {
                   1207:                puts("\nSorry, that reference is not available on the computer.");
                   1208:                return NOOP_I;
                   1209:        }
                   1210:        if (!fork()) {
                   1211:                for (s = sbuf, p = iptrs[ihit - 1]; *p != ' ' && *p != ','; p++)
                   1212:                        *s++ = *p;
                   1213:                for (; *p != '('; p++);
                   1214:                for (*s++ = 0, p++; *p != ')'; p++)
                   1215:                        *s++ = lcase(*p);
                   1216:                for (*s-- = 0; *s; s--);
                   1217:                execlp("man", "man", ++s, sbuf, 0);
                   1218:                PERROR;
                   1219:        }
                   1220:        NO_RUPTS;
                   1221:        wait(0);
                   1222:        OK_RUPTS;
                   1223:        return NOOP_I;
                   1224: }
                   1225: 
                   1226: iwhatnext(s)                           /* indexing version of whatnext */
                   1227: char *s;
                   1228: {
                   1229:        static char word[MAXNAMLEN];
                   1230:        int wlen;
                   1231: 
                   1232:        strcpy(word, s);
                   1233:        isrc = word;
                   1234:        while (!imatch(word))
                   1235:                if (inhits > 1) {
                   1236:                        printf("\nNot precise enough.  Enter more letters, or RETURN:  %s", word);
                   1237:                        fflush(stdout);
                   1238:                        wlen = strlen(word);
                   1239:                        if (gets(word + wlen) == NULL)
                   1240:                                return QUIT_I;
                   1241:                        if (strlen(word) <= wlen)       /* no new letters */
                   1242:                                return NOOP_I;
                   1243:                }
                   1244:                else {
                   1245:                        printf("\nThere is no subject \"%s\".\n", word);
                   1246:                        return NOOP_I;
                   1247:                }
                   1248:        isrc = iptrs[ihit];
                   1249:        return GOT_ONE;
                   1250: }
                   1251: 
                   1252: imatch(abbr)           /* indexing version of match (on unsorted list) */
                   1253: char *abbr;
                   1254: {
                   1255:        register char **t;
                   1256:        register char *p = abbr;
                   1257:        register char **last;
                   1258: 
                   1259:        last = iptrs + (docsegment < 0 ? ni : docsegment);
                   1260:        inhits = 0;
                   1261:        for (t = iptrs + 1; t < last; t += 2)
                   1262:                if (**t != *p)          /* quickly check first character */
                   1263:                        continue;
                   1264:                else if (substr(*t, abbr)) {
                   1265:                        inhits++;
                   1266:                        ihit = t - iptrs;
                   1267:                        if (strcmp(*t, abbr) == 0)
                   1268:                                return (inhits = 1);
                   1269:                }
                   1270:        return (inhits == 1);
                   1271: }
                   1272: 
                   1273: makefname(dirlev, tail)        /* build fname from cwd and tail, return no. matched */
                   1274: int dirlev;                            /* directory level */
                   1275: char *tail;                            /* tail of pathname to use */
                   1276: {
                   1277:        register int i;
                   1278:        register char *p;
                   1279: 
                   1280:        if (dirlev > 0) {
                   1281:                sprintf(fname, "%s/%s", cwd, tail);
                   1282:                fnamect = (EXISTS(fname) ? 1 : 0);
                   1283:                return fnamect;
                   1284:        }
                   1285:        fnamect = 0;    /* count of number of dirs. where tail exists */
                   1286:        p = fname;      /* full names of files with tails as above */
                   1287:        for (i = 0; hvec[i]; i++) {
                   1288:                sprintf(p, "%s/%s", hvec[i], tail);
                   1289:                if (EXISTS(p)) {
                   1290:                        fnamect++;
                   1291:                        p += strlen(p) + 1;
                   1292:                }
                   1293:        }
                   1294:        return fnamect;
                   1295: }
                   1296: 
                   1297: pass(s)                        /* replace = with fname and send to system */
                   1298: register char *s;      /* allow \= to pass as =, but \x passes as \x */
                   1299: {
                   1300:        register char *p;
                   1301:        register int escaped = 0;
                   1302:        char buf[BUFSIZ];
                   1303: 
                   1304:        PUTNL;
                   1305:        for (p = buf; *s; s++) {
                   1306:                if (escaped) {
                   1307:                        if (*s != '=')
                   1308:                                *p++ = '\\';
                   1309:                        *p++ = *s;
                   1310:                        escaped = 0;
                   1311:                }
                   1312:                else if (*s == '\\')
                   1313:                        escaped = 1;
                   1314:                else if (*s == '=' && hit >= 0) {
                   1315:                        makefname(dirlevel, topics[hit]);
                   1316:                        strcpy(p, fname);
                   1317:                        for (; *p; p++);
                   1318:                }
                   1319:                else
                   1320:                        *p++ = *s;
                   1321:        }
                   1322:        *p = 0;
                   1323:        if (!fork()) {
                   1324:                putchar('\n');
                   1325:                execl(shell, shell, "-c", buf, 0);
                   1326:                PERROR;
                   1327:        }
                   1328:        NO_RUPTS;
                   1329:        wait(0);
                   1330:        OK_RUPTS;
                   1331: }
                   1332: 
                   1333: flag(f, val)           /* set flag on or off; 0 in src and dst gives help */
                   1334: char *f;
                   1335: char *val;
                   1336: {
                   1337:        if (!f) {
                   1338:                puts("\nCurrent flag settings and their meanings are:");
                   1339:                printf("  number\t%suse numbers in topic and index listings\n",
                   1340:                        (number ? "on\t" : "off\tdo not "));
                   1341:                printf("  quiet \t%ssuppress the instruction line before prompting\n",
                   1342:                        (quiet ? "on\t" : "off\tdo not "));
                   1343:                return;
                   1344:        }
                   1345:        if (substr("number", f)) {
                   1346:                printf("\nnumber:  was %s,", (number ? "on" : "off"));
                   1347:                if (!val) 
                   1348:                        number = (number ? 0 : 1);              /* toggle */
                   1349:                else
                   1350:                        number = (val[1] == 'n' ? 1 : 0);
                   1351:                printf(" is %s.\n", (number ? "on" : "off"));
                   1352:        }
                   1353:        else if (substr("quiet", f)) {
                   1354:                printf("\nquiet:  was %s,", (quiet ? "on" : "off"));
                   1355:                if (!val) 
                   1356:                        quiet = (quiet ? 0 : 1);                /* toggle */
                   1357:                else
                   1358:                        quiet = (val[1] == 'n' ? 1 : 0);
                   1359:                printf(" is %s.\n", (quiet ? "on" : "off"));
                   1360:                if (more_d)
                   1361:                        strcpy(more_d, (quiet ? "  " : "-d"));
                   1362:        }
                   1363:        else
                   1364:                puts("\nThat is not a flag I know about.  Type * for a complete list.");
                   1365: }
                   1366: 
                   1367: page()                 /* print a help file with more or run program */
                   1368: {
                   1369:        char c[2];
                   1370:        FILE *fp, *more;
                   1371: 
                   1372:        if ((fp = fopen(fname, "r")) == NULL) {
                   1373:                perror(fname);
                   1374:                return;         /* try to continue on this error */
                   1375:        }
                   1376:        c[0] = getc(fp);        /* check first 2 characters of first file */
                   1377:        c[1] = getc(fp);        /* looking for magic characters and numbers */
                   1378:                                /* (should check more than just the first) */
                   1379:        if (runs(c)) {          /* if a program, run it and then return */
                   1380:                fclose(fp);
                   1381:                OK_RUPTS;
                   1382:                return;
                   1383:        }
                   1384:        rewind(fp);
                   1385:        NO_RUPTS;
                   1386:        if ((more = outpipe()) == NULL)
                   1387:                PERROR;
                   1388:        if (setjmp(jmpenv)) {
                   1389:                fclose(fp);
                   1390:                wrapup(more);
                   1391:                return;
                   1392:        }
                   1393:        GET_SIGPIPE;
                   1394:        putfiles(fp, more);
                   1395:        fclose(fp);
                   1396:        wrapup(more);
                   1397: }
                   1398: 
                   1399: #define isblank(s)     (*s == '\n')
                   1400: #define sqspace(s)     { if (!isblank(s) || !wasblank) fputs(s, out); else linect--; wasblank = isblank(s); }
                   1401: 
                   1402: putfiles(in, out)                      /* print file(s) onto out file */
                   1403: FILE *in;                              /* first of the input files */
                   1404: FILE *out;
                   1405: {
                   1406:        register char *fn = fname;
                   1407:        register int linectsum = 0;
                   1408: 
                   1409:        while (fnamect--) {
                   1410:                if (in == NULL && (in = fopen(fn, "r")) == NULL)
                   1411:                        perror(fn);
                   1412:                linectsum += filter(in, out);
                   1413:                fclose(in);
                   1414:                in = NULL;
                   1415:                fn += strlen(fn) + 1;
                   1416:        }
                   1417:        return linectsum;
                   1418: }
                   1419: 
                   1420: filter(in, out)                /* filter out multiple blank lines and page banners */
                   1421: FILE *in;
                   1422: FILE *out;
                   1423: {
                   1424:        char lbuf[BUFSIZ];
                   1425:        register int lineno, wasblank;
                   1426:        register char *s = lbuf;
                   1427:        int linect, i;
                   1428:        char *p;
                   1429: 
                   1430:        /* check page one for proper headers; if none then keep pagination */
                   1431:        wasblank = 0;
                   1432:        for (lineno = 1; fgets(s, BUFSIZ, in) != NULL; lineno++)
                   1433:                if (!isblank(s) || lineno >= 4)
                   1434:                        break;
                   1435:        if (!(lineno == 4 &&
                   1436:                (((p = index(s, 'H')) && substr(p, "HELP"))     /* help */
                   1437:                || index(s, ')') != rindex(s, ')'))))           /* man */
                   1438:                        keeppag = 1;                    /* criteria not met */
                   1439:        if (lineno == 1 && (*s = '#' || *s == ':')) {
                   1440:                keeppag = 1;                    /* this is a script file */
                   1441:                lineno = 0;
                   1442:                fputc('\n', out);
                   1443:        }
                   1444:        else {
                   1445:                for (i = lineno - 1; i; i--)
                   1446:                        if (keeppag)
                   1447:                                fputc('\n', out);
                   1448:                        else
                   1449:                                sqspace("\n");
                   1450:                if (keeppag)
                   1451:                        fputs(s, out);
                   1452:                else
                   1453:                        sqspace(s);
                   1454:        }
                   1455:        linect = lineno;
                   1456:        while (fgets(s, BUFSIZ, in) != NULL) {
                   1457:                lineno++, linect++;
                   1458:                if (lineno > 66)
                   1459:                        lineno = 1;
                   1460:                if (keeppag)    /* this global overrides our page 1 analysis */
                   1461:                        fputs(s, out);
                   1462:                else if (lineno > 7 && lineno < 60      /* skip page banners */
                   1463:                                || linect < 8)          /* let first 7 go */
                   1464:                        sqspace(s)
                   1465:        }
                   1466:        return linect;
                   1467: }
                   1468: 
                   1469: runs(c)                /* run program or script named by fname, else return 0 */
                   1470: char c[];
                   1471: {
                   1472:        int *magic = (int *)c;
                   1473: 
                   1474:        if (c[0] != '#' && c[0] != ':'
                   1475:                && *magic != 0413 && *magic != 0410 && *magic != 0407)
                   1476:                        return 0;
                   1477:        if (!fork()) {
                   1478:                if (*magic == 0413 || *magic == 0410 || *magic == 0407)
                   1479:                        execv(fname, 0);
                   1480:                else if (c[0] == '#')
                   1481:                        execlp("csh", "csh", "-f", fname, 0);
                   1482:                else
                   1483:                        execlp("sh", "sh", fname, 0);
                   1484:                perror(fname);
                   1485:        }
                   1486:        NO_RUPTS;
                   1487:        wait(0);
                   1488:        OK_RUPTS;
                   1489:        return 1;
                   1490: }
                   1491: 
                   1492: comlist()                      /* list help instructions available */
                   1493: {
                   1494:        if (number)
                   1495:                puts("\nTo see a topic, type its name, a unique abbreviation, or its number.");
                   1496:        else
                   1497:                puts("\nTo see a topic, type its name or a unique abbreviation.");
                   1498:        puts("Here is a list of commands:");
                   1499:        printf("  %c             quit from help and return to the shell (control-d works also)\n", shellprompt);
                   1500:        printf("  topic         display a \"topic\", whose name%syou supply\n",
                   1501:                (number ? " or number " : " "));
                   1502:        puts("  topic +       see what more is known about a topic");
                   1503:        puts("  topic > file  save a topic in a file (you supply the name \"file\")");
                   1504:        puts("  topic | lpr   paginate and print a topic on the lineprinter");
                   1505:        puts("  topic >& file save a topic in a file with pagination");
                   1506:        puts("  ?             display this command list");
                   1507:        puts("  .             list topics at the current level");
                   1508:        puts("  ..            back up to and list the next higher level of topics");
                   1509:        puts("  /             back up to and list the top level of topics");
                   1510:        puts("  <             send comments or other input to the maintainer of help");
                   1511:        puts("  !command      do a Unix command and then return to help");
                   1512:        puts("  * flag on/off set a \"flag\" on or off to adjust the behavior of help");
                   1513:        puts("                (type * by itself for a list of flags you can use)");
                   1514:        puts("If you enter no topic in a command or just an equals sign (=),");
                   1515:        puts("the most recent topic at this level is used.");
                   1516: }
                   1517: 
                   1518: match(abbr)            /* find a match for abbr in current directory */
                   1519: char *abbr;
                   1520: {
                   1521:        register char **t;
                   1522:        register char *p = abbr;
                   1523: 
                   1524:        nhits = 0;
                   1525:        for (t = topics; *t; t++)       /* find first string beginning */
                   1526:                if (**t == *p)          /* with same letter */
                   1527:                        break;
                   1528:        for (; *t && **t == *p; t++)
                   1529:                if (substr(*t, abbr)) {
                   1530:                        nhits++;
                   1531:                        hit = t - topics;
                   1532:                        if (strcmp(*t, abbr) == 0)
                   1533:                                return (nhits = 1);
                   1534:                }
                   1535:        return (nhits == 1);
                   1536: }
                   1537: 
                   1538: /*
                   1539:  * radix sort of an alphanumeric list, identical keys deleted
                   1540:  * Originally by D. Wasley, July 1980
                   1541:  */
                   1542: 
                   1543: #define MSB    0100
                   1544: 
                   1545: vsort(list)
                   1546: char *list[];
                   1547: {
                   1548:        char **endlist = tptrs + nt - 1;
                   1549:        int offset = 0;
                   1550: 
                   1551:        if (endlist > list)
                   1552:                _sortb(list, endlist, 0);               /* recursive sort */
                   1553: }
                   1554: 
                   1555: _sortb(list, endlist, offset)
                   1556: char **list, **endlist; int offset;
                   1557: {
                   1558:        register char **high, c;
                   1559: 
                   1560:        _sortr(list, endlist, offset, MSB);     /* radix sort on this char */
                   1561:        while (list < endlist) {                /* now sort each sublist that */
                   1562:                c = (*list)[offset];            /* starts with a common char */
                   1563:                high = list;
                   1564:                while ((*++high)[offset] == c && high <= endlist) ;
                   1565:                if (high  - list > 1) {
                   1566:                        if (c)
                   1567:                                _sortb(list, high-1, offset+1);
                   1568:                        else                    /* kill off identical keys */
                   1569:                                for (list++; list < high; list++)
                   1570:                                        *list = 0;
                   1571:                }
                   1572:                list = high;
                   1573:        }
                   1574: }
                   1575: 
                   1576: _sortr(list, endlist, offset, mask)
                   1577: int offset, mask; char **list, **endlist;
                   1578: {
                   1579:        register char **low, **high, *temp;
                   1580: 
                   1581:        low = list;
                   1582:        high = endlist;
                   1583:        while (low < high) {
                   1584:                while (low < endlist && ((*low)[offset] & mask) == 0)
                   1585:                        low++;
                   1586:                while (high > list && ((*high)[offset] & mask) != 0)
                   1587:                        high--;
                   1588:                if (high > low) {
                   1589:                        temp = *high;
                   1590:                        *high = *low;
                   1591:                        *low = temp;
                   1592:                }
                   1593:        }
                   1594:        if ((mask >>= 1) != 0) {        /* redefine mask and sort sublists */
                   1595:                if (endlist > low)
                   1596:                        _sortr(low, endlist, offset, mask);
                   1597:                if (high > list)
                   1598:                        _sortr(list, high, offset, mask);
                   1599:        }
                   1600: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.