1 /* $NetBSD: catman.c,v 1.28 2008/05/02 19:59:19 xtraeme Exp $ */
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
7 * Author: Baldassare Dante Profeta <dante@mclink.it>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/types.h>
32 #include <sys/queue.h>
33 #include <sys/cdefs.h>
34 #include <sys/param.h>
37 #include <sys/utsname.h>
52 #include "pathnames.h"
61 TAG
*defp
; /* pointer to _default list */
63 int main(int, char * const *);
64 static void setdefentries(char *, char *, const char *);
65 static void uniquepath(void);
66 static void catman(void);
67 static void scanmandir(const char *, const char *);
68 static int splitentry(char *, char *, size_t, char *, size_t);
69 static void setcatsuffix(char *, const char *, const char *);
70 static void makecat(const char *, const char *, const char *, const char *);
71 static void makewhatis(void);
72 static void dosystem(const char *);
73 static void usage(void);
77 main(int argc
, char * const *argv
)
83 while ((c
= getopt(argc
, argv
, "km:M:npsw")) != -1) {
114 if (f_noprint
&& f_noaction
)
120 config(_PATH_MANCONF
);
121 setdefentries(m_path
, m_add
, (argc
== 0) ? NULL
: argv
[argc
-1]);
124 if (f_noformat
== 0 || f_nowhatis
== 0)
126 if (f_nowhatis
== 0 && dowhatis
)
133 setdefentries(char *m_path
, char *m_add
, const char *sections
)
135 TAG
*defnewp
, *sectnewp
, *subp
;
136 ENTRY
*e_defp
, *e_subp
;
137 const char *p
, *slashp
;
139 char buf
[MAXPATHLEN
* 2];
142 /* Get the machine type. */
143 if ((machine
= getenv("MACHINE")) == NULL
) {
144 struct utsname utsname
;
146 if (uname(&utsname
) == -1) {
150 machine
= utsname
.machine
;
153 /* If there's no _default list, create an empty one. */
154 defp
= gettag("_default", 1);
155 subp
= gettag("_subdir", 1);
156 if (defp
== NULL
|| subp
== NULL
)
160 * 0: If one or more sections was specified, rewrite _subdir list.
162 if (sections
!= NULL
) {
163 if ((sectnewp
= gettag("_section_new", 1)) == NULL
)
165 for (p
= sections
; *p
;) {
166 i
= snprintf(buf
, sizeof(buf
), "man%c", *p
++);
167 for (; *p
&& !isdigit((unsigned char)*p
) && i
< (int)sizeof(buf
) - 1; i
++)
170 if (addentry(sectnewp
, buf
, 0) < 0)
177 * 1: If the user specified a MANPATH variable, or set the -M
178 * option, we replace the _default list with the user's list,
179 * appending the entries in the _subdir list and the machine.
182 m_path
= getenv("MANPATH");
183 if (m_path
!= NULL
) {
184 while ((e_defp
= TAILQ_FIRST(&defp
->entrylist
)) != NULL
) {
186 TAILQ_REMOVE(&defp
->entrylist
, e_defp
, q
);
188 for (p
= strtok(m_path
, ":");
189 p
!= NULL
; p
= strtok(NULL
, ":")) {
190 slashp
= p
[strlen(p
) - 1] == '/' ? "" : "/";
191 TAILQ_FOREACH(e_subp
, &subp
->entrylist
, q
) {
192 if (!strncmp(e_subp
->s
, "cat", 3))
194 (void)snprintf(buf
, sizeof(buf
), "%s%s%s{/%s,}",
195 p
, slashp
, e_subp
->s
, machine
);
196 if (addentry(defp
, buf
, 0) < 0)
203 * 2: If the user did not specify MANPATH, -M or a section, rewrite
204 * the _default list to include the _subdir list and the machine.
206 if (m_path
== NULL
) {
207 defp
= gettag("_default", 1);
208 defnewp
= gettag("_default_new1", 1);
209 if (defp
== NULL
|| defnewp
== NULL
)
212 TAILQ_FOREACH(e_defp
, &defp
->entrylist
, q
) {
214 e_defp
->s
[strlen(e_defp
->s
) - 1] == '/' ? "" : "/";
215 TAILQ_FOREACH(e_subp
, &subp
->entrylist
, q
) {
216 if (!strncmp(e_subp
->s
, "cat", 3))
218 (void)snprintf(buf
, sizeof(buf
), "%s%s%s{/%s,}",
219 e_defp
->s
, slashp
, e_subp
->s
, machine
);
220 if (addentry(defnewp
, buf
, 0) < 0)
228 * 3: If the user set the -m option, insert the user's list before
229 * whatever list we have, again appending the _subdir list and
233 for (p
= strtok(m_add
, ":"); p
!= NULL
; p
= strtok(NULL
, ":")) {
234 slashp
= p
[strlen(p
) - 1] == '/' ? "" : "/";
235 TAILQ_FOREACH(e_subp
, &subp
->entrylist
, q
) {
236 if (!strncmp(e_subp
->s
, "cat", 3))
238 (void)snprintf(buf
, sizeof(buf
), "%s%s%s{/%s,}",
239 p
, slashp
, e_subp
->s
, machine
);
240 if (addentry(defp
, buf
, 1) < 0)
247 * Remove entries (directory) which are symbolic links to other entries.
248 * Some examples are showed below:
249 * 1) if /usr/X11 -> /usr/X11R6 then remove all /usr/X11/man entries.
250 * 2) if /usr/local/man -> /usr/share/man then remove all /usr/local/man
262 int len
, lnk
, gflags
;
264 char path
[PATH_MAX
], *p
;
267 TAILQ_FOREACH(e_defp
, &defp
->entrylist
, q
) {
268 glob(e_defp
->s
, GLOB_BRACE
| GLOB_NOSORT
| gflags
, NULL
,
270 gflags
= GLOB_APPEND
;
273 if ((defnewp
= gettag("_default_new2", 1)) == NULL
)
276 for (i
= 0; i
< manpaths
.gl_pathc
; i
++) {
278 lstat(manpaths
.gl_pathv
[i
], &st1
);
279 for (j
= 0; j
< manpaths
.gl_pathc
; j
++) {
281 lstat(manpaths
.gl_pathv
[j
], &st2
);
282 if (st1
.st_ino
== st2
.st_ino
) {
283 strlcpy(path
, manpaths
.gl_pathv
[i
],
285 for (p
= path
; *(p
+1) != '\0';) {
288 if (S_ISLNK(st3
.st_mode
)) {
294 len
= readlink(manpaths
.gl_pathv
[i
],
295 path
, sizeof(path
) - 1);
299 if (!strcmp(path
, manpaths
.gl_pathv
[j
]))
308 if (addentry(defnewp
, manpaths
.gl_pathv
[i
], 0) < 0)
323 char catdir
[PATH_MAX
], *cp
;
325 TAILQ_FOREACH(e_path
, &defp
->entrylist
, q
) {
327 strlcpy(catdir
, mandir
, sizeof(catdir
));
328 if (!(cp
= strstr(catdir
, "man/man")))
330 cp
+= 4; *cp
++ = 'c'; *cp
++ = 'a'; *cp
= 't';
331 scanmandir(catdir
, mandir
);
336 scanmandir(const char *catdir
, const char *mandir
)
338 TAG
*buildp
, *crunchp
;
339 ENTRY
*e_build
, *e_crunch
;
340 char manpage
[PATH_MAX
];
341 char catpage
[PATH_MAX
];
342 char linkname
[PATH_MAX
];
343 char buffer
[PATH_MAX
], *bp
;
345 char buildsuff
[256], buildcmd
[256];
346 char crunchsuff
[256], crunchcmd
[256];
355 if ((dirp
= opendir(mandir
)) == 0) {
356 warn("can't open %s", mandir
);
360 if (stat(catdir
, &catstat
) < 0) {
361 if (errno
!= ENOENT
) {
362 warn("can't stat %s", catdir
);
367 printf("mkdir %s\n", catdir
);
368 if (f_noaction
== 0 && mkdir(catdir
, 0755) < 0) {
369 warn("can't create %s", catdir
);
375 while ((dp
= readdir(dirp
)) != NULL
) {
376 if (strcmp(dp
->d_name
, ".") == 0 ||
377 strcmp(dp
->d_name
, "..") == 0)
380 snprintf(manpage
, sizeof(manpage
), "%s/%s", mandir
, dp
->d_name
);
381 snprintf(catpage
, sizeof(catpage
), "%s/%s", catdir
, dp
->d_name
);
384 if ((buildp
= gettag("_build", 1)) == NULL
)
386 TAILQ_FOREACH(e_build
, &buildp
->entrylist
, q
) {
387 splitentry(e_build
->s
, buildsuff
, sizeof(buildsuff
),
388 buildcmd
, sizeof(buildcmd
));
389 snprintf(match
, sizeof(match
), "*%s",
391 if (!fnmatch(match
, manpage
, 0))
399 if ((crunchp
= gettag("_crunch", 1)) == NULL
)
401 TAILQ_FOREACH(e_crunch
, &crunchp
->entrylist
, q
) {
402 splitentry(e_crunch
->s
, crunchsuff
, sizeof(crunchsuff
),
403 crunchcmd
, sizeof(crunchcmd
));
404 snprintf(match
, sizeof(match
), "*%s", crunchsuff
);
405 if (!fnmatch(match
, manpage
, 0))
409 if (lstat(manpage
, &manstat
) <0) {
410 warn("can't stat %s", manpage
);
413 if (S_ISLNK(manstat
.st_mode
)) {
414 strlcpy(buffer
, catpage
, sizeof(buffer
));
415 strlcpy(linkname
, basename(buffer
),
417 len
= readlink(manpage
, buffer
,
420 warn("can't stat read symbolic link %s",
425 bp
= basename(buffer
);
426 strlcpy(tmp
, manpage
, sizeof(tmp
));
427 snprintf(manpage
, sizeof(manpage
), "%s/%s",
429 strlcpy(tmp
, catpage
, sizeof(tmp
));
430 snprintf(catpage
, sizeof(catpage
), "%s/%s",
431 dirname(tmp
), buffer
);
438 *crunchsuff
= *crunchcmd
= '\0';
440 setcatsuffix(catpage
, buildsuff
, crunchsuff
);
441 if (*linkname
!= '\0')
442 setcatsuffix(linkname
, buildsuff
, crunchsuff
);
444 if (stat(manpage
, &manstat
) < 0) {
445 warn("can't stat %s", manpage
);
449 if (!S_ISREG(manstat
.st_mode
)) {
450 warnx("not a regular file %s", manpage
);
454 if ((error
= stat(catpage
, &catstat
)) &&
456 warn("can't stat %s", catpage
);
460 if ((error
&& errno
== ENOENT
) ||
461 manstat
.st_mtime
> catstat
.st_mtime
) {
466 * reformat out of date manpage
468 makecat(manpage
, catpage
, buildcmd
, crunchcmd
);
473 if (*linkname
!= '\0') {
474 strlcpy(tmp
, catpage
, sizeof(tmp
));
475 snprintf(tmp
, sizeof(tmp
), "%s/%s", dirname(tmp
),
477 if ((error
= lstat(tmp
, &lnkstat
)) &&
479 warn("can't stat %s", tmp
);
483 if (error
&& errno
== ENOENT
) {
488 * create symbolic link
491 printf("ln -s %s %s\n", catpage
,
493 if (f_noaction
== 0) {
494 strlcpy(tmp
, catpage
,
496 if (chdir(dirname(tmp
)) == -1) {
501 if (symlink(catpage
, linkname
)
519 splitentry(char *s
, char *first
, size_t firstlen
, char *second
,
524 for (c
= s
; *c
!= '\0' && !isspace((unsigned char)*c
); ++c
)
528 if ((size_t)(c
- s
+ 1) > firstlen
)
530 strncpy(first
, s
, c
-s
);
532 for (; *c
!= '\0' && isspace((unsigned char)*c
); ++c
)
534 if (strlcpy(second
, c
, secondlen
) >= secondlen
)
540 setcatsuffix(char *catpage
, const char *suffix
, const char *crunchsuff
)
545 for (p
= catpage
+ strlen(catpage
); p
!= catpage
; p
--)
546 if (!fnmatch(suffix
, p
, 0)) {
547 if ((tp
= gettag("_suffix", 1)) == NULL
)
549 if (! TAILQ_EMPTY(&tp
->entrylist
)) {
551 TAILQ_FIRST(&tp
->entrylist
)->s
, crunchsuff
);
553 sprintf(p
, ".0%s", crunchsuff
);
560 makecat(const char *manpage
, const char *catpage
, const char *buildcmd
,
561 const char *crunchcmd
)
563 char crunchbuf
[1024];
566 snprintf(sysbuf
, sizeof(sysbuf
), buildcmd
, manpage
);
568 if (*crunchcmd
!= '\0') {
569 snprintf(crunchbuf
, sizeof(crunchbuf
), crunchcmd
, catpage
);
570 snprintf(sysbuf
, sizeof(sysbuf
), "%s | %s", sysbuf
, crunchbuf
);
572 snprintf(sysbuf
, sizeof(sysbuf
), "%s > %s", sysbuf
, catpage
);
576 printf("%s\n", sysbuf
);
588 if ((whatdbp
= gettag("_whatdb", 1)) == NULL
)
590 TAILQ_FOREACH(e_whatdb
, &whatdbp
->entrylist
, q
) {
591 snprintf(sysbuf
, sizeof(sysbuf
), "%s %s",
592 _PATH_WHATIS
, dirname(e_whatdb
->s
));
594 printf("%s\n", sysbuf
);
601 dosystem(const char *cmd
)
605 if ((status
= system(cmd
)) == 0)
609 err(1, "cannot execute action");
610 if (WIFSIGNALED(status
))
611 errx(1, "child was signaled to quit. aborting");
612 if (WIFSTOPPED(status
))
613 errx(1, "child was stopped. aborting");
615 errx(1, "*** Exited %d", status
);
616 warnx("*** Exited %d (continuing)", status
);
622 (void)fprintf(stderr
,
623 "usage: catman [-knpsw] [-m manpath] [sections]\n");
624 (void)fprintf(stderr
,
625 " catman [-knpsw] [-M manpath] [sections]\n");