Commit | Line | Data |
---|---|---|
99db7d0e | 1 | /* $Id: manpath.c,v 1.43 2020/08/27 14:59:47 schwarze Exp $ */ |
36342e81 | 2 | /* |
99db7d0e | 3 | * Copyright (c) 2011,2014,2015,2017-2019 Ingo Schwarze <schwarze@openbsd.org> |
36342e81 SW |
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 | * | |
54ba9607 | 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
36342e81 | 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
54ba9607 | 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR |
36342e81 SW |
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 | */ | |
36342e81 | 18 | #include "config.h" |
36342e81 | 19 | |
54ba9607 SW |
20 | #include <sys/types.h> |
21 | #include <sys/stat.h> | |
22 | ||
36342e81 | 23 | #include <ctype.h> |
99db7d0e | 24 | #include <errno.h> |
36342e81 SW |
25 | #include <limits.h> |
26 | #include <stdio.h> | |
27 | #include <stdlib.h> | |
28 | #include <string.h> | |
29 | ||
070c62a6 | 30 | #include "mandoc_aux.h" |
99db7d0e | 31 | #include "mandoc.h" |
54ba9607 | 32 | #include "manconf.h" |
36342e81 | 33 | |
54ba9607 | 34 | static void manconf_file(struct manconf *, const char *); |
99db7d0e SW |
35 | static void manpath_add(struct manpaths *, const char *, char); |
36 | static void manpath_parseline(struct manpaths *, char *, char); | |
36342e81 | 37 | |
36342e81 SW |
38 | |
39 | void | |
54ba9607 | 40 | manconf_parse(struct manconf *conf, const char *file, |
36342e81 SW |
41 | char *defp, char *auxp) |
42 | { | |
36342e81 SW |
43 | char *insert; |
44 | ||
45 | /* Always prepend -m. */ | |
99db7d0e | 46 | manpath_parseline(&conf->manpath, auxp, 'm'); |
f88b6c16 | 47 | |
36342e81 SW |
48 | /* If -M is given, it overrides everything else. */ |
49 | if (NULL != defp) { | |
99db7d0e | 50 | manpath_parseline(&conf->manpath, defp, 'M'); |
36342e81 SW |
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]) { | |
54ba9607 | 61 | manconf_file(conf, file); |
36342e81 SW |
62 | return; |
63 | } | |
64 | ||
65 | /* Prepend man.conf(5) to MANPATH. */ | |
66 | if (':' == defp[0]) { | |
54ba9607 | 67 | manconf_file(conf, file); |
99db7d0e | 68 | manpath_parseline(&conf->manpath, defp, '\0'); |
36342e81 SW |
69 | return; |
70 | } | |
71 | ||
72 | /* Append man.conf(5) to MANPATH. */ | |
f88b6c16 | 73 | if (':' == defp[strlen(defp) - 1]) { |
99db7d0e | 74 | manpath_parseline(&conf->manpath, defp, '\0'); |
54ba9607 | 75 | manconf_file(conf, file); |
36342e81 SW |
76 | return; |
77 | } | |
78 | ||
79 | /* Insert man.conf(5) into MANPATH. */ | |
80 | insert = strstr(defp, "::"); | |
81 | if (NULL != insert) { | |
82 | *insert++ = '\0'; | |
99db7d0e | 83 | manpath_parseline(&conf->manpath, defp, '\0'); |
54ba9607 | 84 | manconf_file(conf, file); |
99db7d0e | 85 | manpath_parseline(&conf->manpath, insert + 1, '\0'); |
36342e81 SW |
86 | return; |
87 | } | |
88 | ||
89 | /* MANPATH overrides man.conf(5) completely. */ | |
99db7d0e | 90 | manpath_parseline(&conf->manpath, defp, '\0'); |
54ba9607 SW |
91 | } |
92 | ||
93 | void | |
94 | manpath_base(struct manpaths *dirs) | |
95 | { | |
96 | char path_base[] = MANPATH_BASE; | |
99db7d0e | 97 | manpath_parseline(dirs, path_base, '\0'); |
36342e81 SW |
98 | } |
99 | ||
100 | /* | |
101 | * Parse a FULL pathname from a colon-separated list of arrays. | |
102 | */ | |
103 | static void | |
99db7d0e | 104 | manpath_parseline(struct manpaths *dirs, char *path, char option) |
36342e81 SW |
105 | { |
106 | char *dir; | |
107 | ||
108 | if (NULL == path) | |
109 | return; | |
110 | ||
111 | for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) | |
99db7d0e | 112 | manpath_add(dirs, dir, option); |
36342e81 SW |
113 | } |
114 | ||
115 | /* | |
116 | * Add a directory to the array, ignoring bad directories. | |
117 | * Grow the array one-by-one for simplicity's sake. | |
118 | */ | |
119 | static void | |
99db7d0e | 120 | manpath_add(struct manpaths *dirs, const char *dir, char option) |
36342e81 SW |
121 | { |
122 | char buf[PATH_MAX]; | |
54ba9607 | 123 | struct stat sb; |
36342e81 | 124 | char *cp; |
f88b6c16 | 125 | size_t i; |
36342e81 | 126 | |
99db7d0e SW |
127 | if ((cp = realpath(dir, buf)) == NULL) |
128 | goto fail; | |
36342e81 SW |
129 | |
130 | for (i = 0; i < dirs->sz; i++) | |
99db7d0e | 131 | if (strcmp(dirs->paths[i], dir) == 0) |
36342e81 SW |
132 | return; |
133 | ||
99db7d0e SW |
134 | if (stat(cp, &sb) == -1) |
135 | goto fail; | |
54ba9607 | 136 | |
070c62a6 | 137 | dirs->paths = mandoc_reallocarray(dirs->paths, |
99db7d0e | 138 | dirs->sz + 1, sizeof(*dirs->paths)); |
36342e81 | 139 | dirs->paths[dirs->sz++] = mandoc_strdup(cp); |
99db7d0e SW |
140 | return; |
141 | ||
142 | fail: | |
143 | if (option != '\0') | |
144 | mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, | |
145 | "-%c %s: %s", option, dir, strerror(errno)); | |
36342e81 SW |
146 | } |
147 | ||
148 | void | |
54ba9607 | 149 | manconf_free(struct manconf *conf) |
36342e81 | 150 | { |
f88b6c16 | 151 | size_t i; |
36342e81 | 152 | |
54ba9607 SW |
153 | for (i = 0; i < conf->manpath.sz; i++) |
154 | free(conf->manpath.paths[i]); | |
36342e81 | 155 | |
54ba9607 SW |
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); | |
36342e81 SW |
161 | } |
162 | ||
54ba9607 SW |
163 | static void |
164 | manconf_file(struct manconf *conf, const char *file) | |
36342e81 | 165 | { |
99db7d0e | 166 | const char *const toks[] = { "manpath", "output" }; |
54ba9607 SW |
167 | char manpath_default[] = MANPATH_DEFAULT; |
168 | ||
36342e81 | 169 | FILE *stream; |
54ba9607 SW |
170 | char *line, *cp, *ep; |
171 | size_t linesz, tok, toklen; | |
172 | ssize_t linelen; | |
173 | ||
174 | if ((stream = fopen(file, "r")) == NULL) | |
175 | goto out; | |
176 | ||
177 | line = NULL; | |
178 | linesz = 0; | |
179 | ||
180 | while ((linelen = getline(&line, &linesz, stream)) != -1) { | |
181 | cp = line; | |
182 | ep = cp + linelen - 1; | |
183 | while (ep > cp && isspace((unsigned char)*ep)) | |
184 | *ep-- = '\0'; | |
185 | while (isspace((unsigned char)*cp)) | |
186 | cp++; | |
187 | if (cp == ep || *cp == '#') | |
188 | continue; | |
36342e81 | 189 | |
54ba9607 SW |
190 | for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { |
191 | toklen = strlen(toks[tok]); | |
192 | if (cp + toklen < ep && | |
193 | isspace((unsigned char)cp[toklen]) && | |
194 | strncmp(cp, toks[tok], toklen) == 0) { | |
195 | cp += toklen; | |
196 | while (isspace((unsigned char)*cp)) | |
197 | cp++; | |
198 | break; | |
199 | } | |
200 | } | |
201 | ||
202 | switch (tok) { | |
54ba9607 | 203 | case 0: /* manpath */ |
99db7d0e | 204 | manpath_add(&conf->manpath, cp, '\0'); |
54ba9607 SW |
205 | *manpath_default = '\0'; |
206 | break; | |
207 | case 1: /* output */ | |
208 | manconf_output(&conf->output, cp, 1); | |
209 | break; | |
210 | default: | |
211 | break; | |
212 | } | |
213 | } | |
214 | free(line); | |
215 | fclose(stream); | |
36342e81 | 216 | |
54ba9607 SW |
217 | out: |
218 | if (*manpath_default != '\0') | |
99db7d0e | 219 | manpath_parseline(&conf->manpath, manpath_default, '\0'); |
54ba9607 | 220 | } |
36342e81 | 221 | |
54ba9607 SW |
222 | int |
223 | manconf_output(struct manoutput *conf, const char *cp, int fromfile) | |
224 | { | |
225 | const char *const toks[] = { | |
99db7d0e | 226 | /* Tokens requiring an argument. */ |
54ba9607 | 227 | "includes", "man", "paper", "style", "indent", "width", |
99db7d0e SW |
228 | "outfilename", "tagfilename", |
229 | /* Token taking an optional argument. */ | |
230 | "tag", | |
231 | /* Tokens not taking arguments. */ | |
232 | "fragment", "mdoc", "noval", "toc" | |
54ba9607 | 233 | }; |
99db7d0e | 234 | const size_t ntoks = sizeof(toks) / sizeof(toks[0]); |
54ba9607 SW |
235 | |
236 | const char *errstr; | |
237 | char *oldval; | |
238 | size_t len, tok; | |
239 | ||
99db7d0e | 240 | for (tok = 0; tok < ntoks; tok++) { |
54ba9607 | 241 | len = strlen(toks[tok]); |
99db7d0e | 242 | if (strncmp(cp, toks[tok], len) == 0 && |
54ba9607 SW |
243 | strchr(" = ", cp[len]) != NULL) { |
244 | cp += len; | |
245 | if (*cp == '=') | |
246 | cp++; | |
247 | while (isspace((unsigned char)*cp)) | |
248 | cp++; | |
36342e81 | 249 | break; |
54ba9607 | 250 | } |
36342e81 SW |
251 | } |
252 | ||
99db7d0e SW |
253 | if (tok < 8 && *cp == '\0') { |
254 | mandoc_msg(MANDOCERR_BADVAL_MISS, 0, 0, "-O %s=?", toks[tok]); | |
54ba9607 SW |
255 | return -1; |
256 | } | |
99db7d0e SW |
257 | if (tok > 8 && tok < ntoks && *cp != '\0') { |
258 | mandoc_msg(MANDOCERR_BADVAL, 0, 0, "-O %s=%s", toks[tok], cp); | |
54ba9607 SW |
259 | return -1; |
260 | } | |
261 | ||
262 | switch (tok) { | |
263 | case 0: | |
264 | if (conf->includes != NULL) { | |
265 | oldval = mandoc_strdup(conf->includes); | |
266 | break; | |
267 | } | |
268 | conf->includes = mandoc_strdup(cp); | |
269 | return 0; | |
270 | case 1: | |
271 | if (conf->man != NULL) { | |
272 | oldval = mandoc_strdup(conf->man); | |
273 | break; | |
274 | } | |
275 | conf->man = mandoc_strdup(cp); | |
276 | return 0; | |
277 | case 2: | |
278 | if (conf->paper != NULL) { | |
279 | oldval = mandoc_strdup(conf->paper); | |
280 | break; | |
281 | } | |
282 | conf->paper = mandoc_strdup(cp); | |
283 | return 0; | |
284 | case 3: | |
285 | if (conf->style != NULL) { | |
286 | oldval = mandoc_strdup(conf->style); | |
287 | break; | |
288 | } | |
289 | conf->style = mandoc_strdup(cp); | |
290 | return 0; | |
291 | case 4: | |
292 | if (conf->indent) { | |
293 | mandoc_asprintf(&oldval, "%zu", conf->indent); | |
294 | break; | |
295 | } | |
296 | conf->indent = strtonum(cp, 0, 1000, &errstr); | |
297 | if (errstr == NULL) | |
298 | return 0; | |
99db7d0e SW |
299 | mandoc_msg(MANDOCERR_BADVAL_BAD, 0, 0, |
300 | "-O indent=%s is %s", cp, errstr); | |
54ba9607 SW |
301 | return -1; |
302 | case 5: | |
303 | if (conf->width) { | |
304 | mandoc_asprintf(&oldval, "%zu", conf->width); | |
305 | break; | |
306 | } | |
307 | conf->width = strtonum(cp, 1, 1000, &errstr); | |
308 | if (errstr == NULL) | |
309 | return 0; | |
99db7d0e SW |
310 | mandoc_msg(MANDOCERR_BADVAL_BAD, 0, 0, |
311 | "-O width=%s is %s", cp, errstr); | |
54ba9607 SW |
312 | return -1; |
313 | case 6: | |
99db7d0e SW |
314 | if (conf->outfilename != NULL) { |
315 | oldval = mandoc_strdup(conf->outfilename); | |
316 | break; | |
317 | } | |
318 | conf->outfilename = mandoc_strdup(cp); | |
319 | return 0; | |
320 | case 7: | |
321 | if (conf->tagfilename != NULL) { | |
322 | oldval = mandoc_strdup(conf->tagfilename); | |
323 | break; | |
324 | } | |
325 | conf->tagfilename = mandoc_strdup(cp); | |
326 | return 0; | |
327 | /* | |
328 | * If the index of the following token changes, | |
329 | * do not forget to adjust the range check above the switch. | |
330 | */ | |
331 | case 8: | |
54ba9607 SW |
332 | if (conf->tag != NULL) { |
333 | oldval = mandoc_strdup(conf->tag); | |
334 | break; | |
335 | } | |
336 | conf->tag = mandoc_strdup(cp); | |
337 | return 0; | |
99db7d0e | 338 | case 9: |
54ba9607 SW |
339 | conf->fragment = 1; |
340 | return 0; | |
99db7d0e | 341 | case 10: |
54ba9607 SW |
342 | conf->mdoc = 1; |
343 | return 0; | |
99db7d0e | 344 | case 11: |
54ba9607 SW |
345 | conf->noval = 1; |
346 | return 0; | |
99db7d0e | 347 | case 12: |
54ba9607 SW |
348 | conf->toc = 1; |
349 | return 0; | |
350 | default: | |
99db7d0e SW |
351 | mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-O %s", cp); |
352 | return -1; | |
353 | } | |
354 | if (fromfile) { | |
355 | free(oldval); | |
356 | return 0; | |
357 | } else { | |
358 | mandoc_msg(MANDOCERR_BADVAL_DUPE, 0, 0, | |
359 | "-O %s=%s: already set to %s", toks[tok], cp, oldval); | |
360 | free(oldval); | |
54ba9607 SW |
361 | return -1; |
362 | } | |
36342e81 | 363 | } |