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