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