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