1 /* $Id: manpath.c,v 1.33 2017/02/10 15:45:28 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 "config.h" 19 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 23 #include <ctype.h> 24 #if HAVE_ERR 25 #include <err.h> 26 #endif 27 #include <limits.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 32 #include "mandoc_aux.h" 33 #include "manconf.h" 34 35 static void manconf_file(struct manconf *, const char *); 36 static void manpath_add(struct manpaths *, const char *, int); 37 static void manpath_parseline(struct manpaths *, char *, int); 38 39 40 void 41 manconf_parse(struct manconf *conf, const char *file, 42 char *defp, char *auxp) 43 { 44 char *insert; 45 46 /* Always prepend -m. */ 47 manpath_parseline(&conf->manpath, auxp, 1); 48 49 /* If -M is given, it overrides everything else. */ 50 if (NULL != defp) { 51 manpath_parseline(&conf->manpath, defp, 1); 52 return; 53 } 54 55 /* MANPATH and man.conf(5) cooperate. */ 56 defp = getenv("MANPATH"); 57 if (NULL == file) 58 file = MAN_CONF_FILE; 59 60 /* No MANPATH; use man.conf(5) only. */ 61 if (NULL == defp || '\0' == defp[0]) { 62 manconf_file(conf, file); 63 return; 64 } 65 66 /* Prepend man.conf(5) to MANPATH. */ 67 if (':' == defp[0]) { 68 manconf_file(conf, file); 69 manpath_parseline(&conf->manpath, defp, 0); 70 return; 71 } 72 73 /* Append man.conf(5) to MANPATH. */ 74 if (':' == defp[strlen(defp) - 1]) { 75 manpath_parseline(&conf->manpath, defp, 0); 76 manconf_file(conf, file); 77 return; 78 } 79 80 /* Insert man.conf(5) into MANPATH. */ 81 insert = strstr(defp, "::"); 82 if (NULL != insert) { 83 *insert++ = '\0'; 84 manpath_parseline(&conf->manpath, defp, 0); 85 manconf_file(conf, file); 86 manpath_parseline(&conf->manpath, insert + 1, 0); 87 return; 88 } 89 90 /* MANPATH overrides man.conf(5) completely. */ 91 manpath_parseline(&conf->manpath, defp, 0); 92 } 93 94 /* 95 * Parse a FULL pathname from a colon-separated list of arrays. 96 */ 97 static void 98 manpath_parseline(struct manpaths *dirs, char *path, int complain) 99 { 100 char *dir; 101 102 if (NULL == path) 103 return; 104 105 for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) 106 manpath_add(dirs, dir, complain); 107 } 108 109 /* 110 * Add a directory to the array, ignoring bad directories. 111 * Grow the array one-by-one for simplicity's sake. 112 */ 113 static void 114 manpath_add(struct manpaths *dirs, const char *dir, int complain) 115 { 116 char buf[PATH_MAX]; 117 struct stat sb; 118 char *cp; 119 size_t i; 120 121 if (NULL == (cp = realpath(dir, buf))) { 122 if (complain) 123 warn("manpath: %s", dir); 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 warn("manpath: %s", dir); 134 return; 135 } 136 137 dirs->paths = mandoc_reallocarray(dirs->paths, 138 dirs->sz + 1, sizeof(char *)); 139 140 dirs->paths[dirs->sz++] = mandoc_strdup(cp); 141 } 142 143 void 144 manconf_free(struct manconf *conf) 145 { 146 size_t i; 147 148 for (i = 0; i < conf->manpath.sz; i++) 149 free(conf->manpath.paths[i]); 150 151 free(conf->manpath.paths); 152 free(conf->output.includes); 153 free(conf->output.man); 154 free(conf->output.paper); 155 free(conf->output.style); 156 } 157 158 static void 159 manconf_file(struct manconf *conf, const char *file) 160 { 161 const char *const toks[] = { "manpath", "output", "_whatdb" }; 162 char manpath_default[] = MANPATH_DEFAULT; 163 164 FILE *stream; 165 char *line, *cp, *ep; 166 size_t linesz, tok, toklen; 167 ssize_t linelen; 168 169 if ((stream = fopen(file, "r")) == NULL) 170 goto out; 171 172 line = NULL; 173 linesz = 0; 174 175 while ((linelen = getline(&line, &linesz, stream)) != -1) { 176 cp = line; 177 ep = cp + linelen - 1; 178 while (ep > cp && isspace((unsigned char)*ep)) 179 *ep-- = '\0'; 180 while (isspace((unsigned char)*cp)) 181 cp++; 182 if (cp == ep || *cp == '#') 183 continue; 184 185 for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 186 toklen = strlen(toks[tok]); 187 if (cp + toklen < ep && 188 isspace((unsigned char)cp[toklen]) && 189 strncmp(cp, toks[tok], toklen) == 0) { 190 cp += toklen; 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 *manpath_default = '\0'; 208 break; 209 case 1: /* output */ 210 manconf_output(&conf->output, cp, 1); 211 break; 212 default: 213 break; 214 } 215 } 216 free(line); 217 fclose(stream); 218 219 out: 220 if (*manpath_default != '\0') 221 manpath_parseline(&conf->manpath, manpath_default, 0); 222 } 223 224 int 225 manconf_output(struct manoutput *conf, const char *cp, int fromfile) 226 { 227 const char *const toks[] = { 228 "includes", "man", "paper", "style", 229 "indent", "width", "fragment", "mdoc", "noval" 230 }; 231 232 const char *errstr; 233 char *oldval; 234 size_t len, tok; 235 236 for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 237 len = strlen(toks[tok]); 238 if ( ! strncmp(cp, toks[tok], len) && 239 strchr(" = ", cp[len]) != NULL) { 240 cp += len; 241 if (*cp == '=') 242 cp++; 243 while (isspace((unsigned char)*cp)) 244 cp++; 245 break; 246 } 247 } 248 249 if (tok < 6 && *cp == '\0') { 250 warnx("-O %s=?: Missing argument value", toks[tok]); 251 return -1; 252 } 253 if ((tok == 6 || tok == 7) && *cp != '\0') { 254 warnx("-O %s: Does not take a value: %s", toks[tok], cp); 255 return -1; 256 } 257 258 switch (tok) { 259 case 0: 260 if (conf->includes != NULL) { 261 oldval = mandoc_strdup(conf->includes); 262 break; 263 } 264 conf->includes = mandoc_strdup(cp); 265 return 0; 266 case 1: 267 if (conf->man != NULL) { 268 oldval = mandoc_strdup(conf->man); 269 break; 270 } 271 conf->man = mandoc_strdup(cp); 272 return 0; 273 case 2: 274 if (conf->paper != NULL) { 275 oldval = mandoc_strdup(conf->paper); 276 break; 277 } 278 conf->paper = mandoc_strdup(cp); 279 return 0; 280 case 3: 281 if (conf->style != NULL) { 282 oldval = mandoc_strdup(conf->style); 283 break; 284 } 285 conf->style = mandoc_strdup(cp); 286 return 0; 287 case 4: 288 if (conf->indent) { 289 mandoc_asprintf(&oldval, "%zu", conf->indent); 290 break; 291 } 292 conf->indent = strtonum(cp, 0, 1000, &errstr); 293 if (errstr == NULL) 294 return 0; 295 warnx("-O indent=%s is %s", cp, errstr); 296 return -1; 297 case 5: 298 if (conf->width) { 299 mandoc_asprintf(&oldval, "%zu", conf->width); 300 break; 301 } 302 conf->width = strtonum(cp, 58, 1000, &errstr); 303 if (errstr == NULL) 304 return 0; 305 warnx("-O width=%s is %s", cp, errstr); 306 return -1; 307 case 6: 308 conf->fragment = 1; 309 return 0; 310 case 7: 311 conf->mdoc = 1; 312 return 0; 313 case 8: 314 conf->noval = 1; 315 return 0; 316 default: 317 if (fromfile) 318 warnx("-O %s: Bad argument", cp); 319 return -1; 320 } 321 if (fromfile == 0) 322 warnx("-O %s=%s: Option already set to %s", 323 toks[tok], cp, oldval); 324 free(oldval); 325 return -1; 326 } 327