90089323e390ad1c40575b956b8a6b714d2bd438
[dragonfly.git] / contrib / mdocml / manpath.c
1 /*      $Id: manpath.c,v 1.11 2013/06/05 02:00:26 schwarze Exp $ */
2 /*
3  * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
4  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <assert.h>
23 #include <ctype.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "mandoc.h"
30 #include "manpath.h"
31
32 #define MAN_CONF_FILE   "/etc/man.conf"
33 #define MAN_CONF_KEY    "_whatdb"
34
35 static  void     manpath_add(struct manpaths *, const char *);
36 static  void     manpath_parseline(struct manpaths *, char *);
37
38 void
39 manpath_parse(struct manpaths *dirs, const char *file,
40                 char *defp, char *auxp)
41 {
42 #ifdef  USE_MANPATH
43         char             cmd[(PATH_MAX * 3) + 20];
44         FILE            *stream;
45         char            *buf;
46         size_t           sz, bsz;
47
48         strlcpy(cmd, "manpath", sizeof(cmd));
49         if (file) {
50                 strlcat(cmd, " -C ", sizeof(cmd));
51                 strlcat(cmd, file, sizeof(cmd));
52         }
53         if (auxp) {
54                 strlcat(cmd, " -m ", sizeof(cmd));
55                 strlcat(cmd, auxp, sizeof(cmd));
56         }
57         if (defp) {
58                 strlcat(cmd, " -M ", sizeof(cmd));
59                 strlcat(cmd, defp, sizeof(cmd));
60         }
61
62         /* Open manpath(1).  Ignore errors. */
63
64         stream = popen(cmd, "r");
65         if (NULL == stream)
66                 return;
67
68         buf = NULL;
69         bsz = 0;
70
71         /* Read in as much output as we can. */
72
73         do {
74                 buf = mandoc_realloc(buf, bsz + 1024);
75                 sz = fread(buf + bsz, 1, 1024, stream);
76                 bsz += sz;
77         } while (sz > 0);
78
79         if ( ! ferror(stream) && feof(stream) &&
80                         bsz && '\n' == buf[bsz - 1]) {
81                 buf[bsz - 1] = '\0';
82                 manpath_parseline(dirs, buf);
83         }
84
85         free(buf);
86         pclose(stream);
87 #else
88         char            *insert;
89
90         /* Always prepend -m. */
91         manpath_parseline(dirs, auxp);
92
93         /* If -M is given, it overrides everything else. */
94         if (NULL != defp) {
95                 manpath_parseline(dirs, defp);
96                 return;
97         }
98
99         /* MANPATH and man.conf(5) cooperate. */
100         defp = getenv("MANPATH");
101         if (NULL == file)
102                 file = MAN_CONF_FILE;
103
104         /* No MANPATH; use man.conf(5) only. */
105         if (NULL == defp || '\0' == defp[0]) {
106                 manpath_manconf(dirs, file);
107                 return;
108         }
109
110         /* Prepend man.conf(5) to MANPATH. */
111         if (':' == defp[0]) {
112                 manpath_manconf(dirs, file);
113                 manpath_parseline(dirs, defp);
114                 return;
115         }
116
117         /* Append man.conf(5) to MANPATH. */
118         if (':' == defp[strlen(defp) - 1]) {
119                 manpath_parseline(dirs, defp);
120                 manpath_manconf(dirs, file);
121                 return;
122         }
123
124         /* Insert man.conf(5) into MANPATH. */
125         insert = strstr(defp, "::");
126         if (NULL != insert) {
127                 *insert++ = '\0';
128                 manpath_parseline(dirs, defp);
129                 manpath_manconf(dirs, file);
130                 manpath_parseline(dirs, insert + 1);
131                 return;
132         }
133
134         /* MANPATH overrides man.conf(5) completely. */
135         manpath_parseline(dirs, defp);
136 #endif
137 }
138
139 /*
140  * Parse a FULL pathname from a colon-separated list of arrays.
141  */
142 static void
143 manpath_parseline(struct manpaths *dirs, char *path)
144 {
145         char    *dir;
146
147         if (NULL == path)
148                 return;
149
150         for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
151                 manpath_add(dirs, dir);
152 }
153
154 /*
155  * Add a directory to the array, ignoring bad directories.
156  * Grow the array one-by-one for simplicity's sake.
157  */
158 static void
159 manpath_add(struct manpaths *dirs, const char *dir)
160 {
161         char             buf[PATH_MAX];
162         char            *cp;
163         size_t           i;
164
165         if (NULL == (cp = realpath(dir, buf)))
166                 return;
167
168         for (i = 0; i < dirs->sz; i++)
169                 if (0 == strcmp(dirs->paths[i], dir))
170                         return;
171
172         dirs->paths = mandoc_realloc
173                 (dirs->paths,
174                  (dirs->sz + 1) * sizeof(char *));
175
176         dirs->paths[dirs->sz++] = mandoc_strdup(cp);
177 }
178
179 void
180 manpath_free(struct manpaths *p)
181 {
182         size_t           i;
183
184         for (i = 0; i < p->sz; i++)
185                 free(p->paths[i]);
186
187         free(p->paths);
188 }
189
190 void
191 manpath_manconf(struct manpaths *dirs, const char *file)
192 {
193         FILE            *stream;
194         char            *p, *q;
195         size_t           len, keysz;
196
197         keysz = strlen(MAN_CONF_KEY);
198         assert(keysz > 0);
199
200         if (NULL == (stream = fopen(file, "r")))
201                 return;
202
203         while (NULL != (p = fgetln(stream, &len))) {
204                 if (0 == len || '\n' != p[--len])
205                         break;
206                 p[len] = '\0';
207                 while (isspace((unsigned char)*p))
208                         p++;
209                 if (strncmp(MAN_CONF_KEY, p, keysz))
210                         continue;
211                 p += keysz;
212                 while (isspace(*p))
213                         p++;
214                 if ('\0' == *p)
215                         continue;
216                 if (NULL == (q = strrchr(p, '/')))
217                         continue;
218                 *q = '\0';
219                 manpath_add(dirs, p);
220         }
221
222         fclose(stream);
223 }