195c635efSGarrett D'Amore /*
295c635efSGarrett D'Amore * CDDL HEADER START
395c635efSGarrett D'Amore *
495c635efSGarrett D'Amore * The contents of this file are subject to the terms of the
595c635efSGarrett D'Amore * Common Development and Distribution License (the "License").
695c635efSGarrett D'Amore * You may not use this file except in compliance with the License.
795c635efSGarrett D'Amore *
895c635efSGarrett D'Amore * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
995c635efSGarrett D'Amore * or http://www.opensolaris.org/os/licensing.
1095c635efSGarrett D'Amore * See the License for the specific language governing permissions
1195c635efSGarrett D'Amore * and limitations under the License.
1295c635efSGarrett D'Amore *
1395c635efSGarrett D'Amore * When distributing Covered Code, include this CDDL HEADER in each
1495c635efSGarrett D'Amore * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1595c635efSGarrett D'Amore * If applicable, add the following below this CDDL HEADER, with the
1695c635efSGarrett D'Amore * fields enclosed by brackets "[]" replaced with your own identifying
1795c635efSGarrett D'Amore * information: Portions Copyright [yyyy] [name of copyright owner]
1895c635efSGarrett D'Amore *
1995c635efSGarrett D'Amore * CDDL HEADER END
2095c635efSGarrett D'Amore */
2195c635efSGarrett D'Amore
2295c635efSGarrett D'Amore /*
2395c635efSGarrett D'Amore * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
2495c635efSGarrett D'Amore * Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved.
2595c635efSGarrett D'Amore * Copyright 2014 Garrett D'Amore <garrett@damore.org>
26*d456640dSYuri Pankov * Copyright 2016 Nexenta Systems, Inc.
2795c635efSGarrett D'Amore */
2895c635efSGarrett D'Amore
2995c635efSGarrett D'Amore /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T. */
3095c635efSGarrett D'Amore /* All rights reserved. */
3195c635efSGarrett D'Amore
3295c635efSGarrett D'Amore /*
3395c635efSGarrett D'Amore * University Copyright- Copyright (c) 1982, 1986, 1988
3495c635efSGarrett D'Amore * The Regents of the University of California
3595c635efSGarrett D'Amore * All Rights Reserved
3695c635efSGarrett D'Amore *
3795c635efSGarrett D'Amore * University Acknowledgment- Portions of this document are derived from
3895c635efSGarrett D'Amore * software developed by the University of California, Berkeley, and its
3995c635efSGarrett D'Amore * contributors.
4095c635efSGarrett D'Amore */
4195c635efSGarrett D'Amore
4295c635efSGarrett D'Amore /*
4395c635efSGarrett D'Amore * Find and display reference manual pages. This version includes makewhatis
4495c635efSGarrett D'Amore * functionality as well.
4595c635efSGarrett D'Amore */
4695c635efSGarrett D'Amore
4795c635efSGarrett D'Amore #include <sys/param.h>
4895c635efSGarrett D'Amore #include <sys/stat.h>
4995c635efSGarrett D'Amore #include <sys/termios.h>
5095c635efSGarrett D'Amore #include <sys/types.h>
5195c635efSGarrett D'Amore
5295c635efSGarrett D'Amore #include <ctype.h>
5395c635efSGarrett D'Amore #include <dirent.h>
5495c635efSGarrett D'Amore #include <err.h>
5595c635efSGarrett D'Amore #include <errno.h>
5695c635efSGarrett D'Amore #include <fcntl.h>
5795c635efSGarrett D'Amore #include <fnmatch.h>
5895c635efSGarrett D'Amore #include <limits.h>
5995c635efSGarrett D'Amore #include <locale.h>
6095c635efSGarrett D'Amore #include <malloc.h>
6195c635efSGarrett D'Amore #include <memory.h>
6295c635efSGarrett D'Amore #include <regex.h>
6395c635efSGarrett D'Amore #include <stdio.h>
6495c635efSGarrett D'Amore #include <stdlib.h>
6595c635efSGarrett D'Amore #include <string.h>
6695c635efSGarrett D'Amore #include <unistd.h>
6795c635efSGarrett D'Amore
6895c635efSGarrett D'Amore #include "man.h"
6995c635efSGarrett D'Amore
7095c635efSGarrett D'Amore
7195c635efSGarrett D'Amore /* Mapping of old directories to new directories */
7295c635efSGarrett D'Amore static const struct map_entry {
7395c635efSGarrett D'Amore char *old_name;
7495c635efSGarrett D'Amore char *new_name;
7595c635efSGarrett D'Amore } map[] = {
7695c635efSGarrett D'Amore { "3b", "3ucb" },
7795c635efSGarrett D'Amore { "3e", "3elf" },
7895c635efSGarrett D'Amore { "3g", "3gen" },
7995c635efSGarrett D'Amore { "3k", "3kstat" },
8095c635efSGarrett D'Amore { "3n", "3socket" },
8195c635efSGarrett D'Amore { "3r", "3rt" },
8295c635efSGarrett D'Amore { "3s", "3c" },
8395c635efSGarrett D'Amore { "3t", "3thr" },
8495c635efSGarrett D'Amore { "3x", "3curses" },
8595c635efSGarrett D'Amore { "3xc", "3xcurses" },
8695c635efSGarrett D'Amore { "3xn", "3xnet" },
8795c635efSGarrett D'Amore { NULL, NULL }
8895c635efSGarrett D'Amore };
8995c635efSGarrett D'Amore
9095c635efSGarrett D'Amore struct suffix {
9195c635efSGarrett D'Amore char *ds;
9295c635efSGarrett D'Amore char *fs;
9395c635efSGarrett D'Amore };
9495c635efSGarrett D'Amore
9595c635efSGarrett D'Amore /*
9695c635efSGarrett D'Amore * Flags that control behavior of build_manpath()
9795c635efSGarrett D'Amore *
9895c635efSGarrett D'Amore * BMP_ISPATH pathv is a vector constructed from PATH.
9995c635efSGarrett D'Amore * Perform appropriate path translations for
10095c635efSGarrett D'Amore * manpath.
10195c635efSGarrett D'Amore * BMP_APPEND_DEFMANDIR Add DEFMANDIR to the end if it hasn't
10295c635efSGarrett D'Amore * already appeared earlier.
10395c635efSGarrett D'Amore * BMP_FALLBACK_DEFMANDIR Append /usr/share/man only if no other
10495c635efSGarrett D'Amore * manpath (including derived from PATH)
10595c635efSGarrett D'Amore * elements are valid.
10695c635efSGarrett D'Amore */
10795c635efSGarrett D'Amore #define BMP_ISPATH 1
10895c635efSGarrett D'Amore #define BMP_APPEND_DEFMANDIR 2
10995c635efSGarrett D'Amore #define BMP_FALLBACK_DEFMANDIR 4
11095c635efSGarrett D'Amore
11195c635efSGarrett D'Amore /*
11295c635efSGarrett D'Amore * When doing equality comparisons of directories, device and inode
11395c635efSGarrett D'Amore * comparisons are done. The secnode and dupnode structures are used
11495c635efSGarrett D'Amore * to form a list of lists for this processing.
11595c635efSGarrett D'Amore */
11695c635efSGarrett D'Amore struct secnode {
11795c635efSGarrett D'Amore char *secp;
11895c635efSGarrett D'Amore struct secnode *next;
11995c635efSGarrett D'Amore };
12095c635efSGarrett D'Amore struct dupnode {
12195c635efSGarrett D'Amore dev_t dev; /* from struct stat st_dev */
12295c635efSGarrett D'Amore ino_t ino; /* from struct stat st_ino */
12395c635efSGarrett D'Amore struct secnode *secl; /* sections already considered */
12495c635efSGarrett D'Amore struct dupnode *next;
12595c635efSGarrett D'Amore };
12695c635efSGarrett D'Amore
12795c635efSGarrett D'Amore /*
12895c635efSGarrett D'Amore * Map directories that may appear in PATH to the corresponding
12995c635efSGarrett D'Amore * man directory.
13095c635efSGarrett D'Amore */
13195c635efSGarrett D'Amore static struct pathmap {
13295c635efSGarrett D'Amore char *bindir;
13395c635efSGarrett D'Amore char *mandir;
13495c635efSGarrett D'Amore dev_t dev;
13595c635efSGarrett D'Amore ino_t ino;
13695c635efSGarrett D'Amore } bintoman[] = {
13795c635efSGarrett D'Amore { "/sbin", "/usr/share/man,1m", 0, 0 },
13895c635efSGarrett D'Amore { "/usr/sbin", "/usr/share/man,1m", 0, 0 },
13995c635efSGarrett D'Amore { "/usr/ucb", "/usr/share/man,1b", 0, 0 },
14095c635efSGarrett D'Amore { "/usr/bin", "/usr/share/man,1,1m,1s,1t,1c", 0, 0 },
14195c635efSGarrett D'Amore { "/usr/xpg4/bin", "/usr/share/man,1", 0, 0 },
14295c635efSGarrett D'Amore { "/usr/xpg6/bin", "/usr/share/man,1", 0, 0 },
14395c635efSGarrett D'Amore { NULL, NULL, 0, 0 }
14495c635efSGarrett D'Amore };
14595c635efSGarrett D'Amore
14695c635efSGarrett D'Amore struct man_node {
14795c635efSGarrett D'Amore char *path; /* mandir path */
14895c635efSGarrett D'Amore char **secv; /* submandir suffices */
14995c635efSGarrett D'Amore int defsrch; /* hint for man -p */
15095c635efSGarrett D'Amore int frompath; /* hint for man -d */
15195c635efSGarrett D'Amore struct man_node *next;
15295c635efSGarrett D'Amore };
15395c635efSGarrett D'Amore
15495c635efSGarrett D'Amore static int all = 0;
15595c635efSGarrett D'Amore static int apropos = 0;
15695c635efSGarrett D'Amore static int debug = 0;
15795c635efSGarrett D'Amore static int found = 0;
15895c635efSGarrett D'Amore static int list = 0;
15995c635efSGarrett D'Amore static int makewhatis = 0;
16095c635efSGarrett D'Amore static int printmp = 0;
16195c635efSGarrett D'Amore static int sargs = 0;
16295c635efSGarrett D'Amore static int psoutput = 0;
16395c635efSGarrett D'Amore static int lintout = 0;
16495c635efSGarrett D'Amore static int whatis = 0;
16595c635efSGarrett D'Amore static int makewhatishere = 0;
16695c635efSGarrett D'Amore
16795c635efSGarrett D'Amore static char *mansec;
16895c635efSGarrett D'Amore static char *pager = NULL;
16995c635efSGarrett D'Amore
17095c635efSGarrett D'Amore static char *addlocale(char *);
17195c635efSGarrett D'Amore static struct man_node *build_manpath(char **, int);
17295c635efSGarrett D'Amore static void do_makewhatis(struct man_node *);
17395c635efSGarrett D'Amore static char *check_config(char *);
17495c635efSGarrett D'Amore static int cmp(const void *, const void *);
17595c635efSGarrett D'Amore static int dupcheck(struct man_node *, struct dupnode **);
17695c635efSGarrett D'Amore static int format(char *, char *, char *, char *);
17795c635efSGarrett D'Amore static void free_dupnode(struct dupnode *);
17895c635efSGarrett D'Amore static void free_manp(struct man_node *manp);
17995c635efSGarrett D'Amore static void freev(char **);
18095c635efSGarrett D'Amore static void fullpaths(struct man_node **);
18195c635efSGarrett D'Amore static void get_all_sect(struct man_node *);
18295c635efSGarrett D'Amore static int getdirs(char *, char ***, int);
18395c635efSGarrett D'Amore static void getpath(struct man_node *, char **);
18495c635efSGarrett D'Amore static void getsect(struct man_node *, char **);
18595c635efSGarrett D'Amore static void init_bintoman(void);
18695c635efSGarrett D'Amore static void lower(char *);
18795c635efSGarrett D'Amore static void mandir(char **, char *, char *, int);
18895c635efSGarrett D'Amore static int manual(struct man_node *, char *);
18995c635efSGarrett D'Amore static char *map_section(char *, char *);
19095c635efSGarrett D'Amore static char *path_to_manpath(char *);
19195c635efSGarrett D'Amore static void print_manpath(struct man_node *);
19295c635efSGarrett D'Amore static void search_whatis(char *, char *);
19395c635efSGarrett D'Amore static int searchdir(char *, char *, char *);
19495c635efSGarrett D'Amore static void sortdir(DIR *, char ***);
19595c635efSGarrett D'Amore static char **split(char *, char);
19695c635efSGarrett D'Amore static void usage_man(void);
19795c635efSGarrett D'Amore static void usage_whatapro(void);
19895c635efSGarrett D'Amore static void usage_catman(void);
19995c635efSGarrett D'Amore static void usage_makewhatis(void);
20095c635efSGarrett D'Amore static void whatapro(struct man_node *, char *);
20195c635efSGarrett D'Amore
20295c635efSGarrett D'Amore static char language[MAXPATHLEN]; /* LC_MESSAGES */
20395c635efSGarrett D'Amore static char localedir[MAXPATHLEN]; /* locale specific path component */
20495c635efSGarrett D'Amore
20595c635efSGarrett D'Amore static char *newsection = NULL;
20695c635efSGarrett D'Amore
20795c635efSGarrett D'Amore static int manwidth = 0;
20895c635efSGarrett D'Amore
20995c635efSGarrett D'Amore extern const char *__progname;
21095c635efSGarrett D'Amore
21195c635efSGarrett D'Amore int
main(int argc,char ** argv)21295c635efSGarrett D'Amore main(int argc, char **argv)
21395c635efSGarrett D'Amore {
21495c635efSGarrett D'Amore int c, i;
21595c635efSGarrett D'Amore char **pathv;
21695c635efSGarrett D'Amore char *manpath = NULL;
21795c635efSGarrett D'Amore static struct man_node *mandirs = NULL;
21895c635efSGarrett D'Amore int bmp_flags = 0;
21995c635efSGarrett D'Amore int ret = 0;
22095c635efSGarrett D'Amore char *opts;
22195c635efSGarrett D'Amore char *mwstr;
22295c635efSGarrett D'Amore int catman = 0;
22395c635efSGarrett D'Amore
22495c635efSGarrett D'Amore (void) setlocale(LC_ALL, "");
22595c635efSGarrett D'Amore (void) strcpy(language, setlocale(LC_MESSAGES, (char *)NULL));
22695c635efSGarrett D'Amore if (strcmp("C", language) != 0)
22795c635efSGarrett D'Amore (void) strlcpy(localedir, language, MAXPATHLEN);
22895c635efSGarrett D'Amore
22995c635efSGarrett D'Amore #if !defined(TEXT_DOMAIN)
23095c635efSGarrett D'Amore #define TEXT_DOMAIN "SYS_TEST"
23195c635efSGarrett D'Amore #endif
23295c635efSGarrett D'Amore (void) textdomain(TEXT_DOMAIN);
23395c635efSGarrett D'Amore
23495c635efSGarrett D'Amore if (strcmp(__progname, "apropos") == 0) {
23595c635efSGarrett D'Amore apropos++;
23695c635efSGarrett D'Amore opts = "M:ds:";
23795c635efSGarrett D'Amore } else if (strcmp(__progname, "whatis") == 0) {
23895c635efSGarrett D'Amore apropos++;
23995c635efSGarrett D'Amore whatis++;
24095c635efSGarrett D'Amore opts = "M:ds:";
24195c635efSGarrett D'Amore } else if (strcmp(__progname, "catman") == 0) {
24295c635efSGarrett D'Amore catman++;
24395c635efSGarrett D'Amore makewhatis++;
24495c635efSGarrett D'Amore opts = "P:M:w";
24595c635efSGarrett D'Amore } else if (strcmp(__progname, "makewhatis") == 0) {
24695c635efSGarrett D'Amore makewhatis++;
24795c635efSGarrett D'Amore makewhatishere++;
24895c635efSGarrett D'Amore manpath = ".";
24995c635efSGarrett D'Amore opts = "";
25095c635efSGarrett D'Amore } else {
25195c635efSGarrett D'Amore opts = "FM:P:T:adfklprs:tw";
25295c635efSGarrett D'Amore if (argc > 1 && strcmp(argv[1], "-") == 0) {
25395c635efSGarrett D'Amore pager = "cat";
25495c635efSGarrett D'Amore optind++;
25595c635efSGarrett D'Amore }
25695c635efSGarrett D'Amore }
25795c635efSGarrett D'Amore
25895c635efSGarrett D'Amore opterr = 0;
25995c635efSGarrett D'Amore while ((c = getopt(argc, argv, opts)) != -1) {
26095c635efSGarrett D'Amore switch (c) {
26195c635efSGarrett D'Amore case 'M': /* Respecify path for man pages */
26295c635efSGarrett D'Amore manpath = optarg;
26395c635efSGarrett D'Amore break;
26495c635efSGarrett D'Amore case 'a':
26595c635efSGarrett D'Amore all++;
26695c635efSGarrett D'Amore break;
26795c635efSGarrett D'Amore case 'd':
26895c635efSGarrett D'Amore debug++;
26995c635efSGarrett D'Amore break;
27095c635efSGarrett D'Amore case 'f':
27195c635efSGarrett D'Amore whatis++;
27295c635efSGarrett D'Amore /*FALLTHROUGH*/
27395c635efSGarrett D'Amore case 'k':
27495c635efSGarrett D'Amore apropos++;
27595c635efSGarrett D'Amore break;
27695c635efSGarrett D'Amore case 'l':
27795c635efSGarrett D'Amore list++;
27895c635efSGarrett D'Amore all++;
27995c635efSGarrett D'Amore break;
28095c635efSGarrett D'Amore case 'p':
28195c635efSGarrett D'Amore printmp++;
28295c635efSGarrett D'Amore break;
28395c635efSGarrett D'Amore case 's':
28495c635efSGarrett D'Amore mansec = optarg;
28595c635efSGarrett D'Amore sargs++;
28695c635efSGarrett D'Amore break;
28795c635efSGarrett D'Amore case 'r':
28895c635efSGarrett D'Amore lintout++;
28995c635efSGarrett D'Amore break;
29095c635efSGarrett D'Amore case 't':
29195c635efSGarrett D'Amore psoutput++;
29295c635efSGarrett D'Amore break;
29395c635efSGarrett D'Amore case 'T':
29495c635efSGarrett D'Amore case 'P':
29595c635efSGarrett D'Amore case 'F':
29695c635efSGarrett D'Amore /* legacy options, compatibility only and ignored */
29795c635efSGarrett D'Amore break;
29895c635efSGarrett D'Amore case 'w':
29995c635efSGarrett D'Amore makewhatis++;
30095c635efSGarrett D'Amore break;
30195c635efSGarrett D'Amore case '?':
30295c635efSGarrett D'Amore default:
30395c635efSGarrett D'Amore if (apropos)
30495c635efSGarrett D'Amore usage_whatapro();
30595c635efSGarrett D'Amore else if (catman)
30695c635efSGarrett D'Amore usage_catman();
30795c635efSGarrett D'Amore else if (makewhatishere)
30895c635efSGarrett D'Amore usage_makewhatis();
30995c635efSGarrett D'Amore else
31095c635efSGarrett D'Amore usage_man();
31195c635efSGarrett D'Amore }
31295c635efSGarrett D'Amore }
31395c635efSGarrett D'Amore argc -= optind;
31495c635efSGarrett D'Amore argv += optind;
31595c635efSGarrett D'Amore
31695c635efSGarrett D'Amore if (argc == 0) {
31795c635efSGarrett D'Amore if (apropos) {
31895c635efSGarrett D'Amore (void) fprintf(stderr, gettext("%s what?\n"),
31995c635efSGarrett D'Amore __progname);
32095c635efSGarrett D'Amore exit(1);
32195c635efSGarrett D'Amore } else if (!printmp && !makewhatis) {
32295c635efSGarrett D'Amore (void) fprintf(stderr,
32395c635efSGarrett D'Amore gettext("What manual page do you want?\n"));
32495c635efSGarrett D'Amore exit(1);
32595c635efSGarrett D'Amore }
32695c635efSGarrett D'Amore }
32795c635efSGarrett D'Amore
32895c635efSGarrett D'Amore init_bintoman();
32995c635efSGarrett D'Amore if (manpath == NULL && (manpath = getenv("MANPATH")) == NULL) {
33095c635efSGarrett D'Amore if ((manpath = getenv("PATH")) != NULL)
33195c635efSGarrett D'Amore bmp_flags = BMP_ISPATH | BMP_APPEND_DEFMANDIR;
33295c635efSGarrett D'Amore else
33395c635efSGarrett D'Amore manpath = DEFMANDIR;
33495c635efSGarrett D'Amore }
33595c635efSGarrett D'Amore pathv = split(manpath, ':');
33695c635efSGarrett D'Amore mandirs = build_manpath(pathv, bmp_flags);
33795c635efSGarrett D'Amore freev(pathv);
33895c635efSGarrett D'Amore fullpaths(&mandirs);
33995c635efSGarrett D'Amore
34095c635efSGarrett D'Amore if (makewhatis) {
34195c635efSGarrett D'Amore do_makewhatis(mandirs);
34295c635efSGarrett D'Amore exit(0);
34395c635efSGarrett D'Amore }
34495c635efSGarrett D'Amore
34595c635efSGarrett D'Amore if (printmp) {
34695c635efSGarrett D'Amore print_manpath(mandirs);
34795c635efSGarrett D'Amore exit(0);
34895c635efSGarrett D'Amore }
34995c635efSGarrett D'Amore
35095c635efSGarrett D'Amore /* Collect environment information */
35195c635efSGarrett D'Amore if (isatty(STDOUT_FILENO) && (mwstr = getenv("MANWIDTH")) != NULL &&
35295c635efSGarrett D'Amore *mwstr != '\0') {
35395c635efSGarrett D'Amore if (strcasecmp(mwstr, "tty") == 0) {
35495c635efSGarrett D'Amore struct winsize ws;
35595c635efSGarrett D'Amore
35695c635efSGarrett D'Amore if (ioctl(0, TIOCGWINSZ, &ws) != 0)
35795c635efSGarrett D'Amore warn("TIOCGWINSZ");
35895c635efSGarrett D'Amore else
35995c635efSGarrett D'Amore manwidth = ws.ws_col;
36095c635efSGarrett D'Amore } else {
36195c635efSGarrett D'Amore manwidth = (int)strtol(mwstr, (char **)NULL, 10);
36295c635efSGarrett D'Amore if (manwidth < 0)
36395c635efSGarrett D'Amore manwidth = 0;
36495c635efSGarrett D'Amore }
36595c635efSGarrett D'Amore }
36695c635efSGarrett D'Amore if (manwidth != 0) {
36795c635efSGarrett D'Amore DPRINTF("-- Using non-standard page width: %d\n", manwidth);
36895c635efSGarrett D'Amore }
36995c635efSGarrett D'Amore
37095c635efSGarrett D'Amore if (pager == NULL) {
37195c635efSGarrett D'Amore if ((pager = getenv("PAGER")) == NULL || *pager == '\0')
37295c635efSGarrett D'Amore pager = PAGER;
37395c635efSGarrett D'Amore }
37495c635efSGarrett D'Amore DPRINTF("-- Using pager: %s\n", pager);
37595c635efSGarrett D'Amore
37695c635efSGarrett D'Amore for (i = 0; i < argc; i++) {
37795c635efSGarrett D'Amore char *cmd;
37895c635efSGarrett D'Amore static struct man_node *mp;
37995c635efSGarrett D'Amore char *pv[2];
38095c635efSGarrett D'Amore
38195c635efSGarrett D'Amore /*
38295c635efSGarrett D'Amore * If full path to command specified, customize
38395c635efSGarrett D'Amore * the manpath accordingly.
38495c635efSGarrett D'Amore */
38595c635efSGarrett D'Amore if ((cmd = strrchr(argv[i], '/')) != NULL) {
38695c635efSGarrett D'Amore *cmd = '\0';
38795c635efSGarrett D'Amore if ((pv[0] = strdup(argv[i])) == NULL)
38895c635efSGarrett D'Amore err(1, "strdup");
38995c635efSGarrett D'Amore pv[1] = NULL;
39095c635efSGarrett D'Amore *cmd = '/';
39195c635efSGarrett D'Amore mp = build_manpath(pv,
39295c635efSGarrett D'Amore BMP_ISPATH | BMP_FALLBACK_DEFMANDIR);
39395c635efSGarrett D'Amore } else {
39495c635efSGarrett D'Amore mp = mandirs;
39595c635efSGarrett D'Amore }
39695c635efSGarrett D'Amore
39795c635efSGarrett D'Amore if (apropos)
39895c635efSGarrett D'Amore whatapro(mp, argv[i]);
39995c635efSGarrett D'Amore else
40095c635efSGarrett D'Amore ret += manual(mp, argv[i]);
40195c635efSGarrett D'Amore
40295c635efSGarrett D'Amore if (mp != NULL && mp != mandirs) {
40395c635efSGarrett D'Amore free(pv[0]);
40495c635efSGarrett D'Amore free_manp(mp);
40595c635efSGarrett D'Amore }
40695c635efSGarrett D'Amore }
40795c635efSGarrett D'Amore
40895c635efSGarrett D'Amore return (ret == 0 ? 0 : 1);
40995c635efSGarrett D'Amore }
41095c635efSGarrett D'Amore
41195c635efSGarrett D'Amore /*
41295c635efSGarrett D'Amore * This routine builds the manpage structure from MANPATH or PATH,
41395c635efSGarrett D'Amore * depending on flags. See BMP_* definitions above for valid
41495c635efSGarrett D'Amore * flags.
41595c635efSGarrett D'Amore */
41695c635efSGarrett D'Amore static struct man_node *
build_manpath(char ** pathv,int flags)41795c635efSGarrett D'Amore build_manpath(char **pathv, int flags)
41895c635efSGarrett D'Amore {
41995c635efSGarrett D'Amore struct man_node *manpage = NULL;
42095c635efSGarrett D'Amore struct man_node *currp = NULL;
42195c635efSGarrett D'Amore struct man_node *lastp = NULL;
42295c635efSGarrett D'Amore char **p;
42395c635efSGarrett D'Amore char **q;
42495c635efSGarrett D'Amore char *mand = NULL;
42595c635efSGarrett D'Amore char *mandir = DEFMANDIR;
42695c635efSGarrett D'Amore int s;
42795c635efSGarrett D'Amore struct dupnode *didup = NULL;
42895c635efSGarrett D'Amore struct stat sb;
42995c635efSGarrett D'Amore
43095c635efSGarrett D'Amore s = sizeof (struct man_node);
43195c635efSGarrett D'Amore for (p = pathv; *p != NULL; ) {
43295c635efSGarrett D'Amore if (flags & BMP_ISPATH) {
43395c635efSGarrett D'Amore if ((mand = path_to_manpath(*p)) == NULL)
43495c635efSGarrett D'Amore goto next;
43595c635efSGarrett D'Amore free(*p);
43695c635efSGarrett D'Amore *p = mand;
43795c635efSGarrett D'Amore }
43895c635efSGarrett D'Amore q = split(*p, ',');
43995c635efSGarrett D'Amore if (stat(q[0], &sb) != 0 || (sb.st_mode & S_IFDIR) == 0) {
44095c635efSGarrett D'Amore freev(q);
44195c635efSGarrett D'Amore goto next;
44295c635efSGarrett D'Amore }
44395c635efSGarrett D'Amore
44495c635efSGarrett D'Amore if (access(q[0], R_OK | X_OK) == 0) {
44595c635efSGarrett D'Amore /*
44695c635efSGarrett D'Amore * Some element exists. Do not append DEFMANDIR as a
44795c635efSGarrett D'Amore * fallback.
44895c635efSGarrett D'Amore */
44995c635efSGarrett D'Amore flags &= ~BMP_FALLBACK_DEFMANDIR;
45095c635efSGarrett D'Amore
45195c635efSGarrett D'Amore if ((currp = (struct man_node *)calloc(1, s)) == NULL)
45295c635efSGarrett D'Amore err(1, "calloc");
45395c635efSGarrett D'Amore
45495c635efSGarrett D'Amore currp->frompath = (flags & BMP_ISPATH);
45595c635efSGarrett D'Amore
45695c635efSGarrett D'Amore if (manpage == NULL)
45795c635efSGarrett D'Amore lastp = manpage = currp;
45895c635efSGarrett D'Amore
45995c635efSGarrett D'Amore getpath(currp, p);
46095c635efSGarrett D'Amore getsect(currp, p);
46195c635efSGarrett D'Amore
46295c635efSGarrett D'Amore /*
46395c635efSGarrett D'Amore * If there are no new elements in this path,
46495c635efSGarrett D'Amore * do not add it to the manpage list.
46595c635efSGarrett D'Amore */
46695c635efSGarrett D'Amore if (dupcheck(currp, &didup) != 0) {
46795c635efSGarrett D'Amore freev(currp->secv);
46895c635efSGarrett D'Amore free(currp);
46995c635efSGarrett D'Amore } else {
47095c635efSGarrett D'Amore currp->next = NULL;
47195c635efSGarrett D'Amore if (currp != manpage)
47295c635efSGarrett D'Amore lastp->next = currp;
47395c635efSGarrett D'Amore lastp = currp;
47495c635efSGarrett D'Amore }
47595c635efSGarrett D'Amore }
47695c635efSGarrett D'Amore freev(q);
47795c635efSGarrett D'Amore next:
47895c635efSGarrett D'Amore /*
47995c635efSGarrett D'Amore * Special handling of appending DEFMANDIR. After all pathv
48095c635efSGarrett D'Amore * elements have been processed, append DEFMANDIR if needed.
48195c635efSGarrett D'Amore */
48295c635efSGarrett D'Amore if (p == &mandir)
48395c635efSGarrett D'Amore break;
48495c635efSGarrett D'Amore p++;
48595c635efSGarrett D'Amore if (*p != NULL)
48695c635efSGarrett D'Amore continue;
48795c635efSGarrett D'Amore if (flags & (BMP_APPEND_DEFMANDIR | BMP_FALLBACK_DEFMANDIR)) {
48895c635efSGarrett D'Amore p = &mandir;
48995c635efSGarrett D'Amore flags &= ~BMP_ISPATH;
49095c635efSGarrett D'Amore }
49195c635efSGarrett D'Amore }
49295c635efSGarrett D'Amore
49395c635efSGarrett D'Amore free_dupnode(didup);
49495c635efSGarrett D'Amore
49595c635efSGarrett D'Amore return (manpage);
49695c635efSGarrett D'Amore }
49795c635efSGarrett D'Amore
49895c635efSGarrett D'Amore /*
49995c635efSGarrett D'Amore * Store the mandir path into the manp structure.
50095c635efSGarrett D'Amore */
50195c635efSGarrett D'Amore static void
getpath(struct man_node * manp,char ** pv)50295c635efSGarrett D'Amore getpath(struct man_node *manp, char **pv)
50395c635efSGarrett D'Amore {
50495c635efSGarrett D'Amore char *s = *pv;
50595c635efSGarrett D'Amore int i = 0;
50695c635efSGarrett D'Amore
50795c635efSGarrett D'Amore while (*s != '\0' && *s != ',')
50895c635efSGarrett D'Amore i++, s++;
50995c635efSGarrett D'Amore
51095c635efSGarrett D'Amore if ((manp->path = (char *)malloc(i + 1)) == NULL)
51195c635efSGarrett D'Amore err(1, "malloc");
51295c635efSGarrett D'Amore (void) strlcpy(manp->path, *pv, i + 1);
51395c635efSGarrett D'Amore }
51495c635efSGarrett D'Amore
51595c635efSGarrett D'Amore /*
51695c635efSGarrett D'Amore * Store the mandir's corresponding sections (submandir
51795c635efSGarrett D'Amore * directories) into the manp structure.
51895c635efSGarrett D'Amore */
51995c635efSGarrett D'Amore static void
getsect(struct man_node * manp,char ** pv)52095c635efSGarrett D'Amore getsect(struct man_node *manp, char **pv)
52195c635efSGarrett D'Amore {
52295c635efSGarrett D'Amore char *sections;
52395c635efSGarrett D'Amore char **sectp;
52495c635efSGarrett D'Amore
52595c635efSGarrett D'Amore /* Just store all sections when doing makewhatis or apropos/whatis */
52695c635efSGarrett D'Amore if (makewhatis || apropos) {
52795c635efSGarrett D'Amore manp->defsrch = 1;
52895c635efSGarrett D'Amore DPRINTF("-- Adding %s\n", manp->path);
52995c635efSGarrett D'Amore manp->secv = NULL;
53095c635efSGarrett D'Amore get_all_sect(manp);
53195c635efSGarrett D'Amore } else if (sargs) {
532*d456640dSYuri Pankov DPRINTF("-- Adding %s: sections=%s\n", manp->path, mansec);
53395c635efSGarrett D'Amore manp->secv = split(mansec, ',');
53495c635efSGarrett D'Amore for (sectp = manp->secv; *sectp; sectp++)
53595c635efSGarrett D'Amore lower(*sectp);
53695c635efSGarrett D'Amore } else if ((sections = strchr(*pv, ',')) != NULL) {
537*d456640dSYuri Pankov sections++;
538*d456640dSYuri Pankov DPRINTF("-- Adding %s: sections=%s\n", manp->path, sections);
539*d456640dSYuri Pankov manp->secv = split(sections, ',');
54095c635efSGarrett D'Amore for (sectp = manp->secv; *sectp; sectp++)
54195c635efSGarrett D'Amore lower(*sectp);
54295c635efSGarrett D'Amore if (*manp->secv == NULL)
54395c635efSGarrett D'Amore get_all_sect(manp);
54495c635efSGarrett D'Amore } else if ((sections = check_config(*pv)) != NULL) {
54595c635efSGarrett D'Amore manp->defsrch = 1;
546*d456640dSYuri Pankov DPRINTF("-- Adding %s: sections=%s (from %s)\n", manp->path,
547*d456640dSYuri Pankov sections, CONFIG);
54895c635efSGarrett D'Amore manp->secv = split(sections, ',');
54995c635efSGarrett D'Amore for (sectp = manp->secv; *sectp; sectp++)
55095c635efSGarrett D'Amore lower(*sectp);
55195c635efSGarrett D'Amore if (*manp->secv == NULL)
55295c635efSGarrett D'Amore get_all_sect(manp);
55395c635efSGarrett D'Amore } else {
55495c635efSGarrett D'Amore manp->defsrch = 1;
555*d456640dSYuri Pankov DPRINTF("-- Adding %s: default search order\n", manp->path);
55695c635efSGarrett D'Amore manp->secv = NULL;
55795c635efSGarrett D'Amore get_all_sect(manp);
55895c635efSGarrett D'Amore }
55995c635efSGarrett D'Amore }
56095c635efSGarrett D'Amore
56195c635efSGarrett D'Amore /*
56295c635efSGarrett D'Amore * Get suffices of all sub-mandir directories in a mandir.
56395c635efSGarrett D'Amore */
56495c635efSGarrett D'Amore static void
get_all_sect(struct man_node * manp)56595c635efSGarrett D'Amore get_all_sect(struct man_node *manp)
56695c635efSGarrett D'Amore {
56795c635efSGarrett D'Amore DIR *dp;
56895c635efSGarrett D'Amore char **dirv;
56995c635efSGarrett D'Amore char **dv;
57095c635efSGarrett D'Amore char **p;
57195c635efSGarrett D'Amore char *prev = NULL;
57295c635efSGarrett D'Amore char *tmp = NULL;
57395c635efSGarrett D'Amore int maxentries = MAXTOKENS;
57495c635efSGarrett D'Amore int entries = 0;
57595c635efSGarrett D'Amore
57695c635efSGarrett D'Amore if ((dp = opendir(manp->path)) == 0)
57795c635efSGarrett D'Amore return;
57895c635efSGarrett D'Amore
57995c635efSGarrett D'Amore sortdir(dp, &dirv);
58095c635efSGarrett D'Amore
58195c635efSGarrett D'Amore (void) closedir(dp);
58295c635efSGarrett D'Amore
58395c635efSGarrett D'Amore if (manp->secv == NULL) {
58495c635efSGarrett D'Amore if ((manp->secv = malloc(maxentries * sizeof (char *))) == NULL)
58595c635efSGarrett D'Amore err(1, "malloc");
58695c635efSGarrett D'Amore }
58795c635efSGarrett D'Amore
58895c635efSGarrett D'Amore for (dv = dirv, p = manp->secv; *dv; dv++) {
58995c635efSGarrett D'Amore if (strcmp(*dv, CONFIG) == 0) {
59095c635efSGarrett D'Amore free(*dv);
59195c635efSGarrett D'Amore continue;
59295c635efSGarrett D'Amore }
59395c635efSGarrett D'Amore
59495c635efSGarrett D'Amore free(tmp);
59595c635efSGarrett D'Amore if ((tmp = strdup(*dv + 3)) == NULL)
59695c635efSGarrett D'Amore err(1, "strdup");
59795c635efSGarrett D'Amore
59895c635efSGarrett D'Amore if (prev != NULL && strcmp(prev, tmp) == 0) {
59995c635efSGarrett D'Amore free(*dv);
60095c635efSGarrett D'Amore continue;
60195c635efSGarrett D'Amore }
60295c635efSGarrett D'Amore
60395c635efSGarrett D'Amore free(prev);
60495c635efSGarrett D'Amore if ((prev = strdup(*dv + 3)) == NULL)
60595c635efSGarrett D'Amore err(1, "strdup");
60695c635efSGarrett D'Amore
60795c635efSGarrett D'Amore if ((*p = strdup(*dv + 3)) == NULL)
60895c635efSGarrett D'Amore err(1, "strdup");
60995c635efSGarrett D'Amore
61095c635efSGarrett D'Amore p++; entries++;
61195c635efSGarrett D'Amore
61295c635efSGarrett D'Amore if (entries == maxentries) {
61395c635efSGarrett D'Amore maxentries += MAXTOKENS;
61495c635efSGarrett D'Amore if ((manp->secv = realloc(manp->secv,
61595c635efSGarrett D'Amore sizeof (char *) * maxentries)) == NULL)
61695c635efSGarrett D'Amore err(1, "realloc");
61795c635efSGarrett D'Amore p = manp->secv + entries;
61895c635efSGarrett D'Amore }
61995c635efSGarrett D'Amore free(*dv);
62095c635efSGarrett D'Amore }
62195c635efSGarrett D'Amore free(tmp);
62295c635efSGarrett D'Amore free(prev);
62395c635efSGarrett D'Amore *p = NULL;
62495c635efSGarrett D'Amore free(dirv);
62595c635efSGarrett D'Amore }
62695c635efSGarrett D'Amore
62795c635efSGarrett D'Amore /*
62895c635efSGarrett D'Amore * Build whatis databases.
62995c635efSGarrett D'Amore */
63095c635efSGarrett D'Amore static void
do_makewhatis(struct man_node * manp)63195c635efSGarrett D'Amore do_makewhatis(struct man_node *manp)
63295c635efSGarrett D'Amore {
63395c635efSGarrett D'Amore struct man_node *p;
63495c635efSGarrett D'Amore char *ldir;
63595c635efSGarrett D'Amore
63695c635efSGarrett D'Amore for (p = manp; p != NULL; p = p->next) {
63795c635efSGarrett D'Amore ldir = addlocale(p->path);
63895c635efSGarrett D'Amore if (*localedir != '\0' && getdirs(ldir, NULL, 0) > 0)
63995c635efSGarrett D'Amore mwpath(ldir);
64095c635efSGarrett D'Amore free(ldir);
64195c635efSGarrett D'Amore mwpath(p->path);
64295c635efSGarrett D'Amore }
64395c635efSGarrett D'Amore }
64495c635efSGarrett D'Amore
64595c635efSGarrett D'Amore /*
64695c635efSGarrett D'Amore * Count mandirs under the given manpath
64795c635efSGarrett D'Amore */
64895c635efSGarrett D'Amore static int
getdirs(char * path,char *** dirv,int flag)64995c635efSGarrett D'Amore getdirs(char *path, char ***dirv, int flag)
65095c635efSGarrett D'Amore {
65195c635efSGarrett D'Amore DIR *dp;
65295c635efSGarrett D'Amore struct dirent *d;
65395c635efSGarrett D'Amore int n = 0;
65495c635efSGarrett D'Amore int maxentries = MAXDIRS;
65595c635efSGarrett D'Amore char **dv = NULL;
65695c635efSGarrett D'Amore
65795c635efSGarrett D'Amore if ((dp = opendir(path)) == NULL)
65895c635efSGarrett D'Amore return (0);
65995c635efSGarrett D'Amore
66095c635efSGarrett D'Amore if (flag) {
66195c635efSGarrett D'Amore if ((*dirv = malloc(sizeof (char *) *
66295c635efSGarrett D'Amore maxentries)) == NULL)
66395c635efSGarrett D'Amore err(1, "malloc");
66495c635efSGarrett D'Amore dv = *dirv;
66595c635efSGarrett D'Amore }
66695c635efSGarrett D'Amore while ((d = readdir(dp))) {
66795c635efSGarrett D'Amore if (strncmp(d->d_name, "man", 3) != 0)
66895c635efSGarrett D'Amore continue;
66995c635efSGarrett D'Amore n++;
67095c635efSGarrett D'Amore
67195c635efSGarrett D'Amore if (flag) {
67295c635efSGarrett D'Amore if ((*dv = strdup(d->d_name + 3)) == NULL)
67395c635efSGarrett D'Amore err(1, "strdup");
67495c635efSGarrett D'Amore dv++;
67595c635efSGarrett D'Amore if ((dv - *dirv) == maxentries) {
67695c635efSGarrett D'Amore int entries = maxentries;
67795c635efSGarrett D'Amore
67895c635efSGarrett D'Amore maxentries += MAXTOKENS;
67995c635efSGarrett D'Amore if ((*dirv = realloc(*dirv,
68095c635efSGarrett D'Amore sizeof (char *) * maxentries)) == NULL)
68195c635efSGarrett D'Amore err(1, "realloc");
68295c635efSGarrett D'Amore dv = *dirv + entries;
68395c635efSGarrett D'Amore }
68495c635efSGarrett D'Amore }
68595c635efSGarrett D'Amore }
68695c635efSGarrett D'Amore
68795c635efSGarrett D'Amore (void) closedir(dp);
68895c635efSGarrett D'Amore return (n);
68995c635efSGarrett D'Amore }
69095c635efSGarrett D'Amore
69195c635efSGarrett D'Amore
69295c635efSGarrett D'Amore /*
69395c635efSGarrett D'Amore * Find matching whatis or apropos entries.
69495c635efSGarrett D'Amore */
69595c635efSGarrett D'Amore static void
whatapro(struct man_node * manp,char * word)69695c635efSGarrett D'Amore whatapro(struct man_node *manp, char *word)
69795c635efSGarrett D'Amore {
69895c635efSGarrett D'Amore char whatpath[MAXPATHLEN];
69995c635efSGarrett D'Amore struct man_node *b;
70095c635efSGarrett D'Amore char *ldir;
70195c635efSGarrett D'Amore
70295c635efSGarrett D'Amore for (b = manp; b != NULL; b = b->next) {
70395c635efSGarrett D'Amore if (*localedir != '\0') {
70495c635efSGarrett D'Amore ldir = addlocale(b->path);
70595c635efSGarrett D'Amore if (getdirs(ldir, NULL, 0) != 0) {
70695c635efSGarrett D'Amore (void) snprintf(whatpath, sizeof (whatpath),
70795c635efSGarrett D'Amore "%s/%s", ldir, WHATIS);
70895c635efSGarrett D'Amore search_whatis(whatpath, word);
70995c635efSGarrett D'Amore }
71095c635efSGarrett D'Amore free(ldir);
71195c635efSGarrett D'Amore }
71295c635efSGarrett D'Amore (void) snprintf(whatpath, sizeof (whatpath), "%s/%s", b->path,
71395c635efSGarrett D'Amore WHATIS);
71495c635efSGarrett D'Amore search_whatis(whatpath, word);
71595c635efSGarrett D'Amore }
71695c635efSGarrett D'Amore }
71795c635efSGarrett D'Amore
71895c635efSGarrett D'Amore static void
search_whatis(char * whatpath,char * word)71995c635efSGarrett D'Amore search_whatis(char *whatpath, char *word)
72095c635efSGarrett D'Amore {
72195c635efSGarrett D'Amore FILE *fp;
72295c635efSGarrett D'Amore char *line = NULL;
72395c635efSGarrett D'Amore size_t linecap = 0;
72495c635efSGarrett D'Amore char *pkwd;
72595c635efSGarrett D'Amore regex_t preg;
72695c635efSGarrett D'Amore char **ss = NULL;
72795c635efSGarrett D'Amore char s[MAXNAMELEN];
72895c635efSGarrett D'Amore int i;
72995c635efSGarrett D'Amore
73095c635efSGarrett D'Amore if ((fp = fopen(whatpath, "r")) == NULL) {
73195c635efSGarrett D'Amore perror(whatpath);
73295c635efSGarrett D'Amore return;
73395c635efSGarrett D'Amore }
73495c635efSGarrett D'Amore
73595c635efSGarrett D'Amore DPRINTF("-- Found %s: %s\n", WHATIS, whatpath);
73695c635efSGarrett D'Amore
73795c635efSGarrett D'Amore /* Build keyword regex */
73895c635efSGarrett D'Amore if (asprintf(&pkwd, "%s%s%s", (whatis) ? "\\<" : "",
73995c635efSGarrett D'Amore word, (whatis) ? "\\>" : "") == -1)
74095c635efSGarrett D'Amore err(1, "asprintf");
74195c635efSGarrett D'Amore
74295c635efSGarrett D'Amore if (regcomp(&preg, pkwd, REG_BASIC | REG_ICASE | REG_NOSUB) != 0)
74395c635efSGarrett D'Amore err(1, "regcomp");
74495c635efSGarrett D'Amore
74595c635efSGarrett D'Amore if (sargs)
74695c635efSGarrett D'Amore ss = split(mansec, ',');
74795c635efSGarrett D'Amore
74895c635efSGarrett D'Amore while (getline(&line, &linecap, fp) > 0) {
74995c635efSGarrett D'Amore if (regexec(&preg, line, 0, NULL, 0) == 0) {
75095c635efSGarrett D'Amore if (sargs) {
75195c635efSGarrett D'Amore /* Section-restricted search */
75295c635efSGarrett D'Amore for (i = 0; ss[i] != NULL; i++) {
75395c635efSGarrett D'Amore (void) snprintf(s, sizeof (s), "(%s)",
75495c635efSGarrett D'Amore ss[i]);
75595c635efSGarrett D'Amore if (strstr(line, s) != NULL) {
75695c635efSGarrett D'Amore (void) printf("%s", line);
75795c635efSGarrett D'Amore break;
75895c635efSGarrett D'Amore }
75995c635efSGarrett D'Amore }
76095c635efSGarrett D'Amore } else {
76195c635efSGarrett D'Amore (void) printf("%s", line);
76295c635efSGarrett D'Amore }
76395c635efSGarrett D'Amore }
76495c635efSGarrett D'Amore }
76595c635efSGarrett D'Amore
76695c635efSGarrett D'Amore if (ss != NULL)
76795c635efSGarrett D'Amore freev(ss);
76895c635efSGarrett D'Amore free(pkwd);
76995c635efSGarrett D'Amore (void) fclose(fp);
77095c635efSGarrett D'Amore }
77195c635efSGarrett D'Amore
77295c635efSGarrett D'Amore
77395c635efSGarrett D'Amore /*
77495c635efSGarrett D'Amore * Split a string by specified separator.
77595c635efSGarrett D'Amore */
77695c635efSGarrett D'Amore static char **
split(char * s1,char sep)77795c635efSGarrett D'Amore split(char *s1, char sep)
77895c635efSGarrett D'Amore {
77995c635efSGarrett D'Amore char **tokv, **vp;
78095c635efSGarrett D'Amore char *mp = s1, *tp;
78195c635efSGarrett D'Amore int maxentries = MAXTOKENS;
78295c635efSGarrett D'Amore int entries = 0;
78395c635efSGarrett D'Amore
78495c635efSGarrett D'Amore if ((tokv = vp = malloc(maxentries * sizeof (char *))) == NULL)
78595c635efSGarrett D'Amore err(1, "malloc");
78695c635efSGarrett D'Amore
78795c635efSGarrett D'Amore for (; mp && *mp; mp = tp) {
78895c635efSGarrett D'Amore tp = strchr(mp, sep);
78995c635efSGarrett D'Amore if (mp == tp) {
79095c635efSGarrett D'Amore tp++;
79195c635efSGarrett D'Amore continue;
79295c635efSGarrett D'Amore }
79395c635efSGarrett D'Amore if (tp) {
79495c635efSGarrett D'Amore size_t len;
79595c635efSGarrett D'Amore
79695c635efSGarrett D'Amore len = tp - mp;
79795c635efSGarrett D'Amore if ((*vp = (char *)malloc(sizeof (char) *
79895c635efSGarrett D'Amore len + 1)) == NULL)
79995c635efSGarrett D'Amore err(1, "malloc");
80095c635efSGarrett D'Amore (void) strncpy(*vp, mp, len);
80195c635efSGarrett D'Amore *(*vp + len) = '\0';
80295c635efSGarrett D'Amore tp++;
80395c635efSGarrett D'Amore vp++;
80495c635efSGarrett D'Amore } else {
80595c635efSGarrett D'Amore if ((*vp = strdup(mp)) == NULL)
80695c635efSGarrett D'Amore err(1, "strdup");
80795c635efSGarrett D'Amore vp++;
80895c635efSGarrett D'Amore }
80995c635efSGarrett D'Amore entries++;
81095c635efSGarrett D'Amore if (entries == maxentries) {
81195c635efSGarrett D'Amore maxentries += MAXTOKENS;
81295c635efSGarrett D'Amore if ((tokv = realloc(tokv,
81395c635efSGarrett D'Amore maxentries * sizeof (char *))) == NULL)
81495c635efSGarrett D'Amore err(1, "realloc");
81595c635efSGarrett D'Amore vp = tokv + entries;
81695c635efSGarrett D'Amore }
81795c635efSGarrett D'Amore }
81895c635efSGarrett D'Amore *vp = 0;
81995c635efSGarrett D'Amore
82095c635efSGarrett D'Amore return (tokv);
82195c635efSGarrett D'Amore }
82295c635efSGarrett D'Amore
82395c635efSGarrett D'Amore /*
82495c635efSGarrett D'Amore * Free a vector allocated by split()
82595c635efSGarrett D'Amore */
82695c635efSGarrett D'Amore static void
freev(char ** v)82795c635efSGarrett D'Amore freev(char **v)
82895c635efSGarrett D'Amore {
82995c635efSGarrett D'Amore int i;
83095c635efSGarrett D'Amore if (v != NULL) {
83195c635efSGarrett D'Amore for (i = 0; v[i] != NULL; i++) {
83295c635efSGarrett D'Amore free(v[i]);
83395c635efSGarrett D'Amore }
83495c635efSGarrett D'Amore free(v);
83595c635efSGarrett D'Amore }
83695c635efSGarrett D'Amore }
83795c635efSGarrett D'Amore
83895c635efSGarrett D'Amore /*
83995c635efSGarrett D'Amore * Convert paths to full paths if necessary
84095c635efSGarrett D'Amore */
84195c635efSGarrett D'Amore static void
fullpaths(struct man_node ** manp_head)84295c635efSGarrett D'Amore fullpaths(struct man_node **manp_head)
84395c635efSGarrett D'Amore {
84495c635efSGarrett D'Amore char *cwd = NULL;
84595c635efSGarrett D'Amore char *p;
84695c635efSGarrett D'Amore int cwd_gotten = 0;
84795c635efSGarrett D'Amore struct man_node *manp = *manp_head;
84895c635efSGarrett D'Amore struct man_node *b;
84995c635efSGarrett D'Amore struct man_node *prev = NULL;
85095c635efSGarrett D'Amore
85195c635efSGarrett D'Amore for (b = manp; b != NULL; b = b->next) {
85295c635efSGarrett D'Amore if (*(b->path) == '/') {
85395c635efSGarrett D'Amore prev = b;
85495c635efSGarrett D'Amore continue;
85595c635efSGarrett D'Amore }
85695c635efSGarrett D'Amore
85795c635efSGarrett D'Amore if (!cwd_gotten) {
85895c635efSGarrett D'Amore cwd = getcwd(NULL, MAXPATHLEN);
85995c635efSGarrett D'Amore cwd_gotten = 1;
86095c635efSGarrett D'Amore }
86195c635efSGarrett D'Amore
86295c635efSGarrett D'Amore if (cwd) {
86395c635efSGarrett D'Amore /* Relative manpath with cwd: make absolute */
86495c635efSGarrett D'Amore if (asprintf(&p, "%s/%s", cwd, b->path) == -1)
86595c635efSGarrett D'Amore err(1, "asprintf");
86695c635efSGarrett D'Amore free(b->path);
86795c635efSGarrett D'Amore b->path = p;
86895c635efSGarrett D'Amore } else {
86995c635efSGarrett D'Amore /* Relative manpath but no cwd: omit path entry */
87095c635efSGarrett D'Amore if (prev)
87195c635efSGarrett D'Amore prev->next = b->next;
87295c635efSGarrett D'Amore else
87395c635efSGarrett D'Amore *manp_head = b->next;
87495c635efSGarrett D'Amore
87595c635efSGarrett D'Amore free_manp(b);
87695c635efSGarrett D'Amore }
87795c635efSGarrett D'Amore }
87895c635efSGarrett D'Amore free(cwd);
87995c635efSGarrett D'Amore }
88095c635efSGarrett D'Amore
88195c635efSGarrett D'Amore /*
88295c635efSGarrett D'Amore * Free a man_node structure and its contents
88395c635efSGarrett D'Amore */
88495c635efSGarrett D'Amore static void
free_manp(struct man_node * manp)88595c635efSGarrett D'Amore free_manp(struct man_node *manp)
88695c635efSGarrett D'Amore {
88795c635efSGarrett D'Amore char **p;
88895c635efSGarrett D'Amore
88995c635efSGarrett D'Amore free(manp->path);
89095c635efSGarrett D'Amore p = manp->secv;
89195c635efSGarrett D'Amore while ((p != NULL) && (*p != NULL)) {
89295c635efSGarrett D'Amore free(*p);
89395c635efSGarrett D'Amore p++;
89495c635efSGarrett D'Amore }
89595c635efSGarrett D'Amore free(manp->secv);
89695c635efSGarrett D'Amore free(manp);
89795c635efSGarrett D'Amore }
89895c635efSGarrett D'Amore
89995c635efSGarrett D'Amore
90095c635efSGarrett D'Amore /*
90195c635efSGarrett D'Amore * Map (in place) to lower case.
90295c635efSGarrett D'Amore */
90395c635efSGarrett D'Amore static void
lower(char * s)90495c635efSGarrett D'Amore lower(char *s)
90595c635efSGarrett D'Amore {
90695c635efSGarrett D'Amore
90795c635efSGarrett D'Amore if (s == 0)
90895c635efSGarrett D'Amore return;
90995c635efSGarrett D'Amore while (*s) {
91095c635efSGarrett D'Amore if (isupper(*s))
91195c635efSGarrett D'Amore *s = tolower(*s);
91295c635efSGarrett D'Amore s++;
91395c635efSGarrett D'Amore }
91495c635efSGarrett D'Amore }
91595c635efSGarrett D'Amore
91695c635efSGarrett D'Amore
91795c635efSGarrett D'Amore /*
91895c635efSGarrett D'Amore * Compare function for qsort().
91995c635efSGarrett D'Amore * Sort first by section, then by prefix.
92095c635efSGarrett D'Amore */
92195c635efSGarrett D'Amore static int
cmp(const void * arg1,const void * arg2)92295c635efSGarrett D'Amore cmp(const void *arg1, const void *arg2)
92395c635efSGarrett D'Amore {
92495c635efSGarrett D'Amore int n;
92595c635efSGarrett D'Amore char **p1 = (char **)arg1;
92695c635efSGarrett D'Amore char **p2 = (char **)arg2;
92795c635efSGarrett D'Amore
92895c635efSGarrett D'Amore /* By section */
92995c635efSGarrett D'Amore if ((n = strcmp(*p1 + 3, *p2 + 3)) != 0)
93095c635efSGarrett D'Amore return (n);
93195c635efSGarrett D'Amore
93295c635efSGarrett D'Amore /* By prefix reversed */
93395c635efSGarrett D'Amore return (strncmp(*p2, *p1, 3));
93495c635efSGarrett D'Amore }
93595c635efSGarrett D'Amore
93695c635efSGarrett D'Amore
93795c635efSGarrett D'Amore /*
93895c635efSGarrett D'Amore * Find a manpage.
93995c635efSGarrett D'Amore */
94095c635efSGarrett D'Amore static int
manual(struct man_node * manp,char * name)94195c635efSGarrett D'Amore manual(struct man_node *manp, char *name)
94295c635efSGarrett D'Amore {
94395c635efSGarrett D'Amore struct man_node *p;
94495c635efSGarrett D'Amore struct man_node *local;
94595c635efSGarrett D'Amore int ndirs = 0;
94695c635efSGarrett D'Amore char *ldir;
94795c635efSGarrett D'Amore char *ldirs[2];
94895c635efSGarrett D'Amore char *fullname = name;
94995c635efSGarrett D'Amore char *slash;
95095c635efSGarrett D'Amore
95195c635efSGarrett D'Amore if ((slash = strrchr(name, '/')) != NULL)
95295c635efSGarrett D'Amore name = slash + 1;
95395c635efSGarrett D'Amore
95495c635efSGarrett D'Amore /* For each path in MANPATH */
95595c635efSGarrett D'Amore found = 0;
95695c635efSGarrett D'Amore
95795c635efSGarrett D'Amore for (p = manp; p != NULL; p = p->next) {
95895c635efSGarrett D'Amore DPRINTF("-- Searching mandir: %s\n", p->path);
95995c635efSGarrett D'Amore
96095c635efSGarrett D'Amore if (*localedir != '\0') {
96195c635efSGarrett D'Amore ldir = addlocale(p->path);
96295c635efSGarrett D'Amore ndirs = getdirs(ldir, NULL, 0);
96395c635efSGarrett D'Amore if (ndirs != 0) {
96495c635efSGarrett D'Amore ldirs[0] = ldir;
96595c635efSGarrett D'Amore ldirs[1] = NULL;
96695c635efSGarrett D'Amore local = build_manpath(ldirs, 0);
96795c635efSGarrett D'Amore DPRINTF("-- Locale specific subdir: %s\n",
96895c635efSGarrett D'Amore ldir);
96995c635efSGarrett D'Amore mandir(local->secv, ldir, name, 1);
97095c635efSGarrett D'Amore free_manp(local);
97195c635efSGarrett D'Amore }
97295c635efSGarrett D'Amore free(ldir);
97395c635efSGarrett D'Amore }
97495c635efSGarrett D'Amore
97595c635efSGarrett D'Amore /*
97695c635efSGarrett D'Amore * Locale mandir not valid, man page in locale
97795c635efSGarrett D'Amore * mandir not found, or -a option present
97895c635efSGarrett D'Amore */
97995c635efSGarrett D'Amore if (ndirs == 0 || !found || all)
98095c635efSGarrett D'Amore mandir(p->secv, p->path, name, 0);
98195c635efSGarrett D'Amore
98295c635efSGarrett D'Amore if (found && !all)
98395c635efSGarrett D'Amore break;
98495c635efSGarrett D'Amore }
98595c635efSGarrett D'Amore
98695c635efSGarrett D'Amore if (!found) {
98795c635efSGarrett D'Amore if (sargs) {
98895c635efSGarrett D'Amore (void) fprintf(stderr, gettext(
98995c635efSGarrett D'Amore "No manual entry for %s in section(s) %s\n"),
99095c635efSGarrett D'Amore fullname, mansec);
99195c635efSGarrett D'Amore } else {
99295c635efSGarrett D'Amore (void) fprintf(stderr,
99395c635efSGarrett D'Amore gettext("No manual entry for %s\n"), fullname);
99495c635efSGarrett D'Amore }
99595c635efSGarrett D'Amore
99695c635efSGarrett D'Amore }
99795c635efSGarrett D'Amore
99895c635efSGarrett D'Amore return (!found);
99995c635efSGarrett D'Amore }
100095c635efSGarrett D'Amore
100195c635efSGarrett D'Amore
100295c635efSGarrett D'Amore /*
100395c635efSGarrett D'Amore * For a specified manual directory, read, store and sort section subdirs.
100495c635efSGarrett D'Amore * For each section specified, find and search matching subdirs.
100595c635efSGarrett D'Amore */
100695c635efSGarrett D'Amore static void
mandir(char ** secv,char * path,char * name,int lspec)100795c635efSGarrett D'Amore mandir(char **secv, char *path, char *name, int lspec)
100895c635efSGarrett D'Amore {
100995c635efSGarrett D'Amore DIR *dp;
101095c635efSGarrett D'Amore char **dirv;
101195c635efSGarrett D'Amore char **dv, **pdv;
101295c635efSGarrett D'Amore int len, dslen;
101395c635efSGarrett D'Amore
101495c635efSGarrett D'Amore if ((dp = opendir(path)) == NULL)
101595c635efSGarrett D'Amore return;
101695c635efSGarrett D'Amore
101795c635efSGarrett D'Amore if (lspec)
101895c635efSGarrett D'Amore DPRINTF("-- Searching mandir: %s\n", path);
101995c635efSGarrett D'Amore
102095c635efSGarrett D'Amore sortdir(dp, &dirv);
102195c635efSGarrett D'Amore
102295c635efSGarrett D'Amore /* Search in the order specified by MANSECTS */
102395c635efSGarrett D'Amore for (; *secv; secv++) {
102495c635efSGarrett D'Amore len = strlen(*secv);
102595c635efSGarrett D'Amore for (dv = dirv; *dv; dv++) {
102695c635efSGarrett D'Amore dslen = strlen(*dv + 3);
102795c635efSGarrett D'Amore if (dslen > len)
102895c635efSGarrett D'Amore len = dslen;
102995c635efSGarrett D'Amore if (**secv == '\\') {
103095c635efSGarrett D'Amore if (strcmp(*secv + 1, *dv + 3) != 0)
103195c635efSGarrett D'Amore continue;
103295c635efSGarrett D'Amore } else if (strncasecmp(*secv, *dv + 3, len) != 0) {
103395c635efSGarrett D'Amore if (!all &&
103495c635efSGarrett D'Amore (newsection = map_section(*secv, path))
103595c635efSGarrett D'Amore == NULL) {
103695c635efSGarrett D'Amore continue;
103795c635efSGarrett D'Amore }
103895c635efSGarrett D'Amore if (newsection == NULL)
103995c635efSGarrett D'Amore newsection = "";
104095c635efSGarrett D'Amore if (strncmp(newsection, *dv + 3, len) != 0) {
104195c635efSGarrett D'Amore continue;
104295c635efSGarrett D'Amore }
104395c635efSGarrett D'Amore }
104495c635efSGarrett D'Amore
104595c635efSGarrett D'Amore if (searchdir(path, *dv, name) == 0)
104695c635efSGarrett D'Amore continue;
104795c635efSGarrett D'Amore
104895c635efSGarrett D'Amore if (!all) {
104995c635efSGarrett D'Amore pdv = dirv;
105095c635efSGarrett D'Amore while (*pdv) {
105195c635efSGarrett D'Amore free(*pdv);
105295c635efSGarrett D'Amore pdv++;
105395c635efSGarrett D'Amore }
105495c635efSGarrett D'Amore (void) closedir(dp);
105595c635efSGarrett D'Amore free(dirv);
105695c635efSGarrett D'Amore return;
105795c635efSGarrett D'Amore }
105895c635efSGarrett D'Amore
105995c635efSGarrett D'Amore if (all && **dv == 'm' && *(dv + 1) &&
106095c635efSGarrett D'Amore strcmp(*(dv + 1) + 3, *dv + 3) == 0)
106195c635efSGarrett D'Amore dv++;
106295c635efSGarrett D'Amore }
106395c635efSGarrett D'Amore }
106495c635efSGarrett D'Amore pdv = dirv;
106595c635efSGarrett D'Amore while (*pdv != NULL) {
106695c635efSGarrett D'Amore free(*pdv);
106795c635efSGarrett D'Amore pdv++;
106895c635efSGarrett D'Amore }
106995c635efSGarrett D'Amore free(dirv);
107095c635efSGarrett D'Amore (void) closedir(dp);
107195c635efSGarrett D'Amore }
107295c635efSGarrett D'Amore
107395c635efSGarrett D'Amore /*
107495c635efSGarrett D'Amore * Sort directories.
107595c635efSGarrett D'Amore */
107695c635efSGarrett D'Amore static void
sortdir(DIR * dp,char *** dirv)107795c635efSGarrett D'Amore sortdir(DIR *dp, char ***dirv)
107895c635efSGarrett D'Amore {
107995c635efSGarrett D'Amore struct dirent *d;
108095c635efSGarrett D'Amore char **dv;
108195c635efSGarrett D'Amore int maxentries = MAXDIRS;
108295c635efSGarrett D'Amore int entries = 0;
108395c635efSGarrett D'Amore
108495c635efSGarrett D'Amore if ((dv = *dirv = malloc(sizeof (char *) *
108595c635efSGarrett D'Amore maxentries)) == NULL)
108695c635efSGarrett D'Amore err(1, "malloc");
108795c635efSGarrett D'Amore dv = *dirv;
108895c635efSGarrett D'Amore
108995c635efSGarrett D'Amore while ((d = readdir(dp))) {
109095c635efSGarrett D'Amore if (strcmp(d->d_name, ".") == 0 ||
109195c635efSGarrett D'Amore strcmp(d->d_name, "..") == 0)
109295c635efSGarrett D'Amore continue;
109395c635efSGarrett D'Amore
109495c635efSGarrett D'Amore if (strncmp(d->d_name, "man", 3) == 0 ||
109595c635efSGarrett D'Amore strncmp(d->d_name, "cat", 3) == 0) {
109695c635efSGarrett D'Amore if ((*dv = strdup(d->d_name)) == NULL)
109795c635efSGarrett D'Amore err(1, "strdup");
109895c635efSGarrett D'Amore dv++;
109995c635efSGarrett D'Amore entries++;
110095c635efSGarrett D'Amore if (entries == maxentries) {
110195c635efSGarrett D'Amore maxentries += MAXDIRS;
110295c635efSGarrett D'Amore if ((*dirv = realloc(*dirv,
110395c635efSGarrett D'Amore sizeof (char *) * maxentries)) == NULL)
110495c635efSGarrett D'Amore err(1, "realloc");
110595c635efSGarrett D'Amore dv = *dirv + entries;
110695c635efSGarrett D'Amore }
110795c635efSGarrett D'Amore }
110895c635efSGarrett D'Amore }
110995c635efSGarrett D'Amore *dv = 0;
111095c635efSGarrett D'Amore
111195c635efSGarrett D'Amore qsort((void *)*dirv, dv - *dirv, sizeof (char *), cmp);
111295c635efSGarrett D'Amore
111395c635efSGarrett D'Amore }
111495c635efSGarrett D'Amore
111595c635efSGarrett D'Amore
111695c635efSGarrett D'Amore /*
111795c635efSGarrett D'Amore * Search a section subdir for a given manpage.
111895c635efSGarrett D'Amore */
111995c635efSGarrett D'Amore static int
searchdir(char * path,char * dir,char * name)112095c635efSGarrett D'Amore searchdir(char *path, char *dir, char *name)
112195c635efSGarrett D'Amore {
112295c635efSGarrett D'Amore DIR *sdp;
112395c635efSGarrett D'Amore struct dirent *sd;
112495c635efSGarrett D'Amore char sectpath[MAXPATHLEN];
112595c635efSGarrett D'Amore char file[MAXNAMLEN];
112695c635efSGarrett D'Amore char dname[MAXPATHLEN];
112795c635efSGarrett D'Amore char *last;
112895c635efSGarrett D'Amore int nlen;
112995c635efSGarrett D'Amore
113095c635efSGarrett D'Amore (void) snprintf(sectpath, sizeof (sectpath), "%s/%s", path, dir);
113195c635efSGarrett D'Amore (void) snprintf(file, sizeof (file), "%s.", name);
113295c635efSGarrett D'Amore
113395c635efSGarrett D'Amore if ((sdp = opendir(sectpath)) == NULL)
113495c635efSGarrett D'Amore return (0);
113595c635efSGarrett D'Amore
113695c635efSGarrett D'Amore while ((sd = readdir(sdp))) {
113795c635efSGarrett D'Amore char *pname;
113895c635efSGarrett D'Amore
113995c635efSGarrett D'Amore if ((pname = strdup(sd->d_name)) == NULL)
114095c635efSGarrett D'Amore err(1, "strdup");
114195c635efSGarrett D'Amore if ((last = strrchr(pname, '.')) != NULL &&
114295c635efSGarrett D'Amore (strcmp(last, ".gz") == 0 || strcmp(last, ".bz2") == 0))
114395c635efSGarrett D'Amore *last = '\0';
114495c635efSGarrett D'Amore last = strrchr(pname, '.');
114595c635efSGarrett D'Amore nlen = last - pname;
114695c635efSGarrett D'Amore (void) snprintf(dname, sizeof (dname), "%.*s.", nlen, pname);
114795c635efSGarrett D'Amore if (strcmp(dname, file) == 0 ||
114895c635efSGarrett D'Amore strcmp(pname, name) == 0) {
114995c635efSGarrett D'Amore (void) format(path, dir, name, sd->d_name);
115095c635efSGarrett D'Amore (void) closedir(sdp);
115195c635efSGarrett D'Amore free(pname);
115295c635efSGarrett D'Amore return (1);
115395c635efSGarrett D'Amore }
115495c635efSGarrett D'Amore free(pname);
115595c635efSGarrett D'Amore }
115695c635efSGarrett D'Amore (void) closedir(sdp);
115795c635efSGarrett D'Amore
115895c635efSGarrett D'Amore return (0);
115995c635efSGarrett D'Amore }
116095c635efSGarrett D'Amore
116195c635efSGarrett D'Amore /*
116295c635efSGarrett D'Amore * Check the hash table of old directory names to see if there is a
116395c635efSGarrett D'Amore * new directory name.
116495c635efSGarrett D'Amore */
116595c635efSGarrett D'Amore static char *
map_section(char * section,char * path)116695c635efSGarrett D'Amore map_section(char *section, char *path)
116795c635efSGarrett D'Amore {
116895c635efSGarrett D'Amore int i;
116995c635efSGarrett D'Amore char fullpath[MAXPATHLEN];
117095c635efSGarrett D'Amore
117195c635efSGarrett D'Amore if (list) /* -l option fall through */
117295c635efSGarrett D'Amore return (NULL);
117395c635efSGarrett D'Amore
117495c635efSGarrett D'Amore for (i = 0; map[i].new_name != NULL; i++) {
117595c635efSGarrett D'Amore if (strcmp(section, map[i].old_name) == 0) {
117695c635efSGarrett D'Amore (void) snprintf(fullpath, sizeof (fullpath),
117795c635efSGarrett D'Amore "%s/man%s", path, map[i].new_name);
117895c635efSGarrett D'Amore if (!access(fullpath, R_OK | X_OK)) {
117995c635efSGarrett D'Amore return (map[i].new_name);
118095c635efSGarrett D'Amore } else {
118195c635efSGarrett D'Amore return (NULL);
118295c635efSGarrett D'Amore }
118395c635efSGarrett D'Amore }
118495c635efSGarrett D'Amore }
118595c635efSGarrett D'Amore
118695c635efSGarrett D'Amore return (NULL);
118795c635efSGarrett D'Amore }
118895c635efSGarrett D'Amore
118995c635efSGarrett D'Amore /*
119095c635efSGarrett D'Amore * Format the manpage.
119195c635efSGarrett D'Amore */
119295c635efSGarrett D'Amore static int
format(char * path,char * dir,char * name,char * pg)119395c635efSGarrett D'Amore format(char *path, char *dir, char *name, char *pg)
119495c635efSGarrett D'Amore {
119595c635efSGarrett D'Amore char manpname[MAXPATHLEN], catpname[MAXPATHLEN];
119695c635efSGarrett D'Amore char cmdbuf[BUFSIZ], tmpbuf[BUFSIZ];
119795c635efSGarrett D'Amore char *cattool;
119895c635efSGarrett D'Amore struct stat sbman, sbcat;
119995c635efSGarrett D'Amore
120095c635efSGarrett D'Amore found++;
120195c635efSGarrett D'Amore
120295c635efSGarrett D'Amore if (list) {
120395c635efSGarrett D'Amore (void) printf(gettext("%s(%s)\t-M %s\n"), name, dir + 3, path);
120495c635efSGarrett D'Amore return (-1);
120595c635efSGarrett D'Amore }
120695c635efSGarrett D'Amore
120795c635efSGarrett D'Amore (void) snprintf(manpname, sizeof (manpname), "%s/man%s/%s", path,
120895c635efSGarrett D'Amore dir + 3, pg);
120995c635efSGarrett D'Amore (void) snprintf(catpname, sizeof (catpname), "%s/cat%s/%s", path,
121095c635efSGarrett D'Amore dir + 3, pg);
121195c635efSGarrett D'Amore
121295c635efSGarrett D'Amore /* Can't do PS output if manpage doesn't exist */
121395c635efSGarrett D'Amore if (stat(manpname, &sbman) != 0 && (psoutput|lintout))
121495c635efSGarrett D'Amore return (-1);
121595c635efSGarrett D'Amore
121695c635efSGarrett D'Amore /*
121795c635efSGarrett D'Amore * If both manpage and catpage do not exist, manpname is
121895c635efSGarrett D'Amore * broken symlink, most likely.
121995c635efSGarrett D'Amore */
122095c635efSGarrett D'Amore if (stat(catpname, &sbcat) != 0 && stat(manpname, &sbman) != 0)
122195c635efSGarrett D'Amore err(1, "%s", manpname);
122295c635efSGarrett D'Amore
122395c635efSGarrett D'Amore /* Setup cattool */
122495c635efSGarrett D'Amore if (fnmatch("*.gz", manpname, 0) == 0)
122595c635efSGarrett D'Amore cattool = "gzcat";
122695c635efSGarrett D'Amore else if (fnmatch("*.bz2", manpname, 0) == 0)
122795c635efSGarrett D'Amore cattool = "bzcat";
122895c635efSGarrett D'Amore else
122995c635efSGarrett D'Amore cattool = "cat";
123095c635efSGarrett D'Amore
123195c635efSGarrett D'Amore if (psoutput) {
123295c635efSGarrett D'Amore (void) snprintf(cmdbuf, BUFSIZ,
1233260e9a87SYuri Pankov "cd %s; %s %s | mandoc -Tps | lp -Tpostscript",
1234260e9a87SYuri Pankov path, cattool, manpname);
123595c635efSGarrett D'Amore DPRINTF("-- Using manpage: %s\n", manpname);
123695c635efSGarrett D'Amore goto cmd;
123795c635efSGarrett D'Amore } else if (lintout) {
123895c635efSGarrett D'Amore (void) snprintf(cmdbuf, BUFSIZ,
1239260e9a87SYuri Pankov "cd %s; %s %s | mandoc -Tlint",
1240260e9a87SYuri Pankov path, cattool, manpname);
124195c635efSGarrett D'Amore DPRINTF("-- Linting manpage: %s\n", manpname);
124295c635efSGarrett D'Amore goto cmd;
124395c635efSGarrett D'Amore }
124495c635efSGarrett D'Amore
124595c635efSGarrett D'Amore /*
124695c635efSGarrett D'Amore * Output catpage if:
124795c635efSGarrett D'Amore * - manpage doesn't exist
124895c635efSGarrett D'Amore * - output width is standard and catpage is recent enough
124995c635efSGarrett D'Amore */
125095c635efSGarrett D'Amore if (stat(manpname, &sbman) != 0 || (manwidth == 0 &&
125195c635efSGarrett D'Amore stat(catpname, &sbcat) == 0 && sbcat.st_mtime >= sbman.st_mtime)) {
125295c635efSGarrett D'Amore DPRINTF("-- Using catpage: %s\n", catpname);
125395c635efSGarrett D'Amore (void) snprintf(cmdbuf, BUFSIZ, "%s %s", pager, catpname);
125495c635efSGarrett D'Amore goto cmd;
125595c635efSGarrett D'Amore }
125695c635efSGarrett D'Amore
125795c635efSGarrett D'Amore DPRINTF("-- Using manpage: %s\n", manpname);
125895c635efSGarrett D'Amore if (manwidth > 0)
125995c635efSGarrett D'Amore (void) snprintf(tmpbuf, BUFSIZ, "-Owidth=%d ", manwidth);
1260260e9a87SYuri Pankov (void) snprintf(cmdbuf, BUFSIZ, "cd %s; %s %s | mandoc %s| %s",
1261260e9a87SYuri Pankov path, cattool, manpname, (manwidth > 0) ? tmpbuf : "", pager);
126295c635efSGarrett D'Amore
126395c635efSGarrett D'Amore cmd:
126495c635efSGarrett D'Amore DPRINTF("-- Command: %s\n", cmdbuf);
126595c635efSGarrett D'Amore
126695c635efSGarrett D'Amore if (!debug)
126795c635efSGarrett D'Amore return (system(cmdbuf) == 0);
126895c635efSGarrett D'Amore else
126995c635efSGarrett D'Amore return (0);
127095c635efSGarrett D'Amore }
127195c635efSGarrett D'Amore
127295c635efSGarrett D'Amore /*
127395c635efSGarrett D'Amore * Add <localedir> to the path.
127495c635efSGarrett D'Amore */
127595c635efSGarrett D'Amore static char *
addlocale(char * path)127695c635efSGarrett D'Amore addlocale(char *path)
127795c635efSGarrett D'Amore {
127895c635efSGarrett D'Amore char *tmp;
127995c635efSGarrett D'Amore
128095c635efSGarrett D'Amore if (asprintf(&tmp, "%s/%s", path, localedir) == -1)
128195c635efSGarrett D'Amore err(1, "asprintf");
128295c635efSGarrett D'Amore
128395c635efSGarrett D'Amore return (tmp);
128495c635efSGarrett D'Amore }
128595c635efSGarrett D'Amore
128695c635efSGarrett D'Amore /*
128795c635efSGarrett D'Amore * Get the order of sections from man.cf.
128895c635efSGarrett D'Amore */
128995c635efSGarrett D'Amore static char *
check_config(char * path)129095c635efSGarrett D'Amore check_config(char *path)
129195c635efSGarrett D'Amore {
129295c635efSGarrett D'Amore FILE *fp;
129395c635efSGarrett D'Amore char *rc = NULL;
1294*d456640dSYuri Pankov char *sect = NULL;
129595c635efSGarrett D'Amore char fname[MAXPATHLEN];
129695c635efSGarrett D'Amore char *line = NULL;
1297*d456640dSYuri Pankov char *nl;
129895c635efSGarrett D'Amore size_t linecap = 0;
129995c635efSGarrett D'Amore
130095c635efSGarrett D'Amore (void) snprintf(fname, MAXPATHLEN, "%s/%s", path, CONFIG);
130195c635efSGarrett D'Amore
130295c635efSGarrett D'Amore if ((fp = fopen(fname, "r")) == NULL)
130395c635efSGarrett D'Amore return (NULL);
130495c635efSGarrett D'Amore
130595c635efSGarrett D'Amore while (getline(&line, &linecap, fp) > 0) {
1306*d456640dSYuri Pankov if ((rc = strstr(line, "MANSECTS=")) != NULL)
130795c635efSGarrett D'Amore break;
130895c635efSGarrett D'Amore }
130995c635efSGarrett D'Amore
131095c635efSGarrett D'Amore (void) fclose(fp);
131195c635efSGarrett D'Amore
1312*d456640dSYuri Pankov if (rc != NULL) {
1313*d456640dSYuri Pankov if ((nl = strchr(rc, '\n')) != NULL)
1314*d456640dSYuri Pankov *nl = '\0';
1315*d456640dSYuri Pankov sect = strchr(rc, '=') + 1;
131695c635efSGarrett D'Amore }
131795c635efSGarrett D'Amore
1318*d456640dSYuri Pankov return (sect);
1319*d456640dSYuri Pankov }
132095c635efSGarrett D'Amore
132195c635efSGarrett D'Amore /*
132295c635efSGarrett D'Amore * Initialize the bintoman array with appropriate device and inode info.
132395c635efSGarrett D'Amore */
132495c635efSGarrett D'Amore static void
init_bintoman(void)132595c635efSGarrett D'Amore init_bintoman(void)
132695c635efSGarrett D'Amore {
132795c635efSGarrett D'Amore int i;
132895c635efSGarrett D'Amore struct stat sb;
132995c635efSGarrett D'Amore
133095c635efSGarrett D'Amore for (i = 0; bintoman[i].bindir != NULL; i++) {
133195c635efSGarrett D'Amore if (stat(bintoman[i].bindir, &sb) == 0) {
133295c635efSGarrett D'Amore bintoman[i].dev = sb.st_dev;
133395c635efSGarrett D'Amore bintoman[i].ino = sb.st_ino;
133495c635efSGarrett D'Amore } else {
133595c635efSGarrett D'Amore bintoman[i].dev = NODEV;
133695c635efSGarrett D'Amore }
133795c635efSGarrett D'Amore }
133895c635efSGarrett D'Amore }
133995c635efSGarrett D'Amore
134095c635efSGarrett D'Amore /*
134195c635efSGarrett D'Amore * If a duplicate is found, return 1.
134295c635efSGarrett D'Amore * If a duplicate is not found, add it to the dupnode list and return 0.
134395c635efSGarrett D'Amore */
134495c635efSGarrett D'Amore static int
dupcheck(struct man_node * mnp,struct dupnode ** dnp)134595c635efSGarrett D'Amore dupcheck(struct man_node *mnp, struct dupnode **dnp)
134695c635efSGarrett D'Amore {
134795c635efSGarrett D'Amore struct dupnode *curdnp;
134895c635efSGarrett D'Amore struct secnode *cursnp;
134995c635efSGarrett D'Amore struct stat sb;
135095c635efSGarrett D'Amore int i;
135195c635efSGarrett D'Amore int rv = 1;
135295c635efSGarrett D'Amore int dupfound;
135395c635efSGarrett D'Amore
135495c635efSGarrett D'Amore /* If the path doesn't exist, treat it as a duplicate */
135595c635efSGarrett D'Amore if (stat(mnp->path, &sb) != 0)
135695c635efSGarrett D'Amore return (1);
135795c635efSGarrett D'Amore
135895c635efSGarrett D'Amore /* If no sections were found in the man dir, treat it as duplicate */
135995c635efSGarrett D'Amore if (mnp->secv == NULL)
136095c635efSGarrett D'Amore return (1);
136195c635efSGarrett D'Amore
136295c635efSGarrett D'Amore /*
136395c635efSGarrett D'Amore * Find the dupnode structure for the previous time this directory
136495c635efSGarrett D'Amore * was looked at. Device and inode numbers are compared so that
136595c635efSGarrett D'Amore * directories that are reached via different paths (e.g. /usr/man and
136695c635efSGarrett D'Amore * /usr/share/man) are treated as equivalent.
136795c635efSGarrett D'Amore */
136895c635efSGarrett D'Amore for (curdnp = *dnp; curdnp != NULL; curdnp = curdnp->next) {
136995c635efSGarrett D'Amore if (curdnp->dev == sb.st_dev && curdnp->ino == sb.st_ino)
137095c635efSGarrett D'Amore break;
137195c635efSGarrett D'Amore }
137295c635efSGarrett D'Amore
137395c635efSGarrett D'Amore /*
137495c635efSGarrett D'Amore * First time this directory has been seen. Add a new node to the
137595c635efSGarrett D'Amore * head of the list. Since all entries are guaranteed to be unique
137695c635efSGarrett D'Amore * copy all sections to new node.
137795c635efSGarrett D'Amore */
137895c635efSGarrett D'Amore if (curdnp == NULL) {
137995c635efSGarrett D'Amore if ((curdnp = calloc(1, sizeof (struct dupnode))) == NULL)
138095c635efSGarrett D'Amore err(1, "calloc");
138195c635efSGarrett D'Amore for (i = 0; mnp->secv[i] != NULL; i++) {
138295c635efSGarrett D'Amore if ((cursnp = calloc(1, sizeof (struct secnode)))
138395c635efSGarrett D'Amore == NULL)
138495c635efSGarrett D'Amore err(1, "calloc");
138595c635efSGarrett D'Amore cursnp->next = curdnp->secl;
138695c635efSGarrett D'Amore curdnp->secl = cursnp;
138795c635efSGarrett D'Amore if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
138895c635efSGarrett D'Amore err(1, "strdup");
138995c635efSGarrett D'Amore }
139095c635efSGarrett D'Amore curdnp->dev = sb.st_dev;
139195c635efSGarrett D'Amore curdnp->ino = sb.st_ino;
139295c635efSGarrett D'Amore curdnp->next = *dnp;
139395c635efSGarrett D'Amore *dnp = curdnp;
139495c635efSGarrett D'Amore return (0);
139595c635efSGarrett D'Amore }
139695c635efSGarrett D'Amore
139795c635efSGarrett D'Amore /*
139895c635efSGarrett D'Amore * Traverse the section vector in the man_node and the section list
139995c635efSGarrett D'Amore * in dupnode cache to eliminate all duplicates from man_node.
140095c635efSGarrett D'Amore */
140195c635efSGarrett D'Amore for (i = 0; mnp->secv[i] != NULL; i++) {
140295c635efSGarrett D'Amore dupfound = 0;
140395c635efSGarrett D'Amore for (cursnp = curdnp->secl; cursnp != NULL;
140495c635efSGarrett D'Amore cursnp = cursnp->next) {
140595c635efSGarrett D'Amore if (strcmp(mnp->secv[i], cursnp->secp) == 0) {
140695c635efSGarrett D'Amore dupfound = 1;
140795c635efSGarrett D'Amore break;
140895c635efSGarrett D'Amore }
140995c635efSGarrett D'Amore }
141095c635efSGarrett D'Amore if (dupfound) {
141195c635efSGarrett D'Amore mnp->secv[i][0] = '\0';
141295c635efSGarrett D'Amore continue;
141395c635efSGarrett D'Amore }
141495c635efSGarrett D'Amore
141595c635efSGarrett D'Amore
141695c635efSGarrett D'Amore /*
141795c635efSGarrett D'Amore * Update curdnp and set return value to indicate that this
141895c635efSGarrett D'Amore * was not all duplicates.
141995c635efSGarrett D'Amore */
142095c635efSGarrett D'Amore if ((cursnp = calloc(1, sizeof (struct secnode))) == NULL)
142195c635efSGarrett D'Amore err(1, "calloc");
142295c635efSGarrett D'Amore cursnp->next = curdnp->secl;
142395c635efSGarrett D'Amore curdnp->secl = cursnp;
142495c635efSGarrett D'Amore if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
142595c635efSGarrett D'Amore err(1, "strdup");
142695c635efSGarrett D'Amore rv = 0;
142795c635efSGarrett D'Amore }
142895c635efSGarrett D'Amore
142995c635efSGarrett D'Amore return (rv);
143095c635efSGarrett D'Amore }
143195c635efSGarrett D'Amore
143295c635efSGarrett D'Amore /*
143395c635efSGarrett D'Amore * Given a bindir, return corresponding mandir.
143495c635efSGarrett D'Amore */
143595c635efSGarrett D'Amore static char *
path_to_manpath(char * bindir)143695c635efSGarrett D'Amore path_to_manpath(char *bindir)
143795c635efSGarrett D'Amore {
143895c635efSGarrett D'Amore char *mand, *p;
143995c635efSGarrett D'Amore int i;
144095c635efSGarrett D'Amore struct stat sb;
144195c635efSGarrett D'Amore
144295c635efSGarrett D'Amore /* First look for known translations for specific bin paths */
144395c635efSGarrett D'Amore if (stat(bindir, &sb) != 0) {
144495c635efSGarrett D'Amore return (NULL);
144595c635efSGarrett D'Amore }
144695c635efSGarrett D'Amore for (i = 0; bintoman[i].bindir != NULL; i++) {
144795c635efSGarrett D'Amore if (sb.st_dev == bintoman[i].dev &&
144895c635efSGarrett D'Amore sb.st_ino == bintoman[i].ino) {
144995c635efSGarrett D'Amore if ((mand = strdup(bintoman[i].mandir)) == NULL)
145095c635efSGarrett D'Amore err(1, "strdup");
145195c635efSGarrett D'Amore if ((p = strchr(mand, ',')) != NULL)
145295c635efSGarrett D'Amore *p = '\0';
145395c635efSGarrett D'Amore if (stat(mand, &sb) != 0) {
145495c635efSGarrett D'Amore free(mand);
145595c635efSGarrett D'Amore return (NULL);
145695c635efSGarrett D'Amore }
145795c635efSGarrett D'Amore if (p != NULL)
145895c635efSGarrett D'Amore *p = ',';
145995c635efSGarrett D'Amore return (mand);
146095c635efSGarrett D'Amore }
146195c635efSGarrett D'Amore }
146295c635efSGarrett D'Amore
146395c635efSGarrett D'Amore /*
146495c635efSGarrett D'Amore * No specific translation found. Try `dirname $bindir`/share/man
146595c635efSGarrett D'Amore * and `dirname $bindir`/man
146695c635efSGarrett D'Amore */
146795c635efSGarrett D'Amore if ((mand = malloc(MAXPATHLEN)) == NULL)
146895c635efSGarrett D'Amore err(1, "malloc");
146995c635efSGarrett D'Amore if (strlcpy(mand, bindir, MAXPATHLEN) >= MAXPATHLEN) {
147095c635efSGarrett D'Amore free(mand);
147195c635efSGarrett D'Amore return (NULL);
147295c635efSGarrett D'Amore }
147395c635efSGarrett D'Amore
147495c635efSGarrett D'Amore /*
147595c635efSGarrett D'Amore * Advance to end of buffer, strip trailing /'s then remove last
147695c635efSGarrett D'Amore * directory component.
147795c635efSGarrett D'Amore */
147895c635efSGarrett D'Amore for (p = mand; *p != '\0'; p++)
147995c635efSGarrett D'Amore ;
148095c635efSGarrett D'Amore for (; p > mand && *p == '/'; p--)
148195c635efSGarrett D'Amore ;
148295c635efSGarrett D'Amore for (; p > mand && *p != '/'; p--)
148395c635efSGarrett D'Amore ;
148495c635efSGarrett D'Amore if (p == mand && *p == '.') {
148595c635efSGarrett D'Amore if (realpath("..", mand) == NULL) {
148695c635efSGarrett D'Amore free(mand);
148795c635efSGarrett D'Amore return (NULL);
148895c635efSGarrett D'Amore }
148995c635efSGarrett D'Amore for (; *p != '\0'; p++)
149095c635efSGarrett D'Amore ;
149195c635efSGarrett D'Amore } else {
149295c635efSGarrett D'Amore *p = '\0';
149395c635efSGarrett D'Amore }
149495c635efSGarrett D'Amore
149595c635efSGarrett D'Amore if (strlcat(mand, "/share/man", MAXPATHLEN) >= MAXPATHLEN) {
149695c635efSGarrett D'Amore free(mand);
149795c635efSGarrett D'Amore return (NULL);
149895c635efSGarrett D'Amore }
149995c635efSGarrett D'Amore
150095c635efSGarrett D'Amore if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
150195c635efSGarrett D'Amore return (mand);
150295c635efSGarrett D'Amore }
150395c635efSGarrett D'Amore
150495c635efSGarrett D'Amore /*
150595c635efSGarrett D'Amore * Strip the /share/man off and try /man
150695c635efSGarrett D'Amore */
150795c635efSGarrett D'Amore *p = '\0';
150895c635efSGarrett D'Amore if (strlcat(mand, "/man", MAXPATHLEN) >= MAXPATHLEN) {
150995c635efSGarrett D'Amore free(mand);
151095c635efSGarrett D'Amore return (NULL);
151195c635efSGarrett D'Amore }
151295c635efSGarrett D'Amore if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
151395c635efSGarrett D'Amore return (mand);
151495c635efSGarrett D'Amore }
151595c635efSGarrett D'Amore
151695c635efSGarrett D'Amore /*
151795c635efSGarrett D'Amore * No man or share/man directory found
151895c635efSGarrett D'Amore */
151995c635efSGarrett D'Amore free(mand);
152095c635efSGarrett D'Amore return (NULL);
152195c635efSGarrett D'Amore }
152295c635efSGarrett D'Amore
152395c635efSGarrett D'Amore /*
152495c635efSGarrett D'Amore * Free a linked list of dupnode structs.
152595c635efSGarrett D'Amore */
152695c635efSGarrett D'Amore void
free_dupnode(struct dupnode * dnp)1527*d456640dSYuri Pankov free_dupnode(struct dupnode *dnp)
1528*d456640dSYuri Pankov {
152995c635efSGarrett D'Amore struct dupnode *dnp2;
153095c635efSGarrett D'Amore struct secnode *snp;
153195c635efSGarrett D'Amore
153295c635efSGarrett D'Amore while (dnp != NULL) {
153395c635efSGarrett D'Amore dnp2 = dnp;
153495c635efSGarrett D'Amore dnp = dnp->next;
153595c635efSGarrett D'Amore while (dnp2->secl != NULL) {
153695c635efSGarrett D'Amore snp = dnp2->secl;
153795c635efSGarrett D'Amore dnp2->secl = dnp2->secl->next;
153895c635efSGarrett D'Amore free(snp->secp);
153995c635efSGarrett D'Amore free(snp);
154095c635efSGarrett D'Amore }
154195c635efSGarrett D'Amore free(dnp2);
154295c635efSGarrett D'Amore }
154395c635efSGarrett D'Amore }
154495c635efSGarrett D'Amore
154595c635efSGarrett D'Amore /*
154695c635efSGarrett D'Amore * Print manp linked list to stdout.
154795c635efSGarrett D'Amore */
154895c635efSGarrett D'Amore void
print_manpath(struct man_node * manp)154995c635efSGarrett D'Amore print_manpath(struct man_node *manp)
155095c635efSGarrett D'Amore {
155195c635efSGarrett D'Amore char colon[2] = "\0\0";
155295c635efSGarrett D'Amore char **secp;
155395c635efSGarrett D'Amore
155495c635efSGarrett D'Amore for (; manp != NULL; manp = manp->next) {
155595c635efSGarrett D'Amore (void) printf("%s%s", colon, manp->path);
155695c635efSGarrett D'Amore colon[0] = ':';
155795c635efSGarrett D'Amore
155895c635efSGarrett D'Amore /*
155995c635efSGarrett D'Amore * If man.cf or a directory scan was used to create section
156095c635efSGarrett D'Amore * list, do not print section list again. If the output of
156195c635efSGarrett D'Amore * man -p is used to set MANPATH, subsequent runs of man
156295c635efSGarrett D'Amore * will re-read man.cf and/or scan man directories as
156395c635efSGarrett D'Amore * required.
156495c635efSGarrett D'Amore */
156595c635efSGarrett D'Amore if (manp->defsrch != 0)
156695c635efSGarrett D'Amore continue;
156795c635efSGarrett D'Amore
156895c635efSGarrett D'Amore for (secp = manp->secv; *secp != NULL; secp++) {
156995c635efSGarrett D'Amore /*
157095c635efSGarrett D'Amore * Section deduplication may have eliminated some
157195c635efSGarrett D'Amore * sections from the vector. Avoid displaying this
157295c635efSGarrett D'Amore * detail which would appear as ",," in output
157395c635efSGarrett D'Amore */
157495c635efSGarrett D'Amore if ((*secp)[0] != '\0')
157595c635efSGarrett D'Amore (void) printf(",%s", *secp);
157695c635efSGarrett D'Amore }
157795c635efSGarrett D'Amore }
157895c635efSGarrett D'Amore (void) printf("\n");
157995c635efSGarrett D'Amore }
158095c635efSGarrett D'Amore
158195c635efSGarrett D'Amore static void
usage_man(void)158295c635efSGarrett D'Amore usage_man(void)
158395c635efSGarrett D'Amore {
158495c635efSGarrett D'Amore
158595c635efSGarrett D'Amore (void) fprintf(stderr, gettext(
158695c635efSGarrett D'Amore "usage: man [-alptw] [-M path] [-s section] name ...\n"
158795c635efSGarrett D'Amore " man [-M path] [-s section] -k keyword ...\n"
158895c635efSGarrett D'Amore " man [-M path] [-s section] -f keyword ...\n"));
158995c635efSGarrett D'Amore
159095c635efSGarrett D'Amore exit(1);
159195c635efSGarrett D'Amore }
159295c635efSGarrett D'Amore
159395c635efSGarrett D'Amore static void
usage_whatapro(void)159495c635efSGarrett D'Amore usage_whatapro(void)
159595c635efSGarrett D'Amore {
159695c635efSGarrett D'Amore
159795c635efSGarrett D'Amore (void) fprintf(stderr, gettext(
159895c635efSGarrett D'Amore "usage: %s [-M path] [-s section] keyword ...\n"),
159995c635efSGarrett D'Amore whatis ? "whatis" : "apropos");
160095c635efSGarrett D'Amore
160195c635efSGarrett D'Amore exit(1);
160295c635efSGarrett D'Amore }
160395c635efSGarrett D'Amore
160495c635efSGarrett D'Amore static void
usage_catman(void)160595c635efSGarrett D'Amore usage_catman(void)
160695c635efSGarrett D'Amore {
160795c635efSGarrett D'Amore (void) fprintf(stderr, gettext(
160895c635efSGarrett D'Amore "usage: catman [-M path] [-w]\n"));
160995c635efSGarrett D'Amore
161095c635efSGarrett D'Amore exit(1);
161195c635efSGarrett D'Amore }
161295c635efSGarrett D'Amore
161395c635efSGarrett D'Amore static void
usage_makewhatis(void)161495c635efSGarrett D'Amore usage_makewhatis(void)
161595c635efSGarrett D'Amore {
161695c635efSGarrett D'Amore (void) fprintf(stderr, gettext("usage: makewhatis\n"));
161795c635efSGarrett D'Amore
161895c635efSGarrett D'Amore exit(1);
161995c635efSGarrett D'Amore }
1620