1 /* $NetBSD: manconf.c,v 1.7 2013/07/18 15:39:08 christos 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.7 2013/07/18 15:39:08 christos 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 (void)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(EXIT_FAILURE, "%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 (/*EMPTY*/; *p != '\0' && isspace((unsigned char)*p); ++p) 124 continue; 125 /* Skip empty/comment lines. */ 126 if (*p == '\0' || *p == '#') 127 continue; 128 /* Find first token. */ 129 for (t = p; *t && !isspace((unsigned char)*t); ++t) 130 continue; 131 if (*t == '\0') /* Need more than one token.*/ 132 continue; 133 *t = '\0'; 134 135 tp = gettag(p, 1); 136 if (!tp) 137 errx(EXIT_FAILURE, "gettag: malloc failed"); 138 139 /* 140 * Attach new records. Check to see if it is a 141 * section record or not. 142 */ 143 144 if (*p == '_') { /* not a section record */ 145 /* 146 * Special cases: _build and _crunch take the 147 * rest of the line as a single entry. 148 */ 149 if (!strcmp(p, "_build") || !strcmp(p, "_crunch")) { 150 /* 151 * The reason we're not just using 152 * strtok(3) for all of the parsing is 153 * so we don't get caught if a line 154 * has only a single token on it. 155 */ 156 while (*++t && isspace((unsigned char)*t)); 157 if (addentry(tp, t, 0) == -1) 158 errx(EXIT_FAILURE, 159 "addentry: malloc failed"); 160 } else { 161 for (++t; (p = strtok(t, " \t\n")) != NULL; 162 t = NULL) { 163 if (addentry(tp, p, 0) == -1) 164 errx(EXIT_FAILURE, 165 "addentry: malloc failed"); 166 } 167 } 168 169 } else { /* section record */ 170 171 /* 172 * section entries can either be all absolute 173 * paths or all relative paths, but not both. 174 */ 175 type = (char)((TAILQ_FIRST(&tp->entrylist) != NULL) ? 176 *(TAILQ_FIRST(&tp->entrylist)->s) : '\0'); 177 178 for (++t; (p = strtok(t, " \t\n")) != NULL; t = NULL) { 179 180 /* ensure an assigned type */ 181 if (type == 0) 182 type = *p; 183 184 /* check for illegal mix */ 185 if (*p != type) { 186 warnx("section %s: %s: invalid entry, does not match previous types", 187 tp->s, p); 188 warnx("man.conf cannot mix absolute and relative paths in an entry"); 189 continue; 190 } 191 if (addentry(tp, p, 0) == -1) 192 errx(EXIT_FAILURE, 193 "addentry: malloc failed"); 194 } 195 } 196 } 197 (void)fclose(cfp); 198 } 199 200 /* 201 * gettag -- 202 * if (!create) return tag for given name if it exists, or NULL otherwise 203 * 204 * if (create) return tag for given name if it exists, try and create 205 * a new tag if it does not exist. return NULL if unable to create new 206 * tag. 207 */ 208 TAG * 209 gettag(const char *name, int create) 210 { 211 TAG *tp; 212 213 TAILQ_FOREACH(tp, &head, q) 214 if (!strcmp(name, tp->s)) 215 return tp; 216 if (!create) 217 return NULL; 218 219 /* try and add it in */ 220 tp = malloc(sizeof(*tp)); 221 if (tp) 222 tp->s = xstrdup(name, &tp->len); 223 if (!tp || !tp->s) { 224 if (tp) 225 free(tp); 226 return NULL; 227 } 228 TAILQ_INIT(&tp->entrylist); 229 TAILQ_INSERT_TAIL(&head, tp, q); 230 return tp; 231 } 232 233 /* 234 * addentry -- 235 * add an entry to a list. 236 * returns -1 if malloc failed, otherwise 0. 237 */ 238 int 239 addentry(TAG *tp, const char *newent, int ishead) 240 { 241 ENTRY *ep; 242 243 ep = malloc(sizeof(*ep)); 244 if (ep) 245 ep->s = xstrdup(newent, &ep->len); 246 if (!ep || !ep->s) { 247 if (ep) 248 free(ep); 249 return -1; 250 } 251 if (ishead) 252 TAILQ_INSERT_HEAD(&tp->entrylist, ep, q); 253 else 254 TAILQ_INSERT_TAIL(&tp->entrylist, ep, q); 255 256 return 0; 257 } 258