1 /* $OpenBSD: manpath.c,v 1.12 2015/03/21 17:18:17 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 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 #include <sys/types.h> 19 #include <sys/stat.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_aux.h" 29 #include "manpath.h" 30 31 #define MAN_CONF_FILE "/etc/man.conf" 32 #define MAN_CONF_KEY "_whatdb" 33 #define MANPATH_DEFAULT "/usr/share/man:/usr/X11R6/man:/usr/local/man" 34 35 static void manpath_add(struct manpaths *, const char *, int); 36 static void manpath_parseline(struct manpaths *, char *, int); 37 38 void 39 manpath_parse(struct manpaths *dirs, const char *file, 40 char *defp, char *auxp) 41 { 42 char manpath_default[] = MANPATH_DEFAULT; 43 char *insert; 44 45 /* Always prepend -m. */ 46 manpath_parseline(dirs, auxp, 1); 47 48 /* If -M is given, it overrides everything else. */ 49 if (NULL != defp) { 50 manpath_parseline(dirs, 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 manpath_manconf(dirs, file); 62 if (dirs->sz == 0) 63 manpath_parseline(dirs, manpath_default, 0); 64 return; 65 } 66 67 /* Prepend man.conf(5) to MANPATH. */ 68 if (':' == defp[0]) { 69 manpath_manconf(dirs, file); 70 manpath_parseline(dirs, defp, 0); 71 return; 72 } 73 74 /* Append man.conf(5) to MANPATH. */ 75 if (':' == defp[strlen(defp) - 1]) { 76 manpath_parseline(dirs, defp, 0); 77 manpath_manconf(dirs, file); 78 return; 79 } 80 81 /* Insert man.conf(5) into MANPATH. */ 82 insert = strstr(defp, "::"); 83 if (NULL != insert) { 84 *insert++ = '\0'; 85 manpath_parseline(dirs, defp, 0); 86 manpath_manconf(dirs, file); 87 manpath_parseline(dirs, insert + 1, 0); 88 return; 89 } 90 91 /* MANPATH overrides man.conf(5) completely. */ 92 manpath_parseline(dirs, defp, 0); 93 } 94 95 /* 96 * Parse a FULL pathname from a colon-separated list of arrays. 97 */ 98 static void 99 manpath_parseline(struct manpaths *dirs, char *path, int complain) 100 { 101 char *dir; 102 103 if (NULL == path) 104 return; 105 106 for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) 107 manpath_add(dirs, dir, complain); 108 } 109 110 /* 111 * Add a directory to the array, ignoring bad directories. 112 * Grow the array one-by-one for simplicity's sake. 113 */ 114 static void 115 manpath_add(struct manpaths *dirs, const char *dir, int complain) 116 { 117 char buf[PATH_MAX]; 118 struct stat sb; 119 char *cp; 120 size_t i; 121 122 if (NULL == (cp = realpath(dir, buf))) { 123 if (complain) { 124 fputs("manpath: ", stderr); 125 perror(dir); 126 } 127 return; 128 } 129 130 for (i = 0; i < dirs->sz; i++) 131 if (0 == strcmp(dirs->paths[i], dir)) 132 return; 133 134 if (stat(cp, &sb) == -1) { 135 if (complain) { 136 fputs("manpath: ", stderr); 137 perror(dir); 138 } 139 return; 140 } 141 142 dirs->paths = mandoc_reallocarray(dirs->paths, 143 dirs->sz + 1, sizeof(char *)); 144 145 dirs->paths[dirs->sz++] = mandoc_strdup(cp); 146 } 147 148 void 149 manpath_free(struct manpaths *p) 150 { 151 size_t i; 152 153 for (i = 0; i < p->sz; i++) 154 free(p->paths[i]); 155 156 free(p->paths); 157 } 158 159 void 160 manpath_manconf(struct manpaths *dirs, const char *file) 161 { 162 FILE *stream; 163 char *p, *q; 164 size_t len, keysz; 165 166 keysz = strlen(MAN_CONF_KEY); 167 assert(keysz > 0); 168 169 if (NULL == (stream = fopen(file, "r"))) 170 return; 171 172 while (NULL != (p = fgetln(stream, &len))) { 173 if (0 == len || '\n' != p[--len]) 174 break; 175 p[len] = '\0'; 176 while (isspace((unsigned char)*p)) 177 p++; 178 if (strncmp(MAN_CONF_KEY, p, keysz)) 179 continue; 180 p += keysz; 181 while (isspace((unsigned char)*p)) 182 p++; 183 if ('\0' == *p) 184 continue; 185 if (NULL == (q = strrchr(p, '/'))) 186 continue; 187 *q = '\0'; 188 manpath_add(dirs, p, 0); 189 } 190 191 fclose(stream); 192 } 193