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