1*3327fa00Sschwarze /* $OpenBSD: manpath.c,v 1.23 2018/10/02 14:56:36 schwarze Exp $ */ 227255502Sschwarze /* 3*3327fa00Sschwarze * Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> 427255502Sschwarze * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> 527255502Sschwarze * 627255502Sschwarze * Permission to use, copy, modify, and distribute this software for any 727255502Sschwarze * purpose with or without fee is hereby granted, provided that the above 827255502Sschwarze * copyright notice and this permission notice appear in all copies. 927255502Sschwarze * 10d05e3f9eSschwarze * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1127255502Sschwarze * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12d05e3f9eSschwarze * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 1327255502Sschwarze * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1427255502Sschwarze * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1527255502Sschwarze * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1627255502Sschwarze * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1727255502Sschwarze */ 18a2e5c5f4Sschwarze #include <sys/types.h> 19a2e5c5f4Sschwarze #include <sys/stat.h> 2027255502Sschwarze 2127255502Sschwarze #include <ctype.h> 22eba1598bSschwarze #include <err.h> 2327255502Sschwarze #include <limits.h> 2427255502Sschwarze #include <stdio.h> 2527255502Sschwarze #include <stdlib.h> 2627255502Sschwarze #include <string.h> 2727255502Sschwarze 284f4f7972Sschwarze #include "mandoc_aux.h" 294de77decSschwarze #include "manconf.h" 3027255502Sschwarze 3127255502Sschwarze #define MAN_CONF_FILE "/etc/man.conf" 3219b6bef7Sschwarze #define MANPATH_BASE "/usr/share/man:/usr/X11R6/man" 33263f9f36Sschwarze #define MANPATH_DEFAULT "/usr/share/man:/usr/X11R6/man:/usr/local/man" 3427255502Sschwarze 354de77decSschwarze static void manconf_file(struct manconf *, const char *); 36a2e5c5f4Sschwarze static void manpath_add(struct manpaths *, const char *, int); 37a2e5c5f4Sschwarze static void manpath_parseline(struct manpaths *, char *, int); 3827255502Sschwarze 394de77decSschwarze 4027255502Sschwarze void 414de77decSschwarze manconf_parse(struct manconf *conf, const char *file, 4224547daeSschwarze char *defp, char *auxp) 4327255502Sschwarze { 440f34e0a0Sschwarze char *insert; 4527255502Sschwarze 460f34e0a0Sschwarze /* Always prepend -m. */ 474de77decSschwarze manpath_parseline(&conf->manpath, auxp, 1); 4827255502Sschwarze 490f34e0a0Sschwarze /* If -M is given, it overrides everything else. */ 500f34e0a0Sschwarze if (NULL != defp) { 514de77decSschwarze manpath_parseline(&conf->manpath, defp, 1); 520f34e0a0Sschwarze return; 530f34e0a0Sschwarze } 5427255502Sschwarze 550f34e0a0Sschwarze /* MANPATH and man.conf(5) cooperate. */ 560f34e0a0Sschwarze defp = getenv("MANPATH"); 570f34e0a0Sschwarze if (NULL == file) 580f34e0a0Sschwarze file = MAN_CONF_FILE; 590f34e0a0Sschwarze 600f34e0a0Sschwarze /* No MANPATH; use man.conf(5) only. */ 610f34e0a0Sschwarze if (NULL == defp || '\0' == defp[0]) { 624de77decSschwarze manconf_file(conf, file); 630f34e0a0Sschwarze return; 640f34e0a0Sschwarze } 650f34e0a0Sschwarze 660f34e0a0Sschwarze /* Prepend man.conf(5) to MANPATH. */ 670f34e0a0Sschwarze if (':' == defp[0]) { 684de77decSschwarze manconf_file(conf, file); 694de77decSschwarze manpath_parseline(&conf->manpath, defp, 0); 700f34e0a0Sschwarze return; 710f34e0a0Sschwarze } 720f34e0a0Sschwarze 730f34e0a0Sschwarze /* Append man.conf(5) to MANPATH. */ 743db9e7afSschwarze if (':' == defp[strlen(defp) - 1]) { 754de77decSschwarze manpath_parseline(&conf->manpath, defp, 0); 764de77decSschwarze manconf_file(conf, file); 770f34e0a0Sschwarze return; 780f34e0a0Sschwarze } 790f34e0a0Sschwarze 800f34e0a0Sschwarze /* Insert man.conf(5) into MANPATH. */ 810f34e0a0Sschwarze insert = strstr(defp, "::"); 820f34e0a0Sschwarze if (NULL != insert) { 830f34e0a0Sschwarze *insert++ = '\0'; 844de77decSschwarze manpath_parseline(&conf->manpath, defp, 0); 854de77decSschwarze manconf_file(conf, file); 864de77decSschwarze manpath_parseline(&conf->manpath, insert + 1, 0); 870f34e0a0Sschwarze return; 880f34e0a0Sschwarze } 890f34e0a0Sschwarze 900f34e0a0Sschwarze /* MANPATH overrides man.conf(5) completely. */ 914de77decSschwarze manpath_parseline(&conf->manpath, defp, 0); 9227255502Sschwarze } 9327255502Sschwarze 9419b6bef7Sschwarze void 9519b6bef7Sschwarze manpath_base(struct manpaths *dirs) 9619b6bef7Sschwarze { 9719b6bef7Sschwarze char path_base[] = MANPATH_BASE; 9819b6bef7Sschwarze manpath_parseline(dirs, path_base, 0); 9919b6bef7Sschwarze } 10019b6bef7Sschwarze 10127255502Sschwarze /* 10227255502Sschwarze * Parse a FULL pathname from a colon-separated list of arrays. 10327255502Sschwarze */ 10418eee2d9Sschwarze static void 105a2e5c5f4Sschwarze manpath_parseline(struct manpaths *dirs, char *path, int complain) 10627255502Sschwarze { 10727255502Sschwarze char *dir; 10827255502Sschwarze 10927255502Sschwarze if (NULL == path) 11027255502Sschwarze return; 11127255502Sschwarze 11227255502Sschwarze for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) 113a2e5c5f4Sschwarze manpath_add(dirs, dir, complain); 11427255502Sschwarze } 11527255502Sschwarze 11627255502Sschwarze /* 11727255502Sschwarze * Add a directory to the array, ignoring bad directories. 11827255502Sschwarze * Grow the array one-by-one for simplicity's sake. 11927255502Sschwarze */ 12027255502Sschwarze static void 121a2e5c5f4Sschwarze manpath_add(struct manpaths *dirs, const char *dir, int complain) 12227255502Sschwarze { 12327255502Sschwarze char buf[PATH_MAX]; 124a2e5c5f4Sschwarze struct stat sb; 12527255502Sschwarze char *cp; 1263db9e7afSschwarze size_t i; 12727255502Sschwarze 128a2e5c5f4Sschwarze if (NULL == (cp = realpath(dir, buf))) { 129eba1598bSschwarze if (complain) 130eba1598bSschwarze warn("manpath: %s", dir); 13127255502Sschwarze return; 132a2e5c5f4Sschwarze } 13327255502Sschwarze 13427255502Sschwarze for (i = 0; i < dirs->sz; i++) 13527255502Sschwarze if (0 == strcmp(dirs->paths[i], dir)) 13627255502Sschwarze return; 13727255502Sschwarze 138a2e5c5f4Sschwarze if (stat(cp, &sb) == -1) { 139eba1598bSschwarze if (complain) 140eba1598bSschwarze warn("manpath: %s", dir); 141a2e5c5f4Sschwarze return; 142a2e5c5f4Sschwarze } 143a2e5c5f4Sschwarze 1448286bf36Sschwarze dirs->paths = mandoc_reallocarray(dirs->paths, 1458286bf36Sschwarze dirs->sz + 1, sizeof(char *)); 14627255502Sschwarze 14727255502Sschwarze dirs->paths[dirs->sz++] = mandoc_strdup(cp); 14827255502Sschwarze } 14927255502Sschwarze 15027255502Sschwarze void 1514de77decSschwarze manconf_free(struct manconf *conf) 15227255502Sschwarze { 1533db9e7afSschwarze size_t i; 15427255502Sschwarze 1554de77decSschwarze for (i = 0; i < conf->manpath.sz; i++) 1564de77decSschwarze free(conf->manpath.paths[i]); 15727255502Sschwarze 1584de77decSschwarze free(conf->manpath.paths); 1594de77decSschwarze free(conf->output.includes); 1604de77decSschwarze free(conf->output.man); 1614de77decSschwarze free(conf->output.paper); 1624de77decSschwarze free(conf->output.style); 16327255502Sschwarze } 16427255502Sschwarze 1654de77decSschwarze static void 1664de77decSschwarze manconf_file(struct manconf *conf, const char *file) 16727255502Sschwarze { 1684de77decSschwarze const char *const toks[] = { "manpath", "output", "_whatdb" }; 169ef3112a4Sschwarze char manpath_default[] = MANPATH_DEFAULT; 170d05e3f9eSschwarze 17127255502Sschwarze FILE *stream; 17231f93c25Sschwarze char *line, *cp, *ep; 17331f93c25Sschwarze size_t linesz, tok, toklen; 17431f93c25Sschwarze ssize_t linelen; 17527255502Sschwarze 176d05e3f9eSschwarze if ((stream = fopen(file, "r")) == NULL) 177ef3112a4Sschwarze goto out; 17827255502Sschwarze 17931f93c25Sschwarze line = NULL; 18031f93c25Sschwarze linesz = 0; 18131f93c25Sschwarze 18231f93c25Sschwarze while ((linelen = getline(&line, &linesz, stream)) != -1) { 18331f93c25Sschwarze cp = line; 184eff00e48Smillert ep = cp + linelen - 1; 185eff00e48Smillert while (ep > cp && isspace((unsigned char)*ep)) 186eff00e48Smillert *ep-- = '\0'; 187d05e3f9eSschwarze while (isspace((unsigned char)*cp)) 188d05e3f9eSschwarze cp++; 189eff00e48Smillert if (cp == ep || *cp == '#') 19027255502Sschwarze continue; 191d05e3f9eSschwarze 192d05e3f9eSschwarze for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 19331f93c25Sschwarze toklen = strlen(toks[tok]); 19431f93c25Sschwarze if (cp + toklen < ep && 19531f93c25Sschwarze isspace((unsigned char)cp[toklen]) && 19631f93c25Sschwarze strncmp(cp, toks[tok], toklen) == 0) { 19731f93c25Sschwarze cp += toklen; 198d05e3f9eSschwarze while (isspace((unsigned char)*cp)) 199d05e3f9eSschwarze cp++; 200d05e3f9eSschwarze break; 201d05e3f9eSschwarze } 202d05e3f9eSschwarze } 203d05e3f9eSschwarze 204d05e3f9eSschwarze switch (tok) { 2054de77decSschwarze case 2: /* _whatdb */ 206d05e3f9eSschwarze while (ep > cp && ep[-1] != '/') 207d05e3f9eSschwarze ep--; 208d05e3f9eSschwarze if (ep == cp) 20927255502Sschwarze continue; 210d05e3f9eSschwarze *ep = '\0'; 211d05e3f9eSschwarze /* FALLTHROUGH */ 212d05e3f9eSschwarze case 0: /* manpath */ 2134de77decSschwarze manpath_add(&conf->manpath, cp, 0); 214ef3112a4Sschwarze *manpath_default = '\0'; 2154de77decSschwarze break; 2164de77decSschwarze case 1: /* output */ 2174b502eb4Sschwarze manconf_output(&conf->output, cp, 1); 218d05e3f9eSschwarze break; 219d05e3f9eSschwarze default: 220d05e3f9eSschwarze break; 221d05e3f9eSschwarze } 22227255502Sschwarze } 22331f93c25Sschwarze free(line); 22427255502Sschwarze fclose(stream); 225ef3112a4Sschwarze 226ef3112a4Sschwarze out: 227ef3112a4Sschwarze if (*manpath_default != '\0') 228ef3112a4Sschwarze manpath_parseline(&conf->manpath, manpath_default, 0); 22927255502Sschwarze } 2304de77decSschwarze 2314b502eb4Sschwarze int 2324b502eb4Sschwarze manconf_output(struct manoutput *conf, const char *cp, int fromfile) 2334de77decSschwarze { 2344de77decSschwarze const char *const toks[] = { 2354de77decSschwarze "includes", "man", "paper", "style", 236*3327fa00Sschwarze "indent", "width", "fragment", "mdoc", "noval", "toc" 2374de77decSschwarze }; 2384de77decSschwarze 2394b502eb4Sschwarze const char *errstr; 2404b502eb4Sschwarze char *oldval; 2414de77decSschwarze size_t len, tok; 2424de77decSschwarze 2434de77decSschwarze for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 2444de77decSschwarze len = strlen(toks[tok]); 2454de77decSschwarze if ( ! strncmp(cp, toks[tok], len) && 2464de77decSschwarze strchr(" = ", cp[len]) != NULL) { 2474de77decSschwarze cp += len; 2484de77decSschwarze if (*cp == '=') 2494de77decSschwarze cp++; 2504de77decSschwarze while (isspace((unsigned char)*cp)) 2514de77decSschwarze cp++; 2524de77decSschwarze break; 2534de77decSschwarze } 2544de77decSschwarze } 2554de77decSschwarze 2564b502eb4Sschwarze if (tok < 6 && *cp == '\0') { 2574b502eb4Sschwarze warnx("-O %s=?: Missing argument value", toks[tok]); 2584b502eb4Sschwarze return -1; 2594b502eb4Sschwarze } 2604b502eb4Sschwarze if ((tok == 6 || tok == 7) && *cp != '\0') { 2614b502eb4Sschwarze warnx("-O %s: Does not take a value: %s", toks[tok], cp); 2624b502eb4Sschwarze return -1; 2634b502eb4Sschwarze } 2644de77decSschwarze 2654de77decSschwarze switch (tok) { 2664de77decSschwarze case 0: 2674b502eb4Sschwarze if (conf->includes != NULL) { 2684b502eb4Sschwarze oldval = mandoc_strdup(conf->includes); 2694de77decSschwarze break; 2704de77decSschwarze } 2714b502eb4Sschwarze conf->includes = mandoc_strdup(cp); 2724b502eb4Sschwarze return 0; 2734b502eb4Sschwarze case 1: 2744b502eb4Sschwarze if (conf->man != NULL) { 2754b502eb4Sschwarze oldval = mandoc_strdup(conf->man); 2764b502eb4Sschwarze break; 2774b502eb4Sschwarze } 2784b502eb4Sschwarze conf->man = mandoc_strdup(cp); 2794b502eb4Sschwarze return 0; 2804b502eb4Sschwarze case 2: 2814b502eb4Sschwarze if (conf->paper != NULL) { 2824b502eb4Sschwarze oldval = mandoc_strdup(conf->paper); 2834b502eb4Sschwarze break; 2844b502eb4Sschwarze } 2854b502eb4Sschwarze conf->paper = mandoc_strdup(cp); 2864b502eb4Sschwarze return 0; 2874b502eb4Sschwarze case 3: 2884b502eb4Sschwarze if (conf->style != NULL) { 2894b502eb4Sschwarze oldval = mandoc_strdup(conf->style); 2904b502eb4Sschwarze break; 2914b502eb4Sschwarze } 2924b502eb4Sschwarze conf->style = mandoc_strdup(cp); 2934b502eb4Sschwarze return 0; 2944b502eb4Sschwarze case 4: 2954b502eb4Sschwarze if (conf->indent) { 2964b502eb4Sschwarze mandoc_asprintf(&oldval, "%zu", conf->indent); 2974b502eb4Sschwarze break; 2984b502eb4Sschwarze } 2994b502eb4Sschwarze conf->indent = strtonum(cp, 0, 1000, &errstr); 3004b502eb4Sschwarze if (errstr == NULL) 3014b502eb4Sschwarze return 0; 3024b502eb4Sschwarze warnx("-O indent=%s is %s", cp, errstr); 3034b502eb4Sschwarze return -1; 3044b502eb4Sschwarze case 5: 3054b502eb4Sschwarze if (conf->width) { 3064b502eb4Sschwarze mandoc_asprintf(&oldval, "%zu", conf->width); 3074b502eb4Sschwarze break; 3084b502eb4Sschwarze } 3093d83c79fSschwarze conf->width = strtonum(cp, 1, 1000, &errstr); 3104b502eb4Sschwarze if (errstr == NULL) 3114b502eb4Sschwarze return 0; 3124b502eb4Sschwarze warnx("-O width=%s is %s", cp, errstr); 3134b502eb4Sschwarze return -1; 3144b502eb4Sschwarze case 6: 3154b502eb4Sschwarze conf->fragment = 1; 3164b502eb4Sschwarze return 0; 3174b502eb4Sschwarze case 7: 3184b502eb4Sschwarze conf->mdoc = 1; 3194b502eb4Sschwarze return 0; 320385121c9Sschwarze case 8: 321385121c9Sschwarze conf->noval = 1; 322385121c9Sschwarze return 0; 323*3327fa00Sschwarze case 9: 324*3327fa00Sschwarze conf->toc = 1; 325*3327fa00Sschwarze return 0; 3264b502eb4Sschwarze default: 3274b502eb4Sschwarze if (fromfile) 3284b502eb4Sschwarze warnx("-O %s: Bad argument", cp); 3294b502eb4Sschwarze return -1; 3304b502eb4Sschwarze } 3314b502eb4Sschwarze if (fromfile == 0) 3324b502eb4Sschwarze warnx("-O %s=%s: Option already set to %s", 3334b502eb4Sschwarze toks[tok], cp, oldval); 3344b502eb4Sschwarze free(oldval); 3354b502eb4Sschwarze return -1; 3364de77decSschwarze } 337