X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/4610900a57f4e105e3854ec4b65be20b560c736f..bc258617f323d737755d6043c64ffea2c59b69d4:/usr/src/usr.bin/man/man.c diff --git a/usr/src/usr.bin/man/man.c b/usr/src/usr.bin/man/man.c index 3ada475db5..c1bea033f1 100644 --- a/usr/src/usr.bin/man/man.c +++ b/usr/src/usr.bin/man/man.c @@ -1,279 +1,437 @@ /* * Copyright (c) 1987 Regents of the University of California. - * All rights reserved. The Berkeley software License Agreement - * specifies the terms and conditions for redistribution. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint char copyright[] = "@(#) Copyright (c) 1987 Regents of the University of California.\n\ All rights reserved.\n"; -#endif not lint +#endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)man.c 5.2 (Berkeley) %G%"; -#endif not lint +static char sccsid[] = "@(#)man.c 5.17 (Berkeley) %G%"; +#endif /* not lint */ #include #include #include -#define DEF_PAGER "more -s" -#define DEF_PATH "/usr/man:/usr/local/man" +#define DEF_PAGER "/usr/ucb/more -s" +#define DEF_PATH "/usr/man:/usr/new/man:/usr/local/man" #define LOCAL_PATH "/usr/local/man" -#define LOCAL_NAME "local" -#define NO 0 -#define YES 1 +#define NEW_PATH "/usr/new/man" -#define NO_SECTION 0 -#define S_THREEF 9 -#define S_NEW 10 -#define S_OLD 11 +#define NO 0 +#define YES 1 -/* this array maps a character (ex: '4') to an offset in dirlist */ -#define secno(x) (seclist[(int)(x - '0')]) -static int seclist[] = { -1, 1, 4, 5, 6, 7, 3, 8, 2, -1, -1 }; - -/* sub directory list, ordered for searching */ -typedef struct something_meaningful { - char *name, - *msg; -} DIR; - -DIR dirlist[] = { /* sub-directory list */ - "notused", "", "cat1", "1st", "cat8", "8th", - "cat6", "6th", "cat2", "2nd", "cat3", "3rd", - "cat4", "4th", "cat5", "5th", "cat7", "7th", - "cat3f", "3rd (F)", "new", "new", "old", "old", - NULL, NULL, -}; - -static int nomore; /* copy file to stdout */ -static char *defpath, /* default search path */ +static char *command, /* command buffer */ + *defpath, /* default search path */ *locpath, /* local search path */ *machine, /* machine type */ *manpath, /* current search path */ - *pager; /* requested pager */ + *newpath, /* new search path */ + *pager, /* requested pager */ + how; /* how to display */ + +#define ALL 0x1 /* show all man pages */ +#define CAT 0x2 /* copy file to stdout */ +#define WHERE 0x4 /* just tell me where */ main(argc, argv) - int argc; - register char **argv; + int argc; + register char **argv; { - int section; - char **arg_start, **arg, - *getenv(); + extern char *optarg; + extern int optind; + int ch; + char *getenv(), *malloc(); - arg_start = argv; - for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv) - switch((*argv)[1]) { - case 0: /* just write to stdout */ - nomore = YES; + while ((ch = getopt(argc, argv, "-M:P:afkw")) != EOF) + switch((char)ch) { + case '-': + how |= CAT; break; case 'M': case 'P': /* backward compatibility */ - if ((*argv)[2]) - manpath = *argv + 2; - else { - if (argc < 2) { - fprintf(stderr, "%s: missing path\n", *argv); - exit(1); - } - --argc; - manpath = *++argv; - } + defpath = optarg; + break; + case 'a': + how |= ALL; break; /* - * "man -f" and "man -k" are undocumented ways of calling - * whatis(1) and apropos(1). Just strip out the flag - * argument and jump. + * "man -f" and "man -k" are backward contemptible, + * undocumented ways of calling whatis(1) and apropos(1). */ case 'f': - for (arg = argv; arg[0] = arg[1]; ++arg); - *arg_start = "whatis"; - execvp(*arg_start, arg_start); - fputs("whatis: Command not found.\n", stderr); - exit(1); + jump(argv, "-f", "whatis"); + /*NOTREACHED*/ case 'k': - for (arg = argv; *arg = arg[1]; ++arg); - *arg_start = "apropos"; - execvp(*arg_start, arg_start); - fputs("apropos: Command not found.\n", stderr); - exit(1); + jump(argv, "-k", "apropos"); + /*NOTREACHED*/ + /* + * Deliberately undocumented; really only useful when + * you're moving man pages around. Not worth adding. + */ + case 'w': + how |= WHERE | ALL; + break; case '?': default: usage(); } - if (!argc) + argv += optind; + + if (!*argv) usage(); - if (!nomore) + if (!(how & CAT)) if (!isatty(1)) - nomore = YES; - else if (!(pager = getenv("PAGER"))) + how |= CAT; + else if (pager = getenv("PAGER")) { + register char *p; + + /* + * if the user uses "more", we make it "more -s" + * watch out for PAGER = "mypager /usr/ucb/more" + */ + for (p = pager; *p && !isspace(*p); ++p); + for (; p > pager && *p != '/'; --p); + if (p != pager) + ++p; + /* make sure it's "more", not "morex" */ + if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ + char *opager = pager; + /* + * allocate space to add the "-s" + */ + if (!(pager = malloc((u_int)(strlen(opager) + + sizeof("-s") + 1)))) { + fputs("man: out of space.\n", stderr); + exit(1); + } + (void)sprintf(pager, "%s %s", opager, "-s"); + } + } + else pager = DEF_PAGER; if (!(machine = getenv("MACHINE"))) machine = MACHINE; if (!defpath && !(defpath = getenv("MANPATH"))) defpath = DEF_PATH; locpath = LOCAL_PATH; - for (; *defpath && *defpath == ':'; ++defpath); + newpath = NEW_PATH; + man(argv); + /* use system(3) in case someone's pager is "pager arg1 arg2" */ + if (command) + (void)system(command); + exit(0); +} + +typedef struct { + char *name, *msg; +} DIR; +static DIR list1[] = { /* section one list */ + "cat1", "1st", "cat8", "8th", "cat6", "6th", + "cat.old", "old", NULL, NULL, +}, list2[] = { /* rest of the list */ + "cat2", "2nd", "cat3", "3rd", "cat4", "4th", + "cat5", "5th", "cat7", "7th", "cat3f", "3rd (F)", + NULL, NULL, +}, list3[2]; /* single section */ + +static +man(argv) + char **argv; +{ + register char *p; + DIR *section, *getsect(); + int res; for (; *argv; ++argv) { - section = NO_SECTION; - manpath = DEF_PATH; + manpath = defpath; + section = NULL; switch(**argv) { - /* hardwired section numbers, fix here if they change */ - case '1': case '2': case '4': case '5': case '6': - case '7': case '8': - if (!(*argv)[1]) { - section = secno((*argv)[0]); - goto numtest; - } - break; - case '3': - if (!(*argv)[1]) { /* "3" */ - section = secno((*argv)[0]); -numtest: if (!*++argv) { - fprintf(stderr, "man: what do you want from the %s section of the manual?\n", dirlist[section].msg); - exit(1); - } - } /* "3[fF]" */ - if (((*argv)[1] == 'f' || (*argv)[1] == 'F') && !(*argv)[2]) { - section = S_THREEF; - if (!*++argv) { - fprintf(stderr, "man: what do you want from the %s section of the manual?\n", dirlist[S_THREEF].msg); - exit(1); - } + case 'l': /* local */ + /* support the "{l,local,n,new}###" syntax */ + for (p = *argv; isalpha(*p); ++p); + if (!strncmp(*argv, "l", p - *argv) || + !strncmp(*argv, "local", p - *argv)) { + ++argv; + manpath = locpath; + section = getsect(p); } break; - case 'l': /* local */ - if (!(*argv)[1]) /* "l" */ - section = NO_SECTION; /* "l2" */ - else if (isdigit((*argv)[1]) && !(*argv)[2]) - section = secno((*argv)[1]); - else { - int lex; - lex = strcmp(LOCAL_NAME, *argv); - if (!lex) /* "local" */ - section = NO_SECTION; /* "local2" */ - else if (lex < 0 && isdigit((*argv)[sizeof(LOCAL_NAME) - 1]) && !(*argv)[sizeof(LOCAL_NAME)]) - section = secno((*argv)[sizeof(LOCAL_NAME) - 1]); - else - break; - } - if (!*++argv) { - fputs("man: what do you want from the local section of the manual?\n", stderr); - exit(1); + case 'n': /* new */ + for (p = *argv; isalpha(*p); ++p); + if (!strncmp(*argv, "n", p - *argv) || + !strncmp(*argv, "new", p - *argv)) { + ++argv; + manpath = newpath; + section = getsect(p); } - manpath = locpath; break; - case 'n': /* new */ - if (!*argv[1] || !strcmp(*argv, dirlist[S_NEW].name)) { - section = S_NEW; - goto strtest; + /* + * old isn't really a separate section of the manual, + * and its entries are all in a single directory. + */ + case 'o': /* old */ + for (p = *argv; isalpha(*p); ++p); + if (!strncmp(*argv, "o", p - *argv) || + !strncmp(*argv, "old", p - *argv)) { + ++argv; + list3[0] = list1[3]; + section = list3; } break; - case 'o': /* old */ - if (!*argv[1] || !strcmp(*argv, dirlist[S_OLD].name)) { - section = S_OLD; -strtest: if (!*++argv) { - fprintf(stderr, "man: what do you want from the %s section of the manual?\n", dirlist[section].msg); - exit(1); - } + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + if (section = getsect(*argv)) + ++argv; + } + + if (*argv) { + if (section) + res = manual(section, *argv); + else { + res = manual(list1, *argv); + if (!res || (how & ALL)) + res += manual(list2, *argv); } - break; + if (res || how&WHERE) + continue; } - if (!manual(section, *argv)) - if (manpath == locpath) - fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, dirlist[section].msg); - else if (section == NO_SECTION) - fprintf(stderr, "No entry for %s in the manual.\n", *argv); - else - fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, dirlist[section].msg); + + fputs("man: ", stderr); + if (*argv) + fprintf(stderr, "no entry for %s in the ", *argv); + else + fputs("what do you want from the ", stderr); + if (section) + fprintf(stderr, "%s section of the ", section->msg); + if (manpath == locpath) + fputs("local ", stderr); + else if (manpath == newpath) + fputs("new ", stderr); + if (*argv) + fputs("manual.\n", stderr); + else + fputs("manual?\n", stderr); + exit(1); } - exit(0); } +/* + * manual -- + * given a directory list and a file name find a file that + * matches; check ${directory}/${dir}/{file name} and + * ${directory}/${dir}/${machine}/${file name}. + */ static manual(section, name) - int section; - char *name; + DIR *section; + char *name; { - register DIR *dir; - register char *beg, *end; - char *index(); + register char *beg, *end; + register DIR *dp; + register int res; + char fname[MAXPATHLEN + 1], *index(); - for (beg = manpath;; beg = end + 1) { + for (beg = manpath, res = 0;; beg = end + 1) { if (end = index(beg, ':')) *end = '\0'; - if (section == NO_SECTION) { - for (dir = dirlist + 1; dir->name; ++dir) - if (find(beg, dir->name, name)) - return(YES); + for (dp = section; dp->name; ++dp) { + (void)sprintf(fname, "%s/%s/%s.0", beg, dp->name, name); + if (access(fname, R_OK)) { + (void)sprintf(fname, "%s/%s/%s/%s.0", beg, + dp->name, machine, name); + if (access(fname, R_OK)) + continue; + } + if (how & WHERE) + printf("man: found in %s.\n", fname); + else if (how & CAT) + cat(fname); + else + add(fname); + if (!(how & ALL)) + return(1); + res = 1; } - else if (find(beg, dirlist[section].name, name)) - return(YES); if (!end) - return(NO); + return(res); + *end = ':'; } /*NOTREACHED*/ } +/* + * cat -- + * cat out the file + */ static -find(beg, dir, name) - char *beg, *dir, *name; +cat(fname) + char *fname; { - char fname[MAXPATHLEN + 1]; + register int fd, n; + char buf[BUFSIZ]; - (void)sprintf(fname, "%s/%s/%s.0", beg, dir, name); - if (!access(fname, R_OK)) { - show(fname); - return(YES); + if (!(fd = open(fname, O_RDONLY, 0))) { + perror("man: open"); + exit(1); } - (void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name); - if (!access(fname, R_OK)) { - show(fname); - return(YES); + while ((n = read(fd, buf, sizeof(buf))) > 0) + if (write(1, buf, n) != n) { + perror("man: write"); + exit(1); + } + if (n == -1) { + perror("man: read"); + exit(1); } - return(NO); + (void)close(fd); } +/* + * add -- + * add a file name to the list for future paging + */ static -show(fname) - char *fname; +add(fname) + char *fname; { - register int fd, n; - char buf[BUFSIZ]; + static u_int buflen; + static int len; + static char *cp; + int flen; + char *malloc(), *realloc(), *strcpy(); - if (nomore) { - if (!(fd = open(fname, O_RDONLY, 0))) { - perror("man: open"); + if (!command) { + if (!(command = malloc(buflen = 1024))) { + fputs("man: out of space.\n", stderr); exit(1); } - while ((n = read(fd, buf, sizeof(buf))) > 0) - if (write(1, buf, n) != n) { - perror("man: write"); - exit(1); - } - if (n == -1) { - perror("man: read"); + len = strlen(strcpy(command, pager)); + cp = command + len; + } + flen = strlen(fname); + if (len + flen + 2 > buflen) { /* +2 == space, EOS */ + if (!(command = realloc(command, buflen += 1024))) { + fputs("man: out of space.\n", stderr); exit(1); } - (void)close(fd); + cp = command + len; } - else { - /* - * use system(2) in case someone's pager is - * "command arg1 arg2" - */ - (void)sprintf(buf, "%s %s", pager, fname); - (void)system(buf); + *cp++ = ' '; + len += flen + 1; /* +1 = space */ + (void)strcpy(cp, fname); + cp += flen; +} + +/* + * getsect -- + * return a point to the section structure for a particular suffix + */ +static DIR * +getsect(s) + char *s; +{ + switch(*s++) { + case '1': + if (!*s) + return(list1); + break; + case '2': + if (!*s) { + list3[0] = list2[0]; + return(list3); + } + break; + /* sect. 3 requests are for either section 3, or section 3[fF]. */ + case '3': + if (!*s) { + list3[0] = list2[1]; + return(list3); + } + else if ((*s == 'f' || *s == 'F') && !*++s) { + list3[0] = list2[5]; + return(list3); + } + break; + case '4': + if (!*s) { + list3[0] = list2[2]; + return(list3); + } + break; + case '5': + if (!*s) { + list3[0] = list2[3]; + return(list3); + } + break; + case '6': + if (!*s) { + list3[0] = list1[2]; + return(list3); + } + break; + case '7': + if (!*s) { + list3[0] = list2[4]; + return(list3); + } + break; + case '8': + if (!*s) { + list3[0] = list1[1]; + return(list3); + } } + return((DIR *)NULL); } +/* + * jump -- + * strip out flag argument and jump + */ +static +jump(argv, flag, name) + char **argv, *name; + register char *flag; +{ + register char **arg; + + argv[0] = name; + for (arg = argv + 1; *arg; ++arg) + if (!strcmp(*arg, flag)) + break; + for (; *arg; ++arg) + arg[0] = arg[1]; + execvp(name, argv); + fprintf(stderr, "%s: Command not found.\n", name); + exit(1); +} + +/* + * usage -- + * print usage and die + */ static usage() { - fputs("usage: man [-] [-M path] [section] title ...\n", stderr); + fputs("usage: man [-] [-a] [-M path] [section] title ...\n", stderr); exit(1); }