xref: /openbsd-src/usr.bin/mandoc/manpath.c (revision 3327fa001cdea3a379b54bd358dba2db2bfce9cd)
1*3327fa00Sschwarze /*	$OpenBSD: manpath.c,v 1.23 2018/10/02 14:56:36 schwarze Exp $ */
227255502Sschwarze /*
3*3327fa00Sschwarze  * Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
427255502Sschwarze  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
527255502Sschwarze  *
627255502Sschwarze  * Permission to use, copy, modify, and distribute this software for any
727255502Sschwarze  * purpose with or without fee is hereby granted, provided that the above
827255502Sschwarze  * copyright notice and this permission notice appear in all copies.
927255502Sschwarze  *
10d05e3f9eSschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1127255502Sschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d05e3f9eSschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1327255502Sschwarze  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1427255502Sschwarze  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1527255502Sschwarze  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1627255502Sschwarze  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1727255502Sschwarze  */
18a2e5c5f4Sschwarze #include <sys/types.h>
19a2e5c5f4Sschwarze #include <sys/stat.h>
2027255502Sschwarze 
2127255502Sschwarze #include <ctype.h>
22eba1598bSschwarze #include <err.h>
2327255502Sschwarze #include <limits.h>
2427255502Sschwarze #include <stdio.h>
2527255502Sschwarze #include <stdlib.h>
2627255502Sschwarze #include <string.h>
2727255502Sschwarze 
284f4f7972Sschwarze #include "mandoc_aux.h"
294de77decSschwarze #include "manconf.h"
3027255502Sschwarze 
3127255502Sschwarze #define MAN_CONF_FILE	"/etc/man.conf"
3219b6bef7Sschwarze #define MANPATH_BASE	"/usr/share/man:/usr/X11R6/man"
33263f9f36Sschwarze #define MANPATH_DEFAULT	"/usr/share/man:/usr/X11R6/man:/usr/local/man"
3427255502Sschwarze 
354de77decSschwarze static	void	 manconf_file(struct manconf *, const char *);
36a2e5c5f4Sschwarze static	void	 manpath_add(struct manpaths *, const char *, int);
37a2e5c5f4Sschwarze static	void	 manpath_parseline(struct manpaths *, char *, int);
3827255502Sschwarze 
394de77decSschwarze 
4027255502Sschwarze void
414de77decSschwarze manconf_parse(struct manconf *conf, const char *file,
4224547daeSschwarze 		char *defp, char *auxp)
4327255502Sschwarze {
440f34e0a0Sschwarze 	char		*insert;
4527255502Sschwarze 
460f34e0a0Sschwarze 	/* Always prepend -m. */
474de77decSschwarze 	manpath_parseline(&conf->manpath, auxp, 1);
4827255502Sschwarze 
490f34e0a0Sschwarze 	/* If -M is given, it overrides everything else. */
500f34e0a0Sschwarze 	if (NULL != defp) {
514de77decSschwarze 		manpath_parseline(&conf->manpath, defp, 1);
520f34e0a0Sschwarze 		return;
530f34e0a0Sschwarze 	}
5427255502Sschwarze 
550f34e0a0Sschwarze 	/* MANPATH and man.conf(5) cooperate. */
560f34e0a0Sschwarze 	defp = getenv("MANPATH");
570f34e0a0Sschwarze 	if (NULL == file)
580f34e0a0Sschwarze 		file = MAN_CONF_FILE;
590f34e0a0Sschwarze 
600f34e0a0Sschwarze 	/* No MANPATH; use man.conf(5) only. */
610f34e0a0Sschwarze 	if (NULL == defp || '\0' == defp[0]) {
624de77decSschwarze 		manconf_file(conf, file);
630f34e0a0Sschwarze 		return;
640f34e0a0Sschwarze 	}
650f34e0a0Sschwarze 
660f34e0a0Sschwarze 	/* Prepend man.conf(5) to MANPATH. */
670f34e0a0Sschwarze 	if (':' == defp[0]) {
684de77decSschwarze 		manconf_file(conf, file);
694de77decSschwarze 		manpath_parseline(&conf->manpath, defp, 0);
700f34e0a0Sschwarze 		return;
710f34e0a0Sschwarze 	}
720f34e0a0Sschwarze 
730f34e0a0Sschwarze 	/* Append man.conf(5) to MANPATH. */
743db9e7afSschwarze 	if (':' == defp[strlen(defp) - 1]) {
754de77decSschwarze 		manpath_parseline(&conf->manpath, defp, 0);
764de77decSschwarze 		manconf_file(conf, file);
770f34e0a0Sschwarze 		return;
780f34e0a0Sschwarze 	}
790f34e0a0Sschwarze 
800f34e0a0Sschwarze 	/* Insert man.conf(5) into MANPATH. */
810f34e0a0Sschwarze 	insert = strstr(defp, "::");
820f34e0a0Sschwarze 	if (NULL != insert) {
830f34e0a0Sschwarze 		*insert++ = '\0';
844de77decSschwarze 		manpath_parseline(&conf->manpath, defp, 0);
854de77decSschwarze 		manconf_file(conf, file);
864de77decSschwarze 		manpath_parseline(&conf->manpath, insert + 1, 0);
870f34e0a0Sschwarze 		return;
880f34e0a0Sschwarze 	}
890f34e0a0Sschwarze 
900f34e0a0Sschwarze 	/* MANPATH overrides man.conf(5) completely. */
914de77decSschwarze 	manpath_parseline(&conf->manpath, defp, 0);
9227255502Sschwarze }
9327255502Sschwarze 
9419b6bef7Sschwarze void
9519b6bef7Sschwarze manpath_base(struct manpaths *dirs)
9619b6bef7Sschwarze {
9719b6bef7Sschwarze 	char path_base[] = MANPATH_BASE;
9819b6bef7Sschwarze 	manpath_parseline(dirs, path_base, 0);
9919b6bef7Sschwarze }
10019b6bef7Sschwarze 
10127255502Sschwarze /*
10227255502Sschwarze  * Parse a FULL pathname from a colon-separated list of arrays.
10327255502Sschwarze  */
10418eee2d9Sschwarze static void
105a2e5c5f4Sschwarze manpath_parseline(struct manpaths *dirs, char *path, int complain)
10627255502Sschwarze {
10727255502Sschwarze 	char	*dir;
10827255502Sschwarze 
10927255502Sschwarze 	if (NULL == path)
11027255502Sschwarze 		return;
11127255502Sschwarze 
11227255502Sschwarze 	for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
113a2e5c5f4Sschwarze 		manpath_add(dirs, dir, complain);
11427255502Sschwarze }
11527255502Sschwarze 
11627255502Sschwarze /*
11727255502Sschwarze  * Add a directory to the array, ignoring bad directories.
11827255502Sschwarze  * Grow the array one-by-one for simplicity's sake.
11927255502Sschwarze  */
12027255502Sschwarze static void
121a2e5c5f4Sschwarze manpath_add(struct manpaths *dirs, const char *dir, int complain)
12227255502Sschwarze {
12327255502Sschwarze 	char		 buf[PATH_MAX];
124a2e5c5f4Sschwarze 	struct stat	 sb;
12527255502Sschwarze 	char		*cp;
1263db9e7afSschwarze 	size_t		 i;
12727255502Sschwarze 
128a2e5c5f4Sschwarze 	if (NULL == (cp = realpath(dir, buf))) {
129eba1598bSschwarze 		if (complain)
130eba1598bSschwarze 			warn("manpath: %s", dir);
13127255502Sschwarze 		return;
132a2e5c5f4Sschwarze 	}
13327255502Sschwarze 
13427255502Sschwarze 	for (i = 0; i < dirs->sz; i++)
13527255502Sschwarze 		if (0 == strcmp(dirs->paths[i], dir))
13627255502Sschwarze 			return;
13727255502Sschwarze 
138a2e5c5f4Sschwarze 	if (stat(cp, &sb) == -1) {
139eba1598bSschwarze 		if (complain)
140eba1598bSschwarze 			warn("manpath: %s", dir);
141a2e5c5f4Sschwarze 		return;
142a2e5c5f4Sschwarze 	}
143a2e5c5f4Sschwarze 
1448286bf36Sschwarze 	dirs->paths = mandoc_reallocarray(dirs->paths,
1458286bf36Sschwarze 	    dirs->sz + 1, sizeof(char *));
14627255502Sschwarze 
14727255502Sschwarze 	dirs->paths[dirs->sz++] = mandoc_strdup(cp);
14827255502Sschwarze }
14927255502Sschwarze 
15027255502Sschwarze void
1514de77decSschwarze manconf_free(struct manconf *conf)
15227255502Sschwarze {
1533db9e7afSschwarze 	size_t		 i;
15427255502Sschwarze 
1554de77decSschwarze 	for (i = 0; i < conf->manpath.sz; i++)
1564de77decSschwarze 		free(conf->manpath.paths[i]);
15727255502Sschwarze 
1584de77decSschwarze 	free(conf->manpath.paths);
1594de77decSschwarze 	free(conf->output.includes);
1604de77decSschwarze 	free(conf->output.man);
1614de77decSschwarze 	free(conf->output.paper);
1624de77decSschwarze 	free(conf->output.style);
16327255502Sschwarze }
16427255502Sschwarze 
1654de77decSschwarze static void
1664de77decSschwarze manconf_file(struct manconf *conf, const char *file)
16727255502Sschwarze {
1684de77decSschwarze 	const char *const toks[] = { "manpath", "output", "_whatdb" };
169ef3112a4Sschwarze 	char manpath_default[] = MANPATH_DEFAULT;
170d05e3f9eSschwarze 
17127255502Sschwarze 	FILE		*stream;
17231f93c25Sschwarze 	char		*line, *cp, *ep;
17331f93c25Sschwarze 	size_t		 linesz, tok, toklen;
17431f93c25Sschwarze 	ssize_t		 linelen;
17527255502Sschwarze 
176d05e3f9eSschwarze 	if ((stream = fopen(file, "r")) == NULL)
177ef3112a4Sschwarze 		goto out;
17827255502Sschwarze 
17931f93c25Sschwarze 	line = NULL;
18031f93c25Sschwarze 	linesz = 0;
18131f93c25Sschwarze 
18231f93c25Sschwarze 	while ((linelen = getline(&line, &linesz, stream)) != -1) {
18331f93c25Sschwarze 		cp = line;
184eff00e48Smillert 		ep = cp + linelen - 1;
185eff00e48Smillert 		while (ep > cp && isspace((unsigned char)*ep))
186eff00e48Smillert 			*ep-- = '\0';
187d05e3f9eSschwarze 		while (isspace((unsigned char)*cp))
188d05e3f9eSschwarze 			cp++;
189eff00e48Smillert 		if (cp == ep || *cp == '#')
19027255502Sschwarze 			continue;
191d05e3f9eSschwarze 
192d05e3f9eSschwarze 		for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
19331f93c25Sschwarze 			toklen = strlen(toks[tok]);
19431f93c25Sschwarze 			if (cp + toklen < ep &&
19531f93c25Sschwarze 			    isspace((unsigned char)cp[toklen]) &&
19631f93c25Sschwarze 			    strncmp(cp, toks[tok], toklen) == 0) {
19731f93c25Sschwarze 				cp += toklen;
198d05e3f9eSschwarze 				while (isspace((unsigned char)*cp))
199d05e3f9eSschwarze 					cp++;
200d05e3f9eSschwarze 				break;
201d05e3f9eSschwarze 			}
202d05e3f9eSschwarze 		}
203d05e3f9eSschwarze 
204d05e3f9eSschwarze 		switch (tok) {
2054de77decSschwarze 		case 2:  /* _whatdb */
206d05e3f9eSschwarze 			while (ep > cp && ep[-1] != '/')
207d05e3f9eSschwarze 				ep--;
208d05e3f9eSschwarze 			if (ep == cp)
20927255502Sschwarze 				continue;
210d05e3f9eSschwarze 			*ep = '\0';
211d05e3f9eSschwarze 			/* FALLTHROUGH */
212d05e3f9eSschwarze 		case 0:  /* manpath */
2134de77decSschwarze 			manpath_add(&conf->manpath, cp, 0);
214ef3112a4Sschwarze 			*manpath_default = '\0';
2154de77decSschwarze 			break;
2164de77decSschwarze 		case 1:  /* output */
2174b502eb4Sschwarze 			manconf_output(&conf->output, cp, 1);
218d05e3f9eSschwarze 			break;
219d05e3f9eSschwarze 		default:
220d05e3f9eSschwarze 			break;
221d05e3f9eSschwarze 		}
22227255502Sschwarze 	}
22331f93c25Sschwarze 	free(line);
22427255502Sschwarze 	fclose(stream);
225ef3112a4Sschwarze 
226ef3112a4Sschwarze out:
227ef3112a4Sschwarze 	if (*manpath_default != '\0')
228ef3112a4Sschwarze 		manpath_parseline(&conf->manpath, manpath_default, 0);
22927255502Sschwarze }
2304de77decSschwarze 
2314b502eb4Sschwarze int
2324b502eb4Sschwarze manconf_output(struct manoutput *conf, const char *cp, int fromfile)
2334de77decSschwarze {
2344de77decSschwarze 	const char *const toks[] = {
2354de77decSschwarze 	    "includes", "man", "paper", "style",
236*3327fa00Sschwarze 	    "indent", "width", "fragment", "mdoc", "noval", "toc"
2374de77decSschwarze 	};
2384de77decSschwarze 
2394b502eb4Sschwarze 	const char	*errstr;
2404b502eb4Sschwarze 	char		*oldval;
2414de77decSschwarze 	size_t		 len, tok;
2424de77decSschwarze 
2434de77decSschwarze 	for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
2444de77decSschwarze 		len = strlen(toks[tok]);
2454de77decSschwarze 		if ( ! strncmp(cp, toks[tok], len) &&
2464de77decSschwarze 		    strchr(" =	", cp[len]) != NULL) {
2474de77decSschwarze 			cp += len;
2484de77decSschwarze 			if (*cp == '=')
2494de77decSschwarze 				cp++;
2504de77decSschwarze 			while (isspace((unsigned char)*cp))
2514de77decSschwarze 				cp++;
2524de77decSschwarze 			break;
2534de77decSschwarze 		}
2544de77decSschwarze 	}
2554de77decSschwarze 
2564b502eb4Sschwarze 	if (tok < 6 && *cp == '\0') {
2574b502eb4Sschwarze 		warnx("-O %s=?: Missing argument value", toks[tok]);
2584b502eb4Sschwarze 		return -1;
2594b502eb4Sschwarze 	}
2604b502eb4Sschwarze 	if ((tok == 6 || tok == 7) && *cp != '\0') {
2614b502eb4Sschwarze 		warnx("-O %s: Does not take a value: %s", toks[tok], cp);
2624b502eb4Sschwarze 		return -1;
2634b502eb4Sschwarze 	}
2644de77decSschwarze 
2654de77decSschwarze 	switch (tok) {
2664de77decSschwarze 	case 0:
2674b502eb4Sschwarze 		if (conf->includes != NULL) {
2684b502eb4Sschwarze 			oldval = mandoc_strdup(conf->includes);
2694de77decSschwarze 			break;
2704de77decSschwarze 		}
2714b502eb4Sschwarze 		conf->includes = mandoc_strdup(cp);
2724b502eb4Sschwarze 		return 0;
2734b502eb4Sschwarze 	case 1:
2744b502eb4Sschwarze 		if (conf->man != NULL) {
2754b502eb4Sschwarze 			oldval = mandoc_strdup(conf->man);
2764b502eb4Sschwarze 			break;
2774b502eb4Sschwarze 		}
2784b502eb4Sschwarze 		conf->man = mandoc_strdup(cp);
2794b502eb4Sschwarze 		return 0;
2804b502eb4Sschwarze 	case 2:
2814b502eb4Sschwarze 		if (conf->paper != NULL) {
2824b502eb4Sschwarze 			oldval = mandoc_strdup(conf->paper);
2834b502eb4Sschwarze 			break;
2844b502eb4Sschwarze 		}
2854b502eb4Sschwarze 		conf->paper = mandoc_strdup(cp);
2864b502eb4Sschwarze 		return 0;
2874b502eb4Sschwarze 	case 3:
2884b502eb4Sschwarze 		if (conf->style != NULL) {
2894b502eb4Sschwarze 			oldval = mandoc_strdup(conf->style);
2904b502eb4Sschwarze 			break;
2914b502eb4Sschwarze 		}
2924b502eb4Sschwarze 		conf->style = mandoc_strdup(cp);
2934b502eb4Sschwarze 		return 0;
2944b502eb4Sschwarze 	case 4:
2954b502eb4Sschwarze 		if (conf->indent) {
2964b502eb4Sschwarze 			mandoc_asprintf(&oldval, "%zu", conf->indent);
2974b502eb4Sschwarze 			break;
2984b502eb4Sschwarze 		}
2994b502eb4Sschwarze 		conf->indent = strtonum(cp, 0, 1000, &errstr);
3004b502eb4Sschwarze 		if (errstr == NULL)
3014b502eb4Sschwarze 			return 0;
3024b502eb4Sschwarze 		warnx("-O indent=%s is %s", cp, errstr);
3034b502eb4Sschwarze 		return -1;
3044b502eb4Sschwarze 	case 5:
3054b502eb4Sschwarze 		if (conf->width) {
3064b502eb4Sschwarze 			mandoc_asprintf(&oldval, "%zu", conf->width);
3074b502eb4Sschwarze 			break;
3084b502eb4Sschwarze 		}
3093d83c79fSschwarze 		conf->width = strtonum(cp, 1, 1000, &errstr);
3104b502eb4Sschwarze 		if (errstr == NULL)
3114b502eb4Sschwarze 			return 0;
3124b502eb4Sschwarze 		warnx("-O width=%s is %s", cp, errstr);
3134b502eb4Sschwarze 		return -1;
3144b502eb4Sschwarze 	case 6:
3154b502eb4Sschwarze 		conf->fragment = 1;
3164b502eb4Sschwarze 		return 0;
3174b502eb4Sschwarze 	case 7:
3184b502eb4Sschwarze 		conf->mdoc = 1;
3194b502eb4Sschwarze 		return 0;
320385121c9Sschwarze 	case 8:
321385121c9Sschwarze 		conf->noval = 1;
322385121c9Sschwarze 		return 0;
323*3327fa00Sschwarze 	case 9:
324*3327fa00Sschwarze 		conf->toc = 1;
325*3327fa00Sschwarze 		return 0;
3264b502eb4Sschwarze 	default:
3274b502eb4Sschwarze 		if (fromfile)
3284b502eb4Sschwarze 			warnx("-O %s: Bad argument", cp);
3294b502eb4Sschwarze 		return -1;
3304b502eb4Sschwarze 	}
3314b502eb4Sschwarze 	if (fromfile == 0)
3324b502eb4Sschwarze 		warnx("-O %s=%s: Option already set to %s",
3334b502eb4Sschwarze 		    toks[tok], cp, oldval);
3344b502eb4Sschwarze 	free(oldval);
3354b502eb4Sschwarze 	return -1;
3364de77decSschwarze }
337