1*84d9c625SLionel Sambuc /* $NetBSD: makewhatis.c,v 1.49 2013/06/24 20:57:47 christos Exp $ */
20c3983b2SBen Gras
30c3983b2SBen Gras /*-
40c3983b2SBen Gras * Copyright (c) 1999 The NetBSD Foundation, Inc.
50c3983b2SBen Gras * All rights reserved.
60c3983b2SBen Gras *
70c3983b2SBen Gras * This code is derived from software contributed to The NetBSD Foundation
80c3983b2SBen Gras * by Matthias Scheler.
90c3983b2SBen Gras *
100c3983b2SBen Gras * Redistribution and use in source and binary forms, with or without
110c3983b2SBen Gras * modification, are permitted provided that the following conditions
120c3983b2SBen Gras * are met:
130c3983b2SBen Gras * 1. Redistributions of source code must retain the above copyright
140c3983b2SBen Gras * notice, this list of conditions and the following disclaimer.
150c3983b2SBen Gras * 2. Redistributions in binary form must reproduce the above copyright
160c3983b2SBen Gras * notice, this list of conditions and the following disclaimer in the
170c3983b2SBen Gras * documentation and/or other materials provided with the distribution.
180c3983b2SBen Gras *
190c3983b2SBen Gras * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
200c3983b2SBen Gras * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
210c3983b2SBen Gras * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
220c3983b2SBen Gras * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
230c3983b2SBen Gras * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
240c3983b2SBen Gras * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
250c3983b2SBen Gras * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
260c3983b2SBen Gras * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
270c3983b2SBen Gras * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
280c3983b2SBen Gras * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
290c3983b2SBen Gras * POSSIBILITY OF SUCH DAMAGE.
300c3983b2SBen Gras */
310c3983b2SBen Gras
320c3983b2SBen Gras #if HAVE_NBTOOL_CONFIG_H
330c3983b2SBen Gras #include "nbtool_config.h"
340c3983b2SBen Gras #endif
350c3983b2SBen Gras
360c3983b2SBen Gras #include <sys/cdefs.h>
370c3983b2SBen Gras #if !defined(lint)
380c3983b2SBen Gras __COPYRIGHT("@(#) Copyright (c) 1999\
390c3983b2SBen Gras The NetBSD Foundation, Inc. All rights reserved.");
40*84d9c625SLionel Sambuc __RCSID("$NetBSD: makewhatis.c,v 1.49 2013/06/24 20:57:47 christos Exp $");
410c3983b2SBen Gras #endif /* not lint */
420c3983b2SBen Gras
430c3983b2SBen Gras #include <sys/types.h>
440c3983b2SBen Gras #include <sys/param.h>
450c3983b2SBen Gras #include <sys/queue.h>
460c3983b2SBen Gras #include <sys/stat.h>
470c3983b2SBen Gras #include <sys/wait.h>
480c3983b2SBen Gras
490c3983b2SBen Gras #include <ctype.h>
500c3983b2SBen Gras #include <err.h>
510c3983b2SBen Gras #include <errno.h>
520c3983b2SBen Gras #include <fcntl.h>
530c3983b2SBen Gras #include <fts.h>
540c3983b2SBen Gras #include <glob.h>
550c3983b2SBen Gras #include <locale.h>
560c3983b2SBen Gras #include <paths.h>
570c3983b2SBen Gras #include <signal.h>
580c3983b2SBen Gras #include <stdio.h>
590c3983b2SBen Gras #include <stdlib.h>
600c3983b2SBen Gras #include <string.h>
610c3983b2SBen Gras #include <unistd.h>
620c3983b2SBen Gras #include <zlib.h>
630c3983b2SBen Gras #include <util.h>
640c3983b2SBen Gras
650c3983b2SBen Gras #include <man/manconf.h>
660c3983b2SBen Gras #include <man/pathnames.h>
670c3983b2SBen Gras
680c3983b2SBen Gras #ifndef NROFF
690c3983b2SBen Gras #define NROFF "nroff"
700c3983b2SBen Gras #endif
710c3983b2SBen Gras
720c3983b2SBen Gras typedef struct manpagestruct manpage;
730c3983b2SBen Gras struct manpagestruct {
740c3983b2SBen Gras manpage *mp_left, *mp_right;
750c3983b2SBen Gras ino_t mp_inode;
760c3983b2SBen Gras size_t mp_sdoff;
770c3983b2SBen Gras size_t mp_sdlen;
780c3983b2SBen Gras char mp_name[1];
790c3983b2SBen Gras };
800c3983b2SBen Gras
810c3983b2SBen Gras typedef struct whatisstruct whatis;
820c3983b2SBen Gras struct whatisstruct {
830c3983b2SBen Gras whatis *wi_left, *wi_right;
840c3983b2SBen Gras char *wi_data;
850c3983b2SBen Gras char wi_prefix[1];
860c3983b2SBen Gras };
870c3983b2SBen Gras
880c3983b2SBen Gras int main(int, char * const *);
890c3983b2SBen Gras static char *findwhitespace(char *);
900c3983b2SBen Gras static char *strmove(char *, char *);
910c3983b2SBen Gras static char *GetS(gzFile, char *, size_t);
920c3983b2SBen Gras static int pathnamesection(const char *, const char *);
930c3983b2SBen Gras static int manpagesection(char *);
940c3983b2SBen Gras static char *createsectionstring(char *);
950c3983b2SBen Gras static void addmanpage(manpage **, ino_t, char *, size_t, size_t);
960c3983b2SBen Gras static void addwhatis(whatis **, char *, char *);
970c3983b2SBen Gras static char *makesection(int);
980c3983b2SBen Gras static char *makewhatisline(const char *, const char *, const char *);
990c3983b2SBen Gras static void catpreprocess(char *);
1000c3983b2SBen Gras static char *parsecatpage(const char *, gzFile *);
1010c3983b2SBen Gras static int manpreprocess(char *);
1020c3983b2SBen Gras static char *nroff(const char *, gzFile *);
1030c3983b2SBen Gras static char *parsemanpage(const char *, gzFile *, int);
1040c3983b2SBen Gras static char *getwhatisdata(char *);
1050c3983b2SBen Gras static void processmanpages(manpage **, whatis **);
1060c3983b2SBen Gras static void dumpwhatis(FILE *, whatis *);
1070c3983b2SBen Gras static int makewhatis(char * const *manpath);
1080c3983b2SBen Gras
1090c3983b2SBen Gras static char * const default_manpath[] = {
110*84d9c625SLionel Sambuc #if defined(__minix)
1110c3983b2SBen Gras "/usr/man",
112*84d9c625SLionel Sambuc #endif /* defined(__minix) */
113910825eeSBen Gras "/usr/share/man",
1140c3983b2SBen Gras NULL
1150c3983b2SBen Gras };
1160c3983b2SBen Gras
1170c3983b2SBen Gras static const char *sectionext = "0123456789ln";
1180c3983b2SBen Gras static const char *whatisdb = _PATH_WHATIS;
1190c3983b2SBen Gras static const char *whatisdb_new = _PATH_WHATIS ".new";
1200c3983b2SBen Gras static int dowarn = 0;
1210c3983b2SBen Gras
1220c3983b2SBen Gras #define ISALPHA(c) isalpha((unsigned char)(c))
1230c3983b2SBen Gras #define ISDIGIT(c) isdigit((unsigned char)(c))
1240c3983b2SBen Gras #define ISSPACE(c) isspace((unsigned char)(c))
1250c3983b2SBen Gras
1260c3983b2SBen Gras int
main(int argc,char * const * argv)1270c3983b2SBen Gras main(int argc, char *const *argv)
1280c3983b2SBen Gras {
1290c3983b2SBen Gras char * const *manpath;
1300c3983b2SBen Gras int c, dofork;
1310c3983b2SBen Gras const char *conffile;
1320c3983b2SBen Gras ENTRY *ep;
1330c3983b2SBen Gras TAG *tp;
1340c3983b2SBen Gras int rv, jobs, status;
1350c3983b2SBen Gras glob_t pg;
1360c3983b2SBen Gras char *paths[2], **p, *sl;
1370c3983b2SBen Gras int retval;
1380c3983b2SBen Gras
1390c3983b2SBen Gras dofork = 1;
1400c3983b2SBen Gras conffile = NULL;
1410c3983b2SBen Gras jobs = 0;
1420c3983b2SBen Gras retval = EXIT_SUCCESS;
1430c3983b2SBen Gras
1440c3983b2SBen Gras (void)setlocale(LC_ALL, "");
1450c3983b2SBen Gras
1460c3983b2SBen Gras while ((c = getopt(argc, argv, "C:fw")) != -1) {
1470c3983b2SBen Gras switch (c) {
1480c3983b2SBen Gras case 'C':
1490c3983b2SBen Gras conffile = optarg;
1500c3983b2SBen Gras break;
1510c3983b2SBen Gras case 'f':
1520c3983b2SBen Gras /* run all processing on foreground */
1530c3983b2SBen Gras dofork = 0;
1540c3983b2SBen Gras break;
1550c3983b2SBen Gras case 'w':
1560c3983b2SBen Gras dowarn++;
1570c3983b2SBen Gras break;
1580c3983b2SBen Gras default:
1590c3983b2SBen Gras fprintf(stderr, "Usage: %s [-fw] [-C file] [manpath ...]\n",
1600c3983b2SBen Gras getprogname());
1610c3983b2SBen Gras exit(EXIT_FAILURE);
1620c3983b2SBen Gras }
1630c3983b2SBen Gras }
1640c3983b2SBen Gras argc -= optind;
1650c3983b2SBen Gras argv += optind;
1660c3983b2SBen Gras
1670c3983b2SBen Gras if (argc >= 1) {
1680c3983b2SBen Gras manpath = &argv[0];
1690c3983b2SBen Gras
1700c3983b2SBen Gras mkwhatis:
1710c3983b2SBen Gras return makewhatis(manpath);
1720c3983b2SBen Gras }
1730c3983b2SBen Gras
1740c3983b2SBen Gras /*
1750c3983b2SBen Gras * Try read config file, fallback to default_manpath[]
1760c3983b2SBen Gras * if man.conf not available.
1770c3983b2SBen Gras */
1780c3983b2SBen Gras config(conffile);
1790c3983b2SBen Gras if ((tp = gettag("_whatdb", 0)) == NULL) {
1800c3983b2SBen Gras manpath = default_manpath;
1810c3983b2SBen Gras goto mkwhatis;
1820c3983b2SBen Gras }
1830c3983b2SBen Gras
1840c3983b2SBen Gras /* Build individual databases */
1850c3983b2SBen Gras paths[1] = NULL;
1860c3983b2SBen Gras TAILQ_FOREACH(ep, &tp->entrylist, q) {
1870c3983b2SBen Gras if ((rv = glob(ep->s,
1880c3983b2SBen Gras GLOB_BRACE | GLOB_NOSORT | GLOB_ERR | GLOB_NOCHECK,
1890c3983b2SBen Gras NULL, &pg)) != 0)
1900c3983b2SBen Gras err(EXIT_FAILURE, "glob('%s')", ep->s);
1910c3983b2SBen Gras
1920c3983b2SBen Gras /* We always have something to work with here */
1930c3983b2SBen Gras for (p = pg.gl_pathv; *p; p++) {
1940c3983b2SBen Gras sl = strrchr(*p, '/');
1950c3983b2SBen Gras if (sl == NULL) {
1960c3983b2SBen Gras err(EXIT_FAILURE, "glob: _whatdb entry '%s' "
1970c3983b2SBen Gras "doesn't contain slash", ep->s);
1980c3983b2SBen Gras }
1990c3983b2SBen Gras
2000c3983b2SBen Gras /*
2010c3983b2SBen Gras * Cut the last component of path, leaving just
2020c3983b2SBen Gras * the directory. We will use the result as root
2030c3983b2SBen Gras * for manpage search.
2040c3983b2SBen Gras * glob malloc()s space for the paths, so it's
2050c3983b2SBen Gras * okay to change it in-place.
2060c3983b2SBen Gras */
2070c3983b2SBen Gras *sl = '\0';
2080c3983b2SBen Gras paths[0] = *p;
2090c3983b2SBen Gras
2100c3983b2SBen Gras if (!dofork) {
2110c3983b2SBen Gras /* Do not fork child */
2120c3983b2SBen Gras makewhatis(paths);
2130c3983b2SBen Gras continue;
2140c3983b2SBen Gras }
2150c3983b2SBen Gras
2160c3983b2SBen Gras switch (fork()) {
2170c3983b2SBen Gras case 0:
2180c3983b2SBen Gras exit(makewhatis(paths));
2190c3983b2SBen Gras break;
2200c3983b2SBen Gras case -1:
2210c3983b2SBen Gras warn("fork");
2220c3983b2SBen Gras makewhatis(paths);
2230c3983b2SBen Gras break;
2240c3983b2SBen Gras default:
2250c3983b2SBen Gras jobs++;
2260c3983b2SBen Gras break;
2270c3983b2SBen Gras }
2280c3983b2SBen Gras
2290c3983b2SBen Gras }
2300c3983b2SBen Gras
2310c3983b2SBen Gras globfree(&pg);
2320c3983b2SBen Gras }
2330c3983b2SBen Gras
2340c3983b2SBen Gras /* Wait for the childern to finish */
2350c3983b2SBen Gras while (jobs > 0) {
2360c3983b2SBen Gras (void)wait(&status);
2370c3983b2SBen Gras if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS)
2380c3983b2SBen Gras retval = EXIT_FAILURE;
2390c3983b2SBen Gras jobs--;
2400c3983b2SBen Gras }
2410c3983b2SBen Gras
2420c3983b2SBen Gras return retval;
2430c3983b2SBen Gras }
2440c3983b2SBen Gras
2450c3983b2SBen Gras static int
makewhatis(char * const * manpath)2460c3983b2SBen Gras makewhatis(char * const * manpath)
2470c3983b2SBen Gras {
2480c3983b2SBen Gras FTS *fts;
2490c3983b2SBen Gras FTSENT *fe;
2500c3983b2SBen Gras manpage *source;
2510c3983b2SBen Gras whatis *dest;
2520c3983b2SBen Gras FILE *out;
2530c3983b2SBen Gras size_t sdoff, sdlen;
2540c3983b2SBen Gras int outfd;
2550c3983b2SBen Gras struct stat st_before, st_after;
2560c3983b2SBen Gras
2570c3983b2SBen Gras if ((fts = fts_open(manpath, FTS_LOGICAL, NULL)) == NULL)
2580c3983b2SBen Gras err(EXIT_FAILURE, "Cannot open `%s'", *manpath);
2590c3983b2SBen Gras
2600c3983b2SBen Gras source = NULL;
2610c3983b2SBen Gras while ((fe = fts_read(fts)) != NULL) {
2620c3983b2SBen Gras switch (fe->fts_info) {
2630c3983b2SBen Gras case FTS_F:
2640c3983b2SBen Gras if (manpagesection(fe->fts_path) >= 0) {
2650c3983b2SBen Gras /*
2660c3983b2SBen Gras * Get manpage subdirectory prefix. Most
2670c3983b2SBen Gras * commonly, this is arch-specific subdirectory.
2680c3983b2SBen Gras */
2690c3983b2SBen Gras if (fe->fts_level >= 3) {
2700c3983b2SBen Gras int sl;
2710c3983b2SBen Gras const char *s, *lsl;
2720c3983b2SBen Gras
2730c3983b2SBen Gras lsl = NULL;
2740c3983b2SBen Gras s = &fe->fts_path[fe->fts_pathlen - 1];
2750c3983b2SBen Gras for(sl = fe->fts_level - 1; sl > 0;
2760c3983b2SBen Gras sl--) {
2770c3983b2SBen Gras s--;
2780c3983b2SBen Gras while (s[0] != '/')
2790c3983b2SBen Gras s--;
2800c3983b2SBen Gras if (lsl == NULL)
2810c3983b2SBen Gras lsl = s;
2820c3983b2SBen Gras }
2830c3983b2SBen Gras
2840c3983b2SBen Gras /*
2850c3983b2SBen Gras * Include trailing '/', so we get
2860c3983b2SBen Gras * 'arch/'.
2870c3983b2SBen Gras */
2880c3983b2SBen Gras sdoff = s + 1 - fe->fts_path;
2890c3983b2SBen Gras sdlen = lsl - s + 1;
2900c3983b2SBen Gras } else {
2910c3983b2SBen Gras sdoff = 0;
2920c3983b2SBen Gras sdlen = 0;
2930c3983b2SBen Gras }
2940c3983b2SBen Gras
2950c3983b2SBen Gras addmanpage(&source, fe->fts_statp->st_ino,
2960c3983b2SBen Gras fe->fts_path, sdoff, sdlen);
2970c3983b2SBen Gras }
2980c3983b2SBen Gras /*FALLTHROUGH*/
2990c3983b2SBen Gras case FTS_D:
3000c3983b2SBen Gras case FTS_DC:
3010c3983b2SBen Gras case FTS_DEFAULT:
3020c3983b2SBen Gras case FTS_DP:
3030c3983b2SBen Gras case FTS_SL:
3040c3983b2SBen Gras case FTS_DOT:
3050c3983b2SBen Gras case FTS_W:
3060c3983b2SBen Gras case FTS_NSOK:
3070c3983b2SBen Gras case FTS_INIT:
3080c3983b2SBen Gras break;
3090c3983b2SBen Gras case FTS_SLNONE:
3100c3983b2SBen Gras warnx("Symbolic link with no target: `%s'",
3110c3983b2SBen Gras fe->fts_path);
3120c3983b2SBen Gras break;
3130c3983b2SBen Gras case FTS_DNR:
3140c3983b2SBen Gras warnx("Unreadable directory: `%s'", fe->fts_path);
3150c3983b2SBen Gras break;
3160c3983b2SBen Gras case FTS_NS:
3170c3983b2SBen Gras errno = fe->fts_errno;
3180c3983b2SBen Gras warn("Cannot stat `%s'", fe->fts_path);
3190c3983b2SBen Gras break;
3200c3983b2SBen Gras case FTS_ERR:
3210c3983b2SBen Gras errno = fe->fts_errno;
3220c3983b2SBen Gras warn("Error reading `%s'", fe->fts_path);
3230c3983b2SBen Gras break;
3240c3983b2SBen Gras default:
3250c3983b2SBen Gras errx(EXIT_FAILURE, "Unknown info %d returned from fts "
3260c3983b2SBen Gras " for path: `%s'", fe->fts_info, fe->fts_path);
3270c3983b2SBen Gras }
3280c3983b2SBen Gras }
3290c3983b2SBen Gras
3300c3983b2SBen Gras (void)fts_close(fts);
3310c3983b2SBen Gras
3320c3983b2SBen Gras dest = NULL;
3330c3983b2SBen Gras processmanpages(&source, &dest);
3340c3983b2SBen Gras
3350c3983b2SBen Gras if (chdir(manpath[0]) == -1)
3360c3983b2SBen Gras err(EXIT_FAILURE, "Cannot change dir to `%s'", manpath[0]);
3370c3983b2SBen Gras
3380c3983b2SBen Gras /*
3390c3983b2SBen Gras * makewhatis runs unattended, so it needs to be able to
3400c3983b2SBen Gras * recover if the last run crashed out. Therefore, if
3410c3983b2SBen Gras * whatisdb_new exists and is more than (arbitrarily) sixteen
3420c3983b2SBen Gras * hours old, nuke it. If it exists but is not so old, refuse
3430c3983b2SBen Gras * to run until it's cleaned up, in case another makewhatis is
3440c3983b2SBen Gras * already running. Also, open the output with O_EXCL to make
3450c3983b2SBen Gras * sure we get our own, in case two copies start exactly at
3460c3983b2SBen Gras * once. (Unlikely? Maybe, maybe not, if two copies of cron
3470c3983b2SBen Gras * end up running.)
3480c3983b2SBen Gras *
3490c3983b2SBen Gras * Similarly, before renaming the file after we finish writing
3500c3983b2SBen Gras * to it, make sure it's still the same file we opened. This
3510c3983b2SBen Gras * can't be completely race-free, but getting caught by it
3520c3983b2SBen Gras * would require an unexplained sixteen-hour-or-more lag
3530c3983b2SBen Gras * between the last mtime update when we wrote to it and when
3540c3983b2SBen Gras * we get to the stat call *and* another makewhatis starting
3550c3983b2SBen Gras * out to write at exactly the wrong moment. Not impossible,
3560c3983b2SBen Gras * but not likely enough to worry about.
3570c3983b2SBen Gras *
3580c3983b2SBen Gras * This is maybe unnecessarily elaborate, but generating
3590c3983b2SBen Gras * corrupted output isn't so good either.
3600c3983b2SBen Gras */
3610c3983b2SBen Gras
3620c3983b2SBen Gras if (stat(whatisdb_new, &st_before) == 0) {
3630c3983b2SBen Gras if (st_before.st_mtime - time(NULL) > 16*60*60) {
3640c3983b2SBen Gras /* Don't complain if someone else just removed it. */
3650c3983b2SBen Gras if (unlink(whatisdb_new) == -1 && errno != ENOENT) {
3660c3983b2SBen Gras err(EXIT_FAILURE, "Could not remove `%s'",
3670c3983b2SBen Gras whatisdb_new);
3680c3983b2SBen Gras } else {
3690c3983b2SBen Gras warnx("Removed stale `%s'", whatisdb_new);
3700c3983b2SBen Gras }
3710c3983b2SBen Gras } else {
3720c3983b2SBen Gras errx(EXIT_FAILURE, "The file `%s' already exists "
3730c3983b2SBen Gras "-- am I already running?", whatisdb_new);
3740c3983b2SBen Gras }
3750c3983b2SBen Gras } else if (errno != ENOENT) {
3760c3983b2SBen Gras /* Something unexpected happened. */
3770c3983b2SBen Gras err(EXIT_FAILURE, "Cannot stat `%s'", whatisdb_new);
3780c3983b2SBen Gras }
3790c3983b2SBen Gras
3800c3983b2SBen Gras outfd = open(whatisdb_new, O_WRONLY|O_CREAT|O_EXCL,
3810c3983b2SBen Gras S_IRUSR|S_IRGRP|S_IROTH);
3820c3983b2SBen Gras if (outfd < 0)
3830c3983b2SBen Gras err(EXIT_FAILURE, "Cannot open `%s'", whatisdb_new);
3840c3983b2SBen Gras
3850c3983b2SBen Gras if (fstat(outfd, &st_before) == -1)
3860c3983b2SBen Gras err(EXIT_FAILURE, "Cannot fstat `%s'", whatisdb_new);
3870c3983b2SBen Gras
3880c3983b2SBen Gras if ((out = fdopen(outfd, "w")) == NULL)
3890c3983b2SBen Gras err(EXIT_FAILURE, "Cannot fdopen `%s'", whatisdb_new);
3900c3983b2SBen Gras
3910c3983b2SBen Gras dumpwhatis(out, dest);
3920c3983b2SBen Gras if (fchmod(fileno(out), S_IRUSR|S_IRGRP|S_IROTH) == -1)
3930c3983b2SBen Gras err(EXIT_FAILURE, "Cannot chmod `%s'", whatisdb_new);
3940c3983b2SBen Gras if (fclose(out) != 0)
3950c3983b2SBen Gras err(EXIT_FAILURE, "Cannot close `%s'", whatisdb_new);
3960c3983b2SBen Gras
3970c3983b2SBen Gras if (stat(whatisdb_new, &st_after) == -1)
3980c3983b2SBen Gras err(EXIT_FAILURE, "Cannot stat `%s' (after writing)",
3990c3983b2SBen Gras whatisdb_new);
4000c3983b2SBen Gras
4010c3983b2SBen Gras if (st_before.st_dev != st_after.st_dev ||
4020c3983b2SBen Gras st_before.st_ino != st_after.st_ino) {
4030c3983b2SBen Gras errx(EXIT_FAILURE, "The file `%s' changed under me; giving up",
4040c3983b2SBen Gras whatisdb_new);
4050c3983b2SBen Gras }
4060c3983b2SBen Gras
4070c3983b2SBen Gras if (rename(whatisdb_new, whatisdb) == -1)
4080c3983b2SBen Gras err(EXIT_FAILURE, "Could not rename `%s' to `%s'",
4090c3983b2SBen Gras whatisdb_new, whatisdb);
4100c3983b2SBen Gras
4110c3983b2SBen Gras return EXIT_SUCCESS;
4120c3983b2SBen Gras }
4130c3983b2SBen Gras
4140c3983b2SBen Gras static char *
findwhitespace(char * str)4150c3983b2SBen Gras findwhitespace(char *str)
4160c3983b2SBen Gras {
4170c3983b2SBen Gras while (!ISSPACE(*str))
4180c3983b2SBen Gras if (*str++ == '\0') {
4190c3983b2SBen Gras str = NULL;
4200c3983b2SBen Gras break;
4210c3983b2SBen Gras }
4220c3983b2SBen Gras
4230c3983b2SBen Gras return str;
4240c3983b2SBen Gras }
4250c3983b2SBen Gras
4260c3983b2SBen Gras static char *
strmove(char * dest,char * src)4270c3983b2SBen Gras strmove(char *dest, char *src)
4280c3983b2SBen Gras {
4290c3983b2SBen Gras return memmove(dest, src, strlen(src) + 1);
4300c3983b2SBen Gras }
4310c3983b2SBen Gras
4320c3983b2SBen Gras static char *
GetS(gzFile in,char * buffer,size_t length)4330c3983b2SBen Gras GetS(gzFile in, char *buffer, size_t length)
4340c3983b2SBen Gras {
4350c3983b2SBen Gras char *ptr;
4360c3983b2SBen Gras
4370c3983b2SBen Gras if (((ptr = gzgets(in, buffer, (int)length)) != NULL) && (*ptr == '\0'))
4380c3983b2SBen Gras ptr = NULL;
4390c3983b2SBen Gras
4400c3983b2SBen Gras return ptr;
4410c3983b2SBen Gras }
4420c3983b2SBen Gras
4430c3983b2SBen Gras static char *
makesection(int s)4440c3983b2SBen Gras makesection(int s)
4450c3983b2SBen Gras {
4460c3983b2SBen Gras char sectionbuffer[24];
4470c3983b2SBen Gras if (s == -1)
4480c3983b2SBen Gras return NULL;
4490c3983b2SBen Gras (void)snprintf(sectionbuffer, sizeof(sectionbuffer),
4500c3983b2SBen Gras " (%c) - ", sectionext[s]);
4510c3983b2SBen Gras return estrdup(sectionbuffer);
4520c3983b2SBen Gras }
4530c3983b2SBen Gras
4540c3983b2SBen Gras static int
pathnamesection(const char * pat,const char * name)4550c3983b2SBen Gras pathnamesection(const char *pat, const char *name)
4560c3983b2SBen Gras {
4570c3983b2SBen Gras char *ptr, *ext;
4580c3983b2SBen Gras size_t len = strlen(pat);
4590c3983b2SBen Gras
4600c3983b2SBen Gras
4610c3983b2SBen Gras while ((ptr = strstr(name, pat)) != NULL) {
4620c3983b2SBen Gras if ((ext = strchr(sectionext, ptr[len])) != NULL) {
4630c3983b2SBen Gras return ext - sectionext;
4640c3983b2SBen Gras }
4650c3983b2SBen Gras name = ptr + 1;
4660c3983b2SBen Gras }
4670c3983b2SBen Gras return -1;
4680c3983b2SBen Gras }
4690c3983b2SBen Gras
4700c3983b2SBen Gras
4710c3983b2SBen Gras static int
manpagesection(char * name)4720c3983b2SBen Gras manpagesection(char *name)
4730c3983b2SBen Gras {
4740c3983b2SBen Gras char *ptr;
4750c3983b2SBen Gras
4760c3983b2SBen Gras if ((ptr = strrchr(name, '/')) != NULL)
4770c3983b2SBen Gras ptr++;
4780c3983b2SBen Gras else
4790c3983b2SBen Gras ptr = name;
4800c3983b2SBen Gras
4810c3983b2SBen Gras while ((ptr = strchr(ptr, '.')) != NULL) {
4820c3983b2SBen Gras int section;
4830c3983b2SBen Gras
4840c3983b2SBen Gras ptr++;
4850c3983b2SBen Gras section = 0;
4860c3983b2SBen Gras while (sectionext[section] != '\0')
4870c3983b2SBen Gras if (sectionext[section] == *ptr)
4880c3983b2SBen Gras return section;
4890c3983b2SBen Gras else
4900c3983b2SBen Gras section++;
4910c3983b2SBen Gras }
4920c3983b2SBen Gras return -1;
4930c3983b2SBen Gras }
4940c3983b2SBen Gras
4950c3983b2SBen Gras static char *
createsectionstring(char * section_id)4960c3983b2SBen Gras createsectionstring(char *section_id)
4970c3983b2SBen Gras {
4980c3983b2SBen Gras char *section;
4990c3983b2SBen Gras
5000c3983b2SBen Gras if (asprintf(§ion, " (%s) - ", section_id) < 0)
5010c3983b2SBen Gras err(EXIT_FAILURE, "malloc failed");
5020c3983b2SBen Gras return section;
5030c3983b2SBen Gras }
5040c3983b2SBen Gras
5050c3983b2SBen Gras static void
addmanpage(manpage ** tree,ino_t inode,char * name,size_t sdoff,size_t sdlen)5060c3983b2SBen Gras addmanpage(manpage **tree, ino_t inode, char *name, size_t sdoff, size_t sdlen)
5070c3983b2SBen Gras {
5080c3983b2SBen Gras manpage *mp;
5090c3983b2SBen Gras
5100c3983b2SBen Gras while ((mp = *tree) != NULL) {
5110c3983b2SBen Gras if (mp->mp_inode == inode)
5120c3983b2SBen Gras return;
5130c3983b2SBen Gras tree = inode < mp->mp_inode ? &mp->mp_left : &mp->mp_right;
5140c3983b2SBen Gras }
5150c3983b2SBen Gras
5160c3983b2SBen Gras mp = emalloc(sizeof(manpage) + strlen(name));
5170c3983b2SBen Gras mp->mp_left = NULL;
5180c3983b2SBen Gras mp->mp_right = NULL;
5190c3983b2SBen Gras mp->mp_inode = inode;
5200c3983b2SBen Gras mp->mp_sdoff = sdoff;
5210c3983b2SBen Gras mp->mp_sdlen = sdlen;
5220c3983b2SBen Gras (void)strcpy(mp->mp_name, name);
5230c3983b2SBen Gras *tree = mp;
5240c3983b2SBen Gras }
5250c3983b2SBen Gras
5260c3983b2SBen Gras static void
addwhatis(whatis ** tree,char * data,char * prefix)5270c3983b2SBen Gras addwhatis(whatis **tree, char *data, char *prefix)
5280c3983b2SBen Gras {
5290c3983b2SBen Gras whatis *wi;
5300c3983b2SBen Gras int result;
5310c3983b2SBen Gras
5320c3983b2SBen Gras while (ISSPACE(*data))
5330c3983b2SBen Gras data++;
5340c3983b2SBen Gras
5350c3983b2SBen Gras if (*data == '/') {
5360c3983b2SBen Gras char *ptr;
5370c3983b2SBen Gras
5380c3983b2SBen Gras ptr = ++data;
5390c3983b2SBen Gras while ((*ptr != '\0') && !ISSPACE(*ptr))
5400c3983b2SBen Gras if (*ptr++ == '/')
5410c3983b2SBen Gras data = ptr;
5420c3983b2SBen Gras }
5430c3983b2SBen Gras
5440c3983b2SBen Gras while ((wi = *tree) != NULL) {
5450c3983b2SBen Gras result = strcmp(data, wi->wi_data);
5460c3983b2SBen Gras if (result == 0) result = strcmp(prefix, wi->wi_prefix);
5470c3983b2SBen Gras if (result == 0) return;
5480c3983b2SBen Gras tree = result < 0 ? &wi->wi_left : &wi->wi_right;
5490c3983b2SBen Gras }
5500c3983b2SBen Gras
5510c3983b2SBen Gras wi = emalloc(sizeof(whatis) + strlen(prefix));
5520c3983b2SBen Gras
5530c3983b2SBen Gras wi->wi_left = NULL;
5540c3983b2SBen Gras wi->wi_right = NULL;
5550c3983b2SBen Gras wi->wi_data = data;
5560c3983b2SBen Gras if (prefix[0] != '\0')
5570c3983b2SBen Gras (void) strcpy(wi->wi_prefix, prefix);
5580c3983b2SBen Gras else
5590c3983b2SBen Gras wi->wi_prefix[0] = '\0';
5600c3983b2SBen Gras *tree = wi;
5610c3983b2SBen Gras }
5620c3983b2SBen Gras
5630c3983b2SBen Gras static void
catpreprocess(char * from)5640c3983b2SBen Gras catpreprocess(char *from)
5650c3983b2SBen Gras {
5660c3983b2SBen Gras char *to;
5670c3983b2SBen Gras
5680c3983b2SBen Gras to = from;
5690c3983b2SBen Gras while (ISSPACE(*from)) from++;
5700c3983b2SBen Gras
5710c3983b2SBen Gras while (*from != '\0')
5720c3983b2SBen Gras if (ISSPACE(*from)) {
5730c3983b2SBen Gras while (ISSPACE(*++from));
5740c3983b2SBen Gras if (*from != '\0')
5750c3983b2SBen Gras *to++ = ' ';
5760c3983b2SBen Gras }
5770c3983b2SBen Gras else if (*(from + 1) == '\b')
5780c3983b2SBen Gras from += 2;
5790c3983b2SBen Gras else
5800c3983b2SBen Gras *to++ = *from++;
5810c3983b2SBen Gras
5820c3983b2SBen Gras *to = '\0';
5830c3983b2SBen Gras }
5840c3983b2SBen Gras
5850c3983b2SBen Gras static char *
makewhatisline(const char * file,const char * line,const char * section)5860c3983b2SBen Gras makewhatisline(const char *file, const char *line, const char *section)
5870c3983b2SBen Gras {
5880c3983b2SBen Gras static const char *del[] = {
5890c3983b2SBen Gras " - ",
5900c3983b2SBen Gras " -- ",
5910c3983b2SBen Gras "- ",
5920c3983b2SBen Gras " -",
5930c3983b2SBen Gras NULL
5940c3983b2SBen Gras };
5950c3983b2SBen Gras size_t i, pos;
5960c3983b2SBen Gras size_t llen, slen, dlen;
5970c3983b2SBen Gras char *result, *ptr;
5980c3983b2SBen Gras
5990c3983b2SBen Gras ptr = NULL;
6000c3983b2SBen Gras if (section == NULL) {
6010c3983b2SBen Gras if (dowarn)
6020c3983b2SBen Gras warnx("%s: No section provided for `%s'", file, line);
6030c3983b2SBen Gras return estrdup(line);
6040c3983b2SBen Gras }
6050c3983b2SBen Gras
6060c3983b2SBen Gras for (i = 0; del[i]; i++)
6070c3983b2SBen Gras if ((ptr = strstr(line, del[i])) != NULL)
6080c3983b2SBen Gras break;
6090c3983b2SBen Gras
6100c3983b2SBen Gras if (del[i] == NULL) {
6110c3983b2SBen Gras if (dowarn)
6120c3983b2SBen Gras warnx("%s: Bad format line `%s'", file, line);
6130c3983b2SBen Gras return estrdup(line);
6140c3983b2SBen Gras }
6150c3983b2SBen Gras
6160c3983b2SBen Gras slen = strlen(section);
6170c3983b2SBen Gras llen = strlen(line);
6180c3983b2SBen Gras dlen = strlen(del[i]);
6190c3983b2SBen Gras
6200c3983b2SBen Gras result = emalloc(llen - dlen + slen + 1);
6210c3983b2SBen Gras pos = ptr - line;
6220c3983b2SBen Gras
6230c3983b2SBen Gras (void)memcpy(result, line, pos);
6240c3983b2SBen Gras (void)memcpy(&result[pos], section, slen);
6250c3983b2SBen Gras (void)strcpy(&result[pos + slen], &line[pos + dlen]);
6260c3983b2SBen Gras return result;
6270c3983b2SBen Gras }
6280c3983b2SBen Gras
6290c3983b2SBen Gras static char *
parsecatpage(const char * name,gzFile * in)6300c3983b2SBen Gras parsecatpage(const char *name, gzFile *in)
6310c3983b2SBen Gras {
6320c3983b2SBen Gras char buffer[8192];
6330c3983b2SBen Gras char *section, *ptr, *last;
6340c3983b2SBen Gras size_t size;
6350c3983b2SBen Gras
6360c3983b2SBen Gras do {
6370c3983b2SBen Gras if (GetS(in, buffer, sizeof(buffer)) == NULL)
6380c3983b2SBen Gras return NULL;
6390c3983b2SBen Gras }
6400c3983b2SBen Gras while (buffer[0] == '\n');
6410c3983b2SBen Gras
6420c3983b2SBen Gras section = NULL;
6430c3983b2SBen Gras if ((ptr = strchr(buffer, '(')) != NULL) {
6440c3983b2SBen Gras if ((last = strchr(ptr + 1, ')')) !=NULL) {
6450c3983b2SBen Gras size_t length;
6460c3983b2SBen Gras
6470c3983b2SBen Gras length = last - ptr + 1;
6480c3983b2SBen Gras section = emalloc(length + 5);
6490c3983b2SBen Gras *section = ' ';
6500c3983b2SBen Gras (void) memcpy(section + 1, ptr, length);
6510c3983b2SBen Gras (void) strcpy(section + 1 + length, " - ");
6520c3983b2SBen Gras }
6530c3983b2SBen Gras }
6540c3983b2SBen Gras
6550c3983b2SBen Gras for (;;) {
6560c3983b2SBen Gras if (GetS(in, buffer, sizeof(buffer)) == NULL) {
6570c3983b2SBen Gras free(section);
6580c3983b2SBen Gras return NULL;
6590c3983b2SBen Gras }
6600c3983b2SBen Gras catpreprocess(buffer);
6610c3983b2SBen Gras if (strncmp(buffer, "NAME", 4) == 0)
6620c3983b2SBen Gras break;
6630c3983b2SBen Gras }
6640c3983b2SBen Gras if (section == NULL)
6650c3983b2SBen Gras section = makesection(pathnamesection("/cat", name));
6660c3983b2SBen Gras
6670c3983b2SBen Gras ptr = last = buffer;
6680c3983b2SBen Gras size = sizeof(buffer) - 1;
6690c3983b2SBen Gras while ((size > 0) && (GetS(in, ptr, size) != NULL)) {
6700c3983b2SBen Gras int length;
6710c3983b2SBen Gras
6720c3983b2SBen Gras catpreprocess(ptr);
6730c3983b2SBen Gras
6740c3983b2SBen Gras length = strlen(ptr);
6750c3983b2SBen Gras if (length == 0) {
6760c3983b2SBen Gras *last = '\0';
6770c3983b2SBen Gras
6780c3983b2SBen Gras ptr = makewhatisline(name, buffer, section);
6790c3983b2SBen Gras free(section);
6800c3983b2SBen Gras return ptr;
6810c3983b2SBen Gras }
6820c3983b2SBen Gras if ((length > 1) && (ptr[length - 1] == '-') &&
6830c3983b2SBen Gras ISALPHA(ptr[length - 2]))
6840c3983b2SBen Gras last = &ptr[--length];
6850c3983b2SBen Gras else {
6860c3983b2SBen Gras last = &ptr[length++];
6870c3983b2SBen Gras *last = ' ';
6880c3983b2SBen Gras }
6890c3983b2SBen Gras
6900c3983b2SBen Gras ptr += length;
6910c3983b2SBen Gras size -= length;
6920c3983b2SBen Gras }
6930c3983b2SBen Gras
6940c3983b2SBen Gras free(section);
6950c3983b2SBen Gras
6960c3983b2SBen Gras return NULL;
6970c3983b2SBen Gras }
6980c3983b2SBen Gras
6990c3983b2SBen Gras static int
manpreprocess(char * line)7000c3983b2SBen Gras manpreprocess(char *line)
7010c3983b2SBen Gras {
7020c3983b2SBen Gras char *from, *to;
7030c3983b2SBen Gras
7040c3983b2SBen Gras to = from = line;
7050c3983b2SBen Gras while (ISSPACE(*from))
7060c3983b2SBen Gras from++;
7070c3983b2SBen Gras if (strncmp(from, ".\\\"", 3) == 0)
7080c3983b2SBen Gras return 1;
7090c3983b2SBen Gras
7100c3983b2SBen Gras while (*from != '\0')
7110c3983b2SBen Gras if (ISSPACE(*from)) {
7120c3983b2SBen Gras while (ISSPACE(*++from));
7130c3983b2SBen Gras if ((*from != '\0') && (*from != ','))
7140c3983b2SBen Gras *to++ = ' ';
7150c3983b2SBen Gras } else if (*from == '\\') {
7160c3983b2SBen Gras switch (*++from) {
7170c3983b2SBen Gras case '\0':
7180c3983b2SBen Gras case '-':
7190c3983b2SBen Gras break;
7200c3983b2SBen Gras case 'f':
7210c3983b2SBen Gras case 's':
7220c3983b2SBen Gras from++;
7230c3983b2SBen Gras if ((*from=='+') || (*from=='-'))
7240c3983b2SBen Gras from++;
7250c3983b2SBen Gras while (ISDIGIT(*from))
7260c3983b2SBen Gras from++;
7270c3983b2SBen Gras break;
7280c3983b2SBen Gras default:
7290c3983b2SBen Gras from++;
7300c3983b2SBen Gras }
7310c3983b2SBen Gras } else {
7320c3983b2SBen Gras if (*from == '"')
7330c3983b2SBen Gras from++;
7340c3983b2SBen Gras else
7350c3983b2SBen Gras *to++ = *from++;
7360c3983b2SBen Gras }
7370c3983b2SBen Gras
7380c3983b2SBen Gras *to = '\0';
7390c3983b2SBen Gras
7400c3983b2SBen Gras if (strncasecmp(line, ".Xr", 3) == 0) {
7410c3983b2SBen Gras char *sect;
7420c3983b2SBen Gras
7430c3983b2SBen Gras from = line + 3;
7440c3983b2SBen Gras if (ISSPACE(*from))
7450c3983b2SBen Gras from++;
7460c3983b2SBen Gras
7470c3983b2SBen Gras if ((sect = findwhitespace(from)) != NULL) {
7480c3983b2SBen Gras size_t length;
7490c3983b2SBen Gras char *trail;
7500c3983b2SBen Gras
7510c3983b2SBen Gras *sect++ = '\0';
7520c3983b2SBen Gras if ((trail = findwhitespace(sect)) != NULL)
7530c3983b2SBen Gras *trail++ = '\0';
7540c3983b2SBen Gras length = strlen(from);
7550c3983b2SBen Gras (void) memmove(line, from, length);
7560c3983b2SBen Gras line[length++] = '(';
7570c3983b2SBen Gras to = &line[length];
7580c3983b2SBen Gras length = strlen(sect);
7590c3983b2SBen Gras (void) memmove(to, sect, length);
7600c3983b2SBen Gras if (trail == NULL) {
7610c3983b2SBen Gras (void) strcpy(&to[length], ")");
7620c3983b2SBen Gras } else {
7630c3983b2SBen Gras to += length;
7640c3983b2SBen Gras *to++ = ')';
7650c3983b2SBen Gras length = strlen(trail);
7660c3983b2SBen Gras (void) memmove(to, trail, length + 1);
7670c3983b2SBen Gras }
7680c3983b2SBen Gras }
7690c3983b2SBen Gras }
7700c3983b2SBen Gras
7710c3983b2SBen Gras return 0;
7720c3983b2SBen Gras }
7730c3983b2SBen Gras
7740c3983b2SBen Gras static char *
nroff(const char * inname,gzFile * in)7750c3983b2SBen Gras nroff(const char *inname, gzFile *in)
7760c3983b2SBen Gras {
7770c3983b2SBen Gras char tempname[MAXPATHLEN], buffer[65536], *data;
7780c3983b2SBen Gras int tempfd, bytes, pipefd[2], status;
7790c3983b2SBen Gras static int devnull = -1;
7800c3983b2SBen Gras pid_t child;
7810c3983b2SBen Gras
7820c3983b2SBen Gras if (gzrewind(in) < 0)
7830c3983b2SBen Gras err(EXIT_FAILURE, "Cannot rewind pipe");
7840c3983b2SBen Gras
7850c3983b2SBen Gras if ((devnull < 0) &&
7860c3983b2SBen Gras ((devnull = open(_PATH_DEVNULL, O_WRONLY, 0)) < 0))
7870c3983b2SBen Gras err(EXIT_FAILURE, "Cannot open `/dev/null'");
7880c3983b2SBen Gras
7890c3983b2SBen Gras (void)strlcpy(tempname, _PATH_TMP "makewhatis.XXXXXX",
7900c3983b2SBen Gras sizeof(tempname));
7910c3983b2SBen Gras if ((tempfd = mkstemp(tempname)) == -1)
7920c3983b2SBen Gras err(EXIT_FAILURE, "Cannot create temp file");
7930c3983b2SBen Gras
7940c3983b2SBen Gras while ((bytes = gzread(in, buffer, sizeof(buffer))) > 0)
7950c3983b2SBen Gras if (write(tempfd, buffer, (size_t)bytes) != bytes) {
7960c3983b2SBen Gras bytes = -1;
7970c3983b2SBen Gras break;
7980c3983b2SBen Gras }
7990c3983b2SBen Gras
8000c3983b2SBen Gras if (bytes < 0) {
8010c3983b2SBen Gras (void)close(tempfd);
8020c3983b2SBen Gras (void)unlink(tempname);
8030c3983b2SBen Gras err(EXIT_FAILURE, "Read from pipe failed");
8040c3983b2SBen Gras }
8050c3983b2SBen Gras if (lseek(tempfd, (off_t)0, SEEK_SET) == (off_t)-1) {
8060c3983b2SBen Gras (void)close(tempfd);
8070c3983b2SBen Gras (void)unlink(tempname);
8080c3983b2SBen Gras err(EXIT_FAILURE, "Cannot rewind temp file");
8090c3983b2SBen Gras }
8100c3983b2SBen Gras if (pipe(pipefd) == -1) {
8110c3983b2SBen Gras (void)close(tempfd);
8120c3983b2SBen Gras (void)unlink(tempname);
8130c3983b2SBen Gras err(EXIT_FAILURE, "Cannot create pipe");
8140c3983b2SBen Gras }
8150c3983b2SBen Gras
8160c3983b2SBen Gras switch (child = vfork()) {
8170c3983b2SBen Gras case -1:
8180c3983b2SBen Gras (void)close(pipefd[1]);
8190c3983b2SBen Gras (void)close(pipefd[0]);
8200c3983b2SBen Gras (void)close(tempfd);
8210c3983b2SBen Gras (void)unlink(tempname);
8220c3983b2SBen Gras err(EXIT_FAILURE, "Fork failed");
8230c3983b2SBen Gras /* NOTREACHED */
8240c3983b2SBen Gras case 0:
8250c3983b2SBen Gras (void)close(pipefd[0]);
8260c3983b2SBen Gras if (tempfd != STDIN_FILENO) {
8270c3983b2SBen Gras (void)dup2(tempfd, STDIN_FILENO);
8280c3983b2SBen Gras (void)close(tempfd);
8290c3983b2SBen Gras }
8300c3983b2SBen Gras if (pipefd[1] != STDOUT_FILENO) {
8310c3983b2SBen Gras (void)dup2(pipefd[1], STDOUT_FILENO);
8320c3983b2SBen Gras (void)close(pipefd[1]);
8330c3983b2SBen Gras }
8340c3983b2SBen Gras if (devnull != STDERR_FILENO) {
8350c3983b2SBen Gras (void)dup2(devnull, STDERR_FILENO);
8360c3983b2SBen Gras (void)close(devnull);
8370c3983b2SBen Gras }
8380c3983b2SBen Gras (void)execlp(NROFF, NROFF, "-S", "-man", NULL);
8390c3983b2SBen Gras _exit(EXIT_FAILURE);
8400c3983b2SBen Gras /*NOTREACHED*/
8410c3983b2SBen Gras default:
8420c3983b2SBen Gras (void)close(pipefd[1]);
8430c3983b2SBen Gras (void)close(tempfd);
8440c3983b2SBen Gras break;
8450c3983b2SBen Gras }
8460c3983b2SBen Gras
8470c3983b2SBen Gras if ((in = gzdopen(pipefd[0], "r")) == NULL) {
8480c3983b2SBen Gras if (errno == 0)
8490c3983b2SBen Gras errno = ENOMEM;
8500c3983b2SBen Gras (void)close(pipefd[0]);
8510c3983b2SBen Gras (void)kill(child, SIGTERM);
8520c3983b2SBen Gras while (waitpid(child, NULL, 0) != child);
8530c3983b2SBen Gras (void)unlink(tempname);
8540c3983b2SBen Gras err(EXIT_FAILURE, "Cannot read from pipe");
8550c3983b2SBen Gras }
8560c3983b2SBen Gras
8570c3983b2SBen Gras data = parsecatpage(inname, in);
8580c3983b2SBen Gras while (gzread(in, buffer, sizeof(buffer)) > 0);
8590c3983b2SBen Gras (void)gzclose(in);
8600c3983b2SBen Gras
8610c3983b2SBen Gras while (waitpid(child, &status, 0) != child);
8620c3983b2SBen Gras if ((data != NULL) &&
8630c3983b2SBen Gras !(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) {
8640c3983b2SBen Gras free(data);
8650c3983b2SBen Gras errx(EXIT_FAILURE, NROFF " on `%s' exited with %d status",
8660c3983b2SBen Gras inname, WEXITSTATUS(status));
8670c3983b2SBen Gras }
8680c3983b2SBen Gras
8690c3983b2SBen Gras (void)unlink(tempname);
8700c3983b2SBen Gras return data;
8710c3983b2SBen Gras }
8720c3983b2SBen Gras
8730c3983b2SBen Gras static char *
parsemanpage(const char * name,gzFile * in,int defaultsection)8740c3983b2SBen Gras parsemanpage(const char *name, gzFile *in, int defaultsection)
8750c3983b2SBen Gras {
8760c3983b2SBen Gras char *section, buffer[8192], *ptr;
877910825eeSBen Gras static const char POD[] = ".\\\" Automatically generated by Pod";
878910825eeSBen Gras static const char IX[] = ".IX TITLE";
8790c3983b2SBen Gras
8800c3983b2SBen Gras section = NULL;
8810c3983b2SBen Gras do {
8820c3983b2SBen Gras if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) {
8830c3983b2SBen Gras free(section);
8840c3983b2SBen Gras return NULL;
8850c3983b2SBen Gras }
886910825eeSBen Gras
887910825eeSBen Gras /*
888910825eeSBen Gras * Skip over lines in man pages that have been generated
889*84d9c625SLionel Sambuc * by Pod, until we find the TITLE.
890910825eeSBen Gras */
891910825eeSBen Gras if (strncasecmp(buffer, POD, sizeof(POD) - 1) == 0) {
892910825eeSBen Gras do {
893910825eeSBen Gras if (GetS(in, buffer, sizeof(buffer) - 1)
894910825eeSBen Gras == NULL) {
895910825eeSBen Gras free(section);
896910825eeSBen Gras return NULL;
897910825eeSBen Gras }
898910825eeSBen Gras } while (strncasecmp(buffer, IX, sizeof(IX) - 1) != 0);
899910825eeSBen Gras }
900910825eeSBen Gras
9010c3983b2SBen Gras if (manpreprocess(buffer))
9020c3983b2SBen Gras continue;
9030c3983b2SBen Gras if (strncasecmp(buffer, ".Dt", 3) == 0) {
9040c3983b2SBen Gras char *end;
9050c3983b2SBen Gras
9060c3983b2SBen Gras ptr = &buffer[3];
9070c3983b2SBen Gras if (ISSPACE(*ptr))
9080c3983b2SBen Gras ptr++;
9090c3983b2SBen Gras if ((ptr = findwhitespace(ptr)) == NULL)
9100c3983b2SBen Gras continue;
9110c3983b2SBen Gras
9120c3983b2SBen Gras if ((end = findwhitespace(++ptr)) != NULL)
9130c3983b2SBen Gras *end = '\0';
9140c3983b2SBen Gras
9150c3983b2SBen Gras free(section);
9160c3983b2SBen Gras section = createsectionstring(ptr);
9170c3983b2SBen Gras }
9180c3983b2SBen Gras else if (strncasecmp(buffer, ".TH", 3) == 0) {
9190c3983b2SBen Gras ptr = &buffer[3];
9200c3983b2SBen Gras while (ISSPACE(*ptr))
9210c3983b2SBen Gras ptr++;
9220c3983b2SBen Gras if ((ptr = findwhitespace(ptr)) != NULL) {
9230c3983b2SBen Gras char *next;
9240c3983b2SBen Gras
9250c3983b2SBen Gras while (ISSPACE(*ptr))
9260c3983b2SBen Gras ptr++;
9270c3983b2SBen Gras if ((next = findwhitespace(ptr)) != NULL)
9280c3983b2SBen Gras *next = '\0';
9290c3983b2SBen Gras free(section);
9300c3983b2SBen Gras section = createsectionstring(ptr);
9310c3983b2SBen Gras }
9320c3983b2SBen Gras }
9330c3983b2SBen Gras else if (strncasecmp(buffer, ".Ds", 3) == 0) {
9340c3983b2SBen Gras free(section);
9350c3983b2SBen Gras return NULL;
9360c3983b2SBen Gras }
9370c3983b2SBen Gras } while (strncasecmp(buffer, ".Sh NAME", 8) != 0);
9380c3983b2SBen Gras
9390c3983b2SBen Gras do {
9400c3983b2SBen Gras if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) {
9410c3983b2SBen Gras free(section);
9420c3983b2SBen Gras return NULL;
9430c3983b2SBen Gras }
9440c3983b2SBen Gras } while (manpreprocess(buffer));
9450c3983b2SBen Gras
9460c3983b2SBen Gras if (strncasecmp(buffer, ".Nm", 3) == 0) {
9470c3983b2SBen Gras size_t length, offset;
9480c3983b2SBen Gras
9490c3983b2SBen Gras ptr = &buffer[3];
9500c3983b2SBen Gras while (ISSPACE(*ptr))
9510c3983b2SBen Gras ptr++;
9520c3983b2SBen Gras
9530c3983b2SBen Gras length = strlen(ptr);
9540c3983b2SBen Gras if ((length > 1) && (ptr[length - 1] == ',') &&
9550c3983b2SBen Gras ISSPACE(ptr[length - 2])) {
9560c3983b2SBen Gras ptr[--length] = '\0';
9570c3983b2SBen Gras ptr[length - 1] = ',';
9580c3983b2SBen Gras }
9590c3983b2SBen Gras (void) memmove(buffer, ptr, length + 1);
9600c3983b2SBen Gras
9610c3983b2SBen Gras offset = length + 3;
9620c3983b2SBen Gras ptr = &buffer[offset];
9630c3983b2SBen Gras for (;;) {
9640c3983b2SBen Gras size_t more;
9650c3983b2SBen Gras
9660c3983b2SBen Gras if ((sizeof(buffer) == offset) ||
9670c3983b2SBen Gras (GetS(in, ptr, sizeof(buffer) - offset)
9680c3983b2SBen Gras == NULL)) {
9690c3983b2SBen Gras free(section);
9700c3983b2SBen Gras return NULL;
9710c3983b2SBen Gras }
9720c3983b2SBen Gras if (manpreprocess(ptr))
9730c3983b2SBen Gras continue;
9740c3983b2SBen Gras
9750c3983b2SBen Gras if (strncasecmp(ptr, ".Nm", 3) != 0) break;
9760c3983b2SBen Gras
9770c3983b2SBen Gras ptr += 3;
9780c3983b2SBen Gras if (ISSPACE(*ptr))
9790c3983b2SBen Gras ptr++;
9800c3983b2SBen Gras
9810c3983b2SBen Gras buffer[length++] = ' ';
9820c3983b2SBen Gras more = strlen(ptr);
9830c3983b2SBen Gras if ((more > 1) && (ptr[more - 1] == ',') &&
9840c3983b2SBen Gras ISSPACE(ptr[more - 2])) {
9850c3983b2SBen Gras ptr[--more] = '\0';
9860c3983b2SBen Gras ptr[more - 1] = ',';
9870c3983b2SBen Gras }
9880c3983b2SBen Gras
9890c3983b2SBen Gras (void) memmove(&buffer[length], ptr, more + 1);
9900c3983b2SBen Gras length += more;
9910c3983b2SBen Gras offset = length + 3;
9920c3983b2SBen Gras
9930c3983b2SBen Gras ptr = &buffer[offset];
9940c3983b2SBen Gras }
9950c3983b2SBen Gras
9960c3983b2SBen Gras if (strncasecmp(ptr, ".Nd", 3) == 0) {
9970c3983b2SBen Gras (void) strlcpy(&buffer[length], " -",
9980c3983b2SBen Gras sizeof(buffer) - length);
9990c3983b2SBen Gras
10000c3983b2SBen Gras while (strncasecmp(ptr, ".Sh", 3) != 0) {
10010c3983b2SBen Gras int more;
10020c3983b2SBen Gras
10030c3983b2SBen Gras if (*ptr == '.') {
10040c3983b2SBen Gras char *space;
10050c3983b2SBen Gras
10060c3983b2SBen Gras if (strncasecmp(ptr, ".Nd", 3) != 0 ||
10070c3983b2SBen Gras strchr(ptr, '[') != NULL) {
10080c3983b2SBen Gras free(section);
10090c3983b2SBen Gras return NULL;
10100c3983b2SBen Gras }
10110c3983b2SBen Gras space = findwhitespace(ptr);
10120c3983b2SBen Gras if (space == NULL) {
10130c3983b2SBen Gras ptr = "";
10140c3983b2SBen Gras } else {
10150c3983b2SBen Gras space++;
10160c3983b2SBen Gras (void) strmove(ptr, space);
10170c3983b2SBen Gras }
10180c3983b2SBen Gras }
10190c3983b2SBen Gras
10200c3983b2SBen Gras if (*ptr != '\0') {
10210c3983b2SBen Gras buffer[offset - 1] = ' ';
10220c3983b2SBen Gras more = strlen(ptr) + 1;
10230c3983b2SBen Gras offset += more;
10240c3983b2SBen Gras }
10250c3983b2SBen Gras ptr = &buffer[offset];
10260c3983b2SBen Gras if ((sizeof(buffer) == offset) ||
10270c3983b2SBen Gras (GetS(in, ptr, sizeof(buffer) - offset)
10280c3983b2SBen Gras == NULL)) {
10290c3983b2SBen Gras free(section);
10300c3983b2SBen Gras return NULL;
10310c3983b2SBen Gras }
10320c3983b2SBen Gras if (manpreprocess(ptr))
10330c3983b2SBen Gras *ptr = '\0';
10340c3983b2SBen Gras }
10350c3983b2SBen Gras }
10360c3983b2SBen Gras }
10370c3983b2SBen Gras else {
10380c3983b2SBen Gras int offset;
10390c3983b2SBen Gras
10400c3983b2SBen Gras if (*buffer == '.') {
10410c3983b2SBen Gras char *space;
10420c3983b2SBen Gras
10430c3983b2SBen Gras if ((space = findwhitespace(&buffer[1])) == NULL) {
10440c3983b2SBen Gras free(section);
10450c3983b2SBen Gras return NULL;
10460c3983b2SBen Gras }
10470c3983b2SBen Gras space++;
10480c3983b2SBen Gras (void) strmove(buffer, space);
10490c3983b2SBen Gras }
10500c3983b2SBen Gras
10510c3983b2SBen Gras offset = strlen(buffer) + 1;
10520c3983b2SBen Gras for (;;) {
10530c3983b2SBen Gras int more;
10540c3983b2SBen Gras
10550c3983b2SBen Gras ptr = &buffer[offset];
10560c3983b2SBen Gras if ((sizeof(buffer) == offset) ||
10570c3983b2SBen Gras (GetS(in, ptr, sizeof(buffer) - offset)
10580c3983b2SBen Gras == NULL)) {
10590c3983b2SBen Gras free(section);
10600c3983b2SBen Gras return NULL;
10610c3983b2SBen Gras }
10620c3983b2SBen Gras if (manpreprocess(ptr) || (*ptr == '\0'))
10630c3983b2SBen Gras continue;
10640c3983b2SBen Gras
10650c3983b2SBen Gras if ((strncasecmp(ptr, ".Sh", 3) == 0) ||
10660c3983b2SBen Gras (strncasecmp(ptr, ".Ss", 3) == 0))
10670c3983b2SBen Gras break;
10680c3983b2SBen Gras
10690c3983b2SBen Gras if (*ptr == '.') {
10700c3983b2SBen Gras char *space;
10710c3983b2SBen Gras
10720c3983b2SBen Gras if ((space = findwhitespace(ptr)) == NULL) {
10730c3983b2SBen Gras continue;
10740c3983b2SBen Gras }
10750c3983b2SBen Gras
10760c3983b2SBen Gras space++;
10770c3983b2SBen Gras (void) memmove(ptr, space, strlen(space) + 1);
10780c3983b2SBen Gras }
10790c3983b2SBen Gras
10800c3983b2SBen Gras buffer[offset - 1] = ' ';
10810c3983b2SBen Gras more = strlen(ptr);
10820c3983b2SBen Gras if ((more > 1) && (ptr[more - 1] == ',') &&
10830c3983b2SBen Gras ISSPACE(ptr[more - 2])) {
10840c3983b2SBen Gras ptr[more - 1] = '\0';
10850c3983b2SBen Gras ptr[more - 2] = ',';
10860c3983b2SBen Gras }
10870c3983b2SBen Gras else more++;
10880c3983b2SBen Gras offset += more;
10890c3983b2SBen Gras }
10900c3983b2SBen Gras }
10910c3983b2SBen Gras
10920c3983b2SBen Gras if (section == NULL)
10930c3983b2SBen Gras section = makesection(defaultsection);
10940c3983b2SBen Gras
10950c3983b2SBen Gras ptr = makewhatisline(name, buffer, section);
10960c3983b2SBen Gras free(section);
10970c3983b2SBen Gras return ptr;
10980c3983b2SBen Gras }
10990c3983b2SBen Gras
11000c3983b2SBen Gras static char *
getwhatisdata(char * name)11010c3983b2SBen Gras getwhatisdata(char *name)
11020c3983b2SBen Gras {
11030c3983b2SBen Gras gzFile *in;
11040c3983b2SBen Gras char *data;
11050c3983b2SBen Gras int section;
11060c3983b2SBen Gras
11070c3983b2SBen Gras if ((in = gzopen(name, "r")) == NULL) {
11080c3983b2SBen Gras if (errno == 0)
11090c3983b2SBen Gras errno = ENOMEM;
11100c3983b2SBen Gras err(EXIT_FAILURE, "Cannot open `%s'", name);
11110c3983b2SBen Gras /* NOTREACHED */
11120c3983b2SBen Gras }
11130c3983b2SBen Gras
11140c3983b2SBen Gras section = manpagesection(name);
11150c3983b2SBen Gras if (section == 0) {
11160c3983b2SBen Gras data = parsecatpage(name, in);
11170c3983b2SBen Gras } else {
11180c3983b2SBen Gras data = parsemanpage(name, in, section);
11190c3983b2SBen Gras if (data == NULL)
11200c3983b2SBen Gras data = nroff(name, in);
11210c3983b2SBen Gras }
11220c3983b2SBen Gras
11230c3983b2SBen Gras (void) gzclose(in);
11240c3983b2SBen Gras return data;
11250c3983b2SBen Gras }
11260c3983b2SBen Gras
11270c3983b2SBen Gras static void
processmanpages(manpage ** source,whatis ** dest)11280c3983b2SBen Gras processmanpages(manpage **source, whatis **dest)
11290c3983b2SBen Gras {
11300c3983b2SBen Gras manpage *mp;
11310c3983b2SBen Gras char sd[128];
11320c3983b2SBen Gras
11330c3983b2SBen Gras mp = *source;
11340c3983b2SBen Gras *source = NULL;
11350c3983b2SBen Gras
11360c3983b2SBen Gras while (mp != NULL) {
11370c3983b2SBen Gras manpage *obsolete;
11380c3983b2SBen Gras char *data;
11390c3983b2SBen Gras
11400c3983b2SBen Gras if (mp->mp_left != NULL)
11410c3983b2SBen Gras processmanpages(&mp->mp_left, dest);
11420c3983b2SBen Gras
11430c3983b2SBen Gras if ((data = getwhatisdata(mp->mp_name)) != NULL) {
11440c3983b2SBen Gras /* Pass eventual directory prefix to addwhatis() */
11450c3983b2SBen Gras if (mp->mp_sdlen > 0 && mp->mp_sdlen < sizeof(sd)-1)
11460c3983b2SBen Gras strlcpy(sd, &mp->mp_name[mp->mp_sdoff],
11470c3983b2SBen Gras mp->mp_sdlen);
11480c3983b2SBen Gras else
11490c3983b2SBen Gras sd[0] = '\0';
11500c3983b2SBen Gras
11510c3983b2SBen Gras addwhatis(dest, data, sd);
11520c3983b2SBen Gras }
11530c3983b2SBen Gras
11540c3983b2SBen Gras obsolete = mp;
11550c3983b2SBen Gras mp = mp->mp_right;
11560c3983b2SBen Gras free(obsolete);
11570c3983b2SBen Gras }
11580c3983b2SBen Gras }
11590c3983b2SBen Gras
11600c3983b2SBen Gras static void
dumpwhatis(FILE * out,whatis * tree)11610c3983b2SBen Gras dumpwhatis(FILE *out, whatis *tree)
11620c3983b2SBen Gras {
11630c3983b2SBen Gras while (tree != NULL) {
11640c3983b2SBen Gras if (tree->wi_left)
11650c3983b2SBen Gras dumpwhatis(out, tree->wi_left);
11660c3983b2SBen Gras
11670c3983b2SBen Gras if ((tree->wi_data[0] && fputs(tree->wi_prefix, out) == EOF) ||
11680c3983b2SBen Gras (fputs(tree->wi_data, out) == EOF) ||
11690c3983b2SBen Gras (fputc('\n', out) == EOF))
11700c3983b2SBen Gras err(EXIT_FAILURE, "Write failed");
11710c3983b2SBen Gras
11720c3983b2SBen Gras tree = tree->wi_right;
11730c3983b2SBen Gras }
11740c3983b2SBen Gras }
1175