manpath.c revision 307795
1307795Sbapt/*	$Id: manpath.c,v 1.30 2016/05/28 13:44:13 schwarze Exp $	*/
2274880Sbapt/*
3294113Sbapt * Copyright (c) 2011, 2014, 2015 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
35294113Sbapt#if !HAVE_MANPATH
36294113Sbaptstatic	void	 manconf_file(struct manconf *, const char *);
37294113Sbapt#endif
38275432Sbaptstatic	void	 manpath_add(struct manpaths *, const char *, int);
39275432Sbaptstatic	void	 manpath_parseline(struct manpaths *, char *, int);
40274880Sbapt
41294113Sbapt
42274880Sbaptvoid
43294113Sbaptmanconf_parse(struct manconf *conf, const char *file,
44274880Sbapt		char *defp, char *auxp)
45274880Sbapt{
46275432Sbapt#if HAVE_MANPATH
47274880Sbapt	char		 cmd[(PATH_MAX * 3) + 20];
48274880Sbapt	FILE		*stream;
49274880Sbapt	char		*buf;
50274880Sbapt	size_t		 sz, bsz;
51274880Sbapt
52274880Sbapt	strlcpy(cmd, "manpath", sizeof(cmd));
53274880Sbapt	if (file) {
54274880Sbapt		strlcat(cmd, " -C ", sizeof(cmd));
55274880Sbapt		strlcat(cmd, file, sizeof(cmd));
56274880Sbapt	}
57274880Sbapt	if (auxp) {
58274880Sbapt		strlcat(cmd, " -m ", sizeof(cmd));
59274880Sbapt		strlcat(cmd, auxp, sizeof(cmd));
60274880Sbapt	}
61274880Sbapt	if (defp) {
62274880Sbapt		strlcat(cmd, " -M ", sizeof(cmd));
63274880Sbapt		strlcat(cmd, defp, sizeof(cmd));
64274880Sbapt	}
65274880Sbapt
66274880Sbapt	/* Open manpath(1).  Ignore errors. */
67274880Sbapt
68274880Sbapt	stream = popen(cmd, "r");
69274880Sbapt	if (NULL == stream)
70274880Sbapt		return;
71274880Sbapt
72274880Sbapt	buf = NULL;
73274880Sbapt	bsz = 0;
74274880Sbapt
75274880Sbapt	/* Read in as much output as we can. */
76274880Sbapt
77274880Sbapt	do {
78274880Sbapt		buf = mandoc_realloc(buf, bsz + 1024);
79274880Sbapt		sz = fread(buf + bsz, 1, 1024, stream);
80274880Sbapt		bsz += sz;
81274880Sbapt	} while (sz > 0);
82274880Sbapt
83274880Sbapt	if ( ! ferror(stream) && feof(stream) &&
84274880Sbapt			bsz && '\n' == buf[bsz - 1]) {
85274880Sbapt		buf[bsz - 1] = '\0';
86294113Sbapt		manpath_parseline(&conf->manpath, buf, 1);
87274880Sbapt	}
88274880Sbapt
89274880Sbapt	free(buf);
90274880Sbapt	pclose(stream);
91274880Sbapt#else
92274880Sbapt	char		*insert;
93274880Sbapt
94274880Sbapt	/* Always prepend -m. */
95294113Sbapt	manpath_parseline(&conf->manpath, auxp, 1);
96274880Sbapt
97274880Sbapt	/* If -M is given, it overrides everything else. */
98274880Sbapt	if (NULL != defp) {
99294113Sbapt		manpath_parseline(&conf->manpath, defp, 1);
100274880Sbapt		return;
101274880Sbapt	}
102274880Sbapt
103274880Sbapt	/* MANPATH and man.conf(5) cooperate. */
104274880Sbapt	defp = getenv("MANPATH");
105274880Sbapt	if (NULL == file)
106274880Sbapt		file = MAN_CONF_FILE;
107274880Sbapt
108274880Sbapt	/* No MANPATH; use man.conf(5) only. */
109274880Sbapt	if (NULL == defp || '\0' == defp[0]) {
110294113Sbapt		manconf_file(conf, file);
111274880Sbapt		return;
112274880Sbapt	}
113274880Sbapt
114274880Sbapt	/* Prepend man.conf(5) to MANPATH. */
115274880Sbapt	if (':' == defp[0]) {
116294113Sbapt		manconf_file(conf, file);
117294113Sbapt		manpath_parseline(&conf->manpath, defp, 0);
118274880Sbapt		return;
119274880Sbapt	}
120274880Sbapt
121274880Sbapt	/* Append man.conf(5) to MANPATH. */
122274880Sbapt	if (':' == defp[strlen(defp) - 1]) {
123294113Sbapt		manpath_parseline(&conf->manpath, defp, 0);
124294113Sbapt		manconf_file(conf, file);
125274880Sbapt		return;
126274880Sbapt	}
127274880Sbapt
128274880Sbapt	/* Insert man.conf(5) into MANPATH. */
129274880Sbapt	insert = strstr(defp, "::");
130274880Sbapt	if (NULL != insert) {
131274880Sbapt		*insert++ = '\0';
132294113Sbapt		manpath_parseline(&conf->manpath, defp, 0);
133294113Sbapt		manconf_file(conf, file);
134294113Sbapt		manpath_parseline(&conf->manpath, insert + 1, 0);
135274880Sbapt		return;
136274880Sbapt	}
137274880Sbapt
138274880Sbapt	/* MANPATH overrides man.conf(5) completely. */
139294113Sbapt	manpath_parseline(&conf->manpath, defp, 0);
140274880Sbapt#endif
141274880Sbapt}
142274880Sbapt
143274880Sbapt/*
144274880Sbapt * Parse a FULL pathname from a colon-separated list of arrays.
145274880Sbapt */
146274880Sbaptstatic void
147275432Sbaptmanpath_parseline(struct manpaths *dirs, char *path, int complain)
148274880Sbapt{
149274880Sbapt	char	*dir;
150274880Sbapt
151274880Sbapt	if (NULL == path)
152274880Sbapt		return;
153274880Sbapt
154274880Sbapt	for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
155275432Sbapt		manpath_add(dirs, dir, complain);
156274880Sbapt}
157274880Sbapt
158274880Sbapt/*
159274880Sbapt * Add a directory to the array, ignoring bad directories.
160274880Sbapt * Grow the array one-by-one for simplicity's sake.
161274880Sbapt */
162274880Sbaptstatic void
163275432Sbaptmanpath_add(struct manpaths *dirs, const char *dir, int complain)
164274880Sbapt{
165274880Sbapt	char		 buf[PATH_MAX];
166275432Sbapt	struct stat	 sb;
167274880Sbapt	char		*cp;
168274880Sbapt	size_t		 i;
169274880Sbapt
170275432Sbapt	if (NULL == (cp = realpath(dir, buf))) {
171294113Sbapt		if (complain)
172294113Sbapt			warn("manpath: %s", dir);
173274880Sbapt		return;
174275432Sbapt	}
175274880Sbapt
176274880Sbapt	for (i = 0; i < dirs->sz; i++)
177274880Sbapt		if (0 == strcmp(dirs->paths[i], dir))
178274880Sbapt			return;
179274880Sbapt
180275432Sbapt	if (stat(cp, &sb) == -1) {
181294113Sbapt		if (complain)
182294113Sbapt			warn("manpath: %s", dir);
183275432Sbapt		return;
184275432Sbapt	}
185275432Sbapt
186274880Sbapt	dirs->paths = mandoc_reallocarray(dirs->paths,
187274880Sbapt	    dirs->sz + 1, sizeof(char *));
188274880Sbapt
189274880Sbapt	dirs->paths[dirs->sz++] = mandoc_strdup(cp);
190274880Sbapt}
191274880Sbapt
192274880Sbaptvoid
193294113Sbaptmanconf_free(struct manconf *conf)
194274880Sbapt{
195274880Sbapt	size_t		 i;
196274880Sbapt
197294113Sbapt	for (i = 0; i < conf->manpath.sz; i++)
198294113Sbapt		free(conf->manpath.paths[i]);
199274880Sbapt
200294113Sbapt	free(conf->manpath.paths);
201294113Sbapt	free(conf->output.includes);
202294113Sbapt	free(conf->output.man);
203294113Sbapt	free(conf->output.paper);
204294113Sbapt	free(conf->output.style);
205274880Sbapt}
206274880Sbapt
207294113Sbapt#if !HAVE_MANPATH
208294113Sbaptstatic void
209294113Sbaptmanconf_file(struct manconf *conf, const char *file)
210274880Sbapt{
211294113Sbapt	const char *const toks[] = { "manpath", "output", "_whatdb" };
212294113Sbapt	char manpath_default[] = MANPATH_DEFAULT;
213294113Sbapt
214274880Sbapt	FILE		*stream;
215294113Sbapt	char		*line, *cp, *ep;
216294113Sbapt	size_t		 linesz, tok, toklen;
217294113Sbapt	ssize_t		 linelen;
218274880Sbapt
219294113Sbapt	if ((stream = fopen(file, "r")) == NULL)
220294113Sbapt		goto out;
221274880Sbapt
222294113Sbapt	line = NULL;
223294113Sbapt	linesz = 0;
224274880Sbapt
225294113Sbapt	while ((linelen = getline(&line, &linesz, stream)) != -1) {
226294113Sbapt		cp = line;
227307795Sbapt		ep = cp + linelen - 1;
228307795Sbapt		while (ep > cp && isspace((unsigned char)*ep))
229307795Sbapt			*ep-- = '\0';
230294113Sbapt		while (isspace((unsigned char)*cp))
231294113Sbapt			cp++;
232307795Sbapt		if (cp == ep || *cp == '#')
233274880Sbapt			continue;
234294113Sbapt
235294113Sbapt		for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
236294113Sbapt			toklen = strlen(toks[tok]);
237294113Sbapt			if (cp + toklen < ep &&
238294113Sbapt			    isspace((unsigned char)cp[toklen]) &&
239294113Sbapt			    strncmp(cp, toks[tok], toklen) == 0) {
240294113Sbapt				cp += toklen;
241294113Sbapt				while (isspace((unsigned char)*cp))
242294113Sbapt					cp++;
243294113Sbapt				break;
244294113Sbapt			}
245294113Sbapt		}
246294113Sbapt
247294113Sbapt		switch (tok) {
248294113Sbapt		case 2:  /* _whatdb */
249294113Sbapt			while (ep > cp && ep[-1] != '/')
250294113Sbapt				ep--;
251294113Sbapt			if (ep == cp)
252294113Sbapt				continue;
253294113Sbapt			*ep = '\0';
254294113Sbapt			/* FALLTHROUGH */
255294113Sbapt		case 0:  /* manpath */
256294113Sbapt			manpath_add(&conf->manpath, cp, 0);
257294113Sbapt			*manpath_default = '\0';
258294113Sbapt			break;
259294113Sbapt		case 1:  /* output */
260294113Sbapt			manconf_output(&conf->output, cp);
261294113Sbapt			break;
262294113Sbapt		default:
263294113Sbapt			break;
264294113Sbapt		}
265274880Sbapt	}
266294113Sbapt	free(line);
267294113Sbapt	fclose(stream);
268274880Sbapt
269294113Sbaptout:
270294113Sbapt	if (*manpath_default != '\0')
271294113Sbapt		manpath_parseline(&conf->manpath, manpath_default, 0);
272274880Sbapt}
273294113Sbapt#endif
274294113Sbapt
275294113Sbaptvoid
276294113Sbaptmanconf_output(struct manoutput *conf, const char *cp)
277294113Sbapt{
278294113Sbapt	const char *const toks[] = {
279294113Sbapt	    "includes", "man", "paper", "style",
280294113Sbapt	    "indent", "width", "fragment", "mdoc"
281294113Sbapt	};
282294113Sbapt
283294113Sbapt	size_t	 len, tok;
284294113Sbapt
285294113Sbapt	for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
286294113Sbapt		len = strlen(toks[tok]);
287294113Sbapt		if ( ! strncmp(cp, toks[tok], len) &&
288294113Sbapt		    strchr(" =	", cp[len]) != NULL) {
289294113Sbapt			cp += len;
290294113Sbapt			if (*cp == '=')
291294113Sbapt				cp++;
292294113Sbapt			while (isspace((unsigned char)*cp))
293294113Sbapt				cp++;
294294113Sbapt			break;
295294113Sbapt		}
296294113Sbapt	}
297294113Sbapt
298294113Sbapt	if (tok < 6 && *cp == '\0')
299294113Sbapt		return;
300294113Sbapt
301294113Sbapt	switch (tok) {
302294113Sbapt	case 0:
303294113Sbapt		if (conf->includes == NULL)
304294113Sbapt			conf->includes = mandoc_strdup(cp);
305294113Sbapt		break;
306294113Sbapt	case 1:
307294113Sbapt		if (conf->man == NULL)
308294113Sbapt			conf->man = mandoc_strdup(cp);
309294113Sbapt		break;
310294113Sbapt	case 2:
311294113Sbapt		if (conf->paper == NULL)
312294113Sbapt			conf->paper = mandoc_strdup(cp);
313294113Sbapt		break;
314294113Sbapt	case 3:
315294113Sbapt		if (conf->style == NULL)
316294113Sbapt			conf->style = mandoc_strdup(cp);
317294113Sbapt		break;
318294113Sbapt	case 4:
319294113Sbapt		if (conf->indent == 0)
320294113Sbapt			conf->indent = strtonum(cp, 0, 1000, NULL);
321294113Sbapt		break;
322294113Sbapt	case 5:
323294113Sbapt		if (conf->width == 0)
324294113Sbapt			conf->width = strtonum(cp, 58, 1000, NULL);
325294113Sbapt		break;
326294113Sbapt	case 6:
327294113Sbapt		conf->fragment = 1;
328294113Sbapt		break;
329294113Sbapt	case 7:
330294113Sbapt		conf->mdoc = 1;
331294113Sbapt		break;
332294113Sbapt	default:
333294113Sbapt		break;
334294113Sbapt	}
335294113Sbapt}
336