[BACK]Return to man.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / usr.bin / man

Annotation of src/usr.bin/man/man.c, Revision 1.67.2.1

1.67.2.1! martin      1: /*     $NetBSD$        */
1.7       tls         2:
1.1       cgd         3: /*
1.7       tls         4:  * Copyright (c) 1987, 1993, 1994, 1995
1.1       cgd         5:  *     The Regents of the University of California.  All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
1.30      agc        15:  * 3. Neither the name of the University nor the names of its contributors
1.1       cgd        16:  *    may be used to endorse or promote products derived from this software
                     17:  *    without specific prior written permission.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     20:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     21:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     22:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     23:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     24:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     25:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     26:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     27:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     28:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     29:  * SUCH DAMAGE.
                     30:  */
                     31:
1.9       mikel      32: #include <sys/cdefs.h>
                     33:
1.1       cgd        34: #ifndef lint
1.37      lukem      35: __COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994, 1995\
                     36:  The Regents of the University of California.  All rights reserved.");
1.1       cgd        37: #endif /* not lint */
                     38:
                     39: #ifndef lint
1.7       tls        40: #if 0
                     41: static char sccsid[] = "@(#)man.c      8.17 (Berkeley) 1/31/95";
                     42: #else
1.67.2.1! martin     43: __RCSID("$NetBSD$");
1.7       tls        44: #endif
1.1       cgd        45: #endif /* not lint */
                     46:
                     47: #include <sys/param.h>
                     48: #include <sys/queue.h>
1.42      joerg      49: #include <sys/stat.h>
1.22      tsutsui    50: #include <sys/utsname.h>
1.1       cgd        51:
                     52: #include <ctype.h>
                     53: #include <err.h>
                     54: #include <fcntl.h>
                     55: #include <fnmatch.h>
                     56: #include <glob.h>
                     57: #include <signal.h>
                     58: #include <stdio.h>
                     59: #include <stdlib.h>
                     60: #include <string.h>
                     61: #include <unistd.h>
1.36      lukem      62: #include <util.h>
1.40      christos   63: #include <locale.h>
1.1       cgd        64:
1.29      thorpej    65: #include "manconf.h"
1.1       cgd        66: #include "pathnames.h"
                     67:
1.33      chuck      68: #ifndef MAN_DEBUG
                     69: #define MAN_DEBUG 0            /* debug path output */
                     70: #endif
1.1       cgd        71:
1.33      chuck      72: /*
                     73:  * manstate: structure collecting the current global state so we can
                     74:  * easily identify it and pass it to helper functions in one arg.
                     75:  */
                     76: struct manstate {
                     77:        /* command line flags */
                     78:        int all;                /* -a: show all matches rather than first */
                     79:        int cat;                /* -c: do not use a pager */
                     80:        char *conffile;         /* -C: use alternate config file */
                     81:        int how;                /* -h: show SYNOPSIS only */
                     82:        char *manpath;          /* -M: alternate MANPATH */
                     83:        char *addpath;          /* -m: add these dirs to front of manpath */
                     84:        char *pathsearch;       /* -S: path of man must contain this string */
                     85:        char *sectionname;      /* -s: limit search to a given man section */
                     86:        int where;              /* -w: just show paths of all matching files */
1.42      joerg      87:        int getpath;    /* -p: print the path of directories containing man pages */
                     88:
1.33      chuck      89:        /* important tags from the config file */
                     90:        TAG *defaultpath;       /* _default: default MANPATH */
                     91:        TAG *subdirs;           /* _subdir: default subdir search list */
                     92:        TAG *suffixlist;        /* _suffix: for files that can be cat()'d */
                     93:        TAG *buildlist;         /* _build: for files that must be built */
                     94:
                     95:        /* tags for internal use */
                     96:        TAG *intmp;             /* _intmp: tmp files we must cleanup */
                     97:        TAG *missinglist;       /* _missing: pages we couldn't find */
                     98:        TAG *mymanpath;         /* _new_path: final version of MANPATH */
                     99:        TAG *section;           /* <sec>: tag for m.sectionname */
                    100:
                    101:        /* other misc stuff */
                    102:        const char *pager;      /* pager to use */
1.45      uwe       103:        size_t pagerlen;        /* length of the above */
1.40      christos  104:        const char *machine;    /* machine */
                    105:        const char *machclass;  /* machine class */
1.33      chuck     106: };
                    107:
                    108: /*
                    109:  * prototypes
                    110:  */
1.55      uwe       111: static void     build_page(const char *, char **, struct manstate *);
                    112: static void     cat(const char *);
1.33      chuck     113: static const char      *check_pager(const char *);
                    114: static int      cleanup(void);
1.55      uwe       115: static void     how(const char *);
1.56      joerg     116: static void     jump(char **, const char *, const char *) __dead;
1.33      chuck     117: static int      manual(char *, struct manstate *, glob_t *);
1.56      joerg     118: static void     onsig(int) __dead;
                    119: static void     usage(void) __dead;
1.40      christos  120: static void     addpath(struct manstate *, const char *, size_t, const char *);
                    121: static const char *getclass(const char *);
1.42      joerg     122: static void printmanpath(struct manstate *);
1.1       cgd       123:
1.33      chuck     124: /*
                    125:  * main function
                    126:  */
1.1       cgd       127: int
1.33      chuck     128: main(int argc, char **argv)
1.1       cgd       129: {
1.48      christos  130:        static struct manstate m;
1.67      mrg       131:        struct utsname utsname;
1.33      chuck     132:        int ch, abs_section, found;
                    133:        ENTRY *esubd, *epath;
1.40      christos  134:        char *p, **ap, *cmd;
1.33      chuck     135:        size_t len;
1.1       cgd       136:        glob_t pg;
                    137:
1.40      christos  138:        setprogname(argv[0]);
                    139:        setlocale(LC_ALL, "");
1.33      chuck     140:        /*
                    141:         * parse command line...
                    142:         */
1.42      joerg     143:        while ((ch = getopt(argc, argv, "-aC:cfhkM:m:P:ps:S:w")) != -1)
1.1       cgd       144:                switch (ch) {
                    145:                case 'a':
1.33      chuck     146:                        m.all = 1;
1.1       cgd       147:                        break;
                    148:                case 'C':
1.33      chuck     149:                        m.conffile = optarg;
1.1       cgd       150:                        break;
                    151:                case 'c':
1.33      chuck     152:                case '-':       /* XXX: '-' is a deprecated version of '-c' */
                    153:                        m.cat = 1;
1.1       cgd       154:                        break;
                    155:                case 'h':
1.33      chuck     156:                        m.how = 1;
1.1       cgd       157:                        break;
                    158:                case 'm':
1.33      chuck     159:                        m.addpath = optarg;
1.1       cgd       160:                        break;
                    161:                case 'M':
1.33      chuck     162:                case 'P':       /* -P for backward compatibility */
1.63      abhinav   163:                        if ((m.manpath = strdup(optarg)) == NULL)
                    164:                                err(EXIT_FAILURE, "malloc failed");
1.1       cgd       165:                        break;
1.42      joerg     166:                case 'p':
                    167:                        m.getpath = 1;
                    168:                        break;
1.1       cgd       169:                /*
                    170:                 * The -f and -k options are backward compatible,
                    171:                 * undocumented ways of calling whatis(1) and apropos(1).
                    172:                 */
                    173:                case 'f':
                    174:                        jump(argv, "-f", "whatis");
                    175:                        /* NOTREACHED */
                    176:                case 'k':
                    177:                        jump(argv, "-k", "apropos");
                    178:                        /* NOTREACHED */
1.25      simonb    179:                case 's':
1.33      chuck     180:                        if (m.sectionname != NULL)
1.25      simonb    181:                                usage();
1.33      chuck     182:                        m.sectionname = optarg;
1.25      simonb    183:                        break;
1.23      jdolecek  184:                case 'S':
1.33      chuck     185:                        m.pathsearch = optarg;
1.23      jdolecek  186:                        break;
1.1       cgd       187:                case 'w':
1.33      chuck     188:                        m.all = m.where = 1;
1.1       cgd       189:                        break;
                    190:                case '?':
                    191:                default:
                    192:                        usage();
                    193:                }
                    194:        argc -= optind;
                    195:        argv += optind;
                    196:
1.42      joerg     197:        if (!m.getpath && !argc)
1.1       cgd       198:                usage();
                    199:
1.33      chuck     200:        /*
                    201:         * read the configuration file and collect any other information
                    202:         * we will need (machine type, pager, section [if specified
                    203:         * without '-s'], and MANPATH through the environment).
                    204:         */
                    205:        config(m.conffile);    /* exits on error ... */
1.1       cgd       206:
1.40      christos  207:        if ((m.machine = getenv("MACHINE")) == NULL) {
                    208:                if (uname(&utsname) == -1)
                    209:                        err(EXIT_FAILURE, "uname");
                    210:                m.machine = utsname.machine;
1.22      tsutsui   211:        }
1.1       cgd       212:
1.40      christos  213:        m.machclass = getclass(m.machine);
                    214:
1.33      chuck     215:        if (!m.cat && !m.how && !m.where) {  /* if we need a pager ... */
                    216:                if (!isatty(STDOUT_FILENO)) {
                    217:                        m.cat = 1;
                    218:                } else {
                    219:                        if ((m.pager = getenv("PAGER")) != NULL &&
                    220:                            m.pager[0] != '\0')
                    221:                                m.pager = check_pager(m.pager);
                    222:                        else
                    223:                                m.pager = _PATH_PAGER;
                    224:                        m.pagerlen = strlen(m.pager);
                    225:                }
                    226:        }
                    227:
                    228:        /* do we need to set m.section to a non-null value? */
                    229:        if (m.sectionname) {
1.1       cgd       230:
1.33      chuck     231:                m.section = gettag(m.sectionname, 0); /* -s must be a section */
                    232:                if (m.section == NULL)
1.38      cegger    233:                        errx(EXIT_FAILURE, "unknown section: %s", m.sectionname);
1.23      jdolecek  234:
1.33      chuck     235:        } else if (argc > 1) {
                    236:
                    237:                m.section = gettag(*argv, 0);  /* might be a section? */
                    238:                if (m.section) {
1.25      simonb    239:                        argv++;
                    240:                        argc--;
                    241:                }
1.1       cgd       242:
1.33      chuck     243:        }
                    244:
                    245:        if (m.manpath == NULL)
                    246:                m.manpath = getenv("MANPATH"); /* note: -M overrides getenv */
1.23      jdolecek  247:
                    248:
                    249:        /*
1.33      chuck     250:         * get default values from config file, plus create the tags we
                    251:         * use for keeping internal state.  make sure all our mallocs
                    252:         * go through.
                    253:         */
                    254:        /* from cfg file */
                    255:        m.defaultpath = gettag("_default", 1);
                    256:        m.subdirs = gettag("_subdir", 1);
                    257:        m.suffixlist = gettag("_suffix", 1);
                    258:        m.buildlist = gettag("_build", 1);
                    259:        /* internal use */
                    260:        m.mymanpath = gettag("_new_path", 1);
                    261:        m.missinglist = gettag("_missing", 1);
                    262:        m.intmp = gettag("_intmp", 1);
                    263:        if (!m.defaultpath || !m.subdirs || !m.suffixlist || !m.buildlist ||
                    264:            !m.mymanpath || !m.missinglist || !m.intmp)
1.38      cegger    265:                errx(EXIT_FAILURE, "malloc failed");
1.33      chuck     266:
                    267:        /*
                    268:         * are we using a section whose elements are all absolute paths?
                    269:         * (we only need to look at the first entry on the section list,
                    270:         * as config() will ensure that any additional entries will match
                    271:         * the first one.)
                    272:         */
                    273:        abs_section = (m.section != NULL &&
                    274:                !TAILQ_EMPTY(&m.section->entrylist) &&
                    275:                        *(TAILQ_FIRST(&m.section->entrylist)->s) == '/');
                    276:
                    277:        /*
                    278:         * now that we have all the data we need, we must determine the
                    279:         * manpath we are going to use to find the requested entries using
                    280:         * the following steps...
                    281:         *
                    282:         * [1] if the user specified a section and that section's elements
                    283:         *     from the config file are all absolute paths, then we override
                    284:         *     defaultpath and -M/MANPATH with the section's absolute paths.
1.23      jdolecek  285:         */
                    286:        if (abs_section) {
1.33      chuck     287:                m.manpath = NULL;               /* ignore -M/MANPATH */
                    288:                m.defaultpath = m.section;      /* overwrite _default path */
                    289:                m.section = NULL;               /* promoted to defaultpath */
1.1       cgd       290:        }
                    291:
1.23      jdolecek  292:        /*
1.33      chuck     293:         * [2] section can now only be non-null if the user asked for
                    294:         *     a section and that section's elements did not have
1.65      abhinav   295:         *     absolute paths.  in this case we use the section's
1.33      chuck     296:         *     elements to override _subdir from the config file.
                    297:         *
                    298:         * after this step, we are done processing "m.section"...
1.23      jdolecek  299:         */
1.33      chuck     300:        if (m.section)
                    301:                m.subdirs = m.section;
1.23      jdolecek  302:
1.1       cgd       303:        /*
1.33      chuck     304:         * [3] we need to setup the path we want to use (m.mymanpath).
                    305:         *     if the user gave us a path (m.manpath) use it, otherwise
                    306:         *     go with the default.   in either case we need to append
                    307:         *     the subdir and machine spec to each element of the path.
1.23      jdolecek  308:         *
1.33      chuck     309:         *     for absolute section paths that come from the config file,
                    310:         *     we only append the subdir spec if the path ends in
                    311:         *     a '/' --- elements that do not end in '/' are assumed to
                    312:         *     not have subdirectories.  this is mainly for backward compat,
                    313:         *     but it allows non-subdir configs like:
                    314:         *      sect3       /usr/share/man/{old/,}cat3
                    315:         *      doc         /usr/{pkg,share}/doc/{sendmail/op,sendmail/intro}
                    316:         *
                    317:         *     note that we try and be careful to not put double slashes
                    318:         *     in the path (e.g. we want /usr/share/man/man1, not
                    319:         *     /usr/share/man//man1) because "more" will put the filename
                    320:         *     we generate in its prompt and the double slashes look ugly.
                    321:         */
                    322:        if (m.manpath) {
                    323:
                    324:                /* note: strtok is going to destroy m.manpath */
                    325:                for (p = strtok(m.manpath, ":") ; p ; p = strtok(NULL, ":")) {
                    326:                        len = strlen(p);
                    327:                        if (len < 1)
                    328:                                continue;
1.40      christos  329:                        TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q)
                    330:                                addpath(&m, p, len, esubd->s);
1.1       cgd       331:                }
1.33      chuck     332:
1.23      jdolecek  333:        } else {
                    334:
1.33      chuck     335:                TAILQ_FOREACH(epath, &m.defaultpath->entrylist, q) {
1.23      jdolecek  336:                        /* handle trailing "/" magic here ... */
1.40      christos  337:                        if (abs_section && epath->s[epath->len - 1] != '/') {
                    338:                                addpath(&m, "", 1, epath->s);
1.1       cgd       339:                                continue;
                    340:                        }
1.23      jdolecek  341:
1.40      christos  342:                        TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q)
                    343:                                addpath(&m, epath->s, epath->len, esubd->s);
1.23      jdolecek  344:                }
                    345:
1.33      chuck     346:        }
1.23      jdolecek  347:
                    348:        /*
1.33      chuck     349:         * [4] finally, prepend the "-m" m.addpath to mymanpath if it
                    350:         *     was specified.   subdirs and machine are always applied to
                    351:         *     m.addpath.
                    352:         */
                    353:        if (m.addpath) {
                    354:
                    355:                /* note: strtok is going to destroy m.addpath */
                    356:                for (p = strtok(m.addpath, ":") ; p ; p = strtok(NULL, ":")) {
                    357:                        len = strlen(p);
                    358:                        if (len < 1)
                    359:                                continue;
1.40      christos  360:                        TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q)
                    361:                                addpath(&m, p, len, esubd->s);
1.1       cgd       362:                }
1.33      chuck     363:
1.1       cgd       364:        }
                    365:
1.67.2.1! martin    366:        if (m.getpath) {
1.42      joerg     367:                printmanpath(&m);
1.67.2.1! martin    368:                exit(cleanup());
        !           369:        }
1.42      joerg     370:
1.33      chuck     371:        /*
                    372:         * now m.mymanpath is complete!
                    373:         */
1.35      jwise     374: #if MAN_DEBUG
                    375:        printf("mymanpath:\n");
                    376:        TAILQ_FOREACH(epath, &m.mymanpath->entrylist, q) {
                    377:                printf("\t%s\n", epath->s);
1.33      chuck     378:        }
1.35      jwise     379: #endif
1.23      jdolecek  380:
1.1       cgd       381:        /*
1.33      chuck     382:         * start searching for matching files and format them if necessary.
                    383:         * setup an interrupt handler so that we can ensure that temporary
                    384:         * files go away.
1.1       cgd       385:         */
                    386:        (void)signal(SIGINT, onsig);
                    387:        (void)signal(SIGHUP, onsig);
1.17      itohy     388:        (void)signal(SIGPIPE, onsig);
1.1       cgd       389:
                    390:        memset(&pg, 0, sizeof(pg));
                    391:        for (found = 0; *argv; ++argv)
1.33      chuck     392:                if (manual(*argv, &m, &pg)) {
1.1       cgd       393:                        found = 1;
1.33      chuck     394:                }
1.1       cgd       395:
1.33      chuck     396:        /* if nothing found, we're done. */
1.1       cgd       397:        if (!found) {
                    398:                (void)cleanup();
1.38      cegger    399:                exit(EXIT_FAILURE);
1.1       cgd       400:        }
                    401:
1.33      chuck     402:        /*
                    403:         * handle the simple display cases first (m.cat, m.how, m.where)
                    404:         */
                    405:        if (m.cat) {
1.1       cgd       406:                for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
                    407:                        if (**ap == '\0')
                    408:                                continue;
                    409:                        cat(*ap);
                    410:                }
1.38      cegger    411:                exit(cleanup());
1.1       cgd       412:        }
1.33      chuck     413:        if (m.how) {
1.1       cgd       414:                for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
                    415:                        if (**ap == '\0')
                    416:                                continue;
                    417:                        how(*ap);
                    418:                }
                    419:                exit(cleanup());
                    420:        }
1.33      chuck     421:        if (m.where) {
1.1       cgd       422:                for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
                    423:                        if (**ap == '\0')
                    424:                                continue;
                    425:                        (void)printf("%s\n", *ap);
                    426:                }
                    427:                exit(cleanup());
                    428:        }
                    429:
                    430:        /*
1.33      chuck     431:         * normal case - we display things in a single command, so
1.65      abhinav   432:         * build a list of things to display.  first compute total
1.33      chuck     433:         * length of buffer we will need so we can malloc it.
1.1       cgd       434:         */
1.33      chuck     435:        for (ap = pg.gl_pathv, len = m.pagerlen + 1; *ap != NULL; ++ap) {
1.1       cgd       436:                if (**ap == '\0')
                    437:                        continue;
                    438:                len += strlen(*ap) + 1;
                    439:        }
                    440:        if ((cmd = malloc(len)) == NULL) {
1.9       mikel     441:                warn("malloc");
1.1       cgd       442:                (void)cleanup();
1.38      cegger    443:                exit(EXIT_FAILURE);
1.1       cgd       444:        }
1.33      chuck     445:
                    446:        /* now build the command string... */
1.1       cgd       447:        p = cmd;
1.33      chuck     448:        len = m.pagerlen;
                    449:        memcpy(p, m.pager, len);
1.1       cgd       450:        p += len;
                    451:        *p++ = ' ';
                    452:        for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
                    453:                if (**ap == '\0')
                    454:                        continue;
                    455:                len = strlen(*ap);
1.33      chuck     456:                memcpy(p, *ap, len);
1.1       cgd       457:                p += len;
                    458:                *p++ = ' ';
                    459:        }
1.14      wsanchez  460:        *--p = '\0';
1.1       cgd       461:
                    462:        /* Use system(3) in case someone's pager is "pager arg1 arg2". */
                    463:        (void)system(cmd);
                    464:
                    465:        exit(cleanup());
                    466: }
                    467:
1.39      cegger    468: static int
1.60      christos  469: manual_find_literalfile(struct manstate *mp, char **pv)
                    470: {
                    471:        ENTRY *suffix;
                    472:        int found;
                    473:        char buf[MAXPATHLEN];
                    474:        const char *p;
                    475:        int suflen;
                    476:
                    477:        found = 0;
                    478:
                    479:        /*
                    480:         * Expand both '*' and suffix to force an actual
                    481:         * match via fnmatch(3). Since the only match in pg
                    482:         * is the literal file, the match is genuine.
                    483:         */
                    484:
                    485:        TAILQ_FOREACH(suffix, &mp->buildlist->entrylist, q) {
                    486:                for (p = suffix->s, suflen = 0;
                    487:                    *p != '\0' && !isspace((unsigned char)*p);
                    488:                    ++p)
                    489:                        ++suflen;
                    490:                if (*p == '\0')
                    491:                        continue;
                    492:
                    493:                (void)snprintf(buf, sizeof(buf), "*%.*s", suflen, suffix->s);
                    494:
                    495:                if (!fnmatch(buf, *pv, 0)) {
                    496:                        if (!mp->where)
                    497:                                build_page(p + 1, pv, mp);
                    498:                        found = 1;
                    499:                        break;
                    500:                }
                    501:        }
                    502:
                    503:        return found;
                    504: }
                    505:
                    506: static int
1.52      uwe       507: manual_find_buildkeyword(const char *prefix, const char *escpage,
1.60      christos  508:     struct manstate *mp, char **pv)
1.39      cegger    509: {
                    510:        ENTRY *suffix;
                    511:        int found;
1.55      uwe       512:        char buf[MAXPATHLEN];
                    513:        const char *p;
1.53      uwe       514:        int suflen;
1.39      cegger    515:
                    516:        found = 0;
1.54      uwe       517:        /* Try the _build keywords next. */
1.39      cegger    518:        TAILQ_FOREACH(suffix, &mp->buildlist->entrylist, q) {
1.53      uwe       519:                for (p = suffix->s, suflen = 0;
1.39      cegger    520:                    *p != '\0' && !isspace((unsigned char)*p);
                    521:                    ++p)
1.53      uwe       522:                        ++suflen;
1.39      cegger    523:                if (*p == '\0')
                    524:                        continue;
                    525:
1.53      uwe       526:                (void)snprintf(buf, sizeof(buf), "%s%s%.*s",
1.60      christos  527:                    prefix, escpage, suflen, suffix->s);
                    528:                if (!fnmatch(buf, *pv, 0)) {
1.39      cegger    529:                        if (!mp->where)
1.60      christos  530:                                build_page(p + 1, pv, mp);
1.39      cegger    531:                        found = 1;
                    532:                        break;
1.53      uwe       533:                }
1.39      cegger    534:        }
                    535:
                    536:        return found;
                    537: }
                    538:
1.1       cgd       539: /*
                    540:  * manual --
                    541:  *     Search the manuals for the pages.
                    542:  */
                    543: static int
1.33      chuck     544: manual(char *page, struct manstate *mp, glob_t *pg)
1.1       cgd       545: {
1.33      chuck     546:        ENTRY *suffix, *mdir;
1.32      christos  547:        int anyfound, error, found;
                    548:        size_t cnt;
1.27      groo      549:        char *p, buf[MAXPATHLEN], *escpage, *eptr;
                    550:        static const char escglob[] = "\\~?*{}[]";
1.1       cgd       551:
                    552:        anyfound = 0;
                    553:
1.27      groo      554:        /*
                    555:         * Fixup page which may contain glob(3) special characters, e.g.
                    556:         * the famous "No man page for [" FAQ.
                    557:         */
                    558:        if ((escpage = malloc((2 * strlen(page)) + 1)) == NULL) {
                    559:                warn("malloc");
                    560:                (void)cleanup();
1.38      cegger    561:                exit(EXIT_FAILURE);
1.27      groo      562:        }
                    563:
                    564:        p = page;
                    565:        eptr = escpage;
                    566:
                    567:        while (*p) {
                    568:                if (strchr(escglob, *p) != NULL) {
                    569:                        *eptr++ = '\\';
                    570:                        *eptr++ = *p++;
                    571:                } else
                    572:                        *eptr++ = *p++;
                    573:        }
                    574:
                    575:        *eptr = '\0';
                    576:
1.39      cegger    577:        /*
1.62      apb       578:         * If 'page' is given with an absolute path,
                    579:         * or a relative path explicitly beginning with "./"
                    580:         * or "../", then interpret it as a file specification.
1.39      cegger    581:         */
1.62      apb       582:        if ((page[0] == '/')
                    583:            || (page[0] == '.' && page[1] == '/')
                    584:            || (page[0] == '.' && page[1] == '.' && page[2] == '/')
                    585:            ) {
1.39      cegger    586:                /* check if file actually exists */
                    587:                (void)strlcpy(buf, escpage, sizeof(buf));
                    588:                error = glob(buf, GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT, NULL, pg);
                    589:                if (error != 0) {
                    590:                        if (error == GLOB_NOMATCH) {
                    591:                                goto notfound;
                    592:                        } else {
                    593:                                errx(EXIT_FAILURE, "glob failed");
                    594:                        }
                    595:                }
                    596:
                    597:                if (pg->gl_matchc == 0)
                    598:                        goto notfound;
                    599:
1.60      christos  600:                /* literal file only yields one match */
                    601:                cnt = pg->gl_pathc - pg->gl_matchc;
                    602:
                    603:                if (manual_find_literalfile(mp, &pg->gl_pathv[cnt])) {
                    604:                        anyfound = 1;
                    605:                } else {
1.39      cegger    606:                        /* It's not a man page, forget about it. */
1.49      christos  607:                        *pg->gl_pathv[cnt] = '\0';
1.39      cegger    608:                }
                    609:
                    610:   notfound:
                    611:                if (!anyfound) {
                    612:                        if (addentry(mp->missinglist, page, 0) < 0) {
                    613:                                warn("malloc");
                    614:                                (void)cleanup();
                    615:                                exit(EXIT_FAILURE);
                    616:                        }
                    617:                }
                    618:                free(escpage);
                    619:                return anyfound;
                    620:        }
                    621:
1.33      chuck     622:        /* For each man directory in mymanpath ... */
                    623:        TAILQ_FOREACH(mdir, &mp->mymanpath->entrylist, q) {
                    624:
                    625:                /*
                    626:                 * use glob(3) to look in the filesystem for matching files.
                    627:                 * match any suffix here, as we will check that later.
                    628:                 */
                    629:                (void)snprintf(buf, sizeof(buf), "%s/%s.*", mdir->s, escpage);
1.12      kleink    630:                if ((error = glob(buf,
                    631:                    GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT, NULL, pg)) != 0) {
                    632:                        if (error == GLOB_NOMATCH)
                    633:                                continue;
                    634:                        else {
                    635:                                warn("globbing");
                    636:                                (void)cleanup();
1.38      cegger    637:                                exit(EXIT_FAILURE);
1.12      kleink    638:                        }
1.1       cgd       639:                }
                    640:                if (pg->gl_matchc == 0)
                    641:                        continue;
                    642:
1.33      chuck     643:                /*
                    644:                 * start going through the matches glob(3) just found and
                    645:                 * use m.pathsearch (if present) to filter out pages we
                    646:                 * don't want.  then verify the suffix is valid, and build
                    647:                 * the page if we have a _build suffix.
                    648:                 */
1.1       cgd       649:                for (cnt = pg->gl_pathc - pg->gl_matchc;
                    650:                    cnt < pg->gl_pathc; ++cnt) {
                    651:
1.33      chuck     652:                        /* filter on directory path name */
                    653:                        if (mp->pathsearch) {
                    654:                                p = strstr(pg->gl_pathv[cnt], mp->pathsearch);
1.23      jdolecek  655:                                if (!p || strchr(p, '/') == NULL) {
1.49      christos  656:                                        *pg->gl_pathv[cnt] = '\0'; /* zap! */
1.23      jdolecek  657:                                        continue;
                    658:                                }
                    659:                        }
                    660:
1.1       cgd       661:                        /*
1.54      uwe       662:                         * Try the _suffix keywords first.
1.1       cgd       663:                         *
                    664:                         * XXX
1.54      uwe       665:                         * Older versions of man.conf didn't have the _suffix
                    666:                         * keywords, it was assumed that everything was a .0.
1.1       cgd       667:                         * We just test for .0 first, it's fast and probably
                    668:                         * going to hit.
                    669:                         */
1.27      groo      670:                        (void)snprintf(buf, sizeof(buf), "*/%s.0", escpage);
1.1       cgd       671:                        if (!fnmatch(buf, pg->gl_pathv[cnt], 0))
1.3       cgd       672:                                goto next;
1.1       cgd       673:
1.28      lukem     674:                        found = 0;
1.33      chuck     675:                        TAILQ_FOREACH(suffix, &mp->suffixlist->entrylist, q) {
1.1       cgd       676:                                (void)snprintf(buf,
1.27      groo      677:                                     sizeof(buf), "*/%s%s", escpage,
1.33      chuck     678:                                     suffix->s);
1.1       cgd       679:                                if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
                    680:                                        found = 1;
                    681:                                        break;
                    682:                                }
                    683:                        }
1.3       cgd       684:                        if (found)
                    685:                                goto next;
1.1       cgd       686:
1.54      uwe       687:                        /* Try the _build keywords next. */
1.52      uwe       688:                        found = manual_find_buildkeyword("*/", escpage,
1.60      christos  689:                                mp, &pg->gl_pathv[cnt]);
1.1       cgd       690:                        if (found) {
1.3       cgd       691: next:                          anyfound = 1;
1.33      chuck     692:                                if (!mp->all) {
1.3       cgd       693:                                        /* Delete any other matches. */
                    694:                                        while (++cnt< pg->gl_pathc)
1.49      christos  695:                                                *pg->gl_pathv[cnt] = '\0';
1.1       cgd       696:                                        break;
1.3       cgd       697:                                }
1.1       cgd       698:                                continue;
                    699:                        }
                    700:
                    701:                        /* It's not a man page, forget about it. */
1.49      christos  702:                        *pg->gl_pathv[cnt] = '\0';
1.1       cgd       703:                }
                    704:
1.33      chuck     705:                if (anyfound && !mp->all)
1.1       cgd       706:                        break;
                    707:        }
                    708:
                    709:        /* If not found, enter onto the missing list. */
                    710:        if (!anyfound) {
1.33      chuck     711:                if (addentry(mp->missinglist, page, 0) < 0) {
1.9       mikel     712:                        warn("malloc");
1.1       cgd       713:                        (void)cleanup();
1.38      cegger    714:                        exit(EXIT_FAILURE);
1.1       cgd       715:                }
                    716:        }
1.27      groo      717:
                    718:        free(escpage);
1.38      cegger    719:        return anyfound;
1.1       cgd       720: }
                    721:
1.61      uwe       722: /*
                    723:  * A do-nothing counterpart to fmtcheck(3) that only supplies the
                    724:  * __format_arg marker.  Actual fmtcheck(3) call is done once in
                    725:  * config().
                    726:  */
                    727: __always_inline __format_arg(2)
                    728: static inline const char *
                    729: fmtcheck_ok(const char *userfmt, const char *template)
                    730: {
                    731:        return userfmt;
                    732: }
                    733:
1.1       cgd       734: /*
                    735:  * build_page --
                    736:  *     Build a man page for display.
                    737:  */
                    738: static void
1.55      uwe       739: build_page(const char *fmt, char **pathp, struct manstate *mp)
1.1       cgd       740: {
                    741:        static int warned;
1.48      christos  742:        int olddir, fd, n;
                    743:        size_t tmpdirlen;
1.4       jtc       744:        char *p, *b;
1.19      kleink    745:        char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[MAXPATHLEN];
                    746:        const char *tmpdir;
1.1       cgd       747:
                    748:        /* Let the user know this may take awhile. */
                    749:        if (!warned) {
                    750:                warned = 1;
                    751:                warnx("Formatting manual page...");
                    752:        }
                    753:
1.7       tls       754:        /*
                    755:         * Historically man chdir'd to the root of the man tree.
                    756:         * This was used in man pages that contained relative ".so"
                    757:         * directives (including other man pages for command aliases etc.)
                    758:         * It even went one step farther, by examining the first line
                    759:         * of the man page and parsing the .so filename so it would
                    760:         * make hard(?) links to the cat'ted man pages for space savings.
                    761:         * (We don't do that here, but we could).
                    762:         */
                    763:
                    764:        /* copy and find the end */
                    765:        for (b = buf, p = *pathp; (*b++ = *p++) != '\0';)
                    766:                continue;
                    767:
1.33      chuck     768:        /*
                    769:         * skip the last two path components, page name and man[n] ...
                    770:         * (e.g. buf will be "/usr/share/man" and p will be "man1/man.1")
                    771:         * we also save a pointer to our current directory so that we
                    772:         * can fchdir() back to it.  this allows relative MANDIR paths
                    773:         * to work with multiple man pages... e.g. consider:
                    774:         *      cd /usr/share && man -M ./man cat ls
                    775:         * when no "cat1" subdir files are present.
                    776:         */
                    777:        olddir = -1;
1.10      tv        778:        for (--b, --p, n = 2; b != buf; b--, p--)
                    779:                if (*b == '/')
                    780:                        if (--n == 0) {
                    781:                                *b = '\0';
1.33      chuck     782:                                olddir = open(".", O_RDONLY);
1.10      tv        783:                                (void) chdir(buf);
                    784:                                p++;
                    785:                                break;
                    786:                        }
1.4       jtc       787:
                    788:
1.54      uwe       789:        /* advance fmt past the suffix spec to the printf format string */
1.15      christos  790:        for (; *fmt && isspace((unsigned char)*fmt); ++fmt)
                    791:                continue;
1.1       cgd       792:
                    793:        /*
                    794:         * Get a temporary file and build a version of the file
                    795:         * to display.  Replace the old file name with the new one.
                    796:         */
1.19      kleink    797:        if ((tmpdir = getenv("TMPDIR")) == NULL)
                    798:                tmpdir = _PATH_TMP;
1.33      chuck     799:        tmpdirlen = strlen(tmpdir);
                    800:        (void)snprintf(tpath, sizeof (tpath), "%s%s%s", tmpdir,
1.51      uwe       801:            (tmpdirlen > 0 && tmpdir[tmpdirlen-1] == '/') ? "" : "/", TMPFILE);
1.1       cgd       802:        if ((fd = mkstemp(tpath)) == -1) {
                    803:                warn("%s", tpath);
                    804:                (void)cleanup();
1.38      cegger    805:                exit(EXIT_FAILURE);
1.1       cgd       806:        }
                    807:        (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath);
1.61      uwe       808:        (void)snprintf(cmd, sizeof(cmd), fmtcheck_ok(buf, "%s"), p);
1.1       cgd       809:        (void)system(cmd);
                    810:        (void)close(fd);
                    811:        if ((*pathp = strdup(tpath)) == NULL) {
1.9       mikel     812:                warn("malloc");
1.1       cgd       813:                (void)cleanup();
1.38      cegger    814:                exit(EXIT_FAILURE);
1.1       cgd       815:        }
                    816:
                    817:        /* Link the built file into the remove-when-done list. */
1.33      chuck     818:        if (addentry(mp->intmp, *pathp, 0) < 0) {
1.9       mikel     819:                warn("malloc");
1.1       cgd       820:                (void)cleanup();
1.38      cegger    821:                exit(EXIT_FAILURE);
1.1       cgd       822:        }
1.33      chuck     823:
                    824:        /* restore old directory so relative manpaths still work */
                    825:        if (olddir != -1) {
                    826:                fchdir(olddir);
                    827:                close(olddir);
                    828:        }
1.1       cgd       829: }
                    830:
                    831: /*
                    832:  * how --
                    833:  *     display how information
                    834:  */
                    835: static void
1.55      uwe       836: how(const char *fname)
1.1       cgd       837: {
                    838:        FILE *fp;
                    839:
                    840:        int lcnt, print;
1.55      uwe       841:        char buf[256];
                    842:        const char *p;
1.1       cgd       843:
                    844:        if (!(fp = fopen(fname, "r"))) {
                    845:                warn("%s", fname);
                    846:                (void)cleanup();
1.38      cegger    847:                exit(EXIT_FAILURE);
1.1       cgd       848:        }
                    849: #define        S1      "SYNOPSIS"
                    850: #define        S2      "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"
                    851: #define        D1      "DESCRIPTION"
                    852: #define        D2      "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN"
                    853:        for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) {
                    854:                if (!strncmp(buf, S1, sizeof(S1) - 1) ||
                    855:                    !strncmp(buf, S2, sizeof(S2) - 1)) {
                    856:                        print = 1;
                    857:                        continue;
                    858:                } else if (!strncmp(buf, D1, sizeof(D1) - 1) ||
1.34      christos  859:                    !strncmp(buf, D2, sizeof(D2) - 1)) {
                    860:                        if (fp)
                    861:                                (void)fclose(fp);
1.1       cgd       862:                        return;
1.34      christos  863:                }
1.1       cgd       864:                if (!print)
                    865:                        continue;
                    866:                if (*buf == '\n')
                    867:                        ++lcnt;
                    868:                else {
                    869:                        for(; lcnt; --lcnt)
                    870:                                (void)putchar('\n');
1.15      christos  871:                        for (p = buf; isspace((unsigned char)*p); ++p)
                    872:                                continue;
1.1       cgd       873:                        (void)fputs(p, stdout);
                    874:                }
                    875:        }
                    876:        (void)fclose(fp);
                    877: }
                    878:
                    879: /*
                    880:  * cat --
                    881:  *     cat out the file
                    882:  */
                    883: static void
1.55      uwe       884: cat(const char *fname)
1.1       cgd       885: {
1.48      christos  886:        int fd;
                    887:        ssize_t n;
1.1       cgd       888:        char buf[2048];
                    889:
                    890:        if ((fd = open(fname, O_RDONLY, 0)) < 0) {
                    891:                warn("%s", fname);
                    892:                (void)cleanup();
1.38      cegger    893:                exit(EXIT_FAILURE);
1.1       cgd       894:        }
                    895:        while ((n = read(fd, buf, sizeof(buf))) > 0)
1.48      christos  896:                if (write(STDOUT_FILENO, buf, (size_t)n) != n) {
1.1       cgd       897:                        warn("write");
                    898:                        (void)cleanup();
1.38      cegger    899:                        exit(EXIT_FAILURE);
1.1       cgd       900:                }
                    901:        if (n == -1) {
                    902:                warn("read");
                    903:                (void)cleanup();
1.38      cegger    904:                exit(EXIT_FAILURE);
1.1       cgd       905:        }
                    906:        (void)close(fd);
                    907: }
                    908:
                    909: /*
                    910:  * check_pager --
                    911:  *     check the user supplied page information
                    912:  */
1.23      jdolecek  913: static const char *
1.33      chuck     914: check_pager(const char *name)
1.1       cgd       915: {
1.24      thorpej   916:        const char *p;
1.1       cgd       917:
                    918:        /*
                    919:         * if the user uses "more", we make it "more -s"; watch out for
                    920:         * PAGER = "mypager /usr/ucb/more"
                    921:         */
1.15      christos  922:        for (p = name; *p && !isspace((unsigned char)*p); ++p)
                    923:                continue;
1.1       cgd       924:        for (; p > name && *p != '/'; --p);
                    925:        if (p != name)
                    926:                ++p;
                    927:
                    928:        /* make sure it's "more", not "morex" */
1.15      christos  929:        if (!strncmp(p, "more", 4) && (!p[4] || isspace((unsigned char)p[4]))){
1.23      jdolecek  930:                char *newname;
1.24      thorpej   931:                (void)asprintf(&newname, "%s %s", p, "-s");
1.23      jdolecek  932:                name = newname;
1.1       cgd       933:        }
1.23      jdolecek  934:
1.38      cegger    935:        return name;
1.1       cgd       936: }
                    937:
                    938: /*
                    939:  * jump --
                    940:  *     strip out flag argument and jump
                    941:  */
                    942: static void
1.48      christos  943: jump(char **argv, const char *flag, const char *name)
1.1       cgd       944: {
                    945:        char **arg;
                    946:
1.48      christos  947:        argv[0] = __UNCONST(name);
1.1       cgd       948:        for (arg = argv + 1; *arg; ++arg)
                    949:                if (!strcmp(*arg, flag))
                    950:                        break;
                    951:        for (; *arg; ++arg)
                    952:                arg[0] = arg[1];
                    953:        execvp(name, argv);
1.40      christos  954:        err(EXIT_FAILURE, "Cannot execute `%s'", name);
1.1       cgd       955: }
                    956:
                    957: /*
                    958:  * onsig --
                    959:  *     If signaled, delete the temporary files.
                    960:  */
                    961: static void
1.33      chuck     962: onsig(int signo)
1.1       cgd       963: {
1.18      itohy     964:
1.1       cgd       965:        (void)cleanup();
                    966:
1.36      lukem     967:        (void)raise_default_signal(signo);
1.1       cgd       968:
                    969:        /* NOTREACHED */
1.38      cegger    970:        exit(EXIT_FAILURE);
1.1       cgd       971: }
                    972:
                    973: /*
                    974:  * cleanup --
                    975:  *     Clean up temporary files, show any error messages.
                    976:  */
                    977: static int
1.38      cegger    978: cleanup(void)
1.1       cgd       979: {
                    980:        TAG *intmpp, *missp;
                    981:        ENTRY *ep;
                    982:        int rval;
                    983:
1.38      cegger    984:        rval = EXIT_SUCCESS;
1.33      chuck     985:        /*
                    986:         * note that _missing and _intmp were created by main(), so
                    987:         * gettag() cannot return NULL here.
                    988:         */
                    989:        missp = gettag("_missing", 0);  /* missing man pages */
                    990:        intmpp = gettag("_intmp", 0);   /* tmp files we need to unlink */
                    991:
                    992:        TAILQ_FOREACH(ep, &missp->entrylist, q) {
                    993:                warnx("no entry for %s in the manual.", ep->s);
1.38      cegger    994:                rval = EXIT_FAILURE;
1.33      chuck     995:        }
                    996:
                    997:        TAILQ_FOREACH(ep, &intmpp->entrylist, q)
                    998:                (void)unlink(ep->s);
1.1       cgd       999:
1.38      cegger   1000:        return rval;
1.1       cgd      1001: }
                   1002:
1.40      christos 1003: static const char *
                   1004: getclass(const char *machine)
                   1005: {
                   1006:        char buf[BUFSIZ];
                   1007:        TAG *t;
                   1008:        snprintf(buf, sizeof(buf), "_%s", machine);
                   1009:        t = gettag(buf, 0);
1.41      christos 1010:        return t != NULL && !TAILQ_EMPTY(&t->entrylist) ?
                   1011:            TAILQ_FIRST(&t->entrylist)->s : NULL;
1.40      christos 1012: }
                   1013:
                   1014: static void
                   1015: addpath(struct manstate *m, const char *dir, size_t len, const char *sub)
                   1016: {
                   1017:        char buf[2 * MAXPATHLEN + 1];
                   1018:        (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,%s%s%s}",
                   1019:             dir, (dir[len - 1] == '/') ? "" : "/", sub, m->machine,
                   1020:             m->machclass ? "/" : "", m->machclass ? m->machclass : "",
                   1021:             m->machclass ? "," : "");
                   1022:        if (addentry(m->mymanpath, buf, 0) < 0)
                   1023:                errx(EXIT_FAILURE, "malloc failed");
                   1024: }
                   1025:
1.1       cgd      1026: /*
                   1027:  * usage --
                   1028:  *     print usage message and die
                   1029:  */
                   1030: static void
1.38      cegger   1031: usage(void)
1.1       cgd      1032: {
1.40      christos 1033:        (void)fprintf(stderr, "Usage: %s [-acw|-h] [-C cfg] [-M path] "
1.33      chuck    1034:            "[-m path] [-S srch] [[-s] sect] name ...\n", getprogname());
1.64      abhinav  1035:        (void)fprintf(stderr, "Usage: %s [-C file] -f command ...\n", getprogname());
1.33      chuck    1036:        (void)fprintf(stderr,
1.64      abhinav  1037:            "Usage: %s [-C file] -k keyword ...\n",
1.33      chuck    1038:            getprogname());
1.43      wiz      1039:        (void)fprintf(stderr, "Usage: %s -p\n", getprogname());
1.38      cegger   1040:        exit(EXIT_FAILURE);
1.1       cgd      1041: }
1.42      joerg    1042:
                   1043: /*
                   1044:  * printmanpath --
                   1045:  *     Prints a list of directories containing man pages.
                   1046:  */
                   1047: static void
                   1048: printmanpath(struct manstate *m)
                   1049: {
1.66      abhinav  1050:        ENTRY *epath;
1.42      joerg    1051:        char **ap;
                   1052:        glob_t pg;
                   1053:        struct stat sb;
1.66      abhinav  1054:        TAG *path = m->mymanpath;
1.42      joerg    1055:
                   1056:        /* the tail queue is empty if no _default tag is defined in * man.conf */
                   1057:        if (TAILQ_EMPTY(&path->entrylist))
                   1058:                errx(EXIT_FAILURE, "Empty manpath");
                   1059:
1.66      abhinav  1060:        TAILQ_FOREACH(epath, &path->entrylist, q) {
                   1061:                if (glob(epath->s, GLOB_BRACE | GLOB_NOSORT, NULL, &pg) != 0)
                   1062:                        err(EXIT_FAILURE, "glob failed");
1.44      joerg    1063:
1.66      abhinav  1064:                if (pg.gl_matchc == 0) {
                   1065:                        globfree(&pg);
1.42      joerg    1066:                        continue;
1.66      abhinav  1067:                }
1.42      joerg    1068:
                   1069:                for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
1.66      abhinav  1070:                        /* Skip cat page directories */
                   1071:                        if (strstr(*ap, "/cat") != NULL)
                   1072:                                continue;
1.42      joerg    1073:                        /* Skip non-directories. */
1.66      abhinav  1074:                        if (stat(*ap, &sb) == 0 && S_ISDIR(sb.st_mode))
                   1075:                                printf("%s\n", *ap);
1.42      joerg    1076:                }
1.66      abhinav  1077:                globfree(&pg);
1.42      joerg    1078:        }
                   1079: }

CVSweb <webmaster@jp.NetBSD.org>