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