/*
* 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.4 (Berkeley) %G%";
-#endif not lint
+static char sccsid[] = "@(#)man.c 5.17 (Berkeley) %G%";
+#endif /* not lint */
#include <sys/param.h>
#include <sys/file.h>
#include <ctype.h>
-#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 stanlist */
-#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 {
- char *name,
- *msg;
-} DIR;
-
-DIR stanlist[] = { /* standard sub-directory list */
- "notused", "", "cat1", "1st", "cat8", "8th",
- "cat6", "6th", "cat2", "2nd", "cat3", "3rd",
- "cat4", "4th", "cat5", "5th", "cat7", "7th",
- "cat3f", "3rd (F)", "cat.new", "new", "cat.old", "old",
- NULL, NULL,
-}, sec1list[] = { /* section one list */
- "notused", "", "cat1", "1st", "cat8", "8th",
- "cat6", "6th", "cat.new", "new", "cat.old", "old",
- NULL, NULL,
-};
-
-static DIR *dirlist; /* list of directories to search */
-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;
- /* Gentlemen... start your kludges! */
for (; *argv; ++argv) {
- section = NO_SECTION;
- manpath = DEF_PATH;
- dirlist = stanlist;
+ manpath = defpath;
+ section = NULL;
switch(**argv) {
- /*
- * Section 1 requests are really for section 1, 6, 8, new and
- * old. Since new and old aren't broken up into a directory
- * of cat[1-8], we just pretend that they are a subdirectory
- * of /usr/man. Should be fixed -- make new and old full
- * structures just like local is, get rid of "sec1list" and
- * dirlist.
- */
- case '1':
- if (!(*argv)[1]) {
- dirlist = sec1list;
- goto numtest;
+ 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 '2': case '4': case '5': case '6':
- case '7': case '8':
- if (!(*argv)[1]) {
- section = secno((*argv)[0]);
- goto numtest;
- }
- break;
-
- /* sect. 3 requests are for either section 3, or section 3F. */
- 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", stanlist[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", stanlist[S_THREEF].msg);
- 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);
}
break;
/*
- * Requests for the local section can have subsection numbers
- * appended to them, i.e. "local3" is really local/cat3.
+ * old isn't really a separate section of the manual,
+ * and its entries are all in a single directory.
*/
- 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);
- }
- manpath = locpath;
- break;
- case 'n': /* new */
- if (!(*argv)[1] || !strcmp(*argv, stanlist[S_NEW].msg)) {
- section = S_NEW;
- goto strtest;
+ 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, stanlist[S_OLD].msg)) {
- section = S_OLD;
-strtest: if (!*++argv) {
- fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[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;
}
- /*
- * This is really silly, but I wanted to put out rational
- * errors, not just "I couldn't find it." This if statement
- * knows an awful lot about what gets assigned to what in
- * the switch statement we just passed through. Sorry.
- */
- if (!manual(section, *argv))
- if (manpath == locpath)
- fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, stanlist[section].msg);
- else if (dirlist == sec1list)
- fprintf(stderr, "No entry for %s in the 1st section of the manual.\n", *argv);
- 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, stanlist[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 section number and a file name go through the directory
- * list and find a file that matches.
+ * 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; (++dir)->name;) {
- 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;
}
- else if (find(beg, stanlist[section].name, name))
- return(YES);
+ 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;
+ }
if (!end)
- return(NO);
+ return(res);
+ *end = ':';
}
/*NOTREACHED*/
}
/*
- * find --
- * given a directory path, a sub-directory and a file name,
- * see if a file exists in ${directory}/${dir}/{file name}
- * or in ${directory}/${dir}/${machine}/${file name}.
+ * 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);
}
/*
- * show --
- * display the file
+ * 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 out a usage message and die
+ * 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);
}