xref: /dragonfly/contrib/mdocml/manpath.c (revision d4ef6694)
1 /*	$Id: manpath.c,v 1.15 2014/04/23 21:06:41 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_aux.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_reallocarray(dirs->paths,
173 	    dirs->sz + 1, sizeof(char *));
174 
175 	dirs->paths[dirs->sz++] = mandoc_strdup(cp);
176 }
177 
178 void
179 manpath_free(struct manpaths *p)
180 {
181 	size_t		 i;
182 
183 	for (i = 0; i < p->sz; i++)
184 		free(p->paths[i]);
185 
186 	free(p->paths);
187 }
188 
189 void
190 manpath_manconf(struct manpaths *dirs, const char *file)
191 {
192 	FILE		*stream;
193 	char		*p, *q;
194 	size_t		 len, keysz;
195 
196 	keysz = strlen(MAN_CONF_KEY);
197 	assert(keysz > 0);
198 
199 	if (NULL == (stream = fopen(file, "r")))
200 		return;
201 
202 	while (NULL != (p = fgetln(stream, &len))) {
203 		if (0 == len || '\n' != p[--len])
204 			break;
205 		p[len] = '\0';
206 		while (isspace((unsigned char)*p))
207 			p++;
208 		if (strncmp(MAN_CONF_KEY, p, keysz))
209 			continue;
210 		p += keysz;
211 		while (isspace((unsigned char)*p))
212 			p++;
213 		if ('\0' == *p)
214 			continue;
215 		if (NULL == (q = strrchr(p, '/')))
216 			continue;
217 		*q = '\0';
218 		manpath_add(dirs, p);
219 	}
220 
221 	fclose(stream);
222 }
223