1*61d06d6bSBaptiste Daroussin /* $Id: manpath.c,v 1.35 2017/07/01 09:47:30 schwarze Exp $ */ 2*61d06d6bSBaptiste Daroussin /* 3*61d06d6bSBaptiste Daroussin * Copyright (c) 2011, 2014, 2015, 2017 Ingo Schwarze <schwarze@openbsd.org> 4*61d06d6bSBaptiste Daroussin * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> 5*61d06d6bSBaptiste Daroussin * 6*61d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 7*61d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 8*61d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 9*61d06d6bSBaptiste Daroussin * 10*61d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 11*61d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12*61d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 13*61d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14*61d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15*61d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16*61d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17*61d06d6bSBaptiste Daroussin */ 18*61d06d6bSBaptiste Daroussin #include "config.h" 19*61d06d6bSBaptiste Daroussin 20*61d06d6bSBaptiste Daroussin #include <sys/types.h> 21*61d06d6bSBaptiste Daroussin #include <sys/stat.h> 22*61d06d6bSBaptiste Daroussin 23*61d06d6bSBaptiste Daroussin #include <ctype.h> 24*61d06d6bSBaptiste Daroussin #if HAVE_ERR 25*61d06d6bSBaptiste Daroussin #include <err.h> 26*61d06d6bSBaptiste Daroussin #endif 27*61d06d6bSBaptiste Daroussin #include <limits.h> 28*61d06d6bSBaptiste Daroussin #include <stdio.h> 29*61d06d6bSBaptiste Daroussin #include <stdlib.h> 30*61d06d6bSBaptiste Daroussin #include <string.h> 31*61d06d6bSBaptiste Daroussin 32*61d06d6bSBaptiste Daroussin #include "mandoc_aux.h" 33*61d06d6bSBaptiste Daroussin #include "manconf.h" 34*61d06d6bSBaptiste Daroussin 35*61d06d6bSBaptiste Daroussin static void manconf_file(struct manconf *, const char *); 36*61d06d6bSBaptiste Daroussin static void manpath_add(struct manpaths *, const char *, int); 37*61d06d6bSBaptiste Daroussin static void manpath_parseline(struct manpaths *, char *, int); 38*61d06d6bSBaptiste Daroussin 39*61d06d6bSBaptiste Daroussin 40*61d06d6bSBaptiste Daroussin void 41*61d06d6bSBaptiste Daroussin manconf_parse(struct manconf *conf, const char *file, 42*61d06d6bSBaptiste Daroussin char *defp, char *auxp) 43*61d06d6bSBaptiste Daroussin { 44*61d06d6bSBaptiste Daroussin char *insert; 45*61d06d6bSBaptiste Daroussin 46*61d06d6bSBaptiste Daroussin /* Always prepend -m. */ 47*61d06d6bSBaptiste Daroussin manpath_parseline(&conf->manpath, auxp, 1); 48*61d06d6bSBaptiste Daroussin 49*61d06d6bSBaptiste Daroussin /* If -M is given, it overrides everything else. */ 50*61d06d6bSBaptiste Daroussin if (NULL != defp) { 51*61d06d6bSBaptiste Daroussin manpath_parseline(&conf->manpath, defp, 1); 52*61d06d6bSBaptiste Daroussin return; 53*61d06d6bSBaptiste Daroussin } 54*61d06d6bSBaptiste Daroussin 55*61d06d6bSBaptiste Daroussin /* MANPATH and man.conf(5) cooperate. */ 56*61d06d6bSBaptiste Daroussin defp = getenv("MANPATH"); 57*61d06d6bSBaptiste Daroussin if (NULL == file) 58*61d06d6bSBaptiste Daroussin file = MAN_CONF_FILE; 59*61d06d6bSBaptiste Daroussin 60*61d06d6bSBaptiste Daroussin /* No MANPATH; use man.conf(5) only. */ 61*61d06d6bSBaptiste Daroussin if (NULL == defp || '\0' == defp[0]) { 62*61d06d6bSBaptiste Daroussin manconf_file(conf, file); 63*61d06d6bSBaptiste Daroussin return; 64*61d06d6bSBaptiste Daroussin } 65*61d06d6bSBaptiste Daroussin 66*61d06d6bSBaptiste Daroussin /* Prepend man.conf(5) to MANPATH. */ 67*61d06d6bSBaptiste Daroussin if (':' == defp[0]) { 68*61d06d6bSBaptiste Daroussin manconf_file(conf, file); 69*61d06d6bSBaptiste Daroussin manpath_parseline(&conf->manpath, defp, 0); 70*61d06d6bSBaptiste Daroussin return; 71*61d06d6bSBaptiste Daroussin } 72*61d06d6bSBaptiste Daroussin 73*61d06d6bSBaptiste Daroussin /* Append man.conf(5) to MANPATH. */ 74*61d06d6bSBaptiste Daroussin if (':' == defp[strlen(defp) - 1]) { 75*61d06d6bSBaptiste Daroussin manpath_parseline(&conf->manpath, defp, 0); 76*61d06d6bSBaptiste Daroussin manconf_file(conf, file); 77*61d06d6bSBaptiste Daroussin return; 78*61d06d6bSBaptiste Daroussin } 79*61d06d6bSBaptiste Daroussin 80*61d06d6bSBaptiste Daroussin /* Insert man.conf(5) into MANPATH. */ 81*61d06d6bSBaptiste Daroussin insert = strstr(defp, "::"); 82*61d06d6bSBaptiste Daroussin if (NULL != insert) { 83*61d06d6bSBaptiste Daroussin *insert++ = '\0'; 84*61d06d6bSBaptiste Daroussin manpath_parseline(&conf->manpath, defp, 0); 85*61d06d6bSBaptiste Daroussin manconf_file(conf, file); 86*61d06d6bSBaptiste Daroussin manpath_parseline(&conf->manpath, insert + 1, 0); 87*61d06d6bSBaptiste Daroussin return; 88*61d06d6bSBaptiste Daroussin } 89*61d06d6bSBaptiste Daroussin 90*61d06d6bSBaptiste Daroussin /* MANPATH overrides man.conf(5) completely. */ 91*61d06d6bSBaptiste Daroussin manpath_parseline(&conf->manpath, defp, 0); 92*61d06d6bSBaptiste Daroussin } 93*61d06d6bSBaptiste Daroussin 94*61d06d6bSBaptiste Daroussin void 95*61d06d6bSBaptiste Daroussin manpath_base(struct manpaths *dirs) 96*61d06d6bSBaptiste Daroussin { 97*61d06d6bSBaptiste Daroussin char path_base[] = MANPATH_BASE; 98*61d06d6bSBaptiste Daroussin manpath_parseline(dirs, path_base, 0); 99*61d06d6bSBaptiste Daroussin } 100*61d06d6bSBaptiste Daroussin 101*61d06d6bSBaptiste Daroussin /* 102*61d06d6bSBaptiste Daroussin * Parse a FULL pathname from a colon-separated list of arrays. 103*61d06d6bSBaptiste Daroussin */ 104*61d06d6bSBaptiste Daroussin static void 105*61d06d6bSBaptiste Daroussin manpath_parseline(struct manpaths *dirs, char *path, int complain) 106*61d06d6bSBaptiste Daroussin { 107*61d06d6bSBaptiste Daroussin char *dir; 108*61d06d6bSBaptiste Daroussin 109*61d06d6bSBaptiste Daroussin if (NULL == path) 110*61d06d6bSBaptiste Daroussin return; 111*61d06d6bSBaptiste Daroussin 112*61d06d6bSBaptiste Daroussin for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) 113*61d06d6bSBaptiste Daroussin manpath_add(dirs, dir, complain); 114*61d06d6bSBaptiste Daroussin } 115*61d06d6bSBaptiste Daroussin 116*61d06d6bSBaptiste Daroussin /* 117*61d06d6bSBaptiste Daroussin * Add a directory to the array, ignoring bad directories. 118*61d06d6bSBaptiste Daroussin * Grow the array one-by-one for simplicity's sake. 119*61d06d6bSBaptiste Daroussin */ 120*61d06d6bSBaptiste Daroussin static void 121*61d06d6bSBaptiste Daroussin manpath_add(struct manpaths *dirs, const char *dir, int complain) 122*61d06d6bSBaptiste Daroussin { 123*61d06d6bSBaptiste Daroussin char buf[PATH_MAX]; 124*61d06d6bSBaptiste Daroussin struct stat sb; 125*61d06d6bSBaptiste Daroussin char *cp; 126*61d06d6bSBaptiste Daroussin size_t i; 127*61d06d6bSBaptiste Daroussin 128*61d06d6bSBaptiste Daroussin if (NULL == (cp = realpath(dir, buf))) { 129*61d06d6bSBaptiste Daroussin if (complain) 130*61d06d6bSBaptiste Daroussin warn("manpath: %s", dir); 131*61d06d6bSBaptiste Daroussin return; 132*61d06d6bSBaptiste Daroussin } 133*61d06d6bSBaptiste Daroussin 134*61d06d6bSBaptiste Daroussin for (i = 0; i < dirs->sz; i++) 135*61d06d6bSBaptiste Daroussin if (0 == strcmp(dirs->paths[i], dir)) 136*61d06d6bSBaptiste Daroussin return; 137*61d06d6bSBaptiste Daroussin 138*61d06d6bSBaptiste Daroussin if (stat(cp, &sb) == -1) { 139*61d06d6bSBaptiste Daroussin if (complain) 140*61d06d6bSBaptiste Daroussin warn("manpath: %s", dir); 141*61d06d6bSBaptiste Daroussin return; 142*61d06d6bSBaptiste Daroussin } 143*61d06d6bSBaptiste Daroussin 144*61d06d6bSBaptiste Daroussin dirs->paths = mandoc_reallocarray(dirs->paths, 145*61d06d6bSBaptiste Daroussin dirs->sz + 1, sizeof(char *)); 146*61d06d6bSBaptiste Daroussin 147*61d06d6bSBaptiste Daroussin dirs->paths[dirs->sz++] = mandoc_strdup(cp); 148*61d06d6bSBaptiste Daroussin } 149*61d06d6bSBaptiste Daroussin 150*61d06d6bSBaptiste Daroussin void 151*61d06d6bSBaptiste Daroussin manconf_free(struct manconf *conf) 152*61d06d6bSBaptiste Daroussin { 153*61d06d6bSBaptiste Daroussin size_t i; 154*61d06d6bSBaptiste Daroussin 155*61d06d6bSBaptiste Daroussin for (i = 0; i < conf->manpath.sz; i++) 156*61d06d6bSBaptiste Daroussin free(conf->manpath.paths[i]); 157*61d06d6bSBaptiste Daroussin 158*61d06d6bSBaptiste Daroussin free(conf->manpath.paths); 159*61d06d6bSBaptiste Daroussin free(conf->output.includes); 160*61d06d6bSBaptiste Daroussin free(conf->output.man); 161*61d06d6bSBaptiste Daroussin free(conf->output.paper); 162*61d06d6bSBaptiste Daroussin free(conf->output.style); 163*61d06d6bSBaptiste Daroussin } 164*61d06d6bSBaptiste Daroussin 165*61d06d6bSBaptiste Daroussin static void 166*61d06d6bSBaptiste Daroussin manconf_file(struct manconf *conf, const char *file) 167*61d06d6bSBaptiste Daroussin { 168*61d06d6bSBaptiste Daroussin const char *const toks[] = { "manpath", "output", "_whatdb" }; 169*61d06d6bSBaptiste Daroussin char manpath_default[] = MANPATH_DEFAULT; 170*61d06d6bSBaptiste Daroussin 171*61d06d6bSBaptiste Daroussin FILE *stream; 172*61d06d6bSBaptiste Daroussin char *line, *cp, *ep; 173*61d06d6bSBaptiste Daroussin size_t linesz, tok, toklen; 174*61d06d6bSBaptiste Daroussin ssize_t linelen; 175*61d06d6bSBaptiste Daroussin 176*61d06d6bSBaptiste Daroussin if ((stream = fopen(file, "r")) == NULL) 177*61d06d6bSBaptiste Daroussin goto out; 178*61d06d6bSBaptiste Daroussin 179*61d06d6bSBaptiste Daroussin line = NULL; 180*61d06d6bSBaptiste Daroussin linesz = 0; 181*61d06d6bSBaptiste Daroussin 182*61d06d6bSBaptiste Daroussin while ((linelen = getline(&line, &linesz, stream)) != -1) { 183*61d06d6bSBaptiste Daroussin cp = line; 184*61d06d6bSBaptiste Daroussin ep = cp + linelen - 1; 185*61d06d6bSBaptiste Daroussin while (ep > cp && isspace((unsigned char)*ep)) 186*61d06d6bSBaptiste Daroussin *ep-- = '\0'; 187*61d06d6bSBaptiste Daroussin while (isspace((unsigned char)*cp)) 188*61d06d6bSBaptiste Daroussin cp++; 189*61d06d6bSBaptiste Daroussin if (cp == ep || *cp == '#') 190*61d06d6bSBaptiste Daroussin continue; 191*61d06d6bSBaptiste Daroussin 192*61d06d6bSBaptiste Daroussin for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 193*61d06d6bSBaptiste Daroussin toklen = strlen(toks[tok]); 194*61d06d6bSBaptiste Daroussin if (cp + toklen < ep && 195*61d06d6bSBaptiste Daroussin isspace((unsigned char)cp[toklen]) && 196*61d06d6bSBaptiste Daroussin strncmp(cp, toks[tok], toklen) == 0) { 197*61d06d6bSBaptiste Daroussin cp += toklen; 198*61d06d6bSBaptiste Daroussin while (isspace((unsigned char)*cp)) 199*61d06d6bSBaptiste Daroussin cp++; 200*61d06d6bSBaptiste Daroussin break; 201*61d06d6bSBaptiste Daroussin } 202*61d06d6bSBaptiste Daroussin } 203*61d06d6bSBaptiste Daroussin 204*61d06d6bSBaptiste Daroussin switch (tok) { 205*61d06d6bSBaptiste Daroussin case 2: /* _whatdb */ 206*61d06d6bSBaptiste Daroussin while (ep > cp && ep[-1] != '/') 207*61d06d6bSBaptiste Daroussin ep--; 208*61d06d6bSBaptiste Daroussin if (ep == cp) 209*61d06d6bSBaptiste Daroussin continue; 210*61d06d6bSBaptiste Daroussin *ep = '\0'; 211*61d06d6bSBaptiste Daroussin /* FALLTHROUGH */ 212*61d06d6bSBaptiste Daroussin case 0: /* manpath */ 213*61d06d6bSBaptiste Daroussin manpath_add(&conf->manpath, cp, 0); 214*61d06d6bSBaptiste Daroussin *manpath_default = '\0'; 215*61d06d6bSBaptiste Daroussin break; 216*61d06d6bSBaptiste Daroussin case 1: /* output */ 217*61d06d6bSBaptiste Daroussin manconf_output(&conf->output, cp, 1); 218*61d06d6bSBaptiste Daroussin break; 219*61d06d6bSBaptiste Daroussin default: 220*61d06d6bSBaptiste Daroussin break; 221*61d06d6bSBaptiste Daroussin } 222*61d06d6bSBaptiste Daroussin } 223*61d06d6bSBaptiste Daroussin free(line); 224*61d06d6bSBaptiste Daroussin fclose(stream); 225*61d06d6bSBaptiste Daroussin 226*61d06d6bSBaptiste Daroussin out: 227*61d06d6bSBaptiste Daroussin if (*manpath_default != '\0') 228*61d06d6bSBaptiste Daroussin manpath_parseline(&conf->manpath, manpath_default, 0); 229*61d06d6bSBaptiste Daroussin } 230*61d06d6bSBaptiste Daroussin 231*61d06d6bSBaptiste Daroussin int 232*61d06d6bSBaptiste Daroussin manconf_output(struct manoutput *conf, const char *cp, int fromfile) 233*61d06d6bSBaptiste Daroussin { 234*61d06d6bSBaptiste Daroussin const char *const toks[] = { 235*61d06d6bSBaptiste Daroussin "includes", "man", "paper", "style", 236*61d06d6bSBaptiste Daroussin "indent", "width", "fragment", "mdoc", "noval" 237*61d06d6bSBaptiste Daroussin }; 238*61d06d6bSBaptiste Daroussin 239*61d06d6bSBaptiste Daroussin const char *errstr; 240*61d06d6bSBaptiste Daroussin char *oldval; 241*61d06d6bSBaptiste Daroussin size_t len, tok; 242*61d06d6bSBaptiste Daroussin 243*61d06d6bSBaptiste Daroussin for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 244*61d06d6bSBaptiste Daroussin len = strlen(toks[tok]); 245*61d06d6bSBaptiste Daroussin if ( ! strncmp(cp, toks[tok], len) && 246*61d06d6bSBaptiste Daroussin strchr(" = ", cp[len]) != NULL) { 247*61d06d6bSBaptiste Daroussin cp += len; 248*61d06d6bSBaptiste Daroussin if (*cp == '=') 249*61d06d6bSBaptiste Daroussin cp++; 250*61d06d6bSBaptiste Daroussin while (isspace((unsigned char)*cp)) 251*61d06d6bSBaptiste Daroussin cp++; 252*61d06d6bSBaptiste Daroussin break; 253*61d06d6bSBaptiste Daroussin } 254*61d06d6bSBaptiste Daroussin } 255*61d06d6bSBaptiste Daroussin 256*61d06d6bSBaptiste Daroussin if (tok < 6 && *cp == '\0') { 257*61d06d6bSBaptiste Daroussin warnx("-O %s=?: Missing argument value", toks[tok]); 258*61d06d6bSBaptiste Daroussin return -1; 259*61d06d6bSBaptiste Daroussin } 260*61d06d6bSBaptiste Daroussin if ((tok == 6 || tok == 7) && *cp != '\0') { 261*61d06d6bSBaptiste Daroussin warnx("-O %s: Does not take a value: %s", toks[tok], cp); 262*61d06d6bSBaptiste Daroussin return -1; 263*61d06d6bSBaptiste Daroussin } 264*61d06d6bSBaptiste Daroussin 265*61d06d6bSBaptiste Daroussin switch (tok) { 266*61d06d6bSBaptiste Daroussin case 0: 267*61d06d6bSBaptiste Daroussin if (conf->includes != NULL) { 268*61d06d6bSBaptiste Daroussin oldval = mandoc_strdup(conf->includes); 269*61d06d6bSBaptiste Daroussin break; 270*61d06d6bSBaptiste Daroussin } 271*61d06d6bSBaptiste Daroussin conf->includes = mandoc_strdup(cp); 272*61d06d6bSBaptiste Daroussin return 0; 273*61d06d6bSBaptiste Daroussin case 1: 274*61d06d6bSBaptiste Daroussin if (conf->man != NULL) { 275*61d06d6bSBaptiste Daroussin oldval = mandoc_strdup(conf->man); 276*61d06d6bSBaptiste Daroussin break; 277*61d06d6bSBaptiste Daroussin } 278*61d06d6bSBaptiste Daroussin conf->man = mandoc_strdup(cp); 279*61d06d6bSBaptiste Daroussin return 0; 280*61d06d6bSBaptiste Daroussin case 2: 281*61d06d6bSBaptiste Daroussin if (conf->paper != NULL) { 282*61d06d6bSBaptiste Daroussin oldval = mandoc_strdup(conf->paper); 283*61d06d6bSBaptiste Daroussin break; 284*61d06d6bSBaptiste Daroussin } 285*61d06d6bSBaptiste Daroussin conf->paper = mandoc_strdup(cp); 286*61d06d6bSBaptiste Daroussin return 0; 287*61d06d6bSBaptiste Daroussin case 3: 288*61d06d6bSBaptiste Daroussin if (conf->style != NULL) { 289*61d06d6bSBaptiste Daroussin oldval = mandoc_strdup(conf->style); 290*61d06d6bSBaptiste Daroussin break; 291*61d06d6bSBaptiste Daroussin } 292*61d06d6bSBaptiste Daroussin conf->style = mandoc_strdup(cp); 293*61d06d6bSBaptiste Daroussin return 0; 294*61d06d6bSBaptiste Daroussin case 4: 295*61d06d6bSBaptiste Daroussin if (conf->indent) { 296*61d06d6bSBaptiste Daroussin mandoc_asprintf(&oldval, "%zu", conf->indent); 297*61d06d6bSBaptiste Daroussin break; 298*61d06d6bSBaptiste Daroussin } 299*61d06d6bSBaptiste Daroussin conf->indent = strtonum(cp, 0, 1000, &errstr); 300*61d06d6bSBaptiste Daroussin if (errstr == NULL) 301*61d06d6bSBaptiste Daroussin return 0; 302*61d06d6bSBaptiste Daroussin warnx("-O indent=%s is %s", cp, errstr); 303*61d06d6bSBaptiste Daroussin return -1; 304*61d06d6bSBaptiste Daroussin case 5: 305*61d06d6bSBaptiste Daroussin if (conf->width) { 306*61d06d6bSBaptiste Daroussin mandoc_asprintf(&oldval, "%zu", conf->width); 307*61d06d6bSBaptiste Daroussin break; 308*61d06d6bSBaptiste Daroussin } 309*61d06d6bSBaptiste Daroussin conf->width = strtonum(cp, 1, 1000, &errstr); 310*61d06d6bSBaptiste Daroussin if (errstr == NULL) 311*61d06d6bSBaptiste Daroussin return 0; 312*61d06d6bSBaptiste Daroussin warnx("-O width=%s is %s", cp, errstr); 313*61d06d6bSBaptiste Daroussin return -1; 314*61d06d6bSBaptiste Daroussin case 6: 315*61d06d6bSBaptiste Daroussin conf->fragment = 1; 316*61d06d6bSBaptiste Daroussin return 0; 317*61d06d6bSBaptiste Daroussin case 7: 318*61d06d6bSBaptiste Daroussin conf->mdoc = 1; 319*61d06d6bSBaptiste Daroussin return 0; 320*61d06d6bSBaptiste Daroussin case 8: 321*61d06d6bSBaptiste Daroussin conf->noval = 1; 322*61d06d6bSBaptiste Daroussin return 0; 323*61d06d6bSBaptiste Daroussin default: 324*61d06d6bSBaptiste Daroussin if (fromfile) 325*61d06d6bSBaptiste Daroussin warnx("-O %s: Bad argument", cp); 326*61d06d6bSBaptiste Daroussin return -1; 327*61d06d6bSBaptiste Daroussin } 328*61d06d6bSBaptiste Daroussin if (fromfile == 0) 329*61d06d6bSBaptiste Daroussin warnx("-O %s=%s: Option already set to %s", 330*61d06d6bSBaptiste Daroussin toks[tok], cp, oldval); 331*61d06d6bSBaptiste Daroussin free(oldval); 332*61d06d6bSBaptiste Daroussin return -1; 333*61d06d6bSBaptiste Daroussin } 334