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