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