xref: /netbsd-src/usr.sbin/catman/catman.c (revision d89a86ddb3b595937f1859835e82c83167b2e85c)
1*d89a86ddSchristos /*      $NetBSD: catman.c,v 1.38 2019/10/12 17:26:26 christos Exp $       */
2bea74446Sdante 
378df5febSjtc /*
4bea74446Sdante  * Copyright (c) 1998 The NetBSD Foundation, Inc.
578df5febSjtc  * All rights reserved.
678df5febSjtc  *
7bea74446Sdante  * Author: Baldassare Dante Profeta <dante@mclink.it>
8bea74446Sdante  *
978df5febSjtc  * Redistribution and use in source and binary forms, with or without
1078df5febSjtc  * modification, are permitted provided that the following conditions
1178df5febSjtc  * are met:
1278df5febSjtc  * 1. Redistributions of source code must retain the above copyright
1378df5febSjtc  *    notice, this list of conditions and the following disclaimer.
1478df5febSjtc  * 2. Redistributions in binary form must reproduce the above copyright
1578df5febSjtc  *    notice, this list of conditions and the following disclaimer in the
1678df5febSjtc  *    documentation and/or other materials provided with the distribution.
1778df5febSjtc  *
18bea74446Sdante  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19bea74446Sdante  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20bea74446Sdante  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21bea74446Sdante  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22bea74446Sdante  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23bea74446Sdante  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24bea74446Sdante  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25bea74446Sdante  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26bea74446Sdante  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27bea74446Sdante  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28bea74446Sdante  * POSSIBILITY OF SUCH DAMAGE.
2978df5febSjtc  */
3078df5febSjtc 
31db0374afSdholland #include <sys/cdefs.h>
32db0374afSdholland #ifndef lint
33*d89a86ddSchristos __RCSID("$NetBSD: catman.c,v 1.38 2019/10/12 17:26:26 christos Exp $");
34db0374afSdholland #endif /* not lint */
35db0374afSdholland 
36afaaa071Schopps #include <sys/types.h>
37bea74446Sdante #include <sys/queue.h>
38bea74446Sdante #include <sys/param.h>
39afaaa071Schopps #include <sys/stat.h>
40afaaa071Schopps #include <sys/wait.h>
415cb06e64Stsutsui #include <sys/utsname.h>
42bea74446Sdante #include <ctype.h>
43dd243c0bScgd #include <dirent.h>
44dd243c0bScgd #include <err.h>
45dd243c0bScgd #include <errno.h>
46bea74446Sdante #include <fnmatch.h>
47dd243c0bScgd #include <limits.h>
48bea74446Sdante #include <libgen.h>
4978df5febSjtc #include <stdio.h>
5078df5febSjtc #include <stdlib.h>
5178df5febSjtc #include <string.h>
5278df5febSjtc #include <unistd.h>
53bea74446Sdante #include <glob.h>
5478df5febSjtc 
55a939a620Smycroft #include "manconf.h"
56dd243c0bScgd #include "pathnames.h"
57afaaa071Schopps 
58bea74446Sdante int f_nowhatis = 0;
59bea74446Sdante int f_noaction = 0;
60bea74446Sdante int f_noformat = 0;
61bea74446Sdante int f_ignerr = 0;
62bea74446Sdante int f_noprint = 0;
63bea74446Sdante int dowhatis = 0;
6478df5febSjtc 
6528324fdaSjdolecek TAG *defp;	/* pointer to _default list */
6628324fdaSjdolecek 
67d09fe2c4Schuck static void	setdefentries(char *, char *, const char *);
68d09fe2c4Schuck static void	uniquepath(void);
69d09fe2c4Schuck static void	catman(void);
70d09fe2c4Schuck static void	scanmandir(const char *, const char *);
71d09fe2c4Schuck static int	splitentry(char *, char *, size_t, char *, size_t);
72d09fe2c4Schuck static void	setcatsuffix(char *, const char *, const char *);
73d09fe2c4Schuck static void	makecat(const char *, const char *, const char *, const char *);
74d09fe2c4Schuck static void	makewhatis(void);
75d09fe2c4Schuck static void	dosystem(const char *);
76dccf569eSjoerg __dead static void	usage(void);
77bea74446Sdante 
7878df5febSjtc 
7978df5febSjtc int
main(int argc,char * const * argv)80d09fe2c4Schuck main(int argc, char * const *argv)
8178df5febSjtc {
82bea74446Sdante 	char *m_path = NULL;
83bea74446Sdante 	char *m_add = NULL;
8478df5febSjtc 	int c;
8578df5febSjtc 
86bea74446Sdante 	while ((c = getopt(argc, argv, "km:M:npsw")) != -1) {
8778df5febSjtc 		switch (c) {
88afaaa071Schopps 		case 'k':
89afaaa071Schopps 			f_ignerr = 1;
90afaaa071Schopps 			break;
9178df5febSjtc 		case 'n':
92afaaa071Schopps 			f_nowhatis = 1;
9378df5febSjtc 			break;
9478df5febSjtc 		case 'p':
95afaaa071Schopps 			f_noaction = 1;
96afaaa071Schopps 			break;
97afaaa071Schopps 		case 's':
98afaaa071Schopps 			f_noprint = 1;
9978df5febSjtc 			break;
10078df5febSjtc 		case 'w':
101afaaa071Schopps 			f_noformat = 1;
10278df5febSjtc 			break;
103bea74446Sdante 		case 'm':
104bea74446Sdante 			m_add = optarg;
105bea74446Sdante 			break;
10678df5febSjtc 		case 'M':
107bea74446Sdante 			m_path = optarg;
10878df5febSjtc 			break;
10978df5febSjtc 		default:
11078df5febSjtc 			usage();
11178df5febSjtc 		}
11278df5febSjtc 	}
11378df5febSjtc 
11478df5febSjtc 	argc -= optind;
11578df5febSjtc 	argv += optind;
11678df5febSjtc 
117afaaa071Schopps 	if (f_noprint && f_noaction)
118afaaa071Schopps 		f_noprint = 0;
119afaaa071Schopps 
120dd243c0bScgd 	if (argc > 1)
121afaaa071Schopps 		usage();
122bea74446Sdante 
123bea74446Sdante 	config(_PATH_MANCONF);
124bea74446Sdante 	setdefentries(m_path, m_add, (argc == 0) ? NULL : argv[argc-1]);
125bea74446Sdante 	uniquepath();
12678df5febSjtc 
127fb4bff17Schopps 	if (f_noformat == 0 || f_nowhatis == 0)
128bea74446Sdante 		catman();
129afaaa071Schopps 	if (f_nowhatis == 0 && dowhatis)
130bea74446Sdante 		makewhatis();
13178df5febSjtc 
132bea74446Sdante 	return(0);
13378df5febSjtc }
13478df5febSjtc 
135bea74446Sdante static void
setdefentries(char * m_path,char * m_add,const char * sections)136d09fe2c4Schuck setdefentries(char *m_path, char *m_add, const char *sections)
13778df5febSjtc {
13828324fdaSjdolecek 	TAG *defnewp, *sectnewp, *subp;
13928324fdaSjdolecek 	ENTRY *e_defp, *e_subp;
140b2bb6949Sxtraeme 	const char *p, *slashp;
141b2bb6949Sxtraeme 	char *machine;
142bea74446Sdante 	char buf[MAXPATHLEN * 2];
143bea74446Sdante 	int i;
144bea74446Sdante 
145bea74446Sdante 	/* Get the machine type. */
1465cb06e64Stsutsui 	if ((machine = getenv("MACHINE")) == NULL) {
1475cb06e64Stsutsui 		struct utsname utsname;
1485cb06e64Stsutsui 
1495cb06e64Stsutsui 		if (uname(&utsname) == -1) {
1505cb06e64Stsutsui 			perror("uname");
1515cb06e64Stsutsui 			exit(1);
1525cb06e64Stsutsui 		}
1535cb06e64Stsutsui 		machine = utsname.machine;
1545cb06e64Stsutsui 	}
155bea74446Sdante 
156bea74446Sdante 	/* If there's no _default list, create an empty one. */
157d09fe2c4Schuck 	defp = gettag("_default", 1);
158d09fe2c4Schuck 	subp = gettag("_subdir", 1);
159d09fe2c4Schuck 	if (defp == NULL || subp == NULL)
160d09fe2c4Schuck 		err(1, "malloc");
16128324fdaSjdolecek 
162bea74446Sdante 	/*
163bea74446Sdante 	 * 0: If one or more sections was specified, rewrite _subdir list.
164bea74446Sdante 	 */
165bea74446Sdante 	if (sections != NULL) {
166d09fe2c4Schuck 		if ((sectnewp = gettag("_section_new", 1)) == NULL)
167d09fe2c4Schuck 			err(1, "malloc");
168bea74446Sdante 		for (p = sections; *p;) {
16928324fdaSjdolecek 			i = snprintf(buf, sizeof(buf), "man%c", *p++);
170d8a2ebd2Slukem 			for (; *p && !isdigit((unsigned char)*p) && i < (int)sizeof(buf) - 1; i++)
17128324fdaSjdolecek 				buf[i] = *p++;
17228324fdaSjdolecek 			buf[i] = '\0';
173d09fe2c4Schuck 			if (addentry(sectnewp, buf, 0) < 0)
174d09fe2c4Schuck 				err(1, "malloc");
175bea74446Sdante 		}
17628324fdaSjdolecek 		subp = sectnewp;
177bea74446Sdante 	}
178bea74446Sdante 
179bea74446Sdante 	/*
180bea74446Sdante 	 * 1: If the user specified a MANPATH variable, or set the -M
181bea74446Sdante 	 *    option, we replace the _default list with the user's list,
182bea74446Sdante 	 *    appending the entries in the _subdir list and the machine.
183bea74446Sdante 	 */
184bea74446Sdante 	if (m_path == NULL)
185bea74446Sdante 		m_path = getenv("MANPATH");
186bea74446Sdante 	if (m_path != NULL) {
187d09fe2c4Schuck 		while ((e_defp = TAILQ_FIRST(&defp->entrylist)) != NULL) {
188bea74446Sdante 			free(e_defp->s);
189d09fe2c4Schuck 			TAILQ_REMOVE(&defp->entrylist, e_defp, q);
190bea74446Sdante 		}
191bea74446Sdante 		for (p = strtok(m_path, ":");
192bea74446Sdante 		    p != NULL; p = strtok(NULL, ":")) {
193bea74446Sdante 			slashp = p[strlen(p) - 1] == '/' ? "" : "/";
194d09fe2c4Schuck 			TAILQ_FOREACH(e_subp, &subp->entrylist, q) {
195bea74446Sdante 				if (!strncmp(e_subp->s, "cat", 3))
196bea74446Sdante 					continue;
197bea74446Sdante 				(void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}",
198bea74446Sdante 				    p, slashp, e_subp->s, machine);
199d09fe2c4Schuck 				if (addentry(defp, buf, 0) < 0)
200d09fe2c4Schuck 					err(1, "malloc");
201bea74446Sdante 			}
202bea74446Sdante 		}
203bea74446Sdante 	}
204bea74446Sdante 
205bea74446Sdante 	/*
206bea74446Sdante 	 * 2: If the user did not specify MANPATH, -M or a section, rewrite
207bea74446Sdante 	 *    the _default list to include the _subdir list and the machine.
208bea74446Sdante 	 */
209bea74446Sdante 	if (m_path == NULL) {
210d09fe2c4Schuck 		defp = gettag("_default", 1);
211d09fe2c4Schuck 		defnewp = gettag("_default_new1", 1);
212d09fe2c4Schuck 		if (defp == NULL || defnewp == NULL)
213d09fe2c4Schuck 			err(1, "malloc");
214334bc46aSlukem 
215d09fe2c4Schuck 		TAILQ_FOREACH(e_defp, &defp->entrylist, q) {
216bea74446Sdante 			slashp =
217bea74446Sdante 			    e_defp->s[strlen(e_defp->s) - 1] == '/' ? "" : "/";
218d09fe2c4Schuck 			TAILQ_FOREACH(e_subp, &subp->entrylist, q) {
219bea74446Sdante 				if (!strncmp(e_subp->s, "cat", 3))
220bea74446Sdante 					continue;
221bea74446Sdante 				(void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}",
222bea74446Sdante 					e_defp->s, slashp, e_subp->s, machine);
223d09fe2c4Schuck 				if (addentry(defnewp, buf, 0) < 0)
224d09fe2c4Schuck 					err(1, "malloc");
225bea74446Sdante 			}
226bea74446Sdante 		}
22728324fdaSjdolecek 		defp = defnewp;
228bea74446Sdante 	}
229bea74446Sdante 
230bea74446Sdante 	/*
231bea74446Sdante 	 * 3: If the user set the -m option, insert the user's list before
232bea74446Sdante 	 *    whatever list we have, again appending the _subdir list and
233bea74446Sdante 	 *    the machine.
234bea74446Sdante 	 */
235bea74446Sdante 	if (m_add != NULL)
236bea74446Sdante 		for (p = strtok(m_add, ":"); p != NULL; p = strtok(NULL, ":")) {
237bea74446Sdante 			slashp = p[strlen(p) - 1] == '/' ? "" : "/";
238d09fe2c4Schuck 			TAILQ_FOREACH(e_subp, &subp->entrylist, q) {
239bea74446Sdante 				if (!strncmp(e_subp->s, "cat", 3))
240bea74446Sdante 					continue;
241bea74446Sdante 				(void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}",
242bea74446Sdante 				    p, slashp, e_subp->s, machine);
243d09fe2c4Schuck 				if (addentry(defp, buf, 1) < 0)
244d09fe2c4Schuck 					err(1, "malloc");
245bea74446Sdante 			}
246bea74446Sdante 		}
247bea74446Sdante }
248bea74446Sdante 
249bea74446Sdante /*
250bea74446Sdante  * Remove entries (directory) which are symbolic links to other entries.
251bea74446Sdante  * Some examples are showed below:
252bea74446Sdante  * 1) if /usr/X11 -> /usr/X11R6 then remove all /usr/X11/man entries.
253bea74446Sdante  * 2) if /usr/local/man -> /usr/share/man then remove all /usr/local/man
254bea74446Sdante  *    entries
255bea74446Sdante  */
256bea74446Sdante static void
uniquepath(void)257bea74446Sdante uniquepath(void)
258bea74446Sdante {
25928324fdaSjdolecek 	TAG *defnewp;
260bea74446Sdante 	ENTRY *e_defp;
261bea74446Sdante 	glob_t manpaths;
262bea74446Sdante 	struct stat st1;
263bea74446Sdante 	struct stat st2;
264bea74446Sdante 	struct stat st3;
2653b6811d3Schristos 	int len, lnk, gflags;
2663b6811d3Schristos 	size_t i, j;
267bea74446Sdante 	char path[PATH_MAX], *p;
268bea74446Sdante 
269334bc46aSlukem 	gflags = 0;
270d09fe2c4Schuck 	TAILQ_FOREACH(e_defp, &defp->entrylist, q) {
271334bc46aSlukem 		glob(e_defp->s, GLOB_BRACE | GLOB_NOSORT | gflags, NULL,
272bea74446Sdante 		    &manpaths);
273334bc46aSlukem 		gflags = GLOB_APPEND;
274bea74446Sdante 	}
275bea74446Sdante 
276d09fe2c4Schuck 	if ((defnewp = gettag("_default_new2", 1)) == NULL)
277d09fe2c4Schuck 		err(1, "malloc");
278bea74446Sdante 
279bea74446Sdante 	for (i = 0; i < manpaths.gl_pathc; i++) {
280bea74446Sdante 		lnk = 0;
281bea74446Sdante 		lstat(manpaths.gl_pathv[i], &st1);
282bea74446Sdante 		for (j = 0; j < manpaths.gl_pathc; j++) {
283bea74446Sdante 			if (i != j) {
284bea74446Sdante 				lstat(manpaths.gl_pathv[j], &st2);
285bea74446Sdante 				if (st1.st_ino == st2.st_ino) {
286d0a2aaaaSitojun 					strlcpy(path, manpaths.gl_pathv[i],
287d0a2aaaaSitojun 					    sizeof(path));
288bea74446Sdante 					for (p = path; *(p+1) != '\0';) {
289bea74446Sdante 						p = dirname(p);
290bea74446Sdante 						lstat(p, &st3);
291bea74446Sdante 						if (S_ISLNK(st3.st_mode)) {
292bea74446Sdante 							lnk = 1;
293bea74446Sdante 							break;
294bea74446Sdante 						}
295bea74446Sdante 					}
296bea74446Sdante 				} else {
297bea74446Sdante 					len = readlink(manpaths.gl_pathv[i],
298830e70f6Sitojun 					    path, sizeof(path) - 1);
299bea74446Sdante 					if (len == -1)
300bea74446Sdante 						continue;
301830e70f6Sitojun 					path[len] = '\0';
302bea74446Sdante 					if (!strcmp(path, manpaths.gl_pathv[j]))
303bea74446Sdante 						lnk = 1;
304bea74446Sdante 				}
305bea74446Sdante 				if (lnk)
306bea74446Sdante 					break;
307bea74446Sdante 			}
308bea74446Sdante 		}
30928324fdaSjdolecek 
310d09fe2c4Schuck 		if (!lnk) {
311d09fe2c4Schuck 			if (addentry(defnewp, manpaths.gl_pathv[i], 0) < 0)
312d09fe2c4Schuck 				err(1, "malloc");
313d09fe2c4Schuck 		}
314bea74446Sdante 	}
315bea74446Sdante 
316bea74446Sdante 	globfree(&manpaths);
317bea74446Sdante 
31828324fdaSjdolecek 	defp = defnewp;
319bea74446Sdante }
320bea74446Sdante 
321bea74446Sdante static void
catman(void)322bea74446Sdante catman(void)
323bea74446Sdante {
324bea74446Sdante 	ENTRY *e_path;
32528324fdaSjdolecek 	const char *mandir;
326bea74446Sdante 	char catdir[PATH_MAX], *cp;
327bea74446Sdante 
328d09fe2c4Schuck 	TAILQ_FOREACH(e_path, &defp->entrylist, q) {
329bea74446Sdante 		mandir = e_path->s;
330d0a2aaaaSitojun 		strlcpy(catdir, mandir, sizeof(catdir));
331bea74446Sdante 		if (!(cp = strstr(catdir, "man/man")))
332bea74446Sdante 			continue;
333bea74446Sdante 		cp += 4; *cp++ = 'c'; *cp++ = 'a'; *cp = 't';
334bea74446Sdante 		scanmandir(catdir, mandir);
335bea74446Sdante 	}
336bea74446Sdante }
337bea74446Sdante 
338bea74446Sdante static void
scanmandir(const char * catdir,const char * mandir)339d09fe2c4Schuck scanmandir(const char *catdir, const char *mandir)
340bea74446Sdante {
341bea74446Sdante 	TAG *buildp, *crunchp;
342bea74446Sdante 	ENTRY *e_build, *e_crunch;
343*d89a86ddSchristos 	char manpage[2 * PATH_MAX];
344*d89a86ddSchristos 	char catpage[2 * PATH_MAX];
345bea74446Sdante 	char linkname[PATH_MAX];
346bea74446Sdante 	char buffer[PATH_MAX], *bp;
347*d89a86ddSchristos 	char tmp[2 * PATH_MAX];
348bea74446Sdante 	char buildsuff[256], buildcmd[256];
349bea74446Sdante 	char crunchsuff[256], crunchcmd[256];
350*d89a86ddSchristos 	char match[2 * 256];
351afaaa071Schopps 	struct stat manstat;
352afaaa071Schopps 	struct stat catstat;
353bea74446Sdante 	struct stat lnkstat;
354afaaa071Schopps 	struct dirent *dp;
355afaaa071Schopps 	DIR *dirp;
356bea74446Sdante 	int len, error;
35778df5febSjtc 
35878df5febSjtc 	if ((dirp = opendir(mandir)) == 0) {
35978df5febSjtc 		warn("can't open %s", mandir);
360bea74446Sdante 		return;
36178df5febSjtc 	}
36278df5febSjtc 
36378df5febSjtc 	if (stat(catdir, &catstat) < 0) {
36478df5febSjtc 		if (errno != ENOENT) {
36578df5febSjtc 			warn("can't stat %s", catdir);
36678df5febSjtc 			closedir(dirp);
367bea74446Sdante 			return;
36878df5febSjtc 		}
369afaaa071Schopps 		if (f_noprint == 0)
37078df5febSjtc 			printf("mkdir %s\n", catdir);
371afaaa071Schopps 		if (f_noaction == 0 && mkdir(catdir, 0755) < 0) {
37278df5febSjtc 			warn("can't create %s", catdir);
37378df5febSjtc 			closedir(dirp);
37478df5febSjtc 			return;
37578df5febSjtc 		}
37678df5febSjtc 	}
37778df5febSjtc 
37878df5febSjtc 	while ((dp = readdir(dirp)) != NULL) {
379afaaa071Schopps 		if (strcmp(dp->d_name, ".") == 0 ||
380afaaa071Schopps 		    strcmp(dp->d_name, "..") == 0)
38178df5febSjtc 			continue;
38278df5febSjtc 
383bea74446Sdante 		snprintf(manpage, sizeof(manpage), "%s/%s", mandir, dp->d_name);
384bea74446Sdante 		snprintf(catpage, sizeof(catpage), "%s/%s", catdir, dp->d_name);
385bea74446Sdante 
386ce477983Smycroft 		e_build = NULL;
387d09fe2c4Schuck 		if ((buildp = gettag("_build", 1)) == NULL)
388d09fe2c4Schuck 			err(1, "malloc");
389d09fe2c4Schuck 		TAILQ_FOREACH(e_build, &buildp->entrylist, q) {
390d0a2aaaaSitojun 			splitentry(e_build->s, buildsuff, sizeof(buildsuff),
391d0a2aaaaSitojun 			    buildcmd, sizeof(buildcmd));
392ce477983Smycroft 			snprintf(match, sizeof(match), "*%s",
393ce477983Smycroft 						buildsuff);
394bea74446Sdante 			if (!fnmatch(match, manpage, 0))
395bea74446Sdante 				break;
396bea74446Sdante 		}
397bea74446Sdante 
398ce477983Smycroft 		if (e_build == NULL)
399afaaa071Schopps 			continue;
400ce477983Smycroft 
401554cdf7fSdante 		e_crunch = NULL;
402d09fe2c4Schuck 		if ((crunchp = gettag("_crunch", 1)) == NULL)
403d09fe2c4Schuck 			err(1, "malloc");
404d09fe2c4Schuck 		TAILQ_FOREACH(e_crunch, &crunchp->entrylist, q) {
405d0a2aaaaSitojun 			splitentry(e_crunch->s, crunchsuff, sizeof(crunchsuff),
406d0a2aaaaSitojun 			    crunchcmd, sizeof(crunchcmd));
407334bc46aSlukem 			snprintf(match, sizeof(match), "*%s", crunchsuff);
408bea74446Sdante 			if (!fnmatch(match, manpage, 0))
409bea74446Sdante 				break;
410bea74446Sdante 		}
411bea74446Sdante 
412bea74446Sdante 		if (lstat(manpage, &manstat) <0) {
413bea74446Sdante 			warn("can't stat %s", manpage);
414bea74446Sdante 			continue;
415bea74446Sdante 		} else {
416bea74446Sdante 			if (S_ISLNK(manstat.st_mode)) {
417d0a2aaaaSitojun 				strlcpy(buffer, catpage, sizeof(buffer));
418d0a2aaaaSitojun 				strlcpy(linkname, basename(buffer),
419d0a2aaaaSitojun 				    sizeof(linkname));
420830e70f6Sitojun 				len = readlink(manpage, buffer,
421830e70f6Sitojun 				    sizeof(buffer) - 1);
422bea74446Sdante 				if (len == -1) {
423bea74446Sdante 					warn("can't stat read symbolic link %s",
424bea74446Sdante 					    manpage);
425bea74446Sdante 					continue;
426bea74446Sdante 				}
427bea74446Sdante 				buffer[len] = '\0';
428deeb488bSsoren 
429deeb488bSsoren 				if (strcmp(buffer, basename(buffer)) == 0) {
430bea74446Sdante 					bp = basename(buffer);
431d0a2aaaaSitojun 					strlcpy(tmp, manpage, sizeof(tmp));
432deeb488bSsoren 					snprintf(manpage, sizeof(manpage),
433deeb488bSsoren 					    "%s/%s", dirname(tmp), bp);
434d0a2aaaaSitojun 					strlcpy(tmp, catpage, sizeof(tmp));
435deeb488bSsoren 					snprintf(catpage, sizeof(catpage),
436deeb488bSsoren 					    "%s/%s", dirname(tmp), buffer);
437deeb488bSsoren 				} else {
438deeb488bSsoren 					*linkname = '\0';
439deeb488bSsoren 				}
440deeb488bSsoren 
441bea74446Sdante 			}
442bea74446Sdante 			else
443bea74446Sdante 				*linkname = '\0';
444bea74446Sdante 		}
445bea74446Sdante 
446bea74446Sdante 		if (!e_crunch) {
447bea74446Sdante 			*crunchsuff = *crunchcmd = '\0';
448bea74446Sdante 		}
449bea74446Sdante 		setcatsuffix(catpage, buildsuff, crunchsuff);
450bea74446Sdante 		if (*linkname != '\0')
451bea74446Sdante 			setcatsuffix(linkname, buildsuff, crunchsuff);
45278df5febSjtc 
45378df5febSjtc 		if (stat(manpage, &manstat) < 0) {
45478df5febSjtc 			warn("can't stat %s", manpage);
45578df5febSjtc 			continue;
45678df5febSjtc 		}
45778df5febSjtc 
45878df5febSjtc 		if (!S_ISREG(manstat.st_mode)) {
45978df5febSjtc 			warnx("not a regular file %s", manpage);
46078df5febSjtc 			continue;
46178df5febSjtc 		}
462bea74446Sdante 
463fb4bff17Schopps 		if ((error = stat(catpage, &catstat)) &&
464fb4bff17Schopps 		    errno != ENOENT) {
46578df5febSjtc 			warn("can't stat %s", catpage);
46678df5febSjtc 			continue;
46778df5febSjtc 		}
46878df5febSjtc 
469fb4bff17Schopps 		if ((error && errno == ENOENT) ||
470a6b6e0dbSmycroft 		    manstat.st_mtime > catstat.st_mtime) {
471bea74446Sdante 			if (f_noformat) {
472fb4bff17Schopps 				dowhatis = 1;
473bea74446Sdante 			} else {
47478df5febSjtc 				/*
475bea74446Sdante 				 * reformat out of date manpage
47678df5febSjtc 				 */
477bea74446Sdante 				makecat(manpage, catpage, buildcmd, crunchcmd);
478afaaa071Schopps 				dowhatis = 1;
47978df5febSjtc 			}
48078df5febSjtc 		}
481bea74446Sdante 
482bea74446Sdante 		if (*linkname != '\0') {
483d0a2aaaaSitojun 			strlcpy(tmp, catpage, sizeof(tmp));
484bea74446Sdante 			snprintf(tmp, sizeof(tmp), "%s/%s", dirname(tmp),
485bea74446Sdante 					linkname);
486bea74446Sdante 			if ((error = lstat(tmp, &lnkstat)) &&
487bea74446Sdante 			    errno != ENOENT) {
488bea74446Sdante 				warn("can't stat %s", tmp);
489bea74446Sdante 				continue;
490bea74446Sdante 			}
491bea74446Sdante 
492bea74446Sdante 			if (error && errno == ENOENT)  {
493bea74446Sdante 				if (f_noformat) {
494bea74446Sdante 					dowhatis = 1;
495bea74446Sdante 				} else {
496bea74446Sdante 					/*
497bea74446Sdante 					 * create symbolic link
498bea74446Sdante 					 */
499bea74446Sdante 					if (f_noprint == 0)
500bea74446Sdante 						printf("ln -s %s %s\n", catpage,
501bea74446Sdante 						    linkname);
502bea74446Sdante 					if (f_noaction == 0) {
503d0a2aaaaSitojun 						strlcpy(tmp, catpage,
504d0a2aaaaSitojun 						    sizeof(tmp));
505bea74446Sdante 						if (chdir(dirname(tmp)) == -1) {
506bea74446Sdante 							warn("can't chdir");
507bea74446Sdante 							continue;
508bea74446Sdante 						}
509bea74446Sdante 
510bea74446Sdante 						if (symlink(catpage, linkname)
511bea74446Sdante 								== -1) {
512bea74446Sdante 							warn("can't create"
513bea74446Sdante 								" symbolic"
514bea74446Sdante 								" link %s",
515bea74446Sdante 								linkname);
516bea74446Sdante 							continue;
517bea74446Sdante 						}
518bea74446Sdante 					}
519bea74446Sdante 					dowhatis = 1;
520bea74446Sdante 				}
521bea74446Sdante 			}
522bea74446Sdante 		}
523fb4bff17Schopps 	}
52478df5febSjtc 	closedir(dirp);
52578df5febSjtc }
526bea74446Sdante 
527bea74446Sdante static int
splitentry(char * s,char * first,size_t firstlen,char * second,size_t secondlen)528d09fe2c4Schuck splitentry(char *s, char *first, size_t firstlen, char *second,
529d09fe2c4Schuck 	   size_t secondlen)
530bea74446Sdante {
531bea74446Sdante 	char *c;
532bea74446Sdante 
533e09553f3Sdsl 	for (c = s; *c != '\0' && !isspace((unsigned char)*c); ++c)
5345397d22fSitojun 		;
535bea74446Sdante 	if (*c == '\0')
536bea74446Sdante 		return(0);
537d8a2ebd2Slukem 	if ((size_t)(c - s + 1) > firstlen)
538d0a2aaaaSitojun 		return(0);
539bea74446Sdante 	strncpy(first, s, c-s);
540bea74446Sdante 	first[c-s] = '\0';
541e09553f3Sdsl 	for (; *c != '\0' && isspace((unsigned char)*c); ++c)
5425397d22fSitojun 		;
543d0a2aaaaSitojun 	if (strlcpy(second, c, secondlen) >= secondlen)
544d0a2aaaaSitojun 		return(0);
545bea74446Sdante 	return(1);
54678df5febSjtc }
54778df5febSjtc 
548bea74446Sdante static void
setcatsuffix(char * catpage,const char * suffix,const char * crunchsuff)549d09fe2c4Schuck setcatsuffix(char *catpage, const char *suffix, const char *crunchsuff)
55078df5febSjtc {
551bea74446Sdante 	TAG *tp;
552bea74446Sdante 	char *p;
55378df5febSjtc 
554bea74446Sdante 	for (p = catpage + strlen(catpage); p != catpage; p--)
555bea74446Sdante 		if (!fnmatch(suffix, p, 0)) {
556d09fe2c4Schuck 			if ((tp = gettag("_suffix", 1)) == NULL)
557d09fe2c4Schuck 				err(1, "malloc");
558d09fe2c4Schuck 			if (! TAILQ_EMPTY(&tp->entrylist)) {
559334bc46aSlukem 				sprintf(p, "%s%s",
560d09fe2c4Schuck 				    TAILQ_FIRST(&tp->entrylist)->s, crunchsuff);
561bea74446Sdante 			} else {
562bea74446Sdante 				sprintf(p, ".0%s", crunchsuff);
563bea74446Sdante 			}
564bea74446Sdante 			break;
565bea74446Sdante 		}
566bea74446Sdante }
567bea74446Sdante 
568bea74446Sdante static void
makecat(const char * manpage,const char * catpage,const char * buildcmd,const char * crunchcmd)569d09fe2c4Schuck makecat(const char *manpage, const char *catpage, const char *buildcmd,
570d09fe2c4Schuck     const char *crunchcmd)
571bea74446Sdante {
572bea74446Sdante 	char crunchbuf[1024];
573*d89a86ddSchristos 	char sysbuf[2048 + 128];
574c232e0a0Schristos 	size_t len;
575bea74446Sdante 
576588171e0Schristos 	len = snprintf(sysbuf, sizeof(sysbuf), buildcmd, manpage);
577c232e0a0Schristos 	if (len > sizeof(sysbuf))
5780e0461c7Schristos 		errx(1, "snprintf");
579bea74446Sdante 
580bea74446Sdante 	if (*crunchcmd != '\0') {
581bea74446Sdante 		snprintf(crunchbuf, sizeof(crunchbuf), crunchcmd, catpage);
58221f928d3Schristos 		snprintf(sysbuf + len, sizeof(sysbuf) - len, " | %s",
583588171e0Schristos 		    crunchbuf);
584bea74446Sdante 	} else {
58521f928d3Schristos 		snprintf(sysbuf + len, sizeof(sysbuf) - len, " > %s", catpage);
586bea74446Sdante 	}
587bea74446Sdante 
588afaaa071Schopps 	if (f_noprint == 0)
58978df5febSjtc 		printf("%s\n", sysbuf);
590afaaa071Schopps 	if (f_noaction == 0)
591afaaa071Schopps 		dosystem(sysbuf);
59278df5febSjtc }
59378df5febSjtc 
594bea74446Sdante static void
makewhatis(void)595bea74446Sdante makewhatis(void)
596bea74446Sdante {
597bea74446Sdante 	TAG *whatdbp;
598bea74446Sdante 	ENTRY *e_whatdb;
599bea74446Sdante 	char sysbuf[1024];
600bea74446Sdante 
601d09fe2c4Schuck 	if ((whatdbp = gettag("_whatdb", 1)) == NULL)
602d09fe2c4Schuck 		err(1, "malloc");
603d09fe2c4Schuck 	TAILQ_FOREACH(e_whatdb, &whatdbp->entrylist, q) {
604bea74446Sdante 		snprintf(sysbuf, sizeof(sysbuf), "%s %s",
6052e499a79Sdholland 		    _PATH_MAKEWHATIS, dirname(e_whatdb->s));
606bea74446Sdante 		if (f_noprint == 0)
607bea74446Sdante 			printf("%s\n", sysbuf);
608bea74446Sdante 		if (f_noaction == 0)
609bea74446Sdante 			dosystem(sysbuf);
610bea74446Sdante 	}
611bea74446Sdante }
612bea74446Sdante 
613bea74446Sdante static void
dosystem(const char * cmd)614d09fe2c4Schuck dosystem(const char *cmd)
615afaaa071Schopps {
616afaaa071Schopps 	int status;
617afaaa071Schopps 
618afaaa071Schopps 	if ((status = system(cmd)) == 0)
619afaaa071Schopps 		return;
620afaaa071Schopps 
621afaaa071Schopps 	if (status == -1)
622afaaa071Schopps 		err(1, "cannot execute action");
623afaaa071Schopps 	if (WIFSIGNALED(status))
624afaaa071Schopps 		errx(1, "child was signaled to quit. aborting");
625afaaa071Schopps 	if (WIFSTOPPED(status))
626afaaa071Schopps 		errx(1, "child was stopped. aborting");
627afaaa071Schopps 	if (f_ignerr == 0)
628272b4a40Slukem 		errx(1, "*** Exited %d", status);
629272b4a40Slukem 	warnx("*** Exited %d (continuing)", status);
630afaaa071Schopps }
63178df5febSjtc 
632bea74446Sdante static void
usage(void)633bea74446Sdante usage(void)
63478df5febSjtc {
635dd243c0bScgd 	(void)fprintf(stderr,
636bea74446Sdante 	    "usage: catman [-knpsw] [-m manpath] [sections]\n");
637bea74446Sdante 	(void)fprintf(stderr,
638bea74446Sdante 	    "       catman [-knpsw] [-M manpath] [sections]\n");
63978df5febSjtc 	exit(1);
64078df5febSjtc }
641