xref: /openbsd-src/usr.bin/mandoc/manpath.c (revision cb39b41371628601fbe4c618205356d538b9d08a)
1 /*	$OpenBSD: manpath.c,v 1.15 2015/05/07 12:07:29 schwarze Exp $	*/
2 /*
3  * Copyright (c) 2011, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
4  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 
21 #include <ctype.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "mandoc_aux.h"
28 #include "manconf.h"
29 
30 #define MAN_CONF_FILE	"/etc/man.conf"
31 #define MANPATH_DEFAULT	"/usr/share/man:/usr/X11R6/man:/usr/local/man"
32 
33 static	void	 manconf_file(struct manconf *, const char *);
34 static	void	 manpath_add(struct manpaths *, const char *, int);
35 static	void	 manpath_parseline(struct manpaths *, char *, int);
36 
37 
38 void
39 manconf_parse(struct manconf *conf, const char *file,
40 		char *defp, char *auxp)
41 {
42 	char		*insert;
43 
44 	/* Always prepend -m. */
45 	manpath_parseline(&conf->manpath, auxp, 1);
46 
47 	/* If -M is given, it overrides everything else. */
48 	if (NULL != defp) {
49 		manpath_parseline(&conf->manpath, defp, 1);
50 		return;
51 	}
52 
53 	/* MANPATH and man.conf(5) cooperate. */
54 	defp = getenv("MANPATH");
55 	if (NULL == file)
56 		file = MAN_CONF_FILE;
57 
58 	/* No MANPATH; use man.conf(5) only. */
59 	if (NULL == defp || '\0' == defp[0]) {
60 		manconf_file(conf, file);
61 		return;
62 	}
63 
64 	/* Prepend man.conf(5) to MANPATH. */
65 	if (':' == defp[0]) {
66 		manconf_file(conf, file);
67 		manpath_parseline(&conf->manpath, defp, 0);
68 		return;
69 	}
70 
71 	/* Append man.conf(5) to MANPATH. */
72 	if (':' == defp[strlen(defp) - 1]) {
73 		manpath_parseline(&conf->manpath, defp, 0);
74 		manconf_file(conf, file);
75 		return;
76 	}
77 
78 	/* Insert man.conf(5) into MANPATH. */
79 	insert = strstr(defp, "::");
80 	if (NULL != insert) {
81 		*insert++ = '\0';
82 		manpath_parseline(&conf->manpath, defp, 0);
83 		manconf_file(conf, file);
84 		manpath_parseline(&conf->manpath, insert + 1, 0);
85 		return;
86 	}
87 
88 	/* MANPATH overrides man.conf(5) completely. */
89 	manpath_parseline(&conf->manpath, defp, 0);
90 }
91 
92 /*
93  * Parse a FULL pathname from a colon-separated list of arrays.
94  */
95 static void
96 manpath_parseline(struct manpaths *dirs, char *path, int complain)
97 {
98 	char	*dir;
99 
100 	if (NULL == path)
101 		return;
102 
103 	for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
104 		manpath_add(dirs, dir, complain);
105 }
106 
107 /*
108  * Add a directory to the array, ignoring bad directories.
109  * Grow the array one-by-one for simplicity's sake.
110  */
111 static void
112 manpath_add(struct manpaths *dirs, const char *dir, int complain)
113 {
114 	char		 buf[PATH_MAX];
115 	struct stat	 sb;
116 	char		*cp;
117 	size_t		 i;
118 
119 	if (NULL == (cp = realpath(dir, buf))) {
120 		if (complain) {
121 			fputs("manpath: ", stderr);
122 			perror(dir);
123 		}
124 		return;
125 	}
126 
127 	for (i = 0; i < dirs->sz; i++)
128 		if (0 == strcmp(dirs->paths[i], dir))
129 			return;
130 
131 	if (stat(cp, &sb) == -1) {
132 		if (complain) {
133 			fputs("manpath: ", stderr);
134 			perror(dir);
135 		}
136 		return;
137 	}
138 
139 	dirs->paths = mandoc_reallocarray(dirs->paths,
140 	    dirs->sz + 1, sizeof(char *));
141 
142 	dirs->paths[dirs->sz++] = mandoc_strdup(cp);
143 }
144 
145 void
146 manconf_free(struct manconf *conf)
147 {
148 	size_t		 i;
149 
150 	for (i = 0; i < conf->manpath.sz; i++)
151 		free(conf->manpath.paths[i]);
152 
153 	free(conf->manpath.paths);
154 	free(conf->output.includes);
155 	free(conf->output.man);
156 	free(conf->output.paper);
157 	free(conf->output.style);
158 }
159 
160 static void
161 manconf_file(struct manconf *conf, const char *file)
162 {
163 	const char *const toks[] = { "manpath", "output", "_whatdb" };
164 	char manpath_default[] = MANPATH_DEFAULT;
165 
166 	FILE		*stream;
167 	char		*cp, *ep;
168 	size_t		 len, tok;
169 
170 	if ((stream = fopen(file, "r")) == NULL)
171 		goto out;
172 
173 	while ((cp = fgetln(stream, &len)) != NULL) {
174 		ep = cp + len;
175 		if (ep[-1] != '\n')
176 			break;
177 		*--ep = '\0';
178 		while (isspace((unsigned char)*cp))
179 			cp++;
180 		if (*cp == '#')
181 			continue;
182 
183 		for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
184 			len = strlen(toks[tok]);
185 			if (cp + len < ep &&
186 			    isspace((unsigned char)cp[len]) &&
187 			    !strncmp(cp, toks[tok], len)) {
188 				cp += len;
189 				while (isspace((unsigned char)*cp))
190 					cp++;
191 				break;
192 			}
193 		}
194 
195 		switch (tok) {
196 		case 2:  /* _whatdb */
197 			while (ep > cp && ep[-1] != '/')
198 				ep--;
199 			if (ep == cp)
200 				continue;
201 			*ep = '\0';
202 			/* FALLTHROUGH */
203 		case 0:  /* manpath */
204 			manpath_add(&conf->manpath, cp, 0);
205 			*manpath_default = '\0';
206 			break;
207 		case 1:  /* output */
208 			manconf_output(&conf->output, cp);
209 			break;
210 		default:
211 			break;
212 		}
213 	}
214 	fclose(stream);
215 
216 out:
217 	if (*manpath_default != '\0')
218 		manpath_parseline(&conf->manpath, manpath_default, 0);
219 }
220 
221 void
222 manconf_output(struct manoutput *conf, const char *cp)
223 {
224 	const char *const toks[] = {
225 	    "includes", "man", "paper", "style",
226 	    "indent", "width", "fragment", "mdoc"
227 	};
228 
229 	size_t	 len, tok;
230 
231 	for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
232 		len = strlen(toks[tok]);
233 		if ( ! strncmp(cp, toks[tok], len) &&
234 		    strchr(" =	", cp[len]) != NULL) {
235 			cp += len;
236 			if (*cp == '=')
237 				cp++;
238 			while (isspace((unsigned char)*cp))
239 				cp++;
240 			break;
241 		}
242 	}
243 
244 	if (tok < 6 && *cp == '\0')
245 		return;
246 
247 	switch (tok) {
248 	case 0:
249 		if (conf->includes == NULL)
250 			conf->includes = mandoc_strdup(cp);
251 		break;
252 	case 1:
253 		if (conf->man == NULL)
254 			conf->man = mandoc_strdup(cp);
255 		break;
256 	case 2:
257 		if (conf->paper == NULL)
258 			conf->paper = mandoc_strdup(cp);
259 		break;
260 	case 3:
261 		if (conf->style == NULL)
262 			conf->style = mandoc_strdup(cp);
263 		break;
264 	case 4:
265 		if (conf->indent == 0)
266 			conf->indent = strtonum(cp, 0, 1000, NULL);
267 		break;
268 	case 5:
269 		if (conf->width == 0)
270 			conf->width = strtonum(cp, 58, 1000, NULL);
271 		break;
272 	case 6:
273 		conf->fragment = 1;
274 		break;
275 	case 7:
276 		conf->mdoc = 1;
277 		break;
278 	default:
279 		break;
280 	}
281 }
282