xref: /netbsd-src/external/bsd/mdocml/dist/manpath.c (revision 544c191c349c1704c9d5e679d12ec15cff579663)
1*544c191cSchristos /*	Id: manpath.c,v 1.37 2018/11/22 11:30:23 schwarze Exp  */
294561435Sjoerg /*
3*544c191cSchristos  * Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
494561435Sjoerg  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
594561435Sjoerg  *
694561435Sjoerg  * Permission to use, copy, modify, and distribute this software for any
794561435Sjoerg  * purpose with or without fee is hereby granted, provided that the above
894561435Sjoerg  * copyright notice and this permission notice appear in all copies.
994561435Sjoerg  *
109ff1f2acSchristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1194561435Sjoerg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
129ff1f2acSchristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1394561435Sjoerg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1494561435Sjoerg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1594561435Sjoerg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1694561435Sjoerg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1794561435Sjoerg  */
1894561435Sjoerg #include "config.h"
19fec65c98Schristos 
20fec65c98Schristos #include <sys/types.h>
21fec65c98Schristos #include <sys/stat.h>
2294561435Sjoerg 
2394561435Sjoerg #include <ctype.h>
249ff1f2acSchristos #if HAVE_ERR
259ff1f2acSchristos #include <err.h>
269ff1f2acSchristos #endif
2794561435Sjoerg #include <limits.h>
2894561435Sjoerg #include <stdio.h>
2994561435Sjoerg #include <stdlib.h>
3094561435Sjoerg #include <string.h>
3194561435Sjoerg 
32fec65c98Schristos #include "mandoc_aux.h"
339ff1f2acSchristos #include "manconf.h"
3494561435Sjoerg 
359ff1f2acSchristos static	void	 manconf_file(struct manconf *, const char *);
36fec65c98Schristos static	void	 manpath_add(struct manpaths *, const char *, int);
37fec65c98Schristos static	void	 manpath_parseline(struct manpaths *, char *, int);
3894561435Sjoerg 
399ff1f2acSchristos 
4094561435Sjoerg void
manconf_parse(struct manconf * conf,const char * file,char * defp,char * auxp)419ff1f2acSchristos manconf_parse(struct manconf *conf, const char *file,
4294561435Sjoerg 		char *defp, char *auxp)
4394561435Sjoerg {
4494561435Sjoerg 	char		*insert;
4594561435Sjoerg 
4694561435Sjoerg 	/* Always prepend -m. */
479ff1f2acSchristos 	manpath_parseline(&conf->manpath, auxp, 1);
4894561435Sjoerg 
4994561435Sjoerg 	/* If -M is given, it overrides everything else. */
5094561435Sjoerg 	if (NULL != defp) {
519ff1f2acSchristos 		manpath_parseline(&conf->manpath, defp, 1);
5294561435Sjoerg 		return;
5394561435Sjoerg 	}
5494561435Sjoerg 
5594561435Sjoerg 	/* MANPATH and man.conf(5) cooperate. */
5694561435Sjoerg 	defp = getenv("MANPATH");
5794561435Sjoerg 	if (NULL == file)
5894561435Sjoerg 		file = MAN_CONF_FILE;
5994561435Sjoerg 
6094561435Sjoerg 	/* No MANPATH; use man.conf(5) only. */
6194561435Sjoerg 	if (NULL == defp || '\0' == defp[0]) {
629ff1f2acSchristos 		manconf_file(conf, file);
6394561435Sjoerg 		return;
6494561435Sjoerg 	}
6594561435Sjoerg 
6694561435Sjoerg 	/* Prepend man.conf(5) to MANPATH. */
6794561435Sjoerg 	if (':' == defp[0]) {
689ff1f2acSchristos 		manconf_file(conf, file);
699ff1f2acSchristos 		manpath_parseline(&conf->manpath, defp, 0);
7094561435Sjoerg 		return;
7194561435Sjoerg 	}
7294561435Sjoerg 
7394561435Sjoerg 	/* Append man.conf(5) to MANPATH. */
7470f041f9Sjoerg 	if (':' == defp[strlen(defp) - 1]) {
759ff1f2acSchristos 		manpath_parseline(&conf->manpath, defp, 0);
769ff1f2acSchristos 		manconf_file(conf, file);
7794561435Sjoerg 		return;
7894561435Sjoerg 	}
7994561435Sjoerg 
8094561435Sjoerg 	/* Insert man.conf(5) into MANPATH. */
8194561435Sjoerg 	insert = strstr(defp, "::");
8294561435Sjoerg 	if (NULL != insert) {
8394561435Sjoerg 		*insert++ = '\0';
849ff1f2acSchristos 		manpath_parseline(&conf->manpath, defp, 0);
859ff1f2acSchristos 		manconf_file(conf, file);
869ff1f2acSchristos 		manpath_parseline(&conf->manpath, insert + 1, 0);
8794561435Sjoerg 		return;
8894561435Sjoerg 	}
8994561435Sjoerg 
9094561435Sjoerg 	/* MANPATH overrides man.conf(5) completely. */
919ff1f2acSchristos 	manpath_parseline(&conf->manpath, defp, 0);
9294561435Sjoerg }
9394561435Sjoerg 
94c9bcef03Schristos void
manpath_base(struct manpaths * dirs)95c9bcef03Schristos manpath_base(struct manpaths *dirs)
96c9bcef03Schristos {
97c9bcef03Schristos 	char path_base[] = MANPATH_BASE;
98c9bcef03Schristos 	manpath_parseline(dirs, path_base, 0);
99c9bcef03Schristos }
100c9bcef03Schristos 
10194561435Sjoerg /*
10294561435Sjoerg  * Parse a FULL pathname from a colon-separated list of arrays.
10394561435Sjoerg  */
10494561435Sjoerg static void
manpath_parseline(struct manpaths * dirs,char * path,int complain)105fec65c98Schristos manpath_parseline(struct manpaths *dirs, char *path, int complain)
10694561435Sjoerg {
10794561435Sjoerg 	char	*dir;
10894561435Sjoerg 
10994561435Sjoerg 	if (NULL == path)
11094561435Sjoerg 		return;
11194561435Sjoerg 
11294561435Sjoerg 	for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
113fec65c98Schristos 		manpath_add(dirs, dir, complain);
11494561435Sjoerg }
11594561435Sjoerg 
11694561435Sjoerg /*
11794561435Sjoerg  * Add a directory to the array, ignoring bad directories.
11894561435Sjoerg  * Grow the array one-by-one for simplicity's sake.
11994561435Sjoerg  */
12094561435Sjoerg static void
manpath_add(struct manpaths * dirs,const char * dir,int complain)121fec65c98Schristos manpath_add(struct manpaths *dirs, const char *dir, int complain)
12294561435Sjoerg {
12394561435Sjoerg 	char		 buf[PATH_MAX];
124fec65c98Schristos 	struct stat	 sb;
12594561435Sjoerg 	char		*cp;
12670f041f9Sjoerg 	size_t		 i;
12794561435Sjoerg 
128fec65c98Schristos 	if (NULL == (cp = realpath(dir, buf))) {
1299ff1f2acSchristos 		if (complain)
1309ff1f2acSchristos 			warn("manpath: %s", dir);
13194561435Sjoerg 		return;
132fec65c98Schristos 	}
13394561435Sjoerg 
13494561435Sjoerg 	for (i = 0; i < dirs->sz; i++)
13594561435Sjoerg 		if (0 == strcmp(dirs->paths[i], dir))
13694561435Sjoerg 			return;
13794561435Sjoerg 
138fec65c98Schristos 	if (stat(cp, &sb) == -1) {
1399ff1f2acSchristos 		if (complain)
1409ff1f2acSchristos 			warn("manpath: %s", dir);
141fec65c98Schristos 		return;
142fec65c98Schristos 	}
143fec65c98Schristos 
144fec65c98Schristos 	dirs->paths = mandoc_reallocarray(dirs->paths,
145fec65c98Schristos 	    dirs->sz + 1, sizeof(char *));
14694561435Sjoerg 
14794561435Sjoerg 	dirs->paths[dirs->sz++] = mandoc_strdup(cp);
14894561435Sjoerg }
14994561435Sjoerg 
15094561435Sjoerg void
manconf_free(struct manconf * conf)1519ff1f2acSchristos manconf_free(struct manconf *conf)
15294561435Sjoerg {
15370f041f9Sjoerg 	size_t		 i;
15494561435Sjoerg 
1559ff1f2acSchristos 	for (i = 0; i < conf->manpath.sz; i++)
1569ff1f2acSchristos 		free(conf->manpath.paths[i]);
15794561435Sjoerg 
1589ff1f2acSchristos 	free(conf->manpath.paths);
1599ff1f2acSchristos 	free(conf->output.includes);
1609ff1f2acSchristos 	free(conf->output.man);
1619ff1f2acSchristos 	free(conf->output.paper);
1629ff1f2acSchristos 	free(conf->output.style);
16394561435Sjoerg }
16494561435Sjoerg 
1659ff1f2acSchristos static void
manconf_file(struct manconf * conf,const char * file)1669ff1f2acSchristos manconf_file(struct manconf *conf, const char *file)
1679ff1f2acSchristos {
1689ff1f2acSchristos 	const char *const toks[] = { "manpath", "output", "_whatdb" };
1699ff1f2acSchristos 	char manpath_default[] = MANPATH_DEFAULT;
1709ff1f2acSchristos 
1719ff1f2acSchristos 	FILE		*stream;
1729ff1f2acSchristos 	char		*line, *cp, *ep;
1739ff1f2acSchristos 	size_t		 linesz, tok, toklen;
1749ff1f2acSchristos 	ssize_t		 linelen;
1759ff1f2acSchristos 
1769ff1f2acSchristos 	if ((stream = fopen(file, "r")) == NULL)
1779ff1f2acSchristos 		goto out;
1789ff1f2acSchristos 
1799ff1f2acSchristos 	line = NULL;
1809ff1f2acSchristos 	linesz = 0;
1819ff1f2acSchristos 
1829ff1f2acSchristos 	while ((linelen = getline(&line, &linesz, stream)) != -1) {
1839ff1f2acSchristos 		cp = line;
1849ff1f2acSchristos 		ep = cp + linelen - 1;
1859ff1f2acSchristos 		while (ep > cp && isspace((unsigned char)*ep))
1869ff1f2acSchristos 			*ep-- = '\0';
1879ff1f2acSchristos 		while (isspace((unsigned char)*cp))
1889ff1f2acSchristos 			cp++;
1899ff1f2acSchristos 		if (cp == ep || *cp == '#')
1909ff1f2acSchristos 			continue;
1919ff1f2acSchristos 
1929ff1f2acSchristos 		for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
1939ff1f2acSchristos 			toklen = strlen(toks[tok]);
1949ff1f2acSchristos 			if (cp + toklen < ep &&
1959ff1f2acSchristos 			    isspace((unsigned char)cp[toklen]) &&
1969ff1f2acSchristos 			    strncmp(cp, toks[tok], toklen) == 0) {
1979ff1f2acSchristos 				cp += toklen;
1989ff1f2acSchristos 				while (isspace((unsigned char)*cp))
1999ff1f2acSchristos 					cp++;
2009ff1f2acSchristos 				break;
2019ff1f2acSchristos 			}
2029ff1f2acSchristos 		}
2039ff1f2acSchristos 
2049ff1f2acSchristos 		switch (tok) {
2059ff1f2acSchristos 		case 2:  /* _whatdb */
2069ff1f2acSchristos 			while (ep > cp && ep[-1] != '/')
2079ff1f2acSchristos 				ep--;
2089ff1f2acSchristos 			if (ep == cp)
2099ff1f2acSchristos 				continue;
2109ff1f2acSchristos 			*ep = '\0';
2119ff1f2acSchristos 			/* FALLTHROUGH */
2129ff1f2acSchristos 		case 0:  /* manpath */
2139ff1f2acSchristos 			manpath_add(&conf->manpath, cp, 0);
2149ff1f2acSchristos 			*manpath_default = '\0';
2159ff1f2acSchristos 			break;
2169ff1f2acSchristos 		case 1:  /* output */
2179508192eSchristos 			manconf_output(&conf->output, cp, 1);
2189ff1f2acSchristos 			break;
2199ff1f2acSchristos 		default:
2209ff1f2acSchristos 			break;
2219ff1f2acSchristos 		}
2229ff1f2acSchristos 	}
2239ff1f2acSchristos 	free(line);
2249ff1f2acSchristos 	fclose(stream);
2259ff1f2acSchristos 
2269ff1f2acSchristos out:
2279ff1f2acSchristos 	if (*manpath_default != '\0')
2289ff1f2acSchristos 		manpath_parseline(&conf->manpath, manpath_default, 0);
2299ff1f2acSchristos }
2309ff1f2acSchristos 
2319508192eSchristos int
manconf_output(struct manoutput * conf,const char * cp,int fromfile)2329508192eSchristos manconf_output(struct manoutput *conf, const char *cp, int fromfile)
23394561435Sjoerg {
2349ff1f2acSchristos 	const char *const toks[] = {
235*544c191cSchristos 	    "includes", "man", "paper", "style", "indent", "width",
236*544c191cSchristos 	    "tag", "fragment", "mdoc", "noval", "toc"
2379ff1f2acSchristos 	};
23894561435Sjoerg 
2399508192eSchristos 	const char	*errstr;
2409508192eSchristos 	char		*oldval;
2419ff1f2acSchristos 	size_t		 len, tok;
24294561435Sjoerg 
2439ff1f2acSchristos 	for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
2449ff1f2acSchristos 		len = strlen(toks[tok]);
2459ff1f2acSchristos 		if ( ! strncmp(cp, toks[tok], len) &&
2469ff1f2acSchristos 		    strchr(" =	", cp[len]) != NULL) {
2479ff1f2acSchristos 			cp += len;
2489ff1f2acSchristos 			if (*cp == '=')
2499ff1f2acSchristos 				cp++;
2509ff1f2acSchristos 			while (isspace((unsigned char)*cp))
2519ff1f2acSchristos 				cp++;
25294561435Sjoerg 			break;
2539ff1f2acSchristos 		}
25494561435Sjoerg 	}
25594561435Sjoerg 
2569508192eSchristos 	if (tok < 6 && *cp == '\0') {
2579508192eSchristos 		warnx("-O %s=?: Missing argument value", toks[tok]);
2589508192eSchristos 		return -1;
2599508192eSchristos 	}
260*544c191cSchristos 	if (tok > 6 && *cp != '\0') {
2619508192eSchristos 		warnx("-O %s: Does not take a value: %s", toks[tok], cp);
2629508192eSchristos 		return -1;
2639508192eSchristos 	}
2649ff1f2acSchristos 
2659ff1f2acSchristos 	switch (tok) {
2669ff1f2acSchristos 	case 0:
2679508192eSchristos 		if (conf->includes != NULL) {
2689508192eSchristos 			oldval = mandoc_strdup(conf->includes);
2699ff1f2acSchristos 			break;
2709ff1f2acSchristos 		}
2719508192eSchristos 		conf->includes = mandoc_strdup(cp);
2729508192eSchristos 		return 0;
2739508192eSchristos 	case 1:
2749508192eSchristos 		if (conf->man != NULL) {
2759508192eSchristos 			oldval = mandoc_strdup(conf->man);
2769508192eSchristos 			break;
2779508192eSchristos 		}
2789508192eSchristos 		conf->man = mandoc_strdup(cp);
2799508192eSchristos 		return 0;
2809508192eSchristos 	case 2:
2819508192eSchristos 		if (conf->paper != NULL) {
2829508192eSchristos 			oldval = mandoc_strdup(conf->paper);
2839508192eSchristos 			break;
2849508192eSchristos 		}
2859508192eSchristos 		conf->paper = mandoc_strdup(cp);
2869508192eSchristos 		return 0;
2879508192eSchristos 	case 3:
2889508192eSchristos 		if (conf->style != NULL) {
2899508192eSchristos 			oldval = mandoc_strdup(conf->style);
2909508192eSchristos 			break;
2919508192eSchristos 		}
2929508192eSchristos 		conf->style = mandoc_strdup(cp);
2939508192eSchristos 		return 0;
2949508192eSchristos 	case 4:
2959508192eSchristos 		if (conf->indent) {
2969508192eSchristos 			mandoc_asprintf(&oldval, "%zu", conf->indent);
2979508192eSchristos 			break;
2989508192eSchristos 		}
2999508192eSchristos 		conf->indent = strtonum(cp, 0, 1000, &errstr);
3009508192eSchristos 		if (errstr == NULL)
3019508192eSchristos 			return 0;
3029508192eSchristos 		warnx("-O indent=%s is %s", cp, errstr);
3039508192eSchristos 		return -1;
3049508192eSchristos 	case 5:
3059508192eSchristos 		if (conf->width) {
3069508192eSchristos 			mandoc_asprintf(&oldval, "%zu", conf->width);
3079508192eSchristos 			break;
3089508192eSchristos 		}
309c9bcef03Schristos 		conf->width = strtonum(cp, 1, 1000, &errstr);
3109508192eSchristos 		if (errstr == NULL)
3119508192eSchristos 			return 0;
3129508192eSchristos 		warnx("-O width=%s is %s", cp, errstr);
3139508192eSchristos 		return -1;
3149508192eSchristos 	case 6:
315*544c191cSchristos 		if (conf->tag != NULL) {
316*544c191cSchristos 			oldval = mandoc_strdup(conf->tag);
317*544c191cSchristos 			break;
318*544c191cSchristos 		}
319*544c191cSchristos 		conf->tag = mandoc_strdup(cp);
3209508192eSchristos 		return 0;
3219508192eSchristos 	case 7:
322*544c191cSchristos 		conf->fragment = 1;
3239508192eSchristos 		return 0;
3249508192eSchristos 	case 8:
325*544c191cSchristos 		conf->mdoc = 1;
326*544c191cSchristos 		return 0;
327*544c191cSchristos 	case 9:
3289508192eSchristos 		conf->noval = 1;
3299508192eSchristos 		return 0;
330*544c191cSchristos 	case 10:
331*544c191cSchristos 		conf->toc = 1;
332*544c191cSchristos 		return 0;
3339508192eSchristos 	default:
3349508192eSchristos 		if (fromfile)
3359508192eSchristos 			warnx("-O %s: Bad argument", cp);
3369508192eSchristos 		return -1;
3379508192eSchristos 	}
3389508192eSchristos 	if (fromfile == 0)
3399508192eSchristos 		warnx("-O %s=%s: Option already set to %s",
3409508192eSchristos 		    toks[tok], cp, oldval);
3419508192eSchristos 	free(oldval);
3429508192eSchristos 	return -1;
34394561435Sjoerg }
344