manpath.c revision 307795
1307795Sbapt/* $Id: manpath.c,v 1.30 2016/05/28 13:44:13 schwarze Exp $ */ 2274880Sbapt/* 3294113Sbapt * Copyright (c) 2011, 2014, 2015 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 35294113Sbapt#if !HAVE_MANPATH 36294113Sbaptstatic void manconf_file(struct manconf *, const char *); 37294113Sbapt#endif 38275432Sbaptstatic void manpath_add(struct manpaths *, const char *, int); 39275432Sbaptstatic void manpath_parseline(struct manpaths *, char *, int); 40274880Sbapt 41294113Sbapt 42274880Sbaptvoid 43294113Sbaptmanconf_parse(struct manconf *conf, const char *file, 44274880Sbapt char *defp, char *auxp) 45274880Sbapt{ 46275432Sbapt#if HAVE_MANPATH 47274880Sbapt char cmd[(PATH_MAX * 3) + 20]; 48274880Sbapt FILE *stream; 49274880Sbapt char *buf; 50274880Sbapt size_t sz, bsz; 51274880Sbapt 52274880Sbapt strlcpy(cmd, "manpath", sizeof(cmd)); 53274880Sbapt if (file) { 54274880Sbapt strlcat(cmd, " -C ", sizeof(cmd)); 55274880Sbapt strlcat(cmd, file, sizeof(cmd)); 56274880Sbapt } 57274880Sbapt if (auxp) { 58274880Sbapt strlcat(cmd, " -m ", sizeof(cmd)); 59274880Sbapt strlcat(cmd, auxp, sizeof(cmd)); 60274880Sbapt } 61274880Sbapt if (defp) { 62274880Sbapt strlcat(cmd, " -M ", sizeof(cmd)); 63274880Sbapt strlcat(cmd, defp, sizeof(cmd)); 64274880Sbapt } 65274880Sbapt 66274880Sbapt /* Open manpath(1). Ignore errors. */ 67274880Sbapt 68274880Sbapt stream = popen(cmd, "r"); 69274880Sbapt if (NULL == stream) 70274880Sbapt return; 71274880Sbapt 72274880Sbapt buf = NULL; 73274880Sbapt bsz = 0; 74274880Sbapt 75274880Sbapt /* Read in as much output as we can. */ 76274880Sbapt 77274880Sbapt do { 78274880Sbapt buf = mandoc_realloc(buf, bsz + 1024); 79274880Sbapt sz = fread(buf + bsz, 1, 1024, stream); 80274880Sbapt bsz += sz; 81274880Sbapt } while (sz > 0); 82274880Sbapt 83274880Sbapt if ( ! ferror(stream) && feof(stream) && 84274880Sbapt bsz && '\n' == buf[bsz - 1]) { 85274880Sbapt buf[bsz - 1] = '\0'; 86294113Sbapt manpath_parseline(&conf->manpath, buf, 1); 87274880Sbapt } 88274880Sbapt 89274880Sbapt free(buf); 90274880Sbapt pclose(stream); 91274880Sbapt#else 92274880Sbapt char *insert; 93274880Sbapt 94274880Sbapt /* Always prepend -m. */ 95294113Sbapt manpath_parseline(&conf->manpath, auxp, 1); 96274880Sbapt 97274880Sbapt /* If -M is given, it overrides everything else. */ 98274880Sbapt if (NULL != defp) { 99294113Sbapt manpath_parseline(&conf->manpath, defp, 1); 100274880Sbapt return; 101274880Sbapt } 102274880Sbapt 103274880Sbapt /* MANPATH and man.conf(5) cooperate. */ 104274880Sbapt defp = getenv("MANPATH"); 105274880Sbapt if (NULL == file) 106274880Sbapt file = MAN_CONF_FILE; 107274880Sbapt 108274880Sbapt /* No MANPATH; use man.conf(5) only. */ 109274880Sbapt if (NULL == defp || '\0' == defp[0]) { 110294113Sbapt manconf_file(conf, file); 111274880Sbapt return; 112274880Sbapt } 113274880Sbapt 114274880Sbapt /* Prepend man.conf(5) to MANPATH. */ 115274880Sbapt if (':' == defp[0]) { 116294113Sbapt manconf_file(conf, file); 117294113Sbapt manpath_parseline(&conf->manpath, defp, 0); 118274880Sbapt return; 119274880Sbapt } 120274880Sbapt 121274880Sbapt /* Append man.conf(5) to MANPATH. */ 122274880Sbapt if (':' == defp[strlen(defp) - 1]) { 123294113Sbapt manpath_parseline(&conf->manpath, defp, 0); 124294113Sbapt manconf_file(conf, file); 125274880Sbapt return; 126274880Sbapt } 127274880Sbapt 128274880Sbapt /* Insert man.conf(5) into MANPATH. */ 129274880Sbapt insert = strstr(defp, "::"); 130274880Sbapt if (NULL != insert) { 131274880Sbapt *insert++ = '\0'; 132294113Sbapt manpath_parseline(&conf->manpath, defp, 0); 133294113Sbapt manconf_file(conf, file); 134294113Sbapt manpath_parseline(&conf->manpath, insert + 1, 0); 135274880Sbapt return; 136274880Sbapt } 137274880Sbapt 138274880Sbapt /* MANPATH overrides man.conf(5) completely. */ 139294113Sbapt manpath_parseline(&conf->manpath, defp, 0); 140274880Sbapt#endif 141274880Sbapt} 142274880Sbapt 143274880Sbapt/* 144274880Sbapt * Parse a FULL pathname from a colon-separated list of arrays. 145274880Sbapt */ 146274880Sbaptstatic void 147275432Sbaptmanpath_parseline(struct manpaths *dirs, char *path, int complain) 148274880Sbapt{ 149274880Sbapt char *dir; 150274880Sbapt 151274880Sbapt if (NULL == path) 152274880Sbapt return; 153274880Sbapt 154274880Sbapt for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) 155275432Sbapt manpath_add(dirs, dir, complain); 156274880Sbapt} 157274880Sbapt 158274880Sbapt/* 159274880Sbapt * Add a directory to the array, ignoring bad directories. 160274880Sbapt * Grow the array one-by-one for simplicity's sake. 161274880Sbapt */ 162274880Sbaptstatic void 163275432Sbaptmanpath_add(struct manpaths *dirs, const char *dir, int complain) 164274880Sbapt{ 165274880Sbapt char buf[PATH_MAX]; 166275432Sbapt struct stat sb; 167274880Sbapt char *cp; 168274880Sbapt size_t i; 169274880Sbapt 170275432Sbapt if (NULL == (cp = realpath(dir, buf))) { 171294113Sbapt if (complain) 172294113Sbapt warn("manpath: %s", dir); 173274880Sbapt return; 174275432Sbapt } 175274880Sbapt 176274880Sbapt for (i = 0; i < dirs->sz; i++) 177274880Sbapt if (0 == strcmp(dirs->paths[i], dir)) 178274880Sbapt return; 179274880Sbapt 180275432Sbapt if (stat(cp, &sb) == -1) { 181294113Sbapt if (complain) 182294113Sbapt warn("manpath: %s", dir); 183275432Sbapt return; 184275432Sbapt } 185275432Sbapt 186274880Sbapt dirs->paths = mandoc_reallocarray(dirs->paths, 187274880Sbapt dirs->sz + 1, sizeof(char *)); 188274880Sbapt 189274880Sbapt dirs->paths[dirs->sz++] = mandoc_strdup(cp); 190274880Sbapt} 191274880Sbapt 192274880Sbaptvoid 193294113Sbaptmanconf_free(struct manconf *conf) 194274880Sbapt{ 195274880Sbapt size_t i; 196274880Sbapt 197294113Sbapt for (i = 0; i < conf->manpath.sz; i++) 198294113Sbapt free(conf->manpath.paths[i]); 199274880Sbapt 200294113Sbapt free(conf->manpath.paths); 201294113Sbapt free(conf->output.includes); 202294113Sbapt free(conf->output.man); 203294113Sbapt free(conf->output.paper); 204294113Sbapt free(conf->output.style); 205274880Sbapt} 206274880Sbapt 207294113Sbapt#if !HAVE_MANPATH 208294113Sbaptstatic void 209294113Sbaptmanconf_file(struct manconf *conf, const char *file) 210274880Sbapt{ 211294113Sbapt const char *const toks[] = { "manpath", "output", "_whatdb" }; 212294113Sbapt char manpath_default[] = MANPATH_DEFAULT; 213294113Sbapt 214274880Sbapt FILE *stream; 215294113Sbapt char *line, *cp, *ep; 216294113Sbapt size_t linesz, tok, toklen; 217294113Sbapt ssize_t linelen; 218274880Sbapt 219294113Sbapt if ((stream = fopen(file, "r")) == NULL) 220294113Sbapt goto out; 221274880Sbapt 222294113Sbapt line = NULL; 223294113Sbapt linesz = 0; 224274880Sbapt 225294113Sbapt while ((linelen = getline(&line, &linesz, stream)) != -1) { 226294113Sbapt cp = line; 227307795Sbapt ep = cp + linelen - 1; 228307795Sbapt while (ep > cp && isspace((unsigned char)*ep)) 229307795Sbapt *ep-- = '\0'; 230294113Sbapt while (isspace((unsigned char)*cp)) 231294113Sbapt cp++; 232307795Sbapt if (cp == ep || *cp == '#') 233274880Sbapt continue; 234294113Sbapt 235294113Sbapt for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 236294113Sbapt toklen = strlen(toks[tok]); 237294113Sbapt if (cp + toklen < ep && 238294113Sbapt isspace((unsigned char)cp[toklen]) && 239294113Sbapt strncmp(cp, toks[tok], toklen) == 0) { 240294113Sbapt cp += toklen; 241294113Sbapt while (isspace((unsigned char)*cp)) 242294113Sbapt cp++; 243294113Sbapt break; 244294113Sbapt } 245294113Sbapt } 246294113Sbapt 247294113Sbapt switch (tok) { 248294113Sbapt case 2: /* _whatdb */ 249294113Sbapt while (ep > cp && ep[-1] != '/') 250294113Sbapt ep--; 251294113Sbapt if (ep == cp) 252294113Sbapt continue; 253294113Sbapt *ep = '\0'; 254294113Sbapt /* FALLTHROUGH */ 255294113Sbapt case 0: /* manpath */ 256294113Sbapt manpath_add(&conf->manpath, cp, 0); 257294113Sbapt *manpath_default = '\0'; 258294113Sbapt break; 259294113Sbapt case 1: /* output */ 260294113Sbapt manconf_output(&conf->output, cp); 261294113Sbapt break; 262294113Sbapt default: 263294113Sbapt break; 264294113Sbapt } 265274880Sbapt } 266294113Sbapt free(line); 267294113Sbapt fclose(stream); 268274880Sbapt 269294113Sbaptout: 270294113Sbapt if (*manpath_default != '\0') 271294113Sbapt manpath_parseline(&conf->manpath, manpath_default, 0); 272274880Sbapt} 273294113Sbapt#endif 274294113Sbapt 275294113Sbaptvoid 276294113Sbaptmanconf_output(struct manoutput *conf, const char *cp) 277294113Sbapt{ 278294113Sbapt const char *const toks[] = { 279294113Sbapt "includes", "man", "paper", "style", 280294113Sbapt "indent", "width", "fragment", "mdoc" 281294113Sbapt }; 282294113Sbapt 283294113Sbapt size_t len, tok; 284294113Sbapt 285294113Sbapt for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 286294113Sbapt len = strlen(toks[tok]); 287294113Sbapt if ( ! strncmp(cp, toks[tok], len) && 288294113Sbapt strchr(" = ", cp[len]) != NULL) { 289294113Sbapt cp += len; 290294113Sbapt if (*cp == '=') 291294113Sbapt cp++; 292294113Sbapt while (isspace((unsigned char)*cp)) 293294113Sbapt cp++; 294294113Sbapt break; 295294113Sbapt } 296294113Sbapt } 297294113Sbapt 298294113Sbapt if (tok < 6 && *cp == '\0') 299294113Sbapt return; 300294113Sbapt 301294113Sbapt switch (tok) { 302294113Sbapt case 0: 303294113Sbapt if (conf->includes == NULL) 304294113Sbapt conf->includes = mandoc_strdup(cp); 305294113Sbapt break; 306294113Sbapt case 1: 307294113Sbapt if (conf->man == NULL) 308294113Sbapt conf->man = mandoc_strdup(cp); 309294113Sbapt break; 310294113Sbapt case 2: 311294113Sbapt if (conf->paper == NULL) 312294113Sbapt conf->paper = mandoc_strdup(cp); 313294113Sbapt break; 314294113Sbapt case 3: 315294113Sbapt if (conf->style == NULL) 316294113Sbapt conf->style = mandoc_strdup(cp); 317294113Sbapt break; 318294113Sbapt case 4: 319294113Sbapt if (conf->indent == 0) 320294113Sbapt conf->indent = strtonum(cp, 0, 1000, NULL); 321294113Sbapt break; 322294113Sbapt case 5: 323294113Sbapt if (conf->width == 0) 324294113Sbapt conf->width = strtonum(cp, 58, 1000, NULL); 325294113Sbapt break; 326294113Sbapt case 6: 327294113Sbapt conf->fragment = 1; 328294113Sbapt break; 329294113Sbapt case 7: 330294113Sbapt conf->mdoc = 1; 331294113Sbapt break; 332294113Sbapt default: 333294113Sbapt break; 334294113Sbapt } 335294113Sbapt} 336