xref: /openbsd-src/usr.bin/mandoc/manpath.c (revision 263f9f363dde15cf5c18e57b11e3d64e6ecc047c)
1 /*	$OpenBSD: manpath.c,v 1.12 2015/03/21 17:18:17 schwarze Exp $ */
2 /*
3  * Copyright (c) 2011, 2014, 2015 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 <sys/types.h>
19 #include <sys/stat.h>
20 
21 #include <assert.h>
22 #include <ctype.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "mandoc_aux.h"
29 #include "manpath.h"
30 
31 #define MAN_CONF_FILE	"/etc/man.conf"
32 #define MAN_CONF_KEY	"_whatdb"
33 #define MANPATH_DEFAULT	"/usr/share/man:/usr/X11R6/man:/usr/local/man"
34 
35 static	void	 manpath_add(struct manpaths *, const char *, int);
36 static	void	 manpath_parseline(struct manpaths *, char *, int);
37 
38 void
39 manpath_parse(struct manpaths *dirs, const char *file,
40 		char *defp, char *auxp)
41 {
42 	char		 manpath_default[] = MANPATH_DEFAULT;
43 	char		*insert;
44 
45 	/* Always prepend -m. */
46 	manpath_parseline(dirs, auxp, 1);
47 
48 	/* If -M is given, it overrides everything else. */
49 	if (NULL != defp) {
50 		manpath_parseline(dirs, defp, 1);
51 		return;
52 	}
53 
54 	/* MANPATH and man.conf(5) cooperate. */
55 	defp = getenv("MANPATH");
56 	if (NULL == file)
57 		file = MAN_CONF_FILE;
58 
59 	/* No MANPATH; use man.conf(5) only. */
60 	if (NULL == defp || '\0' == defp[0]) {
61 		manpath_manconf(dirs, file);
62 		if (dirs->sz == 0)
63 			manpath_parseline(dirs, manpath_default, 0);
64 		return;
65 	}
66 
67 	/* Prepend man.conf(5) to MANPATH. */
68 	if (':' == defp[0]) {
69 		manpath_manconf(dirs, file);
70 		manpath_parseline(dirs, defp, 0);
71 		return;
72 	}
73 
74 	/* Append man.conf(5) to MANPATH. */
75 	if (':' == defp[strlen(defp) - 1]) {
76 		manpath_parseline(dirs, defp, 0);
77 		manpath_manconf(dirs, file);
78 		return;
79 	}
80 
81 	/* Insert man.conf(5) into MANPATH. */
82 	insert = strstr(defp, "::");
83 	if (NULL != insert) {
84 		*insert++ = '\0';
85 		manpath_parseline(dirs, defp, 0);
86 		manpath_manconf(dirs, file);
87 		manpath_parseline(dirs, insert + 1, 0);
88 		return;
89 	}
90 
91 	/* MANPATH overrides man.conf(5) completely. */
92 	manpath_parseline(dirs, defp, 0);
93 }
94 
95 /*
96  * Parse a FULL pathname from a colon-separated list of arrays.
97  */
98 static void
99 manpath_parseline(struct manpaths *dirs, char *path, int complain)
100 {
101 	char	*dir;
102 
103 	if (NULL == path)
104 		return;
105 
106 	for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
107 		manpath_add(dirs, dir, complain);
108 }
109 
110 /*
111  * Add a directory to the array, ignoring bad directories.
112  * Grow the array one-by-one for simplicity's sake.
113  */
114 static void
115 manpath_add(struct manpaths *dirs, const char *dir, int complain)
116 {
117 	char		 buf[PATH_MAX];
118 	struct stat	 sb;
119 	char		*cp;
120 	size_t		 i;
121 
122 	if (NULL == (cp = realpath(dir, buf))) {
123 		if (complain) {
124 			fputs("manpath: ", stderr);
125 			perror(dir);
126 		}
127 		return;
128 	}
129 
130 	for (i = 0; i < dirs->sz; i++)
131 		if (0 == strcmp(dirs->paths[i], dir))
132 			return;
133 
134 	if (stat(cp, &sb) == -1) {
135 		if (complain) {
136 			fputs("manpath: ", stderr);
137 			perror(dir);
138 		}
139 		return;
140 	}
141 
142 	dirs->paths = mandoc_reallocarray(dirs->paths,
143 	    dirs->sz + 1, sizeof(char *));
144 
145 	dirs->paths[dirs->sz++] = mandoc_strdup(cp);
146 }
147 
148 void
149 manpath_free(struct manpaths *p)
150 {
151 	size_t		 i;
152 
153 	for (i = 0; i < p->sz; i++)
154 		free(p->paths[i]);
155 
156 	free(p->paths);
157 }
158 
159 void
160 manpath_manconf(struct manpaths *dirs, const char *file)
161 {
162 	FILE		*stream;
163 	char		*p, *q;
164 	size_t		 len, keysz;
165 
166 	keysz = strlen(MAN_CONF_KEY);
167 	assert(keysz > 0);
168 
169 	if (NULL == (stream = fopen(file, "r")))
170 		return;
171 
172 	while (NULL != (p = fgetln(stream, &len))) {
173 		if (0 == len || '\n' != p[--len])
174 			break;
175 		p[len] = '\0';
176 		while (isspace((unsigned char)*p))
177 			p++;
178 		if (strncmp(MAN_CONF_KEY, p, keysz))
179 			continue;
180 		p += keysz;
181 		while (isspace((unsigned char)*p))
182 			p++;
183 		if ('\0' == *p)
184 			continue;
185 		if (NULL == (q = strrchr(p, '/')))
186 			continue;
187 		*q = '\0';
188 		manpath_add(dirs, p, 0);
189 	}
190 
191 	fclose(stream);
192 }
193