1 /* $OpenBSD: manpath.c,v 1.20 2017/02/10 15:44:31 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2011, 2014, 2015, 2017 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 <err.h> 23 #include <limits.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 #include "mandoc_aux.h" 29 #include "manconf.h" 30 31 #define MAN_CONF_FILE "/etc/man.conf" 32 #define MANPATH_DEFAULT "/usr/share/man:/usr/X11R6/man:/usr/local/man" 33 34 static void manconf_file(struct manconf *, const char *); 35 static void manpath_add(struct manpaths *, const char *, int); 36 static void manpath_parseline(struct manpaths *, char *, int); 37 38 39 void 40 manconf_parse(struct manconf *conf, const char *file, 41 char *defp, char *auxp) 42 { 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 return; 63 } 64 65 /* Prepend man.conf(5) to MANPATH. */ 66 if (':' == defp[0]) { 67 manconf_file(conf, file); 68 manpath_parseline(&conf->manpath, defp, 0); 69 return; 70 } 71 72 /* Append man.conf(5) to MANPATH. */ 73 if (':' == defp[strlen(defp) - 1]) { 74 manpath_parseline(&conf->manpath, defp, 0); 75 manconf_file(conf, 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(&conf->manpath, defp, 0); 84 manconf_file(conf, file); 85 manpath_parseline(&conf->manpath, insert + 1, 0); 86 return; 87 } 88 89 /* MANPATH overrides man.conf(5) completely. */ 90 manpath_parseline(&conf->manpath, 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 warn("manpath: %s", dir); 123 return; 124 } 125 126 for (i = 0; i < dirs->sz; i++) 127 if (0 == strcmp(dirs->paths[i], dir)) 128 return; 129 130 if (stat(cp, &sb) == -1) { 131 if (complain) 132 warn("manpath: %s", dir); 133 return; 134 } 135 136 dirs->paths = mandoc_reallocarray(dirs->paths, 137 dirs->sz + 1, sizeof(char *)); 138 139 dirs->paths[dirs->sz++] = mandoc_strdup(cp); 140 } 141 142 void 143 manconf_free(struct manconf *conf) 144 { 145 size_t i; 146 147 for (i = 0; i < conf->manpath.sz; i++) 148 free(conf->manpath.paths[i]); 149 150 free(conf->manpath.paths); 151 free(conf->output.includes); 152 free(conf->output.man); 153 free(conf->output.paper); 154 free(conf->output.style); 155 } 156 157 static void 158 manconf_file(struct manconf *conf, const char *file) 159 { 160 const char *const toks[] = { "manpath", "output", "_whatdb" }; 161 char manpath_default[] = MANPATH_DEFAULT; 162 163 FILE *stream; 164 char *line, *cp, *ep; 165 size_t linesz, tok, toklen; 166 ssize_t linelen; 167 168 if ((stream = fopen(file, "r")) == NULL) 169 goto out; 170 171 line = NULL; 172 linesz = 0; 173 174 while ((linelen = getline(&line, &linesz, stream)) != -1) { 175 cp = line; 176 ep = cp + linelen - 1; 177 while (ep > cp && isspace((unsigned char)*ep)) 178 *ep-- = '\0'; 179 while (isspace((unsigned char)*cp)) 180 cp++; 181 if (cp == ep || *cp == '#') 182 continue; 183 184 for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 185 toklen = strlen(toks[tok]); 186 if (cp + toklen < ep && 187 isspace((unsigned char)cp[toklen]) && 188 strncmp(cp, toks[tok], toklen) == 0) { 189 cp += toklen; 190 while (isspace((unsigned char)*cp)) 191 cp++; 192 break; 193 } 194 } 195 196 switch (tok) { 197 case 2: /* _whatdb */ 198 while (ep > cp && ep[-1] != '/') 199 ep--; 200 if (ep == cp) 201 continue; 202 *ep = '\0'; 203 /* FALLTHROUGH */ 204 case 0: /* manpath */ 205 manpath_add(&conf->manpath, cp, 0); 206 *manpath_default = '\0'; 207 break; 208 case 1: /* output */ 209 manconf_output(&conf->output, cp, 1); 210 break; 211 default: 212 break; 213 } 214 } 215 free(line); 216 fclose(stream); 217 218 out: 219 if (*manpath_default != '\0') 220 manpath_parseline(&conf->manpath, manpath_default, 0); 221 } 222 223 int 224 manconf_output(struct manoutput *conf, const char *cp, int fromfile) 225 { 226 const char *const toks[] = { 227 "includes", "man", "paper", "style", 228 "indent", "width", "fragment", "mdoc", "noval" 229 }; 230 231 const char *errstr; 232 char *oldval; 233 size_t len, tok; 234 235 for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 236 len = strlen(toks[tok]); 237 if ( ! strncmp(cp, toks[tok], len) && 238 strchr(" = ", cp[len]) != NULL) { 239 cp += len; 240 if (*cp == '=') 241 cp++; 242 while (isspace((unsigned char)*cp)) 243 cp++; 244 break; 245 } 246 } 247 248 if (tok < 6 && *cp == '\0') { 249 warnx("-O %s=?: Missing argument value", toks[tok]); 250 return -1; 251 } 252 if ((tok == 6 || tok == 7) && *cp != '\0') { 253 warnx("-O %s: Does not take a value: %s", toks[tok], cp); 254 return -1; 255 } 256 257 switch (tok) { 258 case 0: 259 if (conf->includes != NULL) { 260 oldval = mandoc_strdup(conf->includes); 261 break; 262 } 263 conf->includes = mandoc_strdup(cp); 264 return 0; 265 case 1: 266 if (conf->man != NULL) { 267 oldval = mandoc_strdup(conf->man); 268 break; 269 } 270 conf->man = mandoc_strdup(cp); 271 return 0; 272 case 2: 273 if (conf->paper != NULL) { 274 oldval = mandoc_strdup(conf->paper); 275 break; 276 } 277 conf->paper = mandoc_strdup(cp); 278 return 0; 279 case 3: 280 if (conf->style != NULL) { 281 oldval = mandoc_strdup(conf->style); 282 break; 283 } 284 conf->style = mandoc_strdup(cp); 285 return 0; 286 case 4: 287 if (conf->indent) { 288 mandoc_asprintf(&oldval, "%zu", conf->indent); 289 break; 290 } 291 conf->indent = strtonum(cp, 0, 1000, &errstr); 292 if (errstr == NULL) 293 return 0; 294 warnx("-O indent=%s is %s", cp, errstr); 295 return -1; 296 case 5: 297 if (conf->width) { 298 mandoc_asprintf(&oldval, "%zu", conf->width); 299 break; 300 } 301 conf->width = strtonum(cp, 58, 1000, &errstr); 302 if (errstr == NULL) 303 return 0; 304 warnx("-O width=%s is %s", cp, errstr); 305 return -1; 306 case 6: 307 conf->fragment = 1; 308 return 0; 309 case 7: 310 conf->mdoc = 1; 311 return 0; 312 case 8: 313 conf->noval = 1; 314 return 0; 315 default: 316 if (fromfile) 317 warnx("-O %s: Bad argument", cp); 318 return -1; 319 } 320 if (fromfile == 0) 321 warnx("-O %s=%s: Option already set to %s", 322 toks[tok], cp, oldval); 323 free(oldval); 324 return -1; 325 } 326