GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/manpath.c Lines: 45 164 27.4 %
Date: 2017-11-13 Branches: 22 116 19.0 %

Line Branch Exec Source
1
/*	$OpenBSD: manpath.c,v 1.22 2017/07/01 09:47:23 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2011, 2014, 2015, 2017 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 AUTHORS DISCLAIM ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 <ctype.h>
22
#include <err.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 "manconf.h"
30
31
#define MAN_CONF_FILE	"/etc/man.conf"
32
#define MANPATH_BASE	"/usr/share/man:/usr/X11R6/man"
33
#define MANPATH_DEFAULT	"/usr/share/man:/usr/X11R6/man:/usr/local/man"
34
35
static	void	 manconf_file(struct manconf *, const char *);
36
static	void	 manpath_add(struct manpaths *, const char *, int);
37
static	void	 manpath_parseline(struct manpaths *, char *, int);
38
39
40
void
41
manconf_parse(struct manconf *conf, const char *file,
42
		char *defp, char *auxp)
43
{
44
	char		*insert;
45
46
	/* Always prepend -m. */
47
184
	manpath_parseline(&conf->manpath, auxp, 1);
48
49
	/* If -M is given, it overrides everything else. */
50
92
	if (NULL != defp) {
51
92
		manpath_parseline(&conf->manpath, defp, 1);
52
92
		return;
53
	}
54
55
	/* MANPATH and man.conf(5) cooperate. */
56
	defp = getenv("MANPATH");
57
	if (NULL == file)
58
		file = MAN_CONF_FILE;
59
60
	/* No MANPATH; use man.conf(5) only. */
61
	if (NULL == defp || '\0' == defp[0]) {
62
		manconf_file(conf, file);
63
		return;
64
	}
65
66
	/* Prepend man.conf(5) to MANPATH. */
67
	if (':' == defp[0]) {
68
		manconf_file(conf, file);
69
		manpath_parseline(&conf->manpath, defp, 0);
70
		return;
71
	}
72
73
	/* Append man.conf(5) to MANPATH. */
74
	if (':' == defp[strlen(defp) - 1]) {
75
		manpath_parseline(&conf->manpath, defp, 0);
76
		manconf_file(conf, file);
77
		return;
78
	}
79
80
	/* Insert man.conf(5) into MANPATH. */
81
	insert = strstr(defp, "::");
82
	if (NULL != insert) {
83
		*insert++ = '\0';
84
		manpath_parseline(&conf->manpath, defp, 0);
85
		manconf_file(conf, file);
86
		manpath_parseline(&conf->manpath, insert + 1, 0);
87
		return;
88
	}
89
90
	/* MANPATH overrides man.conf(5) completely. */
91
	manpath_parseline(&conf->manpath, defp, 0);
92
92
}
93
94
void
95
manpath_base(struct manpaths *dirs)
96
{
97
1374
	char path_base[] = MANPATH_BASE;
98
687
	manpath_parseline(dirs, path_base, 0);
99
687
}
100
101
/*
102
 * Parse a FULL pathname from a colon-separated list of arrays.
103
 */
104
static void
105
manpath_parseline(struct manpaths *dirs, char *path, int complain)
106
{
107
	char	*dir;
108
109
1742
	if (NULL == path)
110
92
		return;
111
112
4490
	for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":"))
113
1466
		manpath_add(dirs, dir, complain);
114
1650
}
115
116
/*
117
 * Add a directory to the array, ignoring bad directories.
118
 * Grow the array one-by-one for simplicity's sake.
119
 */
120
static void
121
manpath_add(struct manpaths *dirs, const char *dir, int complain)
122
{
123
2932
	char		 buf[PATH_MAX];
124
1466
	struct stat	 sb;
125
	char		*cp;
126
	size_t		 i;
127
128
1466
	if (NULL == (cp = realpath(dir, buf))) {
129
		if (complain)
130
			warn("manpath: %s", dir);
131
		return;
132
	}
133
134
4306
	for (i = 0; i < dirs->sz; i++)
135
687
		if (0 == strcmp(dirs->paths[i], dir))
136
			return;
137
138
1466
	if (stat(cp, &sb) == -1) {
139
		if (complain)
140
			warn("manpath: %s", dir);
141
		return;
142
	}
143
144
2932
	dirs->paths = mandoc_reallocarray(dirs->paths,
145
1466
	    dirs->sz + 1, sizeof(char *));
146
147
1466
	dirs->paths[dirs->sz++] = mandoc_strdup(cp);
148
2932
}
149
150
void
151
manconf_free(struct manconf *conf)
152
{
153
	size_t		 i;
154
155
645
	for (i = 0; i < conf->manpath.sz; i++)
156
129
		free(conf->manpath.paths[i]);
157
158
129
	free(conf->manpath.paths);
159
129
	free(conf->output.includes);
160
129
	free(conf->output.man);
161
129
	free(conf->output.paper);
162
129
	free(conf->output.style);
163
129
}
164
165
static void
166
manconf_file(struct manconf *conf, const char *file)
167
{
168
	const char *const toks[] = { "manpath", "output", "_whatdb" };
169
	char manpath_default[] = MANPATH_DEFAULT;
170
171
	FILE		*stream;
172
	char		*line, *cp, *ep;
173
	size_t		 linesz, tok, toklen;
174
	ssize_t		 linelen;
175
176
	if ((stream = fopen(file, "r")) == NULL)
177
		goto out;
178
179
	line = NULL;
180
	linesz = 0;
181
182
	while ((linelen = getline(&line, &linesz, stream)) != -1) {
183
		cp = line;
184
		ep = cp + linelen - 1;
185
		while (ep > cp && isspace((unsigned char)*ep))
186
			*ep-- = '\0';
187
		while (isspace((unsigned char)*cp))
188
			cp++;
189
		if (cp == ep || *cp == '#')
190
			continue;
191
192
		for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
193
			toklen = strlen(toks[tok]);
194
			if (cp + toklen < ep &&
195
			    isspace((unsigned char)cp[toklen]) &&
196
			    strncmp(cp, toks[tok], toklen) == 0) {
197
				cp += toklen;
198
				while (isspace((unsigned char)*cp))
199
					cp++;
200
				break;
201
			}
202
		}
203
204
		switch (tok) {
205
		case 2:  /* _whatdb */
206
			while (ep > cp && ep[-1] != '/')
207
				ep--;
208
			if (ep == cp)
209
				continue;
210
			*ep = '\0';
211
			/* FALLTHROUGH */
212
		case 0:  /* manpath */
213
			manpath_add(&conf->manpath, cp, 0);
214
			*manpath_default = '\0';
215
			break;
216
		case 1:  /* output */
217
			manconf_output(&conf->output, cp, 1);
218
			break;
219
		default:
220
			break;
221
		}
222
	}
223
	free(line);
224
	fclose(stream);
225
226
out:
227
	if (*manpath_default != '\0')
228
		manpath_parseline(&conf->manpath, manpath_default, 0);
229
}
230
231
int
232
manconf_output(struct manoutput *conf, const char *cp, int fromfile)
233
{
234
	const char *const toks[] = {
235
	    "includes", "man", "paper", "style",
236
	    "indent", "width", "fragment", "mdoc", "noval"
237
	};
238
239
1266
	const char	*errstr;
240
633
	char		*oldval;
241
	size_t		 len, tok;
242
243
10128
	for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) {
244
5064
		len = strlen(toks[tok]);
245

5697
		if ( ! strncmp(cp, toks[tok], len) &&
246
633
		    strchr(" =	", cp[len]) != NULL) {
247
			cp += len;
248
633
			if (*cp == '=')
249
				cp++;
250
1266
			while (isspace((unsigned char)*cp))
251
				cp++;
252
			break;
253
		}
254
	}
255
256

633
	if (tok < 6 && *cp == '\0') {
257
		warnx("-O %s=?: Missing argument value", toks[tok]);
258
		return -1;
259
	}
260

1266
	if ((tok == 6 || tok == 7) && *cp != '\0') {
261
		warnx("-O %s: Does not take a value: %s", toks[tok], cp);
262
		return -1;
263
	}
264
265


633
	switch (tok) {
266
	case 0:
267
		if (conf->includes != NULL) {
268
			oldval = mandoc_strdup(conf->includes);
269
			break;
270
		}
271
		conf->includes = mandoc_strdup(cp);
272
		return 0;
273
	case 1:
274
		if (conf->man != NULL) {
275
			oldval = mandoc_strdup(conf->man);
276
			break;
277
		}
278
		conf->man = mandoc_strdup(cp);
279
		return 0;
280
	case 2:
281
		if (conf->paper != NULL) {
282
			oldval = mandoc_strdup(conf->paper);
283
			break;
284
		}
285
		conf->paper = mandoc_strdup(cp);
286
		return 0;
287
	case 3:
288
		if (conf->style != NULL) {
289
			oldval = mandoc_strdup(conf->style);
290
			break;
291
		}
292
		conf->style = mandoc_strdup(cp);
293
		return 0;
294
	case 4:
295
		if (conf->indent) {
296
			mandoc_asprintf(&oldval, "%zu", conf->indent);
297
			break;
298
		}
299
		conf->indent = strtonum(cp, 0, 1000, &errstr);
300
		if (errstr == NULL)
301
			return 0;
302
		warnx("-O indent=%s is %s", cp, errstr);
303
		return -1;
304
	case 5:
305
		if (conf->width) {
306
			mandoc_asprintf(&oldval, "%zu", conf->width);
307
			break;
308
		}
309
		conf->width = strtonum(cp, 1, 1000, &errstr);
310
		if (errstr == NULL)
311
			return 0;
312
		warnx("-O width=%s is %s", cp, errstr);
313
		return -1;
314
	case 6:
315
		conf->fragment = 1;
316
		return 0;
317
	case 7:
318
633
		conf->mdoc = 1;
319
633
		return 0;
320
	case 8:
321
		conf->noval = 1;
322
		return 0;
323
	default:
324
		if (fromfile)
325
			warnx("-O %s: Bad argument", cp);
326
		return -1;
327
	}
328
	if (fromfile == 0)
329
		warnx("-O %s=%s: Option already set to %s",
330
		    toks[tok], cp, oldval);
331
	free(oldval);
332
	return -1;
333
633
}