1 /* $NetBSD: manconf.c,v 1.5 2006/04/10 14:39:06 chuck Exp $ */ 2 3 /* 4 * Copyright (c) 1989, 1993, 1995 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * manconf.c: provides interface for reading man.conf files 34 * 35 * note that this code is shared across all programs that read man.conf. 36 * (currently: apropos, catman, makewhatis, man, and whatis...) 37 */ 38 39 #if HAVE_NBTOOL_CONFIG_H 40 #include "nbtool_config.h" 41 #endif 42 43 #include <sys/cdefs.h> 44 #ifndef lint 45 #if 0 46 static char sccsid[] = "@(#)config.c 8.8 (Berkeley) 1/31/95"; 47 #else 48 __RCSID("$NetBSD: manconf.c,v 1.5 2006/04/10 14:39:06 chuck Exp $"); 49 #endif 50 #endif /* not lint */ 51 52 #include <sys/types.h> 53 #include <sys/queue.h> 54 55 #include <ctype.h> 56 #include <err.h> 57 #include <errno.h> 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 62 #include "manconf.h" 63 #include "pathnames.h" 64 65 TAILQ_HEAD(_head, _tag); 66 static struct _head head; /* 'head' -- top level data structure */ 67 68 /* 69 * xstrdup: like strdup, but also returns length of string in lenp 70 */ 71 static char * 72 xstrdup(const char *str, size_t *lenp) 73 { 74 size_t len; 75 char *copy; 76 77 len = strlen(str) + 1; 78 copy = malloc(len); 79 if (!copy) 80 return(NULL); 81 memcpy(copy, str, len); 82 if (lenp) 83 *lenp = len - 1; /* subtract out the null */ 84 return(copy); 85 } 86 87 /* 88 * config -- 89 * 90 * Read the configuration file and build a doubly linked 91 * list off of "head" that looks like: 92 * 93 * tag1 <-> entry <-> entry <-> entry 94 * | 95 * tag2 <-> entry <-> entry <-> entry 96 * 97 * note: will err/errx out on error (fopen or malloc failure) 98 */ 99 void 100 config(const char *fname) 101 { 102 TAG *tp; 103 FILE *cfp; 104 size_t len; 105 int lcnt; 106 char *p, *t, type; 107 108 if (fname == NULL) 109 fname = _PATH_MANCONF; 110 if ((cfp = fopen(fname, "r")) == NULL) 111 err(1, "%s", fname); 112 TAILQ_INIT(&head); 113 for (lcnt = 1; (p = fgetln(cfp, &len)) != NULL; ++lcnt) { 114 if (len == 1) /* Skip empty lines. */ 115 continue; 116 if (p[len - 1] != '\n') { /* Skip corrupted lines. */ 117 warnx("%s: line %d corrupted", fname, lcnt); 118 continue; 119 } 120 p[len - 1] = '\0'; /* Terminate the line. */ 121 122 /* Skip leading space. */ 123 for (; *p != '\0' && isspace((unsigned char)*p); ++p); 124 /* Skip empty/comment lines. */ 125 if (*p == '\0' || *p == '#') 126 continue; 127 /* Find first token. */ 128 for (t = p; *t && !isspace((unsigned char)*t); ++t); 129 if (*t == '\0') /* Need more than one token.*/ 130 continue; 131 *t = '\0'; 132 133 tp = gettag(p, 1); 134 if (!tp) 135 errx(1, "gettag: malloc failed"); 136 137 /* 138 * Attach new records. Check to see if it is a 139 * section record or not. 140 */ 141 142 if (*p == '_') { /* not a section record */ 143 /* 144 * Special cases: _build and _crunch take the 145 * rest of the line as a single entry. 146 */ 147 if (!strcmp(p, "_build") || !strcmp(p, "_crunch")) { 148 /* 149 * The reason we're not just using 150 * strtok(3) for all of the parsing is 151 * so we don't get caught if a line 152 * has only a single token on it. 153 */ 154 while (*++t && isspace((unsigned char)*t)); 155 if (addentry(tp, t, 0) < 0) 156 errx(1, "addentry: malloc failed"); 157 } else { 158 for(++t; (p = strtok(t, " \t\n")) != NULL; 159 t = NULL) { 160 if (addentry(tp, p, 0) < 0) 161 errx(1, 162 "addentry: malloc failed"); 163 } 164 } 165 166 } else { /* section record */ 167 168 /* 169 * section entries can either be all absolute 170 * paths or all relative paths, but not both. 171 */ 172 type = (TAILQ_FIRST(&tp->entrylist) != NULL) ? 173 *(TAILQ_FIRST(&tp->entrylist)->s) : 0; 174 175 for (++t; (p = strtok(t, " \t\n")) != NULL; t = NULL) { 176 177 /* ensure an assigned type */ 178 if (type == 0) 179 type = *p; 180 181 /* check for illegal mix */ 182 if (*p != type) { 183 warnx("section %s: %s: invalid entry, does not match previous types", 184 tp->s, p); 185 warnx("man.conf cannot mix absolute and relative paths in an entry"); 186 continue; 187 } 188 if (addentry(tp, p, 0) < 0) 189 errx(1, "addentry: malloc failed"); 190 } 191 } 192 } 193 194 fclose(cfp); 195 } 196 197 /* 198 * gettag -- 199 * if (!create) return tag for given name if it exists, or NULL otherwise 200 * 201 * if (create) return tag for given name if it exists, try and create 202 * a new tag if it does not exist. return NULL if unable to create new 203 * tag. 204 */ 205 TAG * 206 gettag(const char *name, int create) 207 { 208 TAG *tp; 209 210 TAILQ_FOREACH(tp, &head, q) 211 if (!strcmp(name, tp->s)) 212 return (tp); 213 if (!create) 214 return(NULL); 215 216 /* try and add it in */ 217 tp = malloc(sizeof(*tp)); 218 if (tp) 219 tp->s = xstrdup(name, &tp->len); 220 if (!tp || !tp->s) { 221 if (tp) 222 free(tp); 223 return(NULL); 224 } 225 TAILQ_INIT(&tp->entrylist); 226 TAILQ_INSERT_TAIL(&head, tp, q); 227 return (tp); 228 } 229 230 /* 231 * addentry -- 232 * add an entry to a list. 233 * returns -1 if malloc failed, otherwise 0. 234 */ 235 int 236 addentry(TAG *tp, const char *newent, int head) 237 { 238 ENTRY *ep; 239 240 ep = malloc(sizeof(*ep)); 241 if (ep) 242 ep->s = xstrdup(newent, &ep->len); 243 if (!ep || !ep->s) { 244 if (ep) 245 free(ep); 246 return(-1); 247 } 248 249 if (head) 250 TAILQ_INSERT_HEAD(&tp->entrylist, ep, q); 251 else 252 TAILQ_INSERT_TAIL(&tp->entrylist, ep, q); 253 254 return(0); 255 } 256