1 /* $OpenBSD: manpath.c,v 1.13 2015/03/26 22:42:01 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2011, 2014, 2015 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 AUTHORS DISCLAIM ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 <sys/types.h> 19 #include <sys/stat.h> 20 21 #include <ctype.h> 22 #include <limits.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 27 #include "mandoc_aux.h" 28 #include "manpath.h" 29 30 #define MAN_CONF_FILE "/etc/man.conf" 31 #define MANPATH_DEFAULT "/usr/share/man:/usr/X11R6/man:/usr/local/man" 32 33 static void manpath_add(struct manpaths *, const char *, int); 34 static void manpath_parseline(struct manpaths *, char *, int); 35 36 void 37 manpath_parse(struct manpaths *dirs, const char *file, 38 char *defp, char *auxp) 39 { 40 char manpath_default[] = MANPATH_DEFAULT; 41 char *insert; 42 43 /* Always prepend -m. */ 44 manpath_parseline(dirs, auxp, 1); 45 46 /* If -M is given, it overrides everything else. */ 47 if (NULL != defp) { 48 manpath_parseline(dirs, defp, 1); 49 return; 50 } 51 52 /* MANPATH and man.conf(5) cooperate. */ 53 defp = getenv("MANPATH"); 54 if (NULL == file) 55 file = MAN_CONF_FILE; 56 57 /* No MANPATH; use man.conf(5) only. */ 58 if (NULL == defp || '\0' == defp[0]) { 59 manpath_manconf(dirs, file); 60 if (dirs->sz == 0) 61 manpath_parseline(dirs, manpath_default, 0); 62 return; 63 } 64 65 /* Prepend man.conf(5) to MANPATH. */ 66 if (':' == defp[0]) { 67 manpath_manconf(dirs, file); 68 manpath_parseline(dirs, defp, 0); 69 return; 70 } 71 72 /* Append man.conf(5) to MANPATH. */ 73 if (':' == defp[strlen(defp) - 1]) { 74 manpath_parseline(dirs, defp, 0); 75 manpath_manconf(dirs, file); 76 return; 77 } 78 79 /* Insert man.conf(5) into MANPATH. */ 80 insert = strstr(defp, "::"); 81 if (NULL != insert) { 82 *insert++ = '\0'; 83 manpath_parseline(dirs, defp, 0); 84 manpath_manconf(dirs, file); 85 manpath_parseline(dirs, insert + 1, 0); 86 return; 87 } 88 89 /* MANPATH overrides man.conf(5) completely. */ 90 manpath_parseline(dirs, defp, 0); 91 } 92 93 /* 94 * Parse a FULL pathname from a colon-separated list of arrays. 95 */ 96 static void 97 manpath_parseline(struct manpaths *dirs, char *path, int complain) 98 { 99 char *dir; 100 101 if (NULL == path) 102 return; 103 104 for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) 105 manpath_add(dirs, dir, complain); 106 } 107 108 /* 109 * Add a directory to the array, ignoring bad directories. 110 * Grow the array one-by-one for simplicity's sake. 111 */ 112 static void 113 manpath_add(struct manpaths *dirs, const char *dir, int complain) 114 { 115 char buf[PATH_MAX]; 116 struct stat sb; 117 char *cp; 118 size_t i; 119 120 if (NULL == (cp = realpath(dir, buf))) { 121 if (complain) { 122 fputs("manpath: ", stderr); 123 perror(dir); 124 } 125 return; 126 } 127 128 for (i = 0; i < dirs->sz; i++) 129 if (0 == strcmp(dirs->paths[i], dir)) 130 return; 131 132 if (stat(cp, &sb) == -1) { 133 if (complain) { 134 fputs("manpath: ", stderr); 135 perror(dir); 136 } 137 return; 138 } 139 140 dirs->paths = mandoc_reallocarray(dirs->paths, 141 dirs->sz + 1, sizeof(char *)); 142 143 dirs->paths[dirs->sz++] = mandoc_strdup(cp); 144 } 145 146 void 147 manpath_free(struct manpaths *p) 148 { 149 size_t i; 150 151 for (i = 0; i < p->sz; i++) 152 free(p->paths[i]); 153 154 free(p->paths); 155 } 156 157 void 158 manpath_manconf(struct manpaths *dirs, const char *file) 159 { 160 const char *const toks[] = { "manpath", "_whatdb" }; 161 162 FILE *stream; 163 char *cp, *ep; 164 size_t len, tok; 165 166 if ((stream = fopen(file, "r")) == NULL) 167 return; 168 169 while ((cp = fgetln(stream, &len)) != NULL) { 170 ep = cp + len; 171 if (ep[-1] != '\n') 172 break; 173 *--ep = '\0'; 174 while (isspace((unsigned char)*cp)) 175 cp++; 176 if (*cp == '#') 177 continue; 178 179 for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 180 len = strlen(toks[tok]); 181 if (cp + len < ep && 182 isspace((unsigned char)cp[len]) && 183 !strncmp(cp, toks[tok], len)) { 184 cp += len; 185 while (isspace((unsigned char)*cp)) 186 cp++; 187 break; 188 } 189 } 190 191 switch (tok) { 192 case 1: /* _whatdb */ 193 while (ep > cp && ep[-1] != '/') 194 ep--; 195 if (ep == cp) 196 continue; 197 *ep = '\0'; 198 /* FALLTHROUGH */ 199 case 0: /* manpath */ 200 manpath_add(dirs, cp, 0); 201 break; 202 default: 203 break; 204 } 205 } 206 207 fclose(stream); 208 } 209