xref: /dflybsd-src/contrib/mdocml/manpath.c (revision 1e4d43f9c96723e4e55543d240f182e1aac9a4c2)
1*99db7d0eSSascha Wildner /*	$Id: manpath.c,v 1.43 2020/08/27 14:59:47 schwarze Exp $ */
236342e81SSascha Wildner /*
3*99db7d0eSSascha Wildner  * Copyright (c) 2011,2014,2015,2017-2019 Ingo Schwarze <schwarze@openbsd.org>
436342e81SSascha Wildner  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
536342e81SSascha Wildner  *
636342e81SSascha Wildner  * Permission to use, copy, modify, and distribute this software for any
736342e81SSascha Wildner  * purpose with or without fee is hereby granted, provided that the above
836342e81SSascha Wildner  * copyright notice and this permission notice appear in all copies.
936342e81SSascha Wildner  *
1054ba9607SSascha Wildner  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1136342e81SSascha Wildner  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1254ba9607SSascha Wildner  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1336342e81SSascha Wildner  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1436342e81SSascha Wildner  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1536342e81SSascha Wildner  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1636342e81SSascha Wildner  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1736342e81SSascha Wildner  */
1836342e81SSascha Wildner #include "config.h"
1936342e81SSascha Wildner 
2054ba9607SSascha Wildner #include <sys/types.h>
2154ba9607SSascha Wildner #include <sys/stat.h>
2254ba9607SSascha Wildner 
2336342e81SSascha Wildner #include <ctype.h>
24*99db7d0eSSascha Wildner #include <errno.h>
2536342e81SSascha Wildner #include <limits.h>
2636342e81SSascha Wildner #include <stdio.h>
2736342e81SSascha Wildner #include <stdlib.h>
2836342e81SSascha Wildner #include <string.h>
2936342e81SSascha Wildner 
30070c62a6SFranco Fichtner #include "mandoc_aux.h"
31*99db7d0eSSascha Wildner #include "mandoc.h"
3254ba9607SSascha Wildner #include "manconf.h"
3336342e81SSascha Wildner 
3454ba9607SSascha Wildner static	void	 manconf_file(struct manconf *, const char *);
35*99db7d0eSSascha Wildner static	void	 manpath_add(struct manpaths *, const char *, char);
36*99db7d0eSSascha Wildner static	void	 manpath_parseline(struct manpaths *, char *, char);
3736342e81SSascha Wildner 
3836342e81SSascha Wildner 
3936342e81SSascha Wildner void
manconf_parse(struct manconf * conf,const char * file,char * defp,char * auxp)4054ba9607SSascha Wildner manconf_parse(struct manconf *conf, const char *file,
4136342e81SSascha Wildner 		char *defp, char *auxp)
4236342e81SSascha Wildner {
4336342e81SSascha Wildner 	char		*insert;
4436342e81SSascha Wildner 
4536342e81SSascha Wildner 	/* Always prepend -m. */
46*99db7d0eSSascha Wildner 	manpath_parseline(&conf->manpath, auxp, 'm');
4736342e81SSascha Wildner 
4836342e81SSascha Wildner 	/* If -M is given, it overrides everything else. */
4936342e81SSascha Wildner 	if (NULL != defp) {
50*99db7d0eSSascha Wildner 		manpath_parseline(&conf->manpath, defp, 'M');
5136342e81SSascha Wildner 		return;
5236342e81SSascha Wildner 	}
5336342e81SSascha Wildner 
5436342e81SSascha Wildner 	/* MANPATH and man.conf(5) cooperate. */
5536342e81SSascha Wildner 	defp = getenv("MANPATH");
5636342e81SSascha Wildner 	if (NULL == file)
5736342e81SSascha Wildner 		file = MAN_CONF_FILE;
5836342e81SSascha Wildner 
5936342e81SSascha Wildner 	/* No MANPATH; use man.conf(5) only. */
6036342e81SSascha Wildner 	if (NULL == defp || '\0' == defp[0]) {
6154ba9607SSascha Wildner 		manconf_file(conf, file);
6236342e81SSascha Wildner 		return;
6336342e81SSascha Wildner 	}
6436342e81SSascha Wildner 
6536342e81SSascha Wildner 	/* Prepend man.conf(5) to MANPATH. */
6636342e81SSascha Wildner 	if (':' == defp[0]) {
6754ba9607SSascha Wildner 		manconf_file(conf, file);
68*99db7d0eSSascha Wildner 		manpath_parseline(&conf->manpath, defp, '\0');
6936342e81SSascha Wildner 		return;
7036342e81SSascha Wildner 	}
7136342e81SSascha Wildner 
7236342e81SSascha Wildner 	/* Append man.conf(5) to MANPATH. */
73f88b6c16SFranco Fichtner 	if (':' == defp[strlen(defp) - 1]) {
74*99db7d0eSSascha Wildner 		manpath_parseline(&conf->manpath, defp, '\0');
7554ba9607SSascha Wildner 		manconf_file(conf, file);
7636342e81SSascha Wildner 		return;
7736342e81SSascha Wildner 	}
7836342e81SSascha Wildner 
7936342e81SSascha Wildner 	/* Insert man.conf(5) into MANPATH. */
8036342e81SSascha Wildner 	insert = strstr(defp, "::");
8136342e81SSascha Wildner 	if (NULL != insert) {
8236342e81SSascha Wildner 		*insert++ = '\0';
83*99db7d0eSSascha Wildner 		manpath_parseline(&conf->manpath, defp, '\0');
8454ba9607SSascha Wildner 		manconf_file(conf, file);
85*99db7d0eSSascha Wildner 		manpath_parseline(&conf->manpath, insert + 1, '\0');
8636342e81SSascha Wildner 		return;
8736342e81SSascha Wildner 	}
8836342e81SSascha Wildner 
8936342e81SSascha Wildner 	/* MANPATH overrides man.conf(5) completely. */
90*99db7d0eSSascha Wildner 	manpath_parseline(&conf->manpath, defp, '\0');
9154ba9607SSascha Wildner }
9254ba9607SSascha Wildner 
9354ba9607SSascha Wildner void
manpath_base(struct manpaths * dirs)9454ba9607SSascha Wildner manpath_base(struct manpaths *dirs)
9554ba9607SSascha Wildner {
9654ba9607SSascha Wildner 	char path_base[] = MANPATH_BASE;
97*99db7d0eSSascha Wildner 	manpath_parseline(dirs, path_base, '\0');
9836342e81SSascha Wildner }
9936342e81SSascha Wildner 
10036342e81SSascha Wildner /*
10136342e81SSascha Wildner  * Parse a FULL pathname from a colon-separated list of arrays.
10236342e81SSascha Wildner  */
10336342e81SSascha Wildner static void
manpath_parseline(struct manpaths * dirs,char * path,char option)104*99db7d0eSSascha Wildner manpath_parseline(struct manpaths *dirs, char *path, char option)
10536342e81SSascha Wildner {
10636342e81SSascha Wildner 	char	*dir;
10736342e81SSascha Wildner 
10836342e81SSascha Wildner 	if (NULL == path)
10936342e81SSascha Wildner 		return;
11036342e81SSascha Wildner 
11136342e81SSascha Wildner 	for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
112*99db7d0eSSascha Wildner 		manpath_add(dirs, dir, option);
11336342e81SSascha Wildner }
11436342e81SSascha Wildner 
11536342e81SSascha Wildner /*
11636342e81SSascha Wildner  * Add a directory to the array, ignoring bad directories.
11736342e81SSascha Wildner  * Grow the array one-by-one for simplicity's sake.
11836342e81SSascha Wildner  */
11936342e81SSascha Wildner static void
manpath_add(struct manpaths * dirs,const char * dir,char option)120*99db7d0eSSascha Wildner manpath_add(struct manpaths *dirs, const char *dir, char option)
12136342e81SSascha Wildner {
12236342e81SSascha Wildner 	char		 buf[PATH_MAX];
12354ba9607SSascha Wildner 	struct stat	 sb;
12436342e81SSascha Wildner 	char		*cp;
125f88b6c16SFranco Fichtner 	size_t		 i;
12636342e81SSascha Wildner 
127*99db7d0eSSascha Wildner 	if ((cp = realpath(dir, buf)) == NULL)
128*99db7d0eSSascha Wildner 		goto fail;
12936342e81SSascha Wildner 
13036342e81SSascha Wildner 	for (i = 0; i < dirs->sz; i++)
131*99db7d0eSSascha Wildner 		if (strcmp(dirs->paths[i], dir) == 0)
13236342e81SSascha Wildner 			return;
13336342e81SSascha Wildner 
134*99db7d0eSSascha Wildner 	if (stat(cp, &sb) == -1)
135*99db7d0eSSascha Wildner 		goto fail;
13654ba9607SSascha Wildner 
137070c62a6SFranco Fichtner 	dirs->paths = mandoc_reallocarray(dirs->paths,
138*99db7d0eSSascha Wildner 	    dirs->sz + 1, sizeof(*dirs->paths));
13936342e81SSascha Wildner 	dirs->paths[dirs->sz++] = mandoc_strdup(cp);
140*99db7d0eSSascha Wildner 	return;
141*99db7d0eSSascha Wildner 
142*99db7d0eSSascha Wildner fail:
143*99db7d0eSSascha Wildner 	if (option != '\0')
144*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0,
145*99db7d0eSSascha Wildner 		    "-%c %s: %s", option, dir, strerror(errno));
14636342e81SSascha Wildner }
14736342e81SSascha Wildner 
14836342e81SSascha Wildner void
manconf_free(struct manconf * conf)14954ba9607SSascha Wildner manconf_free(struct manconf *conf)
15036342e81SSascha Wildner {
151f88b6c16SFranco Fichtner 	size_t		 i;
15236342e81SSascha Wildner 
15354ba9607SSascha Wildner 	for (i = 0; i < conf->manpath.sz; i++)
15454ba9607SSascha Wildner 		free(conf->manpath.paths[i]);
15536342e81SSascha Wildner 
15654ba9607SSascha Wildner 	free(conf->manpath.paths);
15754ba9607SSascha Wildner 	free(conf->output.includes);
15854ba9607SSascha Wildner 	free(conf->output.man);
15954ba9607SSascha Wildner 	free(conf->output.paper);
16054ba9607SSascha Wildner 	free(conf->output.style);
16136342e81SSascha Wildner }
16236342e81SSascha Wildner 
16354ba9607SSascha Wildner static void
manconf_file(struct manconf * conf,const char * file)16454ba9607SSascha Wildner manconf_file(struct manconf *conf, const char *file)
16536342e81SSascha Wildner {
166*99db7d0eSSascha Wildner 	const char *const toks[] = { "manpath", "output" };
16754ba9607SSascha Wildner 	char manpath_default[] = MANPATH_DEFAULT;
16854ba9607SSascha Wildner 
16936342e81SSascha Wildner 	FILE		*stream;
17054ba9607SSascha Wildner 	char		*line, *cp, *ep;
17154ba9607SSascha Wildner 	size_t		 linesz, tok, toklen;
17254ba9607SSascha Wildner 	ssize_t		 linelen;
17336342e81SSascha Wildner 
17454ba9607SSascha Wildner 	if ((stream = fopen(file, "r")) == NULL)
17554ba9607SSascha Wildner 		goto out;
17636342e81SSascha Wildner 
17754ba9607SSascha Wildner 	line = NULL;
17854ba9607SSascha Wildner 	linesz = 0;
17936342e81SSascha Wildner 
18054ba9607SSascha Wildner 	while ((linelen = getline(&line, &linesz, stream)) != -1) {
18154ba9607SSascha Wildner 		cp = line;
18254ba9607SSascha Wildner 		ep = cp + linelen - 1;
18354ba9607SSascha Wildner 		while (ep > cp && isspace((unsigned char)*ep))
18454ba9607SSascha Wildner 			*ep-- = '\0';
18554ba9607SSascha Wildner 		while (isspace((unsigned char)*cp))
18654ba9607SSascha Wildner 			cp++;
18754ba9607SSascha Wildner 		if (cp == ep || *cp == '#')
18854ba9607SSascha Wildner 			continue;
18954ba9607SSascha Wildner 
19054ba9607SSascha Wildner 		for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
19154ba9607SSascha Wildner 			toklen = strlen(toks[tok]);
19254ba9607SSascha Wildner 			if (cp + toklen < ep &&
19354ba9607SSascha Wildner 			    isspace((unsigned char)cp[toklen]) &&
19454ba9607SSascha Wildner 			    strncmp(cp, toks[tok], toklen) == 0) {
19554ba9607SSascha Wildner 				cp += toklen;
19654ba9607SSascha Wildner 				while (isspace((unsigned char)*cp))
19754ba9607SSascha Wildner 					cp++;
19836342e81SSascha Wildner 				break;
19954ba9607SSascha Wildner 			}
20036342e81SSascha Wildner 		}
20136342e81SSascha Wildner 
20254ba9607SSascha Wildner 		switch (tok) {
20354ba9607SSascha Wildner 		case 0:  /* manpath */
204*99db7d0eSSascha Wildner 			manpath_add(&conf->manpath, cp, '\0');
20554ba9607SSascha Wildner 			*manpath_default = '\0';
20654ba9607SSascha Wildner 			break;
20754ba9607SSascha Wildner 		case 1:  /* output */
20854ba9607SSascha Wildner 			manconf_output(&conf->output, cp, 1);
20954ba9607SSascha Wildner 			break;
21054ba9607SSascha Wildner 		default:
21154ba9607SSascha Wildner 			break;
21254ba9607SSascha Wildner 		}
21354ba9607SSascha Wildner 	}
21454ba9607SSascha Wildner 	free(line);
21536342e81SSascha Wildner 	fclose(stream);
21654ba9607SSascha Wildner 
21754ba9607SSascha Wildner out:
21854ba9607SSascha Wildner 	if (*manpath_default != '\0')
219*99db7d0eSSascha Wildner 		manpath_parseline(&conf->manpath, manpath_default, '\0');
22054ba9607SSascha Wildner }
22154ba9607SSascha Wildner 
22254ba9607SSascha Wildner int
manconf_output(struct manoutput * conf,const char * cp,int fromfile)22354ba9607SSascha Wildner manconf_output(struct manoutput *conf, const char *cp, int fromfile)
22454ba9607SSascha Wildner {
22554ba9607SSascha Wildner 	const char *const toks[] = {
226*99db7d0eSSascha Wildner 	    /* Tokens requiring an argument. */
22754ba9607SSascha Wildner 	    "includes", "man", "paper", "style", "indent", "width",
228*99db7d0eSSascha Wildner 	    "outfilename", "tagfilename",
229*99db7d0eSSascha Wildner 	    /* Token taking an optional argument. */
230*99db7d0eSSascha Wildner 	    "tag",
231*99db7d0eSSascha Wildner 	    /* Tokens not taking arguments. */
232*99db7d0eSSascha Wildner 	    "fragment", "mdoc", "noval", "toc"
23354ba9607SSascha Wildner 	};
234*99db7d0eSSascha Wildner 	const size_t ntoks = sizeof(toks) / sizeof(toks[0]);
23554ba9607SSascha Wildner 
23654ba9607SSascha Wildner 	const char	*errstr;
23754ba9607SSascha Wildner 	char		*oldval;
23854ba9607SSascha Wildner 	size_t		 len, tok;
23954ba9607SSascha Wildner 
240*99db7d0eSSascha Wildner 	for (tok = 0; tok < ntoks; tok++) {
24154ba9607SSascha Wildner 		len = strlen(toks[tok]);
242*99db7d0eSSascha Wildner 		if (strncmp(cp, toks[tok], len) == 0 &&
24354ba9607SSascha Wildner 		    strchr(" =	", cp[len]) != NULL) {
24454ba9607SSascha Wildner 			cp += len;
24554ba9607SSascha Wildner 			if (*cp == '=')
24654ba9607SSascha Wildner 				cp++;
24754ba9607SSascha Wildner 			while (isspace((unsigned char)*cp))
24854ba9607SSascha Wildner 				cp++;
24954ba9607SSascha Wildner 			break;
25054ba9607SSascha Wildner 		}
25154ba9607SSascha Wildner 	}
25254ba9607SSascha Wildner 
253*99db7d0eSSascha Wildner 	if (tok < 8 && *cp == '\0') {
254*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_BADVAL_MISS, 0, 0, "-O %s=?", toks[tok]);
25554ba9607SSascha Wildner 		return -1;
25654ba9607SSascha Wildner 	}
257*99db7d0eSSascha Wildner 	if (tok > 8 && tok < ntoks && *cp != '\0') {
258*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_BADVAL, 0, 0, "-O %s=%s", toks[tok], cp);
25954ba9607SSascha Wildner 		return -1;
26054ba9607SSascha Wildner 	}
26154ba9607SSascha Wildner 
26254ba9607SSascha Wildner 	switch (tok) {
26354ba9607SSascha Wildner 	case 0:
26454ba9607SSascha Wildner 		if (conf->includes != NULL) {
26554ba9607SSascha Wildner 			oldval = mandoc_strdup(conf->includes);
26654ba9607SSascha Wildner 			break;
26754ba9607SSascha Wildner 		}
26854ba9607SSascha Wildner 		conf->includes = mandoc_strdup(cp);
26954ba9607SSascha Wildner 		return 0;
27054ba9607SSascha Wildner 	case 1:
27154ba9607SSascha Wildner 		if (conf->man != NULL) {
27254ba9607SSascha Wildner 			oldval = mandoc_strdup(conf->man);
27354ba9607SSascha Wildner 			break;
27454ba9607SSascha Wildner 		}
27554ba9607SSascha Wildner 		conf->man = mandoc_strdup(cp);
27654ba9607SSascha Wildner 		return 0;
27754ba9607SSascha Wildner 	case 2:
27854ba9607SSascha Wildner 		if (conf->paper != NULL) {
27954ba9607SSascha Wildner 			oldval = mandoc_strdup(conf->paper);
28054ba9607SSascha Wildner 			break;
28154ba9607SSascha Wildner 		}
28254ba9607SSascha Wildner 		conf->paper = mandoc_strdup(cp);
28354ba9607SSascha Wildner 		return 0;
28454ba9607SSascha Wildner 	case 3:
28554ba9607SSascha Wildner 		if (conf->style != NULL) {
28654ba9607SSascha Wildner 			oldval = mandoc_strdup(conf->style);
28754ba9607SSascha Wildner 			break;
28854ba9607SSascha Wildner 		}
28954ba9607SSascha Wildner 		conf->style = mandoc_strdup(cp);
29054ba9607SSascha Wildner 		return 0;
29154ba9607SSascha Wildner 	case 4:
29254ba9607SSascha Wildner 		if (conf->indent) {
29354ba9607SSascha Wildner 			mandoc_asprintf(&oldval, "%zu", conf->indent);
29454ba9607SSascha Wildner 			break;
29554ba9607SSascha Wildner 		}
29654ba9607SSascha Wildner 		conf->indent = strtonum(cp, 0, 1000, &errstr);
29754ba9607SSascha Wildner 		if (errstr == NULL)
29854ba9607SSascha Wildner 			return 0;
299*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_BADVAL_BAD, 0, 0,
300*99db7d0eSSascha Wildner 		    "-O indent=%s is %s", cp, errstr);
30154ba9607SSascha Wildner 		return -1;
30254ba9607SSascha Wildner 	case 5:
30354ba9607SSascha Wildner 		if (conf->width) {
30454ba9607SSascha Wildner 			mandoc_asprintf(&oldval, "%zu", conf->width);
30554ba9607SSascha Wildner 			break;
30654ba9607SSascha Wildner 		}
30754ba9607SSascha Wildner 		conf->width = strtonum(cp, 1, 1000, &errstr);
30854ba9607SSascha Wildner 		if (errstr == NULL)
30954ba9607SSascha Wildner 			return 0;
310*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_BADVAL_BAD, 0, 0,
311*99db7d0eSSascha Wildner 		    "-O width=%s is %s", cp, errstr);
31254ba9607SSascha Wildner 		return -1;
31354ba9607SSascha Wildner 	case 6:
314*99db7d0eSSascha Wildner 		if (conf->outfilename != NULL) {
315*99db7d0eSSascha Wildner 			oldval = mandoc_strdup(conf->outfilename);
316*99db7d0eSSascha Wildner 			break;
317*99db7d0eSSascha Wildner 		}
318*99db7d0eSSascha Wildner 		conf->outfilename = mandoc_strdup(cp);
319*99db7d0eSSascha Wildner 		return 0;
320*99db7d0eSSascha Wildner 	case 7:
321*99db7d0eSSascha Wildner 		if (conf->tagfilename != NULL) {
322*99db7d0eSSascha Wildner 			oldval = mandoc_strdup(conf->tagfilename);
323*99db7d0eSSascha Wildner 			break;
324*99db7d0eSSascha Wildner 		}
325*99db7d0eSSascha Wildner 		conf->tagfilename = mandoc_strdup(cp);
326*99db7d0eSSascha Wildner 		return 0;
327*99db7d0eSSascha Wildner 	/*
328*99db7d0eSSascha Wildner 	 * If the index of the following token changes,
329*99db7d0eSSascha Wildner 	 * do not forget to adjust the range check above the switch.
330*99db7d0eSSascha Wildner 	 */
331*99db7d0eSSascha Wildner 	case 8:
33254ba9607SSascha Wildner 		if (conf->tag != NULL) {
33354ba9607SSascha Wildner 			oldval = mandoc_strdup(conf->tag);
33454ba9607SSascha Wildner 			break;
33554ba9607SSascha Wildner 		}
33654ba9607SSascha Wildner 		conf->tag = mandoc_strdup(cp);
33754ba9607SSascha Wildner 		return 0;
338*99db7d0eSSascha Wildner 	case 9:
33954ba9607SSascha Wildner 		conf->fragment = 1;
34054ba9607SSascha Wildner 		return 0;
341*99db7d0eSSascha Wildner 	case 10:
34254ba9607SSascha Wildner 		conf->mdoc = 1;
34354ba9607SSascha Wildner 		return 0;
344*99db7d0eSSascha Wildner 	case 11:
34554ba9607SSascha Wildner 		conf->noval = 1;
34654ba9607SSascha Wildner 		return 0;
347*99db7d0eSSascha Wildner 	case 12:
34854ba9607SSascha Wildner 		conf->toc = 1;
34954ba9607SSascha Wildner 		return 0;
35054ba9607SSascha Wildner 	default:
351*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-O %s", cp);
35254ba9607SSascha Wildner 		return -1;
35354ba9607SSascha Wildner 	}
354*99db7d0eSSascha Wildner 	if (fromfile) {
355*99db7d0eSSascha Wildner 		free(oldval);
356*99db7d0eSSascha Wildner 		return 0;
357*99db7d0eSSascha Wildner 	} else {
358*99db7d0eSSascha Wildner 		mandoc_msg(MANDOCERR_BADVAL_DUPE, 0, 0,
359*99db7d0eSSascha Wildner 		    "-O %s=%s: already set to %s", toks[tok], cp, oldval);
36054ba9607SSascha Wildner 		free(oldval);
36154ba9607SSascha Wildner 		return -1;
36236342e81SSascha Wildner 	}
363*99db7d0eSSascha Wildner }
364