Merge branch 'vendor/TCSH'
[dragonfly.git] / contrib / mdocml / manpath.c
1 /*      $Id: manpath.c,v 1.8 2011/12/24 22:37:16 kristaps 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 <sys/param.h>
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <limits.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include "mandoc.h"
32 #include "manpath.h"
33
34 #define MAN_CONF_FILE   "/etc/man.conf"
35 #define MAN_CONF_KEY    "_whatdb"
36
37 static  void     manpath_add(struct manpaths *, const char *);
38 static  void     manpath_parseline(struct manpaths *, char *);
39
40 void
41 manpath_parse(struct manpaths *dirs, const char *file,
42                 char *defp, char *auxp)
43 {
44 #ifdef  USE_MANPATH
45         char             cmd[(MAXPATHLEN * 3) + 20];
46         FILE            *stream;
47         char            *buf;
48         size_t           sz, bsz;
49
50         strlcpy(cmd, "manpath", sizeof(cmd));
51         if (file) {
52                 strlcat(cmd, " -C ", sizeof(cmd));
53                 strlcat(cmd, file, sizeof(cmd));
54         }
55         if (auxp) {
56                 strlcat(cmd, " -m ", sizeof(cmd));
57                 strlcat(cmd, auxp, sizeof(cmd));
58         }
59         if (defp) {
60                 strlcat(cmd, " -M ", sizeof(cmd));
61                 strlcat(cmd, defp, sizeof(cmd));
62         }
63
64         /* Open manpath(1).  Ignore errors. */
65
66         stream = popen(cmd, "r");
67         if (NULL == stream)
68                 return;
69
70         buf = NULL;
71         bsz = 0;
72
73         /* Read in as much output as we can. */
74
75         do {
76                 buf = mandoc_realloc(buf, bsz + 1024);
77                 sz = fread(buf + (int)bsz, 1, 1024, stream);
78                 bsz += sz;
79         } while (sz > 0);
80
81         if ( ! ferror(stream) && feof(stream) &&
82                         bsz && '\n' == buf[bsz - 1]) {
83                 buf[bsz - 1] = '\0';
84                 manpath_parseline(dirs, buf);
85         }
86
87         free(buf);
88         pclose(stream);
89 #else
90         char            *insert;
91
92         /* Always prepend -m. */
93         manpath_parseline(dirs, auxp);
94  
95         /* If -M is given, it overrides everything else. */
96         if (NULL != defp) {
97                 manpath_parseline(dirs, defp);
98                 return;
99         }
100
101         /* MANPATH and man.conf(5) cooperate. */
102         defp = getenv("MANPATH");
103         if (NULL == file)
104                 file = MAN_CONF_FILE;
105
106         /* No MANPATH; use man.conf(5) only. */
107         if (NULL == defp || '\0' == defp[0]) {
108                 manpath_manconf(dirs, file);
109                 return;
110         }
111
112         /* Prepend man.conf(5) to MANPATH. */
113         if (':' == defp[0]) {
114                 manpath_manconf(dirs, file);
115                 manpath_parseline(dirs, defp);
116                 return;
117         }
118
119         /* Append man.conf(5) to MANPATH. */
120         if (':' == defp[(int)strlen(defp) - 1]) {
121                 manpath_parseline(dirs, defp);
122                 manpath_manconf(dirs, file);
123                 return;
124         }
125
126         /* Insert man.conf(5) into MANPATH. */
127         insert = strstr(defp, "::");
128         if (NULL != insert) {
129                 *insert++ = '\0';
130                 manpath_parseline(dirs, defp);
131                 manpath_manconf(dirs, file);
132                 manpath_parseline(dirs, insert + 1);
133                 return;
134         }
135
136         /* MANPATH overrides man.conf(5) completely. */
137         manpath_parseline(dirs, defp);
138 #endif
139 }
140
141 /*
142  * Parse a FULL pathname from a colon-separated list of arrays.
143  */
144 static void
145 manpath_parseline(struct manpaths *dirs, char *path)
146 {
147         char    *dir;
148
149         if (NULL == path)
150                 return;
151
152         for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
153                 manpath_add(dirs, dir);
154 }
155
156 /*
157  * Add a directory to the array, ignoring bad directories.
158  * Grow the array one-by-one for simplicity's sake.
159  */
160 static void
161 manpath_add(struct manpaths *dirs, const char *dir)
162 {
163         char             buf[PATH_MAX];
164         char            *cp;
165         int              i;
166
167         if (NULL == (cp = realpath(dir, buf)))
168                 return;
169
170         for (i = 0; i < dirs->sz; i++)
171                 if (0 == strcmp(dirs->paths[i], dir))
172                         return;
173
174         dirs->paths = mandoc_realloc
175                 (dirs->paths,
176                  ((size_t)dirs->sz + 1) * sizeof(char *));
177
178         dirs->paths[dirs->sz++] = mandoc_strdup(cp);
179 }
180
181 void
182 manpath_free(struct manpaths *p)
183 {
184         int              i;
185
186         for (i = 0; i < p->sz; i++)
187                 free(p->paths[i]);
188
189         free(p->paths);
190 }
191
192 void
193 manpath_manconf(struct manpaths *dirs, const char *file)
194 {
195         FILE            *stream;
196         char            *p, *q;
197         size_t           len, keysz;
198
199         keysz = strlen(MAN_CONF_KEY);
200         assert(keysz > 0);
201
202         if (NULL == (stream = fopen(file, "r")))
203                 return;
204
205         while (NULL != (p = fgetln(stream, &len))) {
206                 if (0 == len || '\n' != p[--len])
207                         break;
208                 p[len] = '\0';
209                 while (isspace((unsigned char)*p))
210                         p++;
211                 if (strncmp(MAN_CONF_KEY, p, keysz))
212                         continue;
213                 p += keysz;
214                 while (isspace(*p))
215                         p++;
216                 if ('\0' == *p)
217                         continue;
218                 if (NULL == (q = strrchr(p, '/')))
219                         continue;
220                 *q = '\0';
221                 manpath_add(dirs, p);
222         }
223
224         fclose(stream);
225 }