xref: /minix3/usr.bin/man/manconf.c (revision 0c3983b25a88161cf074524e5c94585a2582ae82)
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