xref: /minix/usr.bin/man/manconf.c (revision 84d9c625)
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