xref: /openbsd-src/usr.bin/mandoc/manpath.c (revision e9260a2109384575f8cd9fa929a31d4eb3290b65)
1 /*	$OpenBSD: manpath.c,v 1.14 2015/03/27 17:36:56 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		 manpath_default[] = MANPATH_DEFAULT;
43 	char		*insert;
44 
45 	/* Always prepend -m. */
46 	manpath_parseline(&conf->manpath, auxp, 1);
47 
48 	/* If -M is given, it overrides everything else. */
49 	if (NULL != defp) {
50 		manpath_parseline(&conf->manpath, defp, 1);
51 		return;
52 	}
53 
54 	/* MANPATH and man.conf(5) cooperate. */
55 	defp = getenv("MANPATH");
56 	if (NULL == file)
57 		file = MAN_CONF_FILE;
58 
59 	/* No MANPATH; use man.conf(5) only. */
60 	if (NULL == defp || '\0' == defp[0]) {
61 		manconf_file(conf, file);
62 		if (conf->manpath.sz == 0)
63 			manpath_parseline(&conf->manpath, manpath_default, 0);
64 		return;
65 	}
66 
67 	/* Prepend man.conf(5) to MANPATH. */
68 	if (':' == defp[0]) {
69 		manconf_file(conf, file);
70 		manpath_parseline(&conf->manpath, defp, 0);
71 		return;
72 	}
73 
74 	/* Append man.conf(5) to MANPATH. */
75 	if (':' == defp[strlen(defp) - 1]) {
76 		manpath_parseline(&conf->manpath, defp, 0);
77 		manconf_file(conf, file);
78 		return;
79 	}
80 
81 	/* Insert man.conf(5) into MANPATH. */
82 	insert = strstr(defp, "::");
83 	if (NULL != insert) {
84 		*insert++ = '\0';
85 		manpath_parseline(&conf->manpath, defp, 0);
86 		manconf_file(conf, file);
87 		manpath_parseline(&conf->manpath, insert + 1, 0);
88 		return;
89 	}
90 
91 	/* MANPATH overrides man.conf(5) completely. */
92 	manpath_parseline(&conf->manpath, defp, 0);
93 }
94 
95 /*
96  * Parse a FULL pathname from a colon-separated list of arrays.
97  */
98 static void
99 manpath_parseline(struct manpaths *dirs, char *path, int complain)
100 {
101 	char	*dir;
102 
103 	if (NULL == path)
104 		return;
105 
106 	for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
107 		manpath_add(dirs, dir, complain);
108 }
109 
110 /*
111  * Add a directory to the array, ignoring bad directories.
112  * Grow the array one-by-one for simplicity's sake.
113  */
114 static void
115 manpath_add(struct manpaths *dirs, const char *dir, int complain)
116 {
117 	char		 buf[PATH_MAX];
118 	struct stat	 sb;
119 	char		*cp;
120 	size_t		 i;
121 
122 	if (NULL == (cp = realpath(dir, buf))) {
123 		if (complain) {
124 			fputs("manpath: ", stderr);
125 			perror(dir);
126 		}
127 		return;
128 	}
129 
130 	for (i = 0; i < dirs->sz; i++)
131 		if (0 == strcmp(dirs->paths[i], dir))
132 			return;
133 
134 	if (stat(cp, &sb) == -1) {
135 		if (complain) {
136 			fputs("manpath: ", stderr);
137 			perror(dir);
138 		}
139 		return;
140 	}
141 
142 	dirs->paths = mandoc_reallocarray(dirs->paths,
143 	    dirs->sz + 1, sizeof(char *));
144 
145 	dirs->paths[dirs->sz++] = mandoc_strdup(cp);
146 }
147 
148 void
149 manconf_free(struct manconf *conf)
150 {
151 	size_t		 i;
152 
153 	for (i = 0; i < conf->manpath.sz; i++)
154 		free(conf->manpath.paths[i]);
155 
156 	free(conf->manpath.paths);
157 	free(conf->output.includes);
158 	free(conf->output.man);
159 	free(conf->output.paper);
160 	free(conf->output.style);
161 }
162 
163 static void
164 manconf_file(struct manconf *conf, const char *file)
165 {
166 	const char *const toks[] = { "manpath", "output", "_whatdb" };
167 
168 	FILE		*stream;
169 	char		*cp, *ep;
170 	size_t		 len, tok;
171 
172 	if ((stream = fopen(file, "r")) == NULL)
173 		return;
174 
175 	while ((cp = fgetln(stream, &len)) != NULL) {
176 		ep = cp + len;
177 		if (ep[-1] != '\n')
178 			break;
179 		*--ep = '\0';
180 		while (isspace((unsigned char)*cp))
181 			cp++;
182 		if (*cp == '#')
183 			continue;
184 
185 		for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
186 			len = strlen(toks[tok]);
187 			if (cp + len < ep &&
188 			    isspace((unsigned char)cp[len]) &&
189 			    !strncmp(cp, toks[tok], len)) {
190 				cp += len;
191 				while (isspace((unsigned char)*cp))
192 					cp++;
193 				break;
194 			}
195 		}
196 
197 		switch (tok) {
198 		case 2:  /* _whatdb */
199 			while (ep > cp && ep[-1] != '/')
200 				ep--;
201 			if (ep == cp)
202 				continue;
203 			*ep = '\0';
204 			/* FALLTHROUGH */
205 		case 0:  /* manpath */
206 			manpath_add(&conf->manpath, cp, 0);
207 			break;
208 		case 1:  /* output */
209 			manconf_output(&conf->output, cp);
210 			break;
211 		default:
212 			break;
213 		}
214 	}
215 
216 	fclose(stream);
217 }
218 
219 void
220 manconf_output(struct manoutput *conf, const char *cp)
221 {
222 	const char *const toks[] = {
223 	    "includes", "man", "paper", "style",
224 	    "indent", "width", "fragment", "mdoc"
225 	};
226 
227 	size_t	 len, tok;
228 
229 	for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
230 		len = strlen(toks[tok]);
231 		if ( ! strncmp(cp, toks[tok], len) &&
232 		    strchr(" =	", cp[len]) != NULL) {
233 			cp += len;
234 			if (*cp == '=')
235 				cp++;
236 			while (isspace((unsigned char)*cp))
237 				cp++;
238 			break;
239 		}
240 	}
241 
242 	if (tok < 6 && *cp == '\0')
243 		return;
244 
245 	switch (tok) {
246 	case 0:
247 		if (conf->includes == NULL)
248 			conf->includes = mandoc_strdup(cp);
249 		break;
250 	case 1:
251 		if (conf->man == NULL)
252 			conf->man = mandoc_strdup(cp);
253 		break;
254 	case 2:
255 		if (conf->paper == NULL)
256 			conf->paper = mandoc_strdup(cp);
257 		break;
258 	case 3:
259 		if (conf->style == NULL)
260 			conf->style = mandoc_strdup(cp);
261 		break;
262 	case 4:
263 		if (conf->indent == 0)
264 			conf->indent = strtonum(cp, 0, 1000, NULL);
265 		break;
266 	case 5:
267 		if (conf->width == 0)
268 			conf->width = strtonum(cp, 58, 1000, NULL);
269 		break;
270 	case 6:
271 		conf->fragment = 1;
272 		break;
273 	case 7:
274 		conf->mdoc = 1;
275 		break;
276 	default:
277 		break;
278 	}
279 }
280