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