xref: /openbsd-src/usr.bin/mandoc/manpath.c (revision e5157e49389faebcb42b7237d55fbf096d9c2523)
1 /*	$Id: manpath.c,v 1.10 2014/04/23 21:06:33 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_aux.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_reallocarray(dirs->paths,
123 	    dirs->sz + 1, sizeof(char *));
124 
125 	dirs->paths[dirs->sz++] = mandoc_strdup(cp);
126 }
127 
128 void
129 manpath_free(struct manpaths *p)
130 {
131 	size_t		 i;
132 
133 	for (i = 0; i < p->sz; i++)
134 		free(p->paths[i]);
135 
136 	free(p->paths);
137 }
138 
139 void
140 manpath_manconf(struct manpaths *dirs, const char *file)
141 {
142 	FILE		*stream;
143 	char		*p, *q;
144 	size_t		 len, keysz;
145 
146 	keysz = strlen(MAN_CONF_KEY);
147 	assert(keysz > 0);
148 
149 	if (NULL == (stream = fopen(file, "r")))
150 		return;
151 
152 	while (NULL != (p = fgetln(stream, &len))) {
153 		if (0 == len || '\n' != p[--len])
154 			break;
155 		p[len] = '\0';
156 		while (isspace((unsigned char)*p))
157 			p++;
158 		if (strncmp(MAN_CONF_KEY, p, keysz))
159 			continue;
160 		p += keysz;
161 		while (isspace((unsigned char)*p))
162 			p++;
163 		if ('\0' == *p)
164 			continue;
165 		if (NULL == (q = strrchr(p, '/')))
166 			continue;
167 		*q = '\0';
168 		manpath_add(dirs, p);
169 	}
170 
171 	fclose(stream);
172 }
173