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