add manual pages; don't produce .o's unless necessary; add new labels;
[unix-history] / usr / src / usr.bin / man / man.c
index 827ff7c..c1bea03 100644 (file)
@@ -3,11 +3,16 @@
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms are permitted
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms are permitted
- * provided that this notice is preserved and that due credit is given
- * to the University of California at 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'' without express or implied warranty.
+ * 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
  */
 
 #ifndef lint
@@ -17,7 +22,7 @@ char copyright[] =
 #endif /* not lint */
 
 #ifndef lint
 #endif /* not lint */
 
 #ifndef lint
-static char sccsid[] = "@(#)man.c      5.12 (Berkeley) %G%";
+static char sccsid[] = "@(#)man.c      5.17 (Berkeley) %G%";
 #endif /* not lint */
 
 #include <sys/param.h>
 #endif /* not lint */
 
 #include <sys/param.h>
@@ -29,85 +34,72 @@ static char sccsid[] = "@(#)man.c   5.12 (Berkeley) %G%";
 #define        LOCAL_PATH      "/usr/local/man"
 #define        NEW_PATH        "/usr/new/man"
 
 #define        LOCAL_PATH      "/usr/local/man"
 #define        NEW_PATH        "/usr/new/man"
 
-#define        NO              0
-#define        YES             1
+#define        NO      0
+#define        YES     1
 
 
-typedef struct {
-       char    *name, *msg;
-} DIR;
-
-static int     nomore,                 /* copy file to stdout */
-               where;                  /* just tell me where */
-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 */
                *newpath,               /* new search path */
                *locpath,               /* local search path */
                *machine,               /* machine type */
                *manpath,               /* current search path */
                *newpath,               /* new search path */
-               *pager;                 /* requested pager */
+               *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;
 {
 
 main(argc, argv)
        int argc;
        register char **argv;
 {
-       char **arg_start, **arg, *getenv(), *malloc(), *strcpy();
+       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 */
                        break;
                case 'M':
                case 'P':               /* backward compatibility */
-                       if ((*argv)[2])
-                               defpath = *argv + 2;
-                       else {
-                               if (argc < 2) {
-                                       fprintf(stderr, "%s: missing path\n", *argv);
-                                       exit(1);
-                               }
-                               --argc;
-                               defpath = *++argv;
-                       }
+                       defpath = optarg;
+                       break;
+               case 'a':
+                       how |= ALL;
                        break;
                /*
                        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':
                 */
                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':
                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':
                /*
                 * Deliberately undocumented; really only useful when
                 * you're moving man pages around.  Not worth adding.
                 */
                case 'w':
-                       where = YES;
+                       how |= WHERE | ALL;
                        break;
                case '?':
                default:
                        break;
                case '?':
                default:
-                       fprintf(stderr, "man: illegal option -- %c\n", (*argv)[1]);
-                       goto usage;
+                       usage();
                }
                }
+       argv += optind;
 
 
-       if (!argc) {
-usage:         fputs("usage: man [-] [-M path] [section] title ...\n", stderr);
-               exit(1);
-       }
+       if (!*argv)
+               usage();
 
 
-       if (!nomore)
+       if (!(how & CAT))
                if (!isatty(1))
                if (!isatty(1))
-                       nomore = YES;
+                       how |= CAT;
                else if (pager = getenv("PAGER")) {
                        register char *p;
 
                else if (pager = getenv("PAGER")) {
                        register char *p;
 
@@ -120,17 +112,17 @@ usage:            fputs("usage: man [-] [-M path] [section] title ...\n", stderr);
                        if (p != pager)
                                ++p;
                        /* make sure it's "more", not "morex" */
                        if (p != pager)
                                ++p;
                        /* make sure it's "more", not "morex" */
-                       if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))) {
-                               p += 4;
+                       if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){
+                               char *opager = pager;
                                /*
                                /*
-                                * allocate for the rest of the PAGER
-                                * environment variable, a space, and the EOS.
+                                * allocate space to add the "-s"
                                 */
                                 */
-                               if (!(pager = malloc((u_int)(strlen(p) + sizeof(DEF_PAGER) + 1)))) {
+                               if (!(pager = malloc((u_int)(strlen(opager) 
+                                   + sizeof("-s") + 1)))) {
                                        fputs("man: out of space.\n", stderr);
                                        exit(1);
                                }
                                        fputs("man: out of space.\n", stderr);
                                        exit(1);
                                }
-                               (void)sprintf(pager, "%s %s", DEF_PAGER, p);
+                               (void)sprintf(pager, "%s %s", opager, "-s");
                        }
                }
                else
                        }
                }
                else
@@ -142,9 +134,15 @@ usage:             fputs("usage: man [-] [-M path] [section] title ...\n", stderr);
        locpath = LOCAL_PATH;
        newpath = NEW_PATH;
        man(argv);
        locpath = LOCAL_PATH;
        newpath = NEW_PATH;
        man(argv);
+       /* use system(3) in case someone's pager is "pager arg1 arg2" */
+       if (command)
+               (void)system(command);
        exit(0);
 }
 
        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,
 static DIR     list1[] = {             /* section one list */
        "cat1", "1st",          "cat8", "8th",          "cat6", "6th",
        "cat.old", "old",       NULL, NULL,
@@ -167,21 +165,22 @@ man(argv)
                section = NULL;
                switch(**argv) {
                case 'l':                               /* local */
                section = NULL;
                switch(**argv) {
                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)) {
                        for (p = *argv; isalpha(*p); ++p);
                        if (!strncmp(*argv, "l", p - *argv) ||
                            !strncmp(*argv, "local", p - *argv)) {
+                               ++argv;
                                manpath = locpath;
                                manpath = locpath;
-                               if (section = getsect(p))
-                                       goto argtest;
+                               section = getsect(p);
                        }
                        break;
                case 'n':                               /* new */
                        for (p = *argv; isalpha(*p); ++p);
                        if (!strncmp(*argv, "n", p - *argv) ||
                            !strncmp(*argv, "new", p - *argv)) {
                        }
                        break;
                case 'n':                               /* new */
                        for (p = *argv; isalpha(*p); ++p);
                        if (!strncmp(*argv, "n", p - *argv) ||
                            !strncmp(*argv, "new", p - *argv)) {
+                               ++argv;
                                manpath = newpath;
                                manpath = newpath;
-                               if (section = getsect(p))
-                                       goto argtest;
+                               section = getsect(p);
                        }
                        break;
                /*
                        }
                        break;
                /*
@@ -192,45 +191,53 @@ man(argv)
                        for (p = *argv; isalpha(*p); ++p);
                        if (!strncmp(*argv, "o", p - *argv) ||
                            !strncmp(*argv, "old", p - *argv)) {
                        for (p = *argv; isalpha(*p); ++p);
                        if (!strncmp(*argv, "o", p - *argv) ||
                            !strncmp(*argv, "old", p - *argv)) {
+                               ++argv;
                                list3[0] = list1[3];
                                section = list3;
                                list3[0] = list1[3];
                                section = list3;
-                               goto argtest;
                        }
                        break;
                case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8':
                        }
                        break;
                case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8':
-                       if (!(section = getsect(*argv)))
-                               break;
-argtest:               if (!*++argv) {
-                               fprintf(stderr, "man: what do you want from the %s section of the manual?\n", section->msg);
-                               exit(1);
+                       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);
                        }
                        }
+                       if (res || how&WHERE)
+                               continue;
                }
 
                }
 
-               res = section ? manual(section, *argv) :
-                   manual(list1, *argv) || manual(list2, *argv);
-               if (!res && !where)
-                       if (manpath == locpath)
-                               if (section)
-                                       fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, section->msg);
-                               else
-                                       fprintf(stderr, "No entry for %s in the local manual.\n", *argv);
-                       else if (manpath == newpath)
-                               if (section)
-                                       fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, section->msg);
-                               else
-                                       fprintf(stderr, "No entry for %s in the new manual.\n", *argv);
-                       else if (section)
-                               fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, section->msg);
-                       else
-                               fprintf(stderr, "No entry for %s in the manual.\n", *argv);
+               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);
        }
 }
 
 /*
  * manual --
        }
 }
 
 /*
  * 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)
  */
 static
 manual(section, name)
@@ -239,84 +246,98 @@ manual(section, name)
 {
        register char *beg, *end;
        register DIR *dp;
 {
        register char *beg, *end;
        register DIR *dp;
-       char *index();
+       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 (end = index(beg, ':'))
                        *end = '\0';
-               for (dp = section; dp->name; ++dp)
-                       if (find(beg, dp->name, name)) {
-                               if (end)
-                                       *end = ':';
-                               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;
+               }
                if (!end)
                if (!end)
-                       return(NO);
+                       return(res);
                *end = ':';
        }
        /*NOTREACHED*/
 }
 
 /*
                *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
  */
 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)) {
-               (void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name);
-               if (access(fname, R_OK))
-                       return(NO);
+       if (!(fd = open(fname, O_RDONLY, 0))) {
+               perror("man: open");
+               exit(1);
        }
        }
-       if (where)
-               printf("man: found in %s.\n", fname);
-       else
-               show(fname);
-       return(!where);
+       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);
+       }
+       (void)close(fd);
 }
 
 /*
 }
 
 /*
- * show --
- *     display the file
+ * add --
+ *     add a file name to the list for future paging
  */
 static
  */
 static
-show(fname)
+add(fname)
        char *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);
                }
                        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);
                }
                        exit(1);
                }
-               (void)close(fd);
-       }
-       else {
-               /*
-                * use system(3) in case someone's pager is
-                * "command arg1 arg2"
-                */
-               (void)sprintf(buf, "%s %s", pager, fname);
-               (void)system(buf);
+               cp = command + len;
        }
        }
+       *cp++ = ' ';
+       len += flen + 1;                        /* +1 = space */
+       (void)strcpy(cp, fname);
+       cp += flen;
 }
 
 /*
 }
 
 /*
@@ -381,3 +402,36 @@ getsect(s)
        }
        return((DIR *)NULL);
 }
        }
        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 [-] [-a] [-M path] [section] title ...\n", stderr);
+       exit(1);
+}