|
|
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.