1 /* $Id: manpath.c,v 1.4 2011/12/24 21:51:40 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2011 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 AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 19 #include <sys/param.h> 20 21 #include <assert.h> 22 #include <ctype.h> 23 #include <limits.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 #include "mandoc.h" 29 #include "manpath.h" 30 31 #define MAN_CONF_FILE "/etc/man.conf" 32 #define MAN_CONF_KEY "_whatdb" 33 34 static void manpath_add(struct manpaths *, const char *); 35 static void manpath_parseline(struct manpaths *, char *); 36 37 void 38 manpath_parse(struct manpaths *dirs, const char *file, 39 char *defp, char *auxp) 40 { 41 char *insert; 42 43 /* Always prepend -m. */ 44 manpath_parseline(dirs, auxp); 45 46 /* If -M is given, it overrides everything else. */ 47 if (NULL != defp) { 48 manpath_parseline(dirs, defp); 49 return; 50 } 51 52 /* MANPATH and man.conf(5) cooperate. */ 53 defp = getenv("MANPATH"); 54 if (NULL == file) 55 file = MAN_CONF_FILE; 56 57 /* No MANPATH; use man.conf(5) only. */ 58 if (NULL == defp || '\0' == defp[0]) { 59 manpath_manconf(dirs, file); 60 return; 61 } 62 63 /* Prepend man.conf(5) to MANPATH. */ 64 if (':' == defp[0]) { 65 manpath_manconf(dirs, file); 66 manpath_parseline(dirs, defp); 67 return; 68 } 69 70 /* Append man.conf(5) to MANPATH. */ 71 if (':' == defp[(int)strlen(defp) - 1]) { 72 manpath_parseline(dirs, defp); 73 manpath_manconf(dirs, file); 74 return; 75 } 76 77 /* Insert man.conf(5) into MANPATH. */ 78 insert = strstr(defp, "::"); 79 if (NULL != insert) { 80 *insert++ = '\0'; 81 manpath_parseline(dirs, defp); 82 manpath_manconf(dirs, file); 83 manpath_parseline(dirs, insert + 1); 84 return; 85 } 86 87 /* MANPATH overrides man.conf(5) completely. */ 88 manpath_parseline(dirs, defp); 89 } 90 91 /* 92 * Parse a FULL pathname from a colon-separated list of arrays. 93 */ 94 static void 95 manpath_parseline(struct manpaths *dirs, char *path) 96 { 97 char *dir; 98 99 if (NULL == path) 100 return; 101 102 for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) 103 manpath_add(dirs, dir); 104 } 105 106 /* 107 * Add a directory to the array, ignoring bad directories. 108 * Grow the array one-by-one for simplicity's sake. 109 */ 110 static void 111 manpath_add(struct manpaths *dirs, const char *dir) 112 { 113 char buf[PATH_MAX]; 114 char *cp; 115 int i; 116 117 if (NULL == (cp = realpath(dir, buf))) 118 return; 119 120 for (i = 0; i < dirs->sz; i++) 121 if (0 == strcmp(dirs->paths[i], dir)) 122 return; 123 124 dirs->paths = mandoc_realloc 125 (dirs->paths, 126 ((size_t)dirs->sz + 1) * sizeof(char *)); 127 128 dirs->paths[dirs->sz++] = mandoc_strdup(cp); 129 } 130 131 void 132 manpath_free(struct manpaths *p) 133 { 134 int i; 135 136 for (i = 0; i < p->sz; i++) 137 free(p->paths[i]); 138 139 free(p->paths); 140 } 141 142 void 143 manpath_manconf(struct manpaths *dirs, const char *file) 144 { 145 FILE *stream; 146 char *p, *q; 147 size_t len, keysz; 148 149 keysz = strlen(MAN_CONF_KEY); 150 assert(keysz > 0); 151 152 if (NULL == (stream = fopen(file, "r"))) 153 return; 154 155 while (NULL != (p = fgetln(stream, &len))) { 156 if (0 == len || '\n' != p[--len]) 157 break; 158 p[len] = '\0'; 159 while (isspace((unsigned char)*p)) 160 p++; 161 if (strncmp(MAN_CONF_KEY, p, keysz)) 162 continue; 163 p += keysz; 164 while (isspace(*p)) 165 p++; 166 if ('\0' == *p) 167 continue; 168 if (NULL == (q = strrchr(p, '/'))) 169 continue; 170 *q = '\0'; 171 manpath_add(dirs, p); 172 } 173 174 fclose(stream); 175 } 176