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