1*99db7d0eSSascha Wildner /* $Id: manpath.c,v 1.43 2020/08/27 14:59:47 schwarze Exp $ */
236342e81SSascha Wildner /*
3*99db7d0eSSascha Wildner * Copyright (c) 2011,2014,2015,2017-2019 Ingo Schwarze <schwarze@openbsd.org>
436342e81SSascha Wildner * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
536342e81SSascha Wildner *
636342e81SSascha Wildner * Permission to use, copy, modify, and distribute this software for any
736342e81SSascha Wildner * purpose with or without fee is hereby granted, provided that the above
836342e81SSascha Wildner * copyright notice and this permission notice appear in all copies.
936342e81SSascha Wildner *
1054ba9607SSascha Wildner * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1136342e81SSascha Wildner * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1254ba9607SSascha Wildner * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1336342e81SSascha Wildner * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1436342e81SSascha Wildner * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1536342e81SSascha Wildner * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1636342e81SSascha Wildner * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1736342e81SSascha Wildner */
1836342e81SSascha Wildner #include "config.h"
1936342e81SSascha Wildner
2054ba9607SSascha Wildner #include <sys/types.h>
2154ba9607SSascha Wildner #include <sys/stat.h>
2254ba9607SSascha Wildner
2336342e81SSascha Wildner #include <ctype.h>
24*99db7d0eSSascha Wildner #include <errno.h>
2536342e81SSascha Wildner #include <limits.h>
2636342e81SSascha Wildner #include <stdio.h>
2736342e81SSascha Wildner #include <stdlib.h>
2836342e81SSascha Wildner #include <string.h>
2936342e81SSascha Wildner
30070c62a6SFranco Fichtner #include "mandoc_aux.h"
31*99db7d0eSSascha Wildner #include "mandoc.h"
3254ba9607SSascha Wildner #include "manconf.h"
3336342e81SSascha Wildner
3454ba9607SSascha Wildner static void manconf_file(struct manconf *, const char *);
35*99db7d0eSSascha Wildner static void manpath_add(struct manpaths *, const char *, char);
36*99db7d0eSSascha Wildner static void manpath_parseline(struct manpaths *, char *, char);
3736342e81SSascha Wildner
3836342e81SSascha Wildner
3936342e81SSascha Wildner void
manconf_parse(struct manconf * conf,const char * file,char * defp,char * auxp)4054ba9607SSascha Wildner manconf_parse(struct manconf *conf, const char *file,
4136342e81SSascha Wildner char *defp, char *auxp)
4236342e81SSascha Wildner {
4336342e81SSascha Wildner char *insert;
4436342e81SSascha Wildner
4536342e81SSascha Wildner /* Always prepend -m. */
46*99db7d0eSSascha Wildner manpath_parseline(&conf->manpath, auxp, 'm');
4736342e81SSascha Wildner
4836342e81SSascha Wildner /* If -M is given, it overrides everything else. */
4936342e81SSascha Wildner if (NULL != defp) {
50*99db7d0eSSascha Wildner manpath_parseline(&conf->manpath, defp, 'M');
5136342e81SSascha Wildner return;
5236342e81SSascha Wildner }
5336342e81SSascha Wildner
5436342e81SSascha Wildner /* MANPATH and man.conf(5) cooperate. */
5536342e81SSascha Wildner defp = getenv("MANPATH");
5636342e81SSascha Wildner if (NULL == file)
5736342e81SSascha Wildner file = MAN_CONF_FILE;
5836342e81SSascha Wildner
5936342e81SSascha Wildner /* No MANPATH; use man.conf(5) only. */
6036342e81SSascha Wildner if (NULL == defp || '\0' == defp[0]) {
6154ba9607SSascha Wildner manconf_file(conf, file);
6236342e81SSascha Wildner return;
6336342e81SSascha Wildner }
6436342e81SSascha Wildner
6536342e81SSascha Wildner /* Prepend man.conf(5) to MANPATH. */
6636342e81SSascha Wildner if (':' == defp[0]) {
6754ba9607SSascha Wildner manconf_file(conf, file);
68*99db7d0eSSascha Wildner manpath_parseline(&conf->manpath, defp, '\0');
6936342e81SSascha Wildner return;
7036342e81SSascha Wildner }
7136342e81SSascha Wildner
7236342e81SSascha Wildner /* Append man.conf(5) to MANPATH. */
73f88b6c16SFranco Fichtner if (':' == defp[strlen(defp) - 1]) {
74*99db7d0eSSascha Wildner manpath_parseline(&conf->manpath, defp, '\0');
7554ba9607SSascha Wildner manconf_file(conf, file);
7636342e81SSascha Wildner return;
7736342e81SSascha Wildner }
7836342e81SSascha Wildner
7936342e81SSascha Wildner /* Insert man.conf(5) into MANPATH. */
8036342e81SSascha Wildner insert = strstr(defp, "::");
8136342e81SSascha Wildner if (NULL != insert) {
8236342e81SSascha Wildner *insert++ = '\0';
83*99db7d0eSSascha Wildner manpath_parseline(&conf->manpath, defp, '\0');
8454ba9607SSascha Wildner manconf_file(conf, file);
85*99db7d0eSSascha Wildner manpath_parseline(&conf->manpath, insert + 1, '\0');
8636342e81SSascha Wildner return;
8736342e81SSascha Wildner }
8836342e81SSascha Wildner
8936342e81SSascha Wildner /* MANPATH overrides man.conf(5) completely. */
90*99db7d0eSSascha Wildner manpath_parseline(&conf->manpath, defp, '\0');
9154ba9607SSascha Wildner }
9254ba9607SSascha Wildner
9354ba9607SSascha Wildner void
manpath_base(struct manpaths * dirs)9454ba9607SSascha Wildner manpath_base(struct manpaths *dirs)
9554ba9607SSascha Wildner {
9654ba9607SSascha Wildner char path_base[] = MANPATH_BASE;
97*99db7d0eSSascha Wildner manpath_parseline(dirs, path_base, '\0');
9836342e81SSascha Wildner }
9936342e81SSascha Wildner
10036342e81SSascha Wildner /*
10136342e81SSascha Wildner * Parse a FULL pathname from a colon-separated list of arrays.
10236342e81SSascha Wildner */
10336342e81SSascha Wildner static void
manpath_parseline(struct manpaths * dirs,char * path,char option)104*99db7d0eSSascha Wildner manpath_parseline(struct manpaths *dirs, char *path, char option)
10536342e81SSascha Wildner {
10636342e81SSascha Wildner char *dir;
10736342e81SSascha Wildner
10836342e81SSascha Wildner if (NULL == path)
10936342e81SSascha Wildner return;
11036342e81SSascha Wildner
11136342e81SSascha Wildner for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
112*99db7d0eSSascha Wildner manpath_add(dirs, dir, option);
11336342e81SSascha Wildner }
11436342e81SSascha Wildner
11536342e81SSascha Wildner /*
11636342e81SSascha Wildner * Add a directory to the array, ignoring bad directories.
11736342e81SSascha Wildner * Grow the array one-by-one for simplicity's sake.
11836342e81SSascha Wildner */
11936342e81SSascha Wildner static void
manpath_add(struct manpaths * dirs,const char * dir,char option)120*99db7d0eSSascha Wildner manpath_add(struct manpaths *dirs, const char *dir, char option)
12136342e81SSascha Wildner {
12236342e81SSascha Wildner char buf[PATH_MAX];
12354ba9607SSascha Wildner struct stat sb;
12436342e81SSascha Wildner char *cp;
125f88b6c16SFranco Fichtner size_t i;
12636342e81SSascha Wildner
127*99db7d0eSSascha Wildner if ((cp = realpath(dir, buf)) == NULL)
128*99db7d0eSSascha Wildner goto fail;
12936342e81SSascha Wildner
13036342e81SSascha Wildner for (i = 0; i < dirs->sz; i++)
131*99db7d0eSSascha Wildner if (strcmp(dirs->paths[i], dir) == 0)
13236342e81SSascha Wildner return;
13336342e81SSascha Wildner
134*99db7d0eSSascha Wildner if (stat(cp, &sb) == -1)
135*99db7d0eSSascha Wildner goto fail;
13654ba9607SSascha Wildner
137070c62a6SFranco Fichtner dirs->paths = mandoc_reallocarray(dirs->paths,
138*99db7d0eSSascha Wildner dirs->sz + 1, sizeof(*dirs->paths));
13936342e81SSascha Wildner dirs->paths[dirs->sz++] = mandoc_strdup(cp);
140*99db7d0eSSascha Wildner return;
141*99db7d0eSSascha Wildner
142*99db7d0eSSascha Wildner fail:
143*99db7d0eSSascha Wildner if (option != '\0')
144*99db7d0eSSascha Wildner mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0,
145*99db7d0eSSascha Wildner "-%c %s: %s", option, dir, strerror(errno));
14636342e81SSascha Wildner }
14736342e81SSascha Wildner
14836342e81SSascha Wildner void
manconf_free(struct manconf * conf)14954ba9607SSascha Wildner manconf_free(struct manconf *conf)
15036342e81SSascha Wildner {
151f88b6c16SFranco Fichtner size_t i;
15236342e81SSascha Wildner
15354ba9607SSascha Wildner for (i = 0; i < conf->manpath.sz; i++)
15454ba9607SSascha Wildner free(conf->manpath.paths[i]);
15536342e81SSascha Wildner
15654ba9607SSascha Wildner free(conf->manpath.paths);
15754ba9607SSascha Wildner free(conf->output.includes);
15854ba9607SSascha Wildner free(conf->output.man);
15954ba9607SSascha Wildner free(conf->output.paper);
16054ba9607SSascha Wildner free(conf->output.style);
16136342e81SSascha Wildner }
16236342e81SSascha Wildner
16354ba9607SSascha Wildner static void
manconf_file(struct manconf * conf,const char * file)16454ba9607SSascha Wildner manconf_file(struct manconf *conf, const char *file)
16536342e81SSascha Wildner {
166*99db7d0eSSascha Wildner const char *const toks[] = { "manpath", "output" };
16754ba9607SSascha Wildner char manpath_default[] = MANPATH_DEFAULT;
16854ba9607SSascha Wildner
16936342e81SSascha Wildner FILE *stream;
17054ba9607SSascha Wildner char *line, *cp, *ep;
17154ba9607SSascha Wildner size_t linesz, tok, toklen;
17254ba9607SSascha Wildner ssize_t linelen;
17336342e81SSascha Wildner
17454ba9607SSascha Wildner if ((stream = fopen(file, "r")) == NULL)
17554ba9607SSascha Wildner goto out;
17636342e81SSascha Wildner
17754ba9607SSascha Wildner line = NULL;
17854ba9607SSascha Wildner linesz = 0;
17936342e81SSascha Wildner
18054ba9607SSascha Wildner while ((linelen = getline(&line, &linesz, stream)) != -1) {
18154ba9607SSascha Wildner cp = line;
18254ba9607SSascha Wildner ep = cp + linelen - 1;
18354ba9607SSascha Wildner while (ep > cp && isspace((unsigned char)*ep))
18454ba9607SSascha Wildner *ep-- = '\0';
18554ba9607SSascha Wildner while (isspace((unsigned char)*cp))
18654ba9607SSascha Wildner cp++;
18754ba9607SSascha Wildner if (cp == ep || *cp == '#')
18854ba9607SSascha Wildner continue;
18954ba9607SSascha Wildner
19054ba9607SSascha Wildner for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
19154ba9607SSascha Wildner toklen = strlen(toks[tok]);
19254ba9607SSascha Wildner if (cp + toklen < ep &&
19354ba9607SSascha Wildner isspace((unsigned char)cp[toklen]) &&
19454ba9607SSascha Wildner strncmp(cp, toks[tok], toklen) == 0) {
19554ba9607SSascha Wildner cp += toklen;
19654ba9607SSascha Wildner while (isspace((unsigned char)*cp))
19754ba9607SSascha Wildner cp++;
19836342e81SSascha Wildner break;
19954ba9607SSascha Wildner }
20036342e81SSascha Wildner }
20136342e81SSascha Wildner
20254ba9607SSascha Wildner switch (tok) {
20354ba9607SSascha Wildner case 0: /* manpath */
204*99db7d0eSSascha Wildner manpath_add(&conf->manpath, cp, '\0');
20554ba9607SSascha Wildner *manpath_default = '\0';
20654ba9607SSascha Wildner break;
20754ba9607SSascha Wildner case 1: /* output */
20854ba9607SSascha Wildner manconf_output(&conf->output, cp, 1);
20954ba9607SSascha Wildner break;
21054ba9607SSascha Wildner default:
21154ba9607SSascha Wildner break;
21254ba9607SSascha Wildner }
21354ba9607SSascha Wildner }
21454ba9607SSascha Wildner free(line);
21536342e81SSascha Wildner fclose(stream);
21654ba9607SSascha Wildner
21754ba9607SSascha Wildner out:
21854ba9607SSascha Wildner if (*manpath_default != '\0')
219*99db7d0eSSascha Wildner manpath_parseline(&conf->manpath, manpath_default, '\0');
22054ba9607SSascha Wildner }
22154ba9607SSascha Wildner
22254ba9607SSascha Wildner int
manconf_output(struct manoutput * conf,const char * cp,int fromfile)22354ba9607SSascha Wildner manconf_output(struct manoutput *conf, const char *cp, int fromfile)
22454ba9607SSascha Wildner {
22554ba9607SSascha Wildner const char *const toks[] = {
226*99db7d0eSSascha Wildner /* Tokens requiring an argument. */
22754ba9607SSascha Wildner "includes", "man", "paper", "style", "indent", "width",
228*99db7d0eSSascha Wildner "outfilename", "tagfilename",
229*99db7d0eSSascha Wildner /* Token taking an optional argument. */
230*99db7d0eSSascha Wildner "tag",
231*99db7d0eSSascha Wildner /* Tokens not taking arguments. */
232*99db7d0eSSascha Wildner "fragment", "mdoc", "noval", "toc"
23354ba9607SSascha Wildner };
234*99db7d0eSSascha Wildner const size_t ntoks = sizeof(toks) / sizeof(toks[0]);
23554ba9607SSascha Wildner
23654ba9607SSascha Wildner const char *errstr;
23754ba9607SSascha Wildner char *oldval;
23854ba9607SSascha Wildner size_t len, tok;
23954ba9607SSascha Wildner
240*99db7d0eSSascha Wildner for (tok = 0; tok < ntoks; tok++) {
24154ba9607SSascha Wildner len = strlen(toks[tok]);
242*99db7d0eSSascha Wildner if (strncmp(cp, toks[tok], len) == 0 &&
24354ba9607SSascha Wildner strchr(" = ", cp[len]) != NULL) {
24454ba9607SSascha Wildner cp += len;
24554ba9607SSascha Wildner if (*cp == '=')
24654ba9607SSascha Wildner cp++;
24754ba9607SSascha Wildner while (isspace((unsigned char)*cp))
24854ba9607SSascha Wildner cp++;
24954ba9607SSascha Wildner break;
25054ba9607SSascha Wildner }
25154ba9607SSascha Wildner }
25254ba9607SSascha Wildner
253*99db7d0eSSascha Wildner if (tok < 8 && *cp == '\0') {
254*99db7d0eSSascha Wildner mandoc_msg(MANDOCERR_BADVAL_MISS, 0, 0, "-O %s=?", toks[tok]);
25554ba9607SSascha Wildner return -1;
25654ba9607SSascha Wildner }
257*99db7d0eSSascha Wildner if (tok > 8 && tok < ntoks && *cp != '\0') {
258*99db7d0eSSascha Wildner mandoc_msg(MANDOCERR_BADVAL, 0, 0, "-O %s=%s", toks[tok], cp);
25954ba9607SSascha Wildner return -1;
26054ba9607SSascha Wildner }
26154ba9607SSascha Wildner
26254ba9607SSascha Wildner switch (tok) {
26354ba9607SSascha Wildner case 0:
26454ba9607SSascha Wildner if (conf->includes != NULL) {
26554ba9607SSascha Wildner oldval = mandoc_strdup(conf->includes);
26654ba9607SSascha Wildner break;
26754ba9607SSascha Wildner }
26854ba9607SSascha Wildner conf->includes = mandoc_strdup(cp);
26954ba9607SSascha Wildner return 0;
27054ba9607SSascha Wildner case 1:
27154ba9607SSascha Wildner if (conf->man != NULL) {
27254ba9607SSascha Wildner oldval = mandoc_strdup(conf->man);
27354ba9607SSascha Wildner break;
27454ba9607SSascha Wildner }
27554ba9607SSascha Wildner conf->man = mandoc_strdup(cp);
27654ba9607SSascha Wildner return 0;
27754ba9607SSascha Wildner case 2:
27854ba9607SSascha Wildner if (conf->paper != NULL) {
27954ba9607SSascha Wildner oldval = mandoc_strdup(conf->paper);
28054ba9607SSascha Wildner break;
28154ba9607SSascha Wildner }
28254ba9607SSascha Wildner conf->paper = mandoc_strdup(cp);
28354ba9607SSascha Wildner return 0;
28454ba9607SSascha Wildner case 3:
28554ba9607SSascha Wildner if (conf->style != NULL) {
28654ba9607SSascha Wildner oldval = mandoc_strdup(conf->style);
28754ba9607SSascha Wildner break;
28854ba9607SSascha Wildner }
28954ba9607SSascha Wildner conf->style = mandoc_strdup(cp);
29054ba9607SSascha Wildner return 0;
29154ba9607SSascha Wildner case 4:
29254ba9607SSascha Wildner if (conf->indent) {
29354ba9607SSascha Wildner mandoc_asprintf(&oldval, "%zu", conf->indent);
29454ba9607SSascha Wildner break;
29554ba9607SSascha Wildner }
29654ba9607SSascha Wildner conf->indent = strtonum(cp, 0, 1000, &errstr);
29754ba9607SSascha Wildner if (errstr == NULL)
29854ba9607SSascha Wildner return 0;
299*99db7d0eSSascha Wildner mandoc_msg(MANDOCERR_BADVAL_BAD, 0, 0,
300*99db7d0eSSascha Wildner "-O indent=%s is %s", cp, errstr);
30154ba9607SSascha Wildner return -1;
30254ba9607SSascha Wildner case 5:
30354ba9607SSascha Wildner if (conf->width) {
30454ba9607SSascha Wildner mandoc_asprintf(&oldval, "%zu", conf->width);
30554ba9607SSascha Wildner break;
30654ba9607SSascha Wildner }
30754ba9607SSascha Wildner conf->width = strtonum(cp, 1, 1000, &errstr);
30854ba9607SSascha Wildner if (errstr == NULL)
30954ba9607SSascha Wildner return 0;
310*99db7d0eSSascha Wildner mandoc_msg(MANDOCERR_BADVAL_BAD, 0, 0,
311*99db7d0eSSascha Wildner "-O width=%s is %s", cp, errstr);
31254ba9607SSascha Wildner return -1;
31354ba9607SSascha Wildner case 6:
314*99db7d0eSSascha Wildner if (conf->outfilename != NULL) {
315*99db7d0eSSascha Wildner oldval = mandoc_strdup(conf->outfilename);
316*99db7d0eSSascha Wildner break;
317*99db7d0eSSascha Wildner }
318*99db7d0eSSascha Wildner conf->outfilename = mandoc_strdup(cp);
319*99db7d0eSSascha Wildner return 0;
320*99db7d0eSSascha Wildner case 7:
321*99db7d0eSSascha Wildner if (conf->tagfilename != NULL) {
322*99db7d0eSSascha Wildner oldval = mandoc_strdup(conf->tagfilename);
323*99db7d0eSSascha Wildner break;
324*99db7d0eSSascha Wildner }
325*99db7d0eSSascha Wildner conf->tagfilename = mandoc_strdup(cp);
326*99db7d0eSSascha Wildner return 0;
327*99db7d0eSSascha Wildner /*
328*99db7d0eSSascha Wildner * If the index of the following token changes,
329*99db7d0eSSascha Wildner * do not forget to adjust the range check above the switch.
330*99db7d0eSSascha Wildner */
331*99db7d0eSSascha Wildner case 8:
33254ba9607SSascha Wildner if (conf->tag != NULL) {
33354ba9607SSascha Wildner oldval = mandoc_strdup(conf->tag);
33454ba9607SSascha Wildner break;
33554ba9607SSascha Wildner }
33654ba9607SSascha Wildner conf->tag = mandoc_strdup(cp);
33754ba9607SSascha Wildner return 0;
338*99db7d0eSSascha Wildner case 9:
33954ba9607SSascha Wildner conf->fragment = 1;
34054ba9607SSascha Wildner return 0;
341*99db7d0eSSascha Wildner case 10:
34254ba9607SSascha Wildner conf->mdoc = 1;
34354ba9607SSascha Wildner return 0;
344*99db7d0eSSascha Wildner case 11:
34554ba9607SSascha Wildner conf->noval = 1;
34654ba9607SSascha Wildner return 0;
347*99db7d0eSSascha Wildner case 12:
34854ba9607SSascha Wildner conf->toc = 1;
34954ba9607SSascha Wildner return 0;
35054ba9607SSascha Wildner default:
351*99db7d0eSSascha Wildner mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-O %s", cp);
35254ba9607SSascha Wildner return -1;
35354ba9607SSascha Wildner }
354*99db7d0eSSascha Wildner if (fromfile) {
355*99db7d0eSSascha Wildner free(oldval);
356*99db7d0eSSascha Wildner return 0;
357*99db7d0eSSascha Wildner } else {
358*99db7d0eSSascha Wildner mandoc_msg(MANDOCERR_BADVAL_DUPE, 0, 0,
359*99db7d0eSSascha Wildner "-O %s=%s: already set to %s", toks[tok], cp, oldval);
36054ba9607SSascha Wildner free(oldval);
36154ba9607SSascha Wildner return -1;
36236342e81SSascha Wildner }
363*99db7d0eSSascha Wildner }
364