man(1): import man(1) replacement
[dragonfly.git] / usr.bin / man / manconf.c
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 }