xref: /netbsd-src/usr.bin/man/manconf.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
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