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