manpath.c revision 275432
1275432Sbapt/* $Id: manpath.c,v 1.19 2014/11/27 00:30:40 schwarze Exp $ */ 2274880Sbapt/* 3275432Sbapt * Copyright (c) 2011, 2014 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 * 10274880Sbapt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11274880Sbapt * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12274880Sbapt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 <assert.h> 24274880Sbapt#include <ctype.h> 25274880Sbapt#include <limits.h> 26274880Sbapt#include <stdio.h> 27274880Sbapt#include <stdlib.h> 28274880Sbapt#include <string.h> 29274880Sbapt 30274880Sbapt#include "mandoc_aux.h" 31274880Sbapt#include "manpath.h" 32274880Sbapt 33274880Sbapt#define MAN_CONF_FILE "/etc/man.conf" 34274880Sbapt#define MAN_CONF_KEY "_whatdb" 35274880Sbapt 36275432Sbaptstatic void manpath_add(struct manpaths *, const char *, int); 37275432Sbaptstatic void manpath_parseline(struct manpaths *, char *, int); 38274880Sbapt 39274880Sbaptvoid 40274880Sbaptmanpath_parse(struct manpaths *dirs, const char *file, 41274880Sbapt char *defp, char *auxp) 42274880Sbapt{ 43275432Sbapt#if HAVE_MANPATH 44274880Sbapt char cmd[(PATH_MAX * 3) + 20]; 45274880Sbapt FILE *stream; 46274880Sbapt char *buf; 47274880Sbapt size_t sz, bsz; 48274880Sbapt 49274880Sbapt strlcpy(cmd, "manpath", sizeof(cmd)); 50274880Sbapt if (file) { 51274880Sbapt strlcat(cmd, " -C ", sizeof(cmd)); 52274880Sbapt strlcat(cmd, file, sizeof(cmd)); 53274880Sbapt } 54274880Sbapt if (auxp) { 55274880Sbapt strlcat(cmd, " -m ", sizeof(cmd)); 56274880Sbapt strlcat(cmd, auxp, sizeof(cmd)); 57274880Sbapt } 58274880Sbapt if (defp) { 59274880Sbapt strlcat(cmd, " -M ", sizeof(cmd)); 60274880Sbapt strlcat(cmd, defp, sizeof(cmd)); 61274880Sbapt } 62274880Sbapt 63274880Sbapt /* Open manpath(1). Ignore errors. */ 64274880Sbapt 65274880Sbapt stream = popen(cmd, "r"); 66274880Sbapt if (NULL == stream) 67274880Sbapt return; 68274880Sbapt 69274880Sbapt buf = NULL; 70274880Sbapt bsz = 0; 71274880Sbapt 72274880Sbapt /* Read in as much output as we can. */ 73274880Sbapt 74274880Sbapt do { 75274880Sbapt buf = mandoc_realloc(buf, bsz + 1024); 76274880Sbapt sz = fread(buf + bsz, 1, 1024, stream); 77274880Sbapt bsz += sz; 78274880Sbapt } while (sz > 0); 79274880Sbapt 80274880Sbapt if ( ! ferror(stream) && feof(stream) && 81274880Sbapt bsz && '\n' == buf[bsz - 1]) { 82274880Sbapt buf[bsz - 1] = '\0'; 83275432Sbapt manpath_parseline(dirs, buf, 1); 84274880Sbapt } 85274880Sbapt 86274880Sbapt free(buf); 87274880Sbapt pclose(stream); 88274880Sbapt#else 89274880Sbapt char *insert; 90274880Sbapt 91274880Sbapt /* Always prepend -m. */ 92275432Sbapt manpath_parseline(dirs, auxp, 1); 93274880Sbapt 94274880Sbapt /* If -M is given, it overrides everything else. */ 95274880Sbapt if (NULL != defp) { 96275432Sbapt manpath_parseline(dirs, defp, 1); 97274880Sbapt return; 98274880Sbapt } 99274880Sbapt 100274880Sbapt /* MANPATH and man.conf(5) cooperate. */ 101274880Sbapt defp = getenv("MANPATH"); 102274880Sbapt if (NULL == file) 103274880Sbapt file = MAN_CONF_FILE; 104274880Sbapt 105274880Sbapt /* No MANPATH; use man.conf(5) only. */ 106274880Sbapt if (NULL == defp || '\0' == defp[0]) { 107274880Sbapt manpath_manconf(dirs, file); 108274880Sbapt return; 109274880Sbapt } 110274880Sbapt 111274880Sbapt /* Prepend man.conf(5) to MANPATH. */ 112274880Sbapt if (':' == defp[0]) { 113274880Sbapt manpath_manconf(dirs, file); 114275432Sbapt manpath_parseline(dirs, defp, 0); 115274880Sbapt return; 116274880Sbapt } 117274880Sbapt 118274880Sbapt /* Append man.conf(5) to MANPATH. */ 119274880Sbapt if (':' == defp[strlen(defp) - 1]) { 120275432Sbapt manpath_parseline(dirs, defp, 0); 121274880Sbapt manpath_manconf(dirs, file); 122274880Sbapt return; 123274880Sbapt } 124274880Sbapt 125274880Sbapt /* Insert man.conf(5) into MANPATH. */ 126274880Sbapt insert = strstr(defp, "::"); 127274880Sbapt if (NULL != insert) { 128274880Sbapt *insert++ = '\0'; 129275432Sbapt manpath_parseline(dirs, defp, 0); 130274880Sbapt manpath_manconf(dirs, file); 131275432Sbapt manpath_parseline(dirs, insert + 1, 0); 132274880Sbapt return; 133274880Sbapt } 134274880Sbapt 135274880Sbapt /* MANPATH overrides man.conf(5) completely. */ 136275432Sbapt manpath_parseline(dirs, defp, 0); 137274880Sbapt#endif 138274880Sbapt} 139274880Sbapt 140274880Sbapt/* 141274880Sbapt * Parse a FULL pathname from a colon-separated list of arrays. 142274880Sbapt */ 143274880Sbaptstatic void 144275432Sbaptmanpath_parseline(struct manpaths *dirs, char *path, int complain) 145274880Sbapt{ 146274880Sbapt char *dir; 147274880Sbapt 148274880Sbapt if (NULL == path) 149274880Sbapt return; 150274880Sbapt 151274880Sbapt for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) 152275432Sbapt manpath_add(dirs, dir, complain); 153274880Sbapt} 154274880Sbapt 155274880Sbapt/* 156274880Sbapt * Add a directory to the array, ignoring bad directories. 157274880Sbapt * Grow the array one-by-one for simplicity's sake. 158274880Sbapt */ 159274880Sbaptstatic void 160275432Sbaptmanpath_add(struct manpaths *dirs, const char *dir, int complain) 161274880Sbapt{ 162274880Sbapt char buf[PATH_MAX]; 163275432Sbapt struct stat sb; 164274880Sbapt char *cp; 165274880Sbapt size_t i; 166274880Sbapt 167275432Sbapt if (NULL == (cp = realpath(dir, buf))) { 168275432Sbapt if (complain) { 169275432Sbapt fputs("manpath: ", stderr); 170275432Sbapt perror(dir); 171275432Sbapt } 172274880Sbapt return; 173275432Sbapt } 174274880Sbapt 175274880Sbapt for (i = 0; i < dirs->sz; i++) 176274880Sbapt if (0 == strcmp(dirs->paths[i], dir)) 177274880Sbapt return; 178274880Sbapt 179275432Sbapt if (stat(cp, &sb) == -1) { 180275432Sbapt if (complain) { 181275432Sbapt fputs("manpath: ", stderr); 182275432Sbapt perror(dir); 183275432Sbapt } 184275432Sbapt return; 185275432Sbapt } 186275432Sbapt 187274880Sbapt dirs->paths = mandoc_reallocarray(dirs->paths, 188274880Sbapt dirs->sz + 1, sizeof(char *)); 189274880Sbapt 190274880Sbapt dirs->paths[dirs->sz++] = mandoc_strdup(cp); 191274880Sbapt} 192274880Sbapt 193274880Sbaptvoid 194274880Sbaptmanpath_free(struct manpaths *p) 195274880Sbapt{ 196274880Sbapt size_t i; 197274880Sbapt 198274880Sbapt for (i = 0; i < p->sz; i++) 199274880Sbapt free(p->paths[i]); 200274880Sbapt 201274880Sbapt free(p->paths); 202274880Sbapt} 203274880Sbapt 204274880Sbaptvoid 205274880Sbaptmanpath_manconf(struct manpaths *dirs, const char *file) 206274880Sbapt{ 207274880Sbapt FILE *stream; 208274880Sbapt char *p, *q; 209274880Sbapt size_t len, keysz; 210274880Sbapt 211274880Sbapt keysz = strlen(MAN_CONF_KEY); 212274880Sbapt assert(keysz > 0); 213274880Sbapt 214274880Sbapt if (NULL == (stream = fopen(file, "r"))) 215274880Sbapt return; 216274880Sbapt 217274880Sbapt while (NULL != (p = fgetln(stream, &len))) { 218274880Sbapt if (0 == len || '\n' != p[--len]) 219274880Sbapt break; 220274880Sbapt p[len] = '\0'; 221274880Sbapt while (isspace((unsigned char)*p)) 222274880Sbapt p++; 223274880Sbapt if (strncmp(MAN_CONF_KEY, p, keysz)) 224274880Sbapt continue; 225274880Sbapt p += keysz; 226274880Sbapt while (isspace((unsigned char)*p)) 227274880Sbapt p++; 228274880Sbapt if ('\0' == *p) 229274880Sbapt continue; 230274880Sbapt if (NULL == (q = strrchr(p, '/'))) 231274880Sbapt continue; 232274880Sbapt *q = '\0'; 233275432Sbapt manpath_add(dirs, p, 0); 234274880Sbapt } 235274880Sbapt 236274880Sbapt fclose(stream); 237274880Sbapt} 238