manpath.c revision 316420
1/* $Id: manpath.c,v 1.31 2016/07/19 22:40:33 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 "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 35static void manconf_file(struct manconf *, const char *); 36static void manpath_add(struct manpaths *, const char *, int); 37static void manpath_parseline(struct manpaths *, char *, int); 38 39 40void 41manconf_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 */ 97static void 98manpath_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 */ 113static void 114manpath_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 143void 144manconf_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 158static void 159manconf_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); 211 break; 212 default: 213 break; 214 } 215 } 216 free(line); 217 fclose(stream); 218 219out: 220 if (*manpath_default != '\0') 221 manpath_parseline(&conf->manpath, manpath_default, 0); 222} 223 224void 225manconf_output(struct manoutput *conf, const char *cp) 226{ 227 const char *const toks[] = { 228 "includes", "man", "paper", "style", 229 "indent", "width", "fragment", "mdoc" 230 }; 231 232 size_t len, tok; 233 234 for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { 235 len = strlen(toks[tok]); 236 if ( ! strncmp(cp, toks[tok], len) && 237 strchr(" = ", cp[len]) != NULL) { 238 cp += len; 239 if (*cp == '=') 240 cp++; 241 while (isspace((unsigned char)*cp)) 242 cp++; 243 break; 244 } 245 } 246 247 if (tok < 6 && *cp == '\0') 248 return; 249 250 switch (tok) { 251 case 0: 252 if (conf->includes == NULL) 253 conf->includes = mandoc_strdup(cp); 254 break; 255 case 1: 256 if (conf->man == NULL) 257 conf->man = mandoc_strdup(cp); 258 break; 259 case 2: 260 if (conf->paper == NULL) 261 conf->paper = mandoc_strdup(cp); 262 break; 263 case 3: 264 if (conf->style == NULL) 265 conf->style = mandoc_strdup(cp); 266 break; 267 case 4: 268 if (conf->indent == 0) 269 conf->indent = strtonum(cp, 0, 1000, NULL); 270 break; 271 case 5: 272 if (conf->width == 0) 273 conf->width = strtonum(cp, 58, 1000, NULL); 274 break; 275 case 6: 276 conf->fragment = 1; 277 break; 278 case 7: 279 conf->mdoc = 1; 280 break; 281 default: 282 break; 283 } 284} 285