1 /* $OpenBSD: manpath.c,v 1.11 2014/11/18 19:40:38 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2011, 2014 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 34 static void manpath_add(struct manpaths *, const char *, int); 35 static void manpath_parseline(struct manpaths *, char *, int); 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, 1); 45 46 /* If -M is given, it overrides everything else. */ 47 if (NULL != defp) { 48 manpath_parseline(dirs, defp, 1); 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, 0); 67 return; 68 } 69 70 /* Append man.conf(5) to MANPATH. */ 71 if (':' == defp[strlen(defp) - 1]) { 72 manpath_parseline(dirs, defp, 0); 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, 0); 82 manpath_manconf(dirs, file); 83 manpath_parseline(dirs, insert + 1, 0); 84 return; 85 } 86 87 /* MANPATH overrides man.conf(5) completely. */ 88 manpath_parseline(dirs, defp, 0); 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, int complain) 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, complain); 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, int complain) 112 { 113 char buf[PATH_MAX]; 114 struct stat sb; 115 char *cp; 116 size_t i; 117 118 if (NULL == (cp = realpath(dir, buf))) { 119 if (complain) { 120 fputs("manpath: ", stderr); 121 perror(dir); 122 } 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 fputs("manpath: ", stderr); 133 perror(dir); 134 } 135 return; 136 } 137 138 dirs->paths = mandoc_reallocarray(dirs->paths, 139 dirs->sz + 1, sizeof(char *)); 140 141 dirs->paths[dirs->sz++] = mandoc_strdup(cp); 142 } 143 144 void 145 manpath_free(struct manpaths *p) 146 { 147 size_t i; 148 149 for (i = 0; i < p->sz; i++) 150 free(p->paths[i]); 151 152 free(p->paths); 153 } 154 155 void 156 manpath_manconf(struct manpaths *dirs, const char *file) 157 { 158 FILE *stream; 159 char *p, *q; 160 size_t len, keysz; 161 162 keysz = strlen(MAN_CONF_KEY); 163 assert(keysz > 0); 164 165 if (NULL == (stream = fopen(file, "r"))) 166 return; 167 168 while (NULL != (p = fgetln(stream, &len))) { 169 if (0 == len || '\n' != p[--len]) 170 break; 171 p[len] = '\0'; 172 while (isspace((unsigned char)*p)) 173 p++; 174 if (strncmp(MAN_CONF_KEY, p, keysz)) 175 continue; 176 p += keysz; 177 while (isspace((unsigned char)*p)) 178 p++; 179 if ('\0' == *p) 180 continue; 181 if (NULL == (q = strrchr(p, '/'))) 182 continue; 183 *q = '\0'; 184 manpath_add(dirs, p, 0); 185 } 186 187 fclose(stream); 188 } 189