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: }

Defined functions

_sortb defined in line 1555; used 2 times
_sortr defined in line 1576; used 3 times
chwd defined in line 562; used 5 times
comlist defined in line 1492; used 2 times
error defined in line 377; used 2 times
filter defined in line 1420; used 1 times
find defined in line 904; used 1 times
flag defined in line 1333; used 2 times
fsubstr defined in line 784; used 2 times
getfiles defined in line 791; used 2 times
getoptions defined in line 328; used 1 times
getrefs defined in line 1019; used 4 times
helpmaint defined in line 394; used 1 times
icomlist defined in line 996; used 1 times
imatch defined in line 1252; used 1 times
iprompt defined in line 1137; used 1 times
isadir defined in line 829; used 4 times
iwhatnext defined in line 1226; used 1 times
list defined in line 857; used 29 times
log defined in line 314; used 4 times
lprt defined in line 266; used 1 times
main defined in line 167; never used
makefname defined in line 1273; used 5 times
match defined in line 1518; used 4 times
nextins defined in line 624; used 1 times
onintr defined in line 840; used 2 times
outpipe defined in line 1115; used 4 times
page defined in line 1367; used 3 times
pass defined in line 1297; used 2 times
prompt defined in line 759; used 1 times
putfiles defined in line 1402; used 2 times
putrefs defined in line 1073; used 2 times
runs defined in line 1469; used 1 times
save defined in line 250; used 1 times
selectref defined in line 1145; used 1 times
setgetenv defined in line 420; used 1 times
startup defined in line 475; used 1 times
substr defined in line 777; used 5 times
vsort defined in line 1545; used 2 times
whatnext defined in line 522; used 2 times
wrapup defined in line 847; used 6 times
yell defined in line 298; used 2 times

Defined variables

argp defined in line 108; used 7 times
cwd defined in line 110; used 8 times
cwdend defined in line 560; used 11 times
dirlevel defined in line 113; used 14 times
dirlist defined in line 109; used 3 times
docsegment defined in line 150; used 6 times
dot defined in line 111; never used
dotdot defined in line 111; never used
dst defined in line 153; used 11 times
dstarg defined in line 153; used 4 times
firstime defined in line 855; used 2 times
fname defined in line 154; used 14 times
fnamect defined in line 155; used 7 times
hdbuf defined in line 106; used 1 times
helpprompt defined in line 116; used 3 times
hvec defined in line 107; used 18 times
idst defined in line 148; used 3 times
iflag defined in line 156; used 2 times
indexbuf defined in line 144; used 10 times
indexprompt defined in line 117; used 3 times
inhits defined in line 147; used 5 times
interactive defined in line 156; used 6 times
iptrs defined in line 145; used 19 times
isrc defined in line 148; used 7 times
jmpenv defined in line 838; used 4 times
keeppag defined in line 114; used 7 times
line defined in line 152; used 6 times
maintkey defined in line 160; used 4 times
mansegment defined in line 149; used 4 times
more_d defined in line 158; used 3 times
nhits defined in line 135; used 6 times
ni defined in line 146; used 13 times
nt defined in line 132; used 13 times
number defined in line 157; used 18 times
progname defined in line 159; used 7 times
quiet defined in line 157; used 11 times
rtlen defined in line 134; used 2 times
sccsid defined in line 4; never used
shell defined in line 115; used 6 times
shellprompt defined in line 115; used 7 times
src defined in line 153; used 21 times
subdir defined in line 112; used 5 times
subt defined in line 133; used 6 times
topicbuf defined in line 129; used 4 times
topics defined in line 131; used 19 times
tptrs defined in line 130; used 11 times
vec defined in line 89; used 8 times

Defined macros

BACK_I defined in line 42; used 2 times
BSD4_2 defined in line 31; used 2 times
CLOSEDIR defined in line 810; used 1 times
DEFSHELL defined in line 73; used 2 times
DIRLOOP defined in line 808; used 1 times
DOCINDEX defined in line 65; used 1 times
DOT defined in line 80; used 4 times
DOTDOT defined in line 81; used 4 times
EXECABLE defined in line 77; used 2 times
EXISTS defined in line 76; used 3 times
FIND_I defined in line 48; used 3 times
FLAG_I defined in line 49; used 2 times
GET_SIGPIPE defined in line 95; used 3 times
GOT_ONE defined in line 51; used 5 times
HDBSIZE defined in line 55; used 1 times
HELPLOG defined in line 72; used 1 times
HELPMAINT defined in line 59; used 2 times
HELPROOT defined in line 57; used 2 times
HELPSAVE defined in line 66; used 1 times
HELP_I defined in line 44; used 2 times
HVSIZE defined in line 56; used 2 times
IBSIZE defined in line 901; used 2 times
IPSIZE defined in line 902; used 2 times
JUNK_I defined in line 41; used 4 times
LIST_I defined in line 37; used 3 times
LPRT_I defined in line 43; used 1 times
MAINTAINER defined in line 70; used 1 times
MANINDEX defined in line 63; used 3 times
MAXDLEN defined in line 809; used 1 times
MAXNAMLEN defined in line 100; used 5 times
MSB defined in line 1543; used 1 times
NOOP_I defined in line 50; used 12 times
NO_RUPTS defined in line 97; used 9 times
OK_RUPTS defined in line 98; used 8 times
OPENDIR defined in line 807; used 1 times
PASS_I defined in line 38; used 2 times
PATHSIZE defined in line 802; never used
PERROR defined in line 74; used 12 times
PUTNL defined in line 75; used 1 times
QUIT_I defined in line 47; used 9 times
READABLE defined in line 79; used 1 times
ROOT defined in line 82; used 1 times
ROOT_I defined in line 45; used 2 times
SAVE_I defined in line 39; used 1 times
SET_SIGPIPE defined in line 96; used 1 times
TOPICINDEX defined in line 58; used 1 times
TYPE_I defined in line 40; used 1 times
WRITABLE defined in line 78; used 1 times
YELL_I defined in line 46; used 2 times
isblank defined in line 1399; used 3 times
isspecial defined in line 83; used 1 times
lcase defined in line 84; used 5 times
sqspace defined in line 1400; used 3 times
Last modified: 1985-08-15
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 6667
Valid CSS Valid XHTML 1.0 Strict