Annotation of 43BSD/contrib/help/help.c, revision 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.