1322249Sbapt/*	$Id: manpath.c,v 1.35 2017/07/01 09:47:30 schwarze Exp $ */
2274880Sbapt/*
3322249Sbapt * Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
4274880Sbapt * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
5274880Sbapt *
6274880Sbapt * Permission to use, copy, modify, and distribute this software for any
7274880Sbapt * purpose with or without fee is hereby granted, provided that the above
8274880Sbapt * copyright notice and this permission notice appear in all copies.
9274880Sbapt *
10294113Sbapt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11274880Sbapt * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12294113Sbapt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13274880Sbapt * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14274880Sbapt * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15274880Sbapt * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16274880Sbapt * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17274880Sbapt */
18274880Sbapt#include "config.h"
19274880Sbapt
20275432Sbapt#include <sys/types.h>
21275432Sbapt#include <sys/stat.h>
22275432Sbapt
23274880Sbapt#include <ctype.h>
24294113Sbapt#if HAVE_ERR
25294113Sbapt#include <err.h>
26294113Sbapt#endif
27274880Sbapt#include <limits.h>
28274880Sbapt#include <stdio.h>
29274880Sbapt#include <stdlib.h>
30274880Sbapt#include <string.h>
31274880Sbapt
32274880Sbapt#include "mandoc_aux.h"
33294113Sbapt#include "manconf.h"
34274880Sbapt
35294113Sbaptstatic	void	 manconf_file(struct manconf *, const char *);
36275432Sbaptstatic	void	 manpath_add(struct manpaths *, const char *, int);
37275432Sbaptstatic	void	 manpath_parseline(struct manpaths *, char *, int);
38274880Sbapt
39294113Sbapt
40274880Sbaptvoid
41294113Sbaptmanconf_parse(struct manconf *conf, const char *file,
42274880Sbapt		char *defp, char *auxp)
43274880Sbapt{
44274880Sbapt	char		*insert;
45274880Sbapt
46274880Sbapt	/* Always prepend -m. */
47294113Sbapt	manpath_parseline(&conf->manpath, auxp, 1);
48274880Sbapt
49274880Sbapt	/* If -M is given, it overrides everything else. */
50274880Sbapt	if (NULL != defp) {
51294113Sbapt		manpath_parseline(&conf->manpath, defp, 1);
52274880Sbapt		return;
53274880Sbapt	}
54274880Sbapt
55274880Sbapt	/* MANPATH and man.conf(5) cooperate. */
56274880Sbapt	defp = getenv("MANPATH");
57274880Sbapt	if (NULL == file)
58274880Sbapt		file = MAN_CONF_FILE;
59274880Sbapt
60274880Sbapt	/* No MANPATH; use man.conf(5) only. */
61274880Sbapt	if (NULL == defp || '\0' == defp[0]) {
62294113Sbapt		manconf_file(conf, file);
63274880Sbapt		return;
64274880Sbapt	}
65274880Sbapt
66274880Sbapt	/* Prepend man.conf(5) to MANPATH. */
67274880Sbapt	if (':' == defp[0]) {
68294113Sbapt		manconf_file(conf, file);
69294113Sbapt		manpath_parseline(&conf->manpath, defp, 0);
70274880Sbapt		return;
71274880Sbapt	}
72274880Sbapt
73274880Sbapt	/* Append man.conf(5) to MANPATH. */
74274880Sbapt	if (':' == defp[strlen(defp) - 1]) {
75294113Sbapt		manpath_parseline(&conf->manpath, defp, 0);
76294113Sbapt		manconf_file(conf, file);
77274880Sbapt		return;
78274880Sbapt	}
79274880Sbapt
80274880Sbapt	/* Insert man.conf(5) into MANPATH. */
81274880Sbapt	insert = strstr(defp, "::");
82274880Sbapt	if (NULL != insert) {
83274880Sbapt		*insert++ = '\0';
84294113Sbapt		manpath_parseline(&conf->manpath, defp, 0);
85294113Sbapt		manconf_file(conf, file);
86294113Sbapt		manpath_parseline(&conf->manpath, insert + 1, 0);
87274880Sbapt		return;
88274880Sbapt	}
89274880Sbapt
90274880Sbapt	/* MANPATH overrides man.conf(5) completely. */
91294113Sbapt	manpath_parseline(&conf->manpath, defp, 0);
92274880Sbapt}
93274880Sbapt
94322249Sbaptvoid
95322249Sbaptmanpath_base(struct manpaths *dirs)
96322249Sbapt{
97322249Sbapt	char path_base[] = MANPATH_BASE;
98322249Sbapt	manpath_parseline(dirs, path_base, 0);
99322249Sbapt}
100322249Sbapt
101274880Sbapt/*
102274880Sbapt * Parse a FULL pathname from a colon-separated list of arrays.
103274880Sbapt */
104274880Sbaptstatic void
105275432Sbaptmanpath_parseline(struct manpaths *dirs, char *path, int complain)
106274880Sbapt{
107274880Sbapt	char	*dir;
108274880Sbapt
109274880Sbapt	if (NULL == path)
110274880Sbapt		return;
111274880Sbapt
112274880Sbapt	for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
113275432Sbapt		manpath_add(dirs, dir, complain);
114274880Sbapt}
115274880Sbapt
116274880Sbapt/*
117274880Sbapt * Add a directory to the array, ignoring bad directories.
118274880Sbapt * Grow the array one-by-one for simplicity's sake.
119274880Sbapt */
120274880Sbaptstatic void
121275432Sbaptmanpath_add(struct manpaths *dirs, const char *dir, int complain)
122274880Sbapt{
123274880Sbapt	char		 buf[PATH_MAX];
124275432Sbapt	struct stat	 sb;
125274880Sbapt	char		*cp;
126274880Sbapt	size_t		 i;
127274880Sbapt
128275432Sbapt	if (NULL == (cp = realpath(dir, buf))) {
129294113Sbapt		if (complain)
130294113Sbapt			warn("manpath: %s", dir);
131274880Sbapt		return;
132275432Sbapt	}
133274880Sbapt
134274880Sbapt	for (i = 0; i < dirs->sz; i++)
135274880Sbapt		if (0 == strcmp(dirs->paths[i], dir))
136274880Sbapt			return;
137274880Sbapt
138275432Sbapt	if (stat(cp, &sb) == -1) {
139294113Sbapt		if (complain)
140294113Sbapt			warn("manpath: %s", dir);
141275432Sbapt		return;
142275432Sbapt	}
143275432Sbapt
144274880Sbapt	dirs->paths = mandoc_reallocarray(dirs->paths,
145274880Sbapt	    dirs->sz + 1, sizeof(char *));
146274880Sbapt
147274880Sbapt	dirs->paths[dirs->sz++] = mandoc_strdup(cp);
148274880Sbapt}
149274880Sbapt
150274880Sbaptvoid
151294113Sbaptmanconf_free(struct manconf *conf)
152274880Sbapt{
153274880Sbapt	size_t		 i;
154274880Sbapt
155294113Sbapt	for (i = 0; i < conf->manpath.sz; i++)
156294113Sbapt		free(conf->manpath.paths[i]);
157274880Sbapt
158294113Sbapt	free(conf->manpath.paths);
159294113Sbapt	free(conf->output.includes);
160294113Sbapt	free(conf->output.man);
161294113Sbapt	free(conf->output.paper);
162294113Sbapt	free(conf->output.style);
163274880Sbapt}
164274880Sbapt
165294113Sbaptstatic void
166294113Sbaptmanconf_file(struct manconf *conf, const char *file)
167274880Sbapt{
168294113Sbapt	const char *const toks[] = { "manpath", "output", "_whatdb" };
169294113Sbapt	char manpath_default[] = MANPATH_DEFAULT;
170294113Sbapt
171274880Sbapt	FILE		*stream;
172294113Sbapt	char		*line, *cp, *ep;
173294113Sbapt	size_t		 linesz, tok, toklen;
174294113Sbapt	ssize_t		 linelen;
175274880Sbapt
176294113Sbapt	if ((stream = fopen(file, "r")) == NULL)
177294113Sbapt		goto out;
178274880Sbapt
179294113Sbapt	line = NULL;
180294113Sbapt	linesz = 0;
181274880Sbapt
182294113Sbapt	while ((linelen = getline(&line, &linesz, stream)) != -1) {
183294113Sbapt		cp = line;
184307795Sbapt		ep = cp + linelen - 1;
185307795Sbapt		while (ep > cp && isspace((unsigned char)*ep))
186307795Sbapt			*ep-- = '\0';
187294113Sbapt		while (isspace((unsigned char)*cp))
188294113Sbapt			cp++;
189307795Sbapt		if (cp == ep || *cp == '#')
190274880Sbapt			continue;
191294113Sbapt
192294113Sbapt		for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
193294113Sbapt			toklen = strlen(toks[tok]);
194294113Sbapt			if (cp + toklen < ep &&
195294113Sbapt			    isspace((unsigned char)cp[toklen]) &&
196294113Sbapt			    strncmp(cp, toks[tok], toklen) == 0) {
197294113Sbapt				cp += toklen;
198294113Sbapt				while (isspace((unsigned char)*cp))
199294113Sbapt					cp++;
200294113Sbapt				break;
201294113Sbapt			}
202294113Sbapt		}
203294113Sbapt
204294113Sbapt		switch (tok) {
205294113Sbapt		case 2:  /* _whatdb */
206294113Sbapt			while (ep > cp && ep[-1] != '/')
207294113Sbapt				ep--;
208294113Sbapt			if (ep == cp)
209294113Sbapt				continue;
210294113Sbapt			*ep = '\0';
211294113Sbapt			/* FALLTHROUGH */
212294113Sbapt		case 0:  /* manpath */
213294113Sbapt			manpath_add(&conf->manpath, cp, 0);
214294113Sbapt			*manpath_default = '\0';
215294113Sbapt			break;
216294113Sbapt		case 1:  /* output */
217322249Sbapt			manconf_output(&conf->output, cp, 1);
218294113Sbapt			break;
219294113Sbapt		default:
220294113Sbapt			break;
221294113Sbapt		}
222274880Sbapt	}
223294113Sbapt	free(line);
224294113Sbapt	fclose(stream);
225274880Sbapt
226294113Sbaptout:
227294113Sbapt	if (*manpath_default != '\0')
228294113Sbapt		manpath_parseline(&conf->manpath, manpath_default, 0);
229274880Sbapt}
230294113Sbapt
231322249Sbaptint
232322249Sbaptmanconf_output(struct manoutput *conf, const char *cp, int fromfile)
233294113Sbapt{
234294113Sbapt	const char *const toks[] = {
235294113Sbapt	    "includes", "man", "paper", "style",
236322249Sbapt	    "indent", "width", "fragment", "mdoc", "noval"
237294113Sbapt	};
238294113Sbapt
239322249Sbapt	const char	*errstr;
240322249Sbapt	char		*oldval;
241322249Sbapt	size_t		 len, tok;
242294113Sbapt
243294113Sbapt	for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
244294113Sbapt		len = strlen(toks[tok]);
245294113Sbapt		if ( ! strncmp(cp, toks[tok], len) &&
246294113Sbapt		    strchr(" =	", cp[len]) != NULL) {
247294113Sbapt			cp += len;
248294113Sbapt			if (*cp == '=')
249294113Sbapt				cp++;
250294113Sbapt			while (isspace((unsigned char)*cp))
251294113Sbapt				cp++;
252294113Sbapt			break;
253294113Sbapt		}
254294113Sbapt	}
255294113Sbapt
256322249Sbapt	if (tok < 6 && *cp == '\0') {
257322249Sbapt		warnx("-O %s=?: Missing argument value", toks[tok]);
258322249Sbapt		return -1;
259322249Sbapt	}
260322249Sbapt	if ((tok == 6 || tok == 7) && *cp != '\0') {
261322249Sbapt		warnx("-O %s: Does not take a value: %s", toks[tok], cp);
262322249Sbapt		return -1;
263322249Sbapt	}
264294113Sbapt
265294113Sbapt	switch (tok) {
266294113Sbapt	case 0:
267322249Sbapt		if (conf->includes != NULL) {
268322249Sbapt			oldval = mandoc_strdup(conf->includes);
269322249Sbapt			break;
270322249Sbapt		}
271322249Sbapt		conf->includes = mandoc_strdup(cp);
272322249Sbapt		return 0;
273294113Sbapt	case 1:
274322249Sbapt		if (conf->man != NULL) {
275322249Sbapt			oldval = mandoc_strdup(conf->man);
276322249Sbapt			break;
277322249Sbapt		}
278322249Sbapt		conf->man = mandoc_strdup(cp);
279322249Sbapt		return 0;
280294113Sbapt	case 2:
281322249Sbapt		if (conf->paper != NULL) {
282322249Sbapt			oldval = mandoc_strdup(conf->paper);
283322249Sbapt			break;
284322249Sbapt		}
285322249Sbapt		conf->paper = mandoc_strdup(cp);
286322249Sbapt		return 0;
287294113Sbapt	case 3:
288322249Sbapt		if (conf->style != NULL) {
289322249Sbapt			oldval = mandoc_strdup(conf->style);
290322249Sbapt			break;
291322249Sbapt		}
292322249Sbapt		conf->style = mandoc_strdup(cp);
293322249Sbapt		return 0;
294294113Sbapt	case 4:
295322249Sbapt		if (conf->indent) {
296322249Sbapt			mandoc_asprintf(&oldval, "%zu", conf->indent);
297322249Sbapt			break;
298322249Sbapt		}
299322249Sbapt		conf->indent = strtonum(cp, 0, 1000, &errstr);
300322249Sbapt		if (errstr == NULL)
301322249Sbapt			return 0;
302322249Sbapt		warnx("-O indent=%s is %s", cp, errstr);
303322249Sbapt		return -1;
304294113Sbapt	case 5:
305322249Sbapt		if (conf->width) {
306322249Sbapt			mandoc_asprintf(&oldval, "%zu", conf->width);
307322249Sbapt			break;
308322249Sbapt		}
309322249Sbapt		conf->width = strtonum(cp, 1, 1000, &errstr);
310322249Sbapt		if (errstr == NULL)
311322249Sbapt			return 0;
312322249Sbapt		warnx("-O width=%s is %s", cp, errstr);
313322249Sbapt		return -1;
314294113Sbapt	case 6:
315294113Sbapt		conf->fragment = 1;
316322249Sbapt		return 0;
317294113Sbapt	case 7:
318294113Sbapt		conf->mdoc = 1;
319322249Sbapt		return 0;
320322249Sbapt	case 8:
321322249Sbapt		conf->noval = 1;
322322249Sbapt		return 0;
323294113Sbapt	default:
324322249Sbapt		if (fromfile)
325322249Sbapt			warnx("-O %s: Bad argument", cp);
326322249Sbapt		return -1;
327294113Sbapt	}
328322249Sbapt	if (fromfile == 0)
329322249Sbapt		warnx("-O %s=%s: Option already set to %s",
330322249Sbapt		    toks[tok], cp, oldval);
331322249Sbapt	free(oldval);
332322249Sbapt	return -1;
333294113Sbapt}
334