1322249Sbapt/* $Id: manpath.c,v 1.35 2017/07/01 09:47:30 schwarze Exp $ */ 2274880Sbapt/* 3322249Sbapt * Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> 4274880Sbapt * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> 5274880Sbapt * 6274880Sbapt * Permission to use, copy, modify, and distribute this software for any 7274880Sbapt * purpose with or without fee is hereby granted, provided that the above 8274880Sbapt * copyright notice and this permission notice appear in all copies. 9274880Sbapt * 10294113Sbapt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 11274880Sbapt * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12294113Sbapt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 13274880Sbapt * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14274880Sbapt * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15274880Sbapt * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16274880Sbapt * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17274880Sbapt */ 18274880Sbapt#include "config.h" 19274880Sbapt 20275432Sbapt#include <sys/types.h> 21275432Sbapt#include <sys/stat.h> 22275432Sbapt 23274880Sbapt#include <ctype.h> 24294113Sbapt#if HAVE_ERR 25294113Sbapt#include <err.h> 26294113Sbapt#endif 27274880Sbapt#include <limits.h> 28274880Sbapt#include <stdio.h> 29274880Sbapt#include <stdlib.h> 30274880Sbapt#include <string.h> 31274880Sbapt 32274880Sbapt#include "mandoc_aux.h" 33294113Sbapt#include "manconf.h" 34274880Sbapt 35294113Sbaptstatic void manconf_file(struct manconf *, const char *); 36275432Sbaptstatic void manpath_add(struct manpaths *, const char *, int); 37275432Sbaptstatic void manpath_parseline(struct manpaths *, char *, int); 38274880Sbapt 39294113Sbapt 40274880Sbaptvoid 41294113Sbaptmanconf_parse(struct manconf *conf, const char *file, 42274880Sbapt char *defp, char *auxp) 43274880Sbapt{ 44274880Sbapt char *insert; 45274880Sbapt 46274880Sbapt /* Always prepend -m. */ 47294113Sbapt manpath_parseline(&conf->manpath, auxp, 1); 48274880Sbapt 49274880Sbapt /* If -M is given, it overrides everything else. */ 50274880Sbapt if (NULL != defp) { 51294113Sbapt manpath_parseline(&conf->manpath, defp, 1); 52274880Sbapt return; 53274880Sbapt } 54274880Sbapt 55274880Sbapt /* MANPATH and man.conf(5) cooperate. */ 56274880Sbapt defp = getenv("MANPATH"); 57274880Sbapt if (NULL == file) 58274880Sbapt file = MAN_CONF_FILE; 59274880Sbapt 60274880Sbapt /* No MANPATH; use man.conf(5) only. */ 61274880Sbapt if (NULL == defp || '\0' == defp[0]) { 62294113Sbapt manconf_file(conf, file); 63274880Sbapt return; 64274880Sbapt } 65274880Sbapt 66274880Sbapt /* Prepend man.conf(5) to MANPATH. */ 67274880Sbapt if (':' == defp[0]) { 68294113Sbapt manconf_file(conf, file); 69294113Sbapt manpath_parseline(&conf->manpath, defp, 0); 70274880Sbapt return; 71274880Sbapt } 72274880Sbapt 73274880Sbapt /* Append man.conf(5) to MANPATH. */ 74274880Sbapt if (':' == defp[strlen(defp) - 1]) { 75294113Sbapt manpath_parseline(&conf->manpath, defp, 0); 76294113Sbapt manconf_file(conf, file); 77274880Sbapt return; 78274880Sbapt } 79274880Sbapt 80274880Sbapt /* Insert man.conf(5) into MANPATH. */ 81274880Sbapt insert = strstr(defp, "::"); 82274880Sbapt if (NULL != insert) { 83274880Sbapt *insert++ = '\0'; 84294113Sbapt manpath_parseline(&conf->manpath, defp, 0); 85294113Sbapt manconf_file(conf, file); 86294113Sbapt manpath_parseline(&conf->manpath, insert + 1, 0); 87274880Sbapt return; 88274880Sbapt } 89274880Sbapt 90274880Sbapt /* MANPATH overrides man.conf(5) completely. */ 91294113Sbapt manpath_parseline(&conf->manpath, defp, 0); 92274880Sbapt} 93274880Sbapt 94322249Sbaptvoid 95322249Sbaptmanpath_base(struct manpaths *dirs) 96322249Sbapt{ 97322249Sbapt char path_base[] = MANPATH_BASE; 98322249Sbapt manpath_parseline(dirs, path_base, 0); 99322249Sbapt} 100322249Sbapt 101274880Sbapt/* 102274880Sbapt * Parse a FULL pathname from a colon-separated list of arrays. 103274880Sbapt */ 104274880Sbaptstatic void 105275432Sbaptmanpath_parseline(struct manpaths *dirs, char *path, int complain) 106274880Sbapt{ 107274880Sbapt char *dir; 108274880Sbapt 109274880Sbapt if (NULL == path) 110274880Sbapt return; 111274880Sbapt 112274880Sbapt for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) 113275432Sbapt manpath_add(dirs, dir, complain); 114274880Sbapt} 115274880Sbapt 116274880Sbapt/* 117274880Sbapt * Add a directory to the array, ignoring bad directories. 118274880Sbapt * Grow the array one-by-one for simplicity's sake. 119274880Sbapt */ 120274880Sbaptstatic void 121275432Sbaptmanpath_add(struct manpaths *dirs, const char *dir, int complain) 122274880Sbapt{ 123274880Sbapt char buf[PATH_MAX]; 124275432Sbapt struct stat sb; 125274880Sbapt char *cp; 126274880Sbapt size_t i; 127274880Sbapt 128275432Sbapt if (NULL == (cp = realpath(dir, buf))) { 129294113Sbapt if (complain) 130294113Sbapt warn("manpath: %s", dir); 131274880Sbapt return; 132275432Sbapt } 133274880Sbapt 134274880Sbapt for (i = 0; i < dirs->sz; i++) 135274880Sbapt if (0 == strcmp(dirs->paths[i], dir)) 136274880Sbapt return; 137274880Sbapt 138275432Sbapt if (stat(cp, &sb) == -1) { 139294113Sbapt if (complain) 140294113Sbapt warn("manpath: %s", dir); 141275432Sbapt return; 142275432Sbapt } 143275432Sbapt 144274880Sbapt dirs->paths = mandoc_reallocarray(dirs->paths, 145274880Sbapt dirs->sz + 1, sizeof(char *)); 146274880Sbapt 147274880Sbapt dirs->paths[dirs->sz++] = mandoc_strdup(cp); 148274880Sbapt} 149274880Sbapt 150274880Sbaptvoid 151294113Sbaptmanconf_free(struct manconf *conf) 152274880Sbapt{ 153274880Sbapt size_t i; 154274880Sbapt 155294113Sbapt for (i = 0; i < conf->manpath.sz; i++) 156294113Sbapt free(conf->manpath.paths[i]); 157274880Sbapt 158294113Sbapt free(conf->manpath.paths); 159294113Sbapt free(conf->output.includes); 160294113Sbapt free(conf->output.man); 161294113Sbapt free(conf->output.paper); 162294113Sbapt free(conf->output.style); 163274880Sbapt} 164274880Sbapt 165294113Sbaptstatic void 166294113Sbaptmanconf_file(struct manconf *conf, const char *file) 167274880Sbapt{ 168294113Sbapt const char *const toks[] = { "manpath", "output", "_whatdb" }; 169294113Sbapt char manpath_default[] = MANPATH_DEFAULT; 170294113Sbapt 171274880Sbapt FILE *stream; 172294113Sbapt char *line, *cp, *ep; 173294113Sbapt size_t linesz, tok, toklen; 174294113Sbapt ssize_t linelen; 175274880Sbapt 176294113Sbapt if ((stream = fopen(file, "r")) == NULL) 177294113Sbapt goto out; 178274880Sbapt 179294113Sbapt line = NULL; 180294113Sbapt linesz = 0; 181274880Sbapt 182294113Sbapt while ((linelen = getline(&line, &linesz, stream)) != -1) { 183294113Sbapt cp = line; 184307795Sbapt ep = cp + linelen - 1; 185307795Sbapt while (ep > cp && isspace((unsigned char)*ep)) 186307795Sbapt *ep-- = '\0'; 187294113Sbapt while (isspace((unsigned char)*cp)) 188294113Sbapt cp++; 189307795Sbapt if (cp == ep || *cp == '#') 190274880Sbapt continue; 191294113Sbapt 192294113Sbapt for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 193294113Sbapt toklen = strlen(toks[tok]); 194294113Sbapt if (cp + toklen < ep && 195294113Sbapt isspace((unsigned char)cp[toklen]) && 196294113Sbapt strncmp(cp, toks[tok], toklen) == 0) { 197294113Sbapt cp += toklen; 198294113Sbapt while (isspace((unsigned char)*cp)) 199294113Sbapt cp++; 200294113Sbapt break; 201294113Sbapt } 202294113Sbapt } 203294113Sbapt 204294113Sbapt switch (tok) { 205294113Sbapt case 2: /* _whatdb */ 206294113Sbapt while (ep > cp && ep[-1] != '/') 207294113Sbapt ep--; 208294113Sbapt if (ep == cp) 209294113Sbapt continue; 210294113Sbapt *ep = '\0'; 211294113Sbapt /* FALLTHROUGH */ 212294113Sbapt case 0: /* manpath */ 213294113Sbapt manpath_add(&conf->manpath, cp, 0); 214294113Sbapt *manpath_default = '\0'; 215294113Sbapt break; 216294113Sbapt case 1: /* output */ 217322249Sbapt manconf_output(&conf->output, cp, 1); 218294113Sbapt break; 219294113Sbapt default: 220294113Sbapt break; 221294113Sbapt } 222274880Sbapt } 223294113Sbapt free(line); 224294113Sbapt fclose(stream); 225274880Sbapt 226294113Sbaptout: 227294113Sbapt if (*manpath_default != '\0') 228294113Sbapt manpath_parseline(&conf->manpath, manpath_default, 0); 229274880Sbapt} 230294113Sbapt 231322249Sbaptint 232322249Sbaptmanconf_output(struct manoutput *conf, const char *cp, int fromfile) 233294113Sbapt{ 234294113Sbapt const char *const toks[] = { 235294113Sbapt "includes", "man", "paper", "style", 236322249Sbapt "indent", "width", "fragment", "mdoc", "noval" 237294113Sbapt }; 238294113Sbapt 239322249Sbapt const char *errstr; 240322249Sbapt char *oldval; 241322249Sbapt size_t len, tok; 242294113Sbapt 243294113Sbapt for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 244294113Sbapt len = strlen(toks[tok]); 245294113Sbapt if ( ! strncmp(cp, toks[tok], len) && 246294113Sbapt strchr(" = ", cp[len]) != NULL) { 247294113Sbapt cp += len; 248294113Sbapt if (*cp == '=') 249294113Sbapt cp++; 250294113Sbapt while (isspace((unsigned char)*cp)) 251294113Sbapt cp++; 252294113Sbapt break; 253294113Sbapt } 254294113Sbapt } 255294113Sbapt 256322249Sbapt if (tok < 6 && *cp == '\0') { 257322249Sbapt warnx("-O %s=?: Missing argument value", toks[tok]); 258322249Sbapt return -1; 259322249Sbapt } 260322249Sbapt if ((tok == 6 || tok == 7) && *cp != '\0') { 261322249Sbapt warnx("-O %s: Does not take a value: %s", toks[tok], cp); 262322249Sbapt return -1; 263322249Sbapt } 264294113Sbapt 265294113Sbapt switch (tok) { 266294113Sbapt case 0: 267322249Sbapt if (conf->includes != NULL) { 268322249Sbapt oldval = mandoc_strdup(conf->includes); 269322249Sbapt break; 270322249Sbapt } 271322249Sbapt conf->includes = mandoc_strdup(cp); 272322249Sbapt return 0; 273294113Sbapt case 1: 274322249Sbapt if (conf->man != NULL) { 275322249Sbapt oldval = mandoc_strdup(conf->man); 276322249Sbapt break; 277322249Sbapt } 278322249Sbapt conf->man = mandoc_strdup(cp); 279322249Sbapt return 0; 280294113Sbapt case 2: 281322249Sbapt if (conf->paper != NULL) { 282322249Sbapt oldval = mandoc_strdup(conf->paper); 283322249Sbapt break; 284322249Sbapt } 285322249Sbapt conf->paper = mandoc_strdup(cp); 286322249Sbapt return 0; 287294113Sbapt case 3: 288322249Sbapt if (conf->style != NULL) { 289322249Sbapt oldval = mandoc_strdup(conf->style); 290322249Sbapt break; 291322249Sbapt } 292322249Sbapt conf->style = mandoc_strdup(cp); 293322249Sbapt return 0; 294294113Sbapt case 4: 295322249Sbapt if (conf->indent) { 296322249Sbapt mandoc_asprintf(&oldval, "%zu", conf->indent); 297322249Sbapt break; 298322249Sbapt } 299322249Sbapt conf->indent = strtonum(cp, 0, 1000, &errstr); 300322249Sbapt if (errstr == NULL) 301322249Sbapt return 0; 302322249Sbapt warnx("-O indent=%s is %s", cp, errstr); 303322249Sbapt return -1; 304294113Sbapt case 5: 305322249Sbapt if (conf->width) { 306322249Sbapt mandoc_asprintf(&oldval, "%zu", conf->width); 307322249Sbapt break; 308322249Sbapt } 309322249Sbapt conf->width = strtonum(cp, 1, 1000, &errstr); 310322249Sbapt if (errstr == NULL) 311322249Sbapt return 0; 312322249Sbapt warnx("-O width=%s is %s", cp, errstr); 313322249Sbapt return -1; 314294113Sbapt case 6: 315294113Sbapt conf->fragment = 1; 316322249Sbapt return 0; 317294113Sbapt case 7: 318294113Sbapt conf->mdoc = 1; 319322249Sbapt return 0; 320322249Sbapt case 8: 321322249Sbapt conf->noval = 1; 322322249Sbapt return 0; 323294113Sbapt default: 324322249Sbapt if (fromfile) 325322249Sbapt warnx("-O %s: Bad argument", cp); 326322249Sbapt return -1; 327294113Sbapt } 328322249Sbapt if (fromfile == 0) 329322249Sbapt warnx("-O %s=%s: Option already set to %s", 330322249Sbapt toks[tok], cp, oldval); 331322249Sbapt free(oldval); 332322249Sbapt return -1; 333294113Sbapt} 334