1*260e9a87SYuri Pankov /* $Id: manpath.c,v 1.19 2014/11/27 00:30:40 schwarze Exp $ */ 2*260e9a87SYuri Pankov /* 3*260e9a87SYuri Pankov * Copyright (c) 2011, 2014 Ingo Schwarze <schwarze@openbsd.org> 4*260e9a87SYuri Pankov * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> 5*260e9a87SYuri Pankov * 6*260e9a87SYuri Pankov * Permission to use, copy, modify, and distribute this software for any 7*260e9a87SYuri Pankov * purpose with or without fee is hereby granted, provided that the above 8*260e9a87SYuri Pankov * copyright notice and this permission notice appear in all copies. 9*260e9a87SYuri Pankov * 10*260e9a87SYuri Pankov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11*260e9a87SYuri Pankov * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12*260e9a87SYuri Pankov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13*260e9a87SYuri Pankov * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14*260e9a87SYuri Pankov * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15*260e9a87SYuri Pankov * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16*260e9a87SYuri Pankov * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17*260e9a87SYuri Pankov */ 18*260e9a87SYuri Pankov #include "config.h" 19*260e9a87SYuri Pankov 20*260e9a87SYuri Pankov #include <sys/types.h> 21*260e9a87SYuri Pankov #include <sys/stat.h> 22*260e9a87SYuri Pankov 23*260e9a87SYuri Pankov #include <assert.h> 24*260e9a87SYuri Pankov #include <ctype.h> 25*260e9a87SYuri Pankov #include <limits.h> 26*260e9a87SYuri Pankov #include <stdio.h> 27*260e9a87SYuri Pankov #include <stdlib.h> 28*260e9a87SYuri Pankov #include <string.h> 29*260e9a87SYuri Pankov 30*260e9a87SYuri Pankov #include "mandoc_aux.h" 31*260e9a87SYuri Pankov #include "manpath.h" 32*260e9a87SYuri Pankov 33*260e9a87SYuri Pankov #define MAN_CONF_FILE "/etc/man.conf" 34*260e9a87SYuri Pankov #define MAN_CONF_KEY "_whatdb" 35*260e9a87SYuri Pankov 36*260e9a87SYuri Pankov static void manpath_add(struct manpaths *, const char *, int); 37*260e9a87SYuri Pankov static void manpath_parseline(struct manpaths *, char *, int); 38*260e9a87SYuri Pankov 39*260e9a87SYuri Pankov void 40*260e9a87SYuri Pankov manpath_parse(struct manpaths *dirs, const char *file, 41*260e9a87SYuri Pankov char *defp, char *auxp) 42*260e9a87SYuri Pankov { 43*260e9a87SYuri Pankov #if HAVE_MANPATH 44*260e9a87SYuri Pankov char cmd[(PATH_MAX * 3) + 20]; 45*260e9a87SYuri Pankov FILE *stream; 46*260e9a87SYuri Pankov char *buf; 47*260e9a87SYuri Pankov size_t sz, bsz; 48*260e9a87SYuri Pankov 49*260e9a87SYuri Pankov strlcpy(cmd, "manpath", sizeof(cmd)); 50*260e9a87SYuri Pankov if (file) { 51*260e9a87SYuri Pankov strlcat(cmd, " -C ", sizeof(cmd)); 52*260e9a87SYuri Pankov strlcat(cmd, file, sizeof(cmd)); 53*260e9a87SYuri Pankov } 54*260e9a87SYuri Pankov if (auxp) { 55*260e9a87SYuri Pankov strlcat(cmd, " -m ", sizeof(cmd)); 56*260e9a87SYuri Pankov strlcat(cmd, auxp, sizeof(cmd)); 57*260e9a87SYuri Pankov } 58*260e9a87SYuri Pankov if (defp) { 59*260e9a87SYuri Pankov strlcat(cmd, " -M ", sizeof(cmd)); 60*260e9a87SYuri Pankov strlcat(cmd, defp, sizeof(cmd)); 61*260e9a87SYuri Pankov } 62*260e9a87SYuri Pankov 63*260e9a87SYuri Pankov /* Open manpath(1). Ignore errors. */ 64*260e9a87SYuri Pankov 65*260e9a87SYuri Pankov stream = popen(cmd, "r"); 66*260e9a87SYuri Pankov if (NULL == stream) 67*260e9a87SYuri Pankov return; 68*260e9a87SYuri Pankov 69*260e9a87SYuri Pankov buf = NULL; 70*260e9a87SYuri Pankov bsz = 0; 71*260e9a87SYuri Pankov 72*260e9a87SYuri Pankov /* Read in as much output as we can. */ 73*260e9a87SYuri Pankov 74*260e9a87SYuri Pankov do { 75*260e9a87SYuri Pankov buf = mandoc_realloc(buf, bsz + 1024); 76*260e9a87SYuri Pankov sz = fread(buf + bsz, 1, 1024, stream); 77*260e9a87SYuri Pankov bsz += sz; 78*260e9a87SYuri Pankov } while (sz > 0); 79*260e9a87SYuri Pankov 80*260e9a87SYuri Pankov if ( ! ferror(stream) && feof(stream) && 81*260e9a87SYuri Pankov bsz && '\n' == buf[bsz - 1]) { 82*260e9a87SYuri Pankov buf[bsz - 1] = '\0'; 83*260e9a87SYuri Pankov manpath_parseline(dirs, buf, 1); 84*260e9a87SYuri Pankov } 85*260e9a87SYuri Pankov 86*260e9a87SYuri Pankov free(buf); 87*260e9a87SYuri Pankov pclose(stream); 88*260e9a87SYuri Pankov #else 89*260e9a87SYuri Pankov char *insert; 90*260e9a87SYuri Pankov 91*260e9a87SYuri Pankov /* Always prepend -m. */ 92*260e9a87SYuri Pankov manpath_parseline(dirs, auxp, 1); 93*260e9a87SYuri Pankov 94*260e9a87SYuri Pankov /* If -M is given, it overrides everything else. */ 95*260e9a87SYuri Pankov if (NULL != defp) { 96*260e9a87SYuri Pankov manpath_parseline(dirs, defp, 1); 97*260e9a87SYuri Pankov return; 98*260e9a87SYuri Pankov } 99*260e9a87SYuri Pankov 100*260e9a87SYuri Pankov /* MANPATH and man.conf(5) cooperate. */ 101*260e9a87SYuri Pankov defp = getenv("MANPATH"); 102*260e9a87SYuri Pankov if (NULL == file) 103*260e9a87SYuri Pankov file = MAN_CONF_FILE; 104*260e9a87SYuri Pankov 105*260e9a87SYuri Pankov /* No MANPATH; use man.conf(5) only. */ 106*260e9a87SYuri Pankov if (NULL == defp || '\0' == defp[0]) { 107*260e9a87SYuri Pankov manpath_manconf(dirs, file); 108*260e9a87SYuri Pankov return; 109*260e9a87SYuri Pankov } 110*260e9a87SYuri Pankov 111*260e9a87SYuri Pankov /* Prepend man.conf(5) to MANPATH. */ 112*260e9a87SYuri Pankov if (':' == defp[0]) { 113*260e9a87SYuri Pankov manpath_manconf(dirs, file); 114*260e9a87SYuri Pankov manpath_parseline(dirs, defp, 0); 115*260e9a87SYuri Pankov return; 116*260e9a87SYuri Pankov } 117*260e9a87SYuri Pankov 118*260e9a87SYuri Pankov /* Append man.conf(5) to MANPATH. */ 119*260e9a87SYuri Pankov if (':' == defp[strlen(defp) - 1]) { 120*260e9a87SYuri Pankov manpath_parseline(dirs, defp, 0); 121*260e9a87SYuri Pankov manpath_manconf(dirs, file); 122*260e9a87SYuri Pankov return; 123*260e9a87SYuri Pankov } 124*260e9a87SYuri Pankov 125*260e9a87SYuri Pankov /* Insert man.conf(5) into MANPATH. */ 126*260e9a87SYuri Pankov insert = strstr(defp, "::"); 127*260e9a87SYuri Pankov if (NULL != insert) { 128*260e9a87SYuri Pankov *insert++ = '\0'; 129*260e9a87SYuri Pankov manpath_parseline(dirs, defp, 0); 130*260e9a87SYuri Pankov manpath_manconf(dirs, file); 131*260e9a87SYuri Pankov manpath_parseline(dirs, insert + 1, 0); 132*260e9a87SYuri Pankov return; 133*260e9a87SYuri Pankov } 134*260e9a87SYuri Pankov 135*260e9a87SYuri Pankov /* MANPATH overrides man.conf(5) completely. */ 136*260e9a87SYuri Pankov manpath_parseline(dirs, defp, 0); 137*260e9a87SYuri Pankov #endif 138*260e9a87SYuri Pankov } 139*260e9a87SYuri Pankov 140*260e9a87SYuri Pankov /* 141*260e9a87SYuri Pankov * Parse a FULL pathname from a colon-separated list of arrays. 142*260e9a87SYuri Pankov */ 143*260e9a87SYuri Pankov static void 144*260e9a87SYuri Pankov manpath_parseline(struct manpaths *dirs, char *path, int complain) 145*260e9a87SYuri Pankov { 146*260e9a87SYuri Pankov char *dir; 147*260e9a87SYuri Pankov 148*260e9a87SYuri Pankov if (NULL == path) 149*260e9a87SYuri Pankov return; 150*260e9a87SYuri Pankov 151*260e9a87SYuri Pankov for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) 152*260e9a87SYuri Pankov manpath_add(dirs, dir, complain); 153*260e9a87SYuri Pankov } 154*260e9a87SYuri Pankov 155*260e9a87SYuri Pankov /* 156*260e9a87SYuri Pankov * Add a directory to the array, ignoring bad directories. 157*260e9a87SYuri Pankov * Grow the array one-by-one for simplicity's sake. 158*260e9a87SYuri Pankov */ 159*260e9a87SYuri Pankov static void 160*260e9a87SYuri Pankov manpath_add(struct manpaths *dirs, const char *dir, int complain) 161*260e9a87SYuri Pankov { 162*260e9a87SYuri Pankov char buf[PATH_MAX]; 163*260e9a87SYuri Pankov struct stat sb; 164*260e9a87SYuri Pankov char *cp; 165*260e9a87SYuri Pankov size_t i; 166*260e9a87SYuri Pankov 167*260e9a87SYuri Pankov if (NULL == (cp = realpath(dir, buf))) { 168*260e9a87SYuri Pankov if (complain) { 169*260e9a87SYuri Pankov fputs("manpath: ", stderr); 170*260e9a87SYuri Pankov perror(dir); 171*260e9a87SYuri Pankov } 172*260e9a87SYuri Pankov return; 173*260e9a87SYuri Pankov } 174*260e9a87SYuri Pankov 175*260e9a87SYuri Pankov for (i = 0; i < dirs->sz; i++) 176*260e9a87SYuri Pankov if (0 == strcmp(dirs->paths[i], dir)) 177*260e9a87SYuri Pankov return; 178*260e9a87SYuri Pankov 179*260e9a87SYuri Pankov if (stat(cp, &sb) == -1) { 180*260e9a87SYuri Pankov if (complain) { 181*260e9a87SYuri Pankov fputs("manpath: ", stderr); 182*260e9a87SYuri Pankov perror(dir); 183*260e9a87SYuri Pankov } 184*260e9a87SYuri Pankov return; 185*260e9a87SYuri Pankov } 186*260e9a87SYuri Pankov 187*260e9a87SYuri Pankov dirs->paths = mandoc_reallocarray(dirs->paths, 188*260e9a87SYuri Pankov dirs->sz + 1, sizeof(char *)); 189*260e9a87SYuri Pankov 190*260e9a87SYuri Pankov dirs->paths[dirs->sz++] = mandoc_strdup(cp); 191*260e9a87SYuri Pankov } 192*260e9a87SYuri Pankov 193*260e9a87SYuri Pankov void 194*260e9a87SYuri Pankov manpath_free(struct manpaths *p) 195*260e9a87SYuri Pankov { 196*260e9a87SYuri Pankov size_t i; 197*260e9a87SYuri Pankov 198*260e9a87SYuri Pankov for (i = 0; i < p->sz; i++) 199*260e9a87SYuri Pankov free(p->paths[i]); 200*260e9a87SYuri Pankov 201*260e9a87SYuri Pankov free(p->paths); 202*260e9a87SYuri Pankov } 203*260e9a87SYuri Pankov 204*260e9a87SYuri Pankov void 205*260e9a87SYuri Pankov manpath_manconf(struct manpaths *dirs, const char *file) 206*260e9a87SYuri Pankov { 207*260e9a87SYuri Pankov FILE *stream; 208*260e9a87SYuri Pankov char *p, *q; 209*260e9a87SYuri Pankov size_t len, keysz; 210*260e9a87SYuri Pankov 211*260e9a87SYuri Pankov keysz = strlen(MAN_CONF_KEY); 212*260e9a87SYuri Pankov assert(keysz > 0); 213*260e9a87SYuri Pankov 214*260e9a87SYuri Pankov if (NULL == (stream = fopen(file, "r"))) 215*260e9a87SYuri Pankov return; 216*260e9a87SYuri Pankov 217*260e9a87SYuri Pankov while (NULL != (p = fgetln(stream, &len))) { 218*260e9a87SYuri Pankov if (0 == len || '\n' != p[--len]) 219*260e9a87SYuri Pankov break; 220*260e9a87SYuri Pankov p[len] = '\0'; 221*260e9a87SYuri Pankov while (isspace((unsigned char)*p)) 222*260e9a87SYuri Pankov p++; 223*260e9a87SYuri Pankov if (strncmp(MAN_CONF_KEY, p, keysz)) 224*260e9a87SYuri Pankov continue; 225*260e9a87SYuri Pankov p += keysz; 226*260e9a87SYuri Pankov while (isspace((unsigned char)*p)) 227*260e9a87SYuri Pankov p++; 228*260e9a87SYuri Pankov if ('\0' == *p) 229*260e9a87SYuri Pankov continue; 230*260e9a87SYuri Pankov if (NULL == (q = strrchr(p, '/'))) 231*260e9a87SYuri Pankov continue; 232*260e9a87SYuri Pankov *q = '\0'; 233*260e9a87SYuri Pankov manpath_add(dirs, p, 0); 234*260e9a87SYuri Pankov } 235*260e9a87SYuri Pankov 236*260e9a87SYuri Pankov fclose(stream); 237*260e9a87SYuri Pankov } 238