1 /* $OpenBSD: manpath.c,v 1.14 2015/03/27 17:36:56 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 "manconf.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 manconf_file(struct manconf *, const char *); 34 static void manpath_add(struct manpaths *, const char *, int); 35 static void manpath_parseline(struct manpaths *, char *, int); 36 37 38 void 39 manconf_parse(struct manconf *conf, const char *file, 40 char *defp, char *auxp) 41 { 42 char manpath_default[] = MANPATH_DEFAULT; 43 char *insert; 44 45 /* Always prepend -m. */ 46 manpath_parseline(&conf->manpath, auxp, 1); 47 48 /* If -M is given, it overrides everything else. */ 49 if (NULL != defp) { 50 manpath_parseline(&conf->manpath, defp, 1); 51 return; 52 } 53 54 /* MANPATH and man.conf(5) cooperate. */ 55 defp = getenv("MANPATH"); 56 if (NULL == file) 57 file = MAN_CONF_FILE; 58 59 /* No MANPATH; use man.conf(5) only. */ 60 if (NULL == defp || '\0' == defp[0]) { 61 manconf_file(conf, file); 62 if (conf->manpath.sz == 0) 63 manpath_parseline(&conf->manpath, manpath_default, 0); 64 return; 65 } 66 67 /* Prepend man.conf(5) to MANPATH. */ 68 if (':' == defp[0]) { 69 manconf_file(conf, file); 70 manpath_parseline(&conf->manpath, defp, 0); 71 return; 72 } 73 74 /* Append man.conf(5) to MANPATH. */ 75 if (':' == defp[strlen(defp) - 1]) { 76 manpath_parseline(&conf->manpath, defp, 0); 77 manconf_file(conf, file); 78 return; 79 } 80 81 /* Insert man.conf(5) into MANPATH. */ 82 insert = strstr(defp, "::"); 83 if (NULL != insert) { 84 *insert++ = '\0'; 85 manpath_parseline(&conf->manpath, defp, 0); 86 manconf_file(conf, file); 87 manpath_parseline(&conf->manpath, insert + 1, 0); 88 return; 89 } 90 91 /* MANPATH overrides man.conf(5) completely. */ 92 manpath_parseline(&conf->manpath, defp, 0); 93 } 94 95 /* 96 * Parse a FULL pathname from a colon-separated list of arrays. 97 */ 98 static void 99 manpath_parseline(struct manpaths *dirs, char *path, int complain) 100 { 101 char *dir; 102 103 if (NULL == path) 104 return; 105 106 for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) 107 manpath_add(dirs, dir, complain); 108 } 109 110 /* 111 * Add a directory to the array, ignoring bad directories. 112 * Grow the array one-by-one for simplicity's sake. 113 */ 114 static void 115 manpath_add(struct manpaths *dirs, const char *dir, int complain) 116 { 117 char buf[PATH_MAX]; 118 struct stat sb; 119 char *cp; 120 size_t i; 121 122 if (NULL == (cp = realpath(dir, buf))) { 123 if (complain) { 124 fputs("manpath: ", stderr); 125 perror(dir); 126 } 127 return; 128 } 129 130 for (i = 0; i < dirs->sz; i++) 131 if (0 == strcmp(dirs->paths[i], dir)) 132 return; 133 134 if (stat(cp, &sb) == -1) { 135 if (complain) { 136 fputs("manpath: ", stderr); 137 perror(dir); 138 } 139 return; 140 } 141 142 dirs->paths = mandoc_reallocarray(dirs->paths, 143 dirs->sz + 1, sizeof(char *)); 144 145 dirs->paths[dirs->sz++] = mandoc_strdup(cp); 146 } 147 148 void 149 manconf_free(struct manconf *conf) 150 { 151 size_t i; 152 153 for (i = 0; i < conf->manpath.sz; i++) 154 free(conf->manpath.paths[i]); 155 156 free(conf->manpath.paths); 157 free(conf->output.includes); 158 free(conf->output.man); 159 free(conf->output.paper); 160 free(conf->output.style); 161 } 162 163 static void 164 manconf_file(struct manconf *conf, const char *file) 165 { 166 const char *const toks[] = { "manpath", "output", "_whatdb" }; 167 168 FILE *stream; 169 char *cp, *ep; 170 size_t len, tok; 171 172 if ((stream = fopen(file, "r")) == NULL) 173 return; 174 175 while ((cp = fgetln(stream, &len)) != NULL) { 176 ep = cp + len; 177 if (ep[-1] != '\n') 178 break; 179 *--ep = '\0'; 180 while (isspace((unsigned char)*cp)) 181 cp++; 182 if (*cp == '#') 183 continue; 184 185 for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 186 len = strlen(toks[tok]); 187 if (cp + len < ep && 188 isspace((unsigned char)cp[len]) && 189 !strncmp(cp, toks[tok], len)) { 190 cp += len; 191 while (isspace((unsigned char)*cp)) 192 cp++; 193 break; 194 } 195 } 196 197 switch (tok) { 198 case 2: /* _whatdb */ 199 while (ep > cp && ep[-1] != '/') 200 ep--; 201 if (ep == cp) 202 continue; 203 *ep = '\0'; 204 /* FALLTHROUGH */ 205 case 0: /* manpath */ 206 manpath_add(&conf->manpath, cp, 0); 207 break; 208 case 1: /* output */ 209 manconf_output(&conf->output, cp); 210 break; 211 default: 212 break; 213 } 214 } 215 216 fclose(stream); 217 } 218 219 void 220 manconf_output(struct manoutput *conf, const char *cp) 221 { 222 const char *const toks[] = { 223 "includes", "man", "paper", "style", 224 "indent", "width", "fragment", "mdoc" 225 }; 226 227 size_t len, tok; 228 229 for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 230 len = strlen(toks[tok]); 231 if ( ! strncmp(cp, toks[tok], len) && 232 strchr(" = ", cp[len]) != NULL) { 233 cp += len; 234 if (*cp == '=') 235 cp++; 236 while (isspace((unsigned char)*cp)) 237 cp++; 238 break; 239 } 240 } 241 242 if (tok < 6 && *cp == '\0') 243 return; 244 245 switch (tok) { 246 case 0: 247 if (conf->includes == NULL) 248 conf->includes = mandoc_strdup(cp); 249 break; 250 case 1: 251 if (conf->man == NULL) 252 conf->man = mandoc_strdup(cp); 253 break; 254 case 2: 255 if (conf->paper == NULL) 256 conf->paper = mandoc_strdup(cp); 257 break; 258 case 3: 259 if (conf->style == NULL) 260 conf->style = mandoc_strdup(cp); 261 break; 262 case 4: 263 if (conf->indent == 0) 264 conf->indent = strtonum(cp, 0, 1000, NULL); 265 break; 266 case 5: 267 if (conf->width == 0) 268 conf->width = strtonum(cp, 58, 1000, NULL); 269 break; 270 case 6: 271 conf->fragment = 1; 272 break; 273 case 7: 274 conf->mdoc = 1; 275 break; 276 default: 277 break; 278 } 279 } 280