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