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