GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/tag.c Lines: 76 100 76.0 %
Date: 2017-11-07 Branches: 37 66 56.1 %

Line Branch Exec Source
1
/*	$OpenBSD: tag.c,v 1.18 2017/02/09 17:19:07 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2015, 2016 Ingo Schwarze <schwarze@openbsd.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
#include <sys/types.h>
18
19
#include <signal.h>
20
#include <stddef.h>
21
#include <stdint.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <unistd.h>
26
27
#include "mandoc_aux.h"
28
#include "mandoc_ohash.h"
29
#include "tag.h"
30
31
struct tag_entry {
32
	size_t	*lines;
33
	size_t	 maxlines;
34
	size_t	 nlines;
35
	int	 prio;
36
	char	 s[];
37
};
38
39
static	void	 tag_signal(int) __attribute__((__noreturn__));
40
41
static struct ohash	 tag_data;
42
static struct tag_files	 tag_files;
43
44
45
/*
46
 * Prepare for using a pager.
47
 * Not all pagers are capable of using a tag file,
48
 * but for simplicity, create it anyway.
49
 */
50
struct tag_files *
51
tag_init(void)
52
{
53
10
	struct sigaction	 sa;
54
	int			 ofd;
55
56
	ofd = -1;
57
5
	tag_files.tfd = -1;
58
5
	tag_files.tcpgid = -1;
59
60
	/* Clean up when dying from a signal. */
61
62
5
	memset(&sa, 0, sizeof(sa));
63
5
	sigfillset(&sa.sa_mask);
64
5
	sa.sa_handler = tag_signal;
65
5
	sigaction(SIGHUP, &sa, NULL);
66
5
	sigaction(SIGINT, &sa, NULL);
67
5
	sigaction(SIGTERM, &sa, NULL);
68
69
	/*
70
	 * POSIX requires that a process calling tcsetpgrp(3)
71
	 * from the background gets a SIGTTOU signal.
72
	 * In that case, do not stop.
73
	 */
74
75
5
	sa.sa_handler = SIG_IGN;
76
5
	sigaction(SIGTTOU, &sa, NULL);
77
78
	/* Save the original standard output for use by the pager. */
79
80
5
	if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1)
81
		goto fail;
82
83
	/* Create both temporary output files. */
84
85
5
	(void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX",
86
	    sizeof(tag_files.ofn));
87
5
	(void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX",
88
	    sizeof(tag_files.tfn));
89
5
	if ((ofd = mkstemp(tag_files.ofn)) == -1)
90
		goto fail;
91
5
	if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1)
92
		goto fail;
93
5
	if (dup2(ofd, STDOUT_FILENO) == -1)
94
		goto fail;
95
5
	close(ofd);
96
97
	/*
98
	 * Set up the ohash table to collect output line numbers
99
	 * where various marked-up terms are documented.
100
	 */
101
102
5
	mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s));
103
5
	return &tag_files;
104
105
fail:
106
	tag_unlink();
107
	if (ofd != -1)
108
		close(ofd);
109
	if (tag_files.ofd != -1)
110
		close(tag_files.ofd);
111
	if (tag_files.tfd != -1)
112
		close(tag_files.tfd);
113
	*tag_files.ofn = '\0';
114
	*tag_files.tfn = '\0';
115
	tag_files.ofd = -1;
116
	tag_files.tfd = -1;
117
	return NULL;
118
5
}
119
120
/*
121
 * Set the line number where a term is defined,
122
 * unless it is already defined at a higher priority.
123
 */
124
void
125
tag_put(const char *s, int prio, size_t line)
126
{
127
	struct tag_entry	*entry;
128
	size_t			 len;
129
	unsigned int		 slot;
130
131
	/* Sanity checks. */
132
133
5132
	if (tag_files.tfd <= 0)
134
2475
		return;
135

91
	if (s[0] == '\\' && (s[1] == '&' || s[1] == 'e'))
136
		s += 2;
137

182
	if (*s == '\0' || strchr(s, ' ') != NULL)
138
3
		return;
139
140
88
	slot = ohash_qlookup(&tag_data, s);
141
88
	entry = ohash_find(&tag_data, slot);
142
143
88
	if (entry == NULL) {
144
145
		/* Build a new entry. */
146
147
76
		len = strlen(s) + 1;
148
76
		entry = mandoc_malloc(sizeof(*entry) + len);
149
76
		memcpy(entry->s, s, len);
150
76
		entry->lines = NULL;
151
76
		entry->maxlines = entry->nlines = 0;
152
76
		ohash_insert(&tag_data, slot, entry);
153
154
76
	} else {
155
156
		/* Handle priority 0 entries. */
157
158
12
		if (prio == 0) {
159
7
			if (entry->prio == 0)
160
3
				entry->prio = -1;
161
7
			return;
162
		}
163
164
		/* A better entry is already present, ignore the new one. */
165
166

10
		if (entry->prio > 0 && entry->prio < prio)
167
			return;
168
169
		/* The existing entry is worse, clear it. */
170
171

10
		if (entry->prio < 1 || entry->prio > prio)
172
			entry->nlines = 0;
173
	}
174
175
	/* Remember the new line. */
176
177
81
	if (entry->maxlines == entry->nlines) {
178
76
		entry->maxlines += 4;
179
76
		entry->lines = mandoc_reallocarray(entry->lines,
180
		    entry->maxlines, sizeof(*entry->lines));
181
76
	}
182
81
	entry->lines[entry->nlines++] = line;
183
81
	entry->prio = prio;
184
2647
}
185
186
/*
187
 * Write out the tags file using the previously collected
188
 * information and clear the ohash table while going along.
189
 */
190
void
191
tag_write(void)
192
{
193
	FILE			*stream;
194
	struct tag_entry	*entry;
195
	size_t			 i;
196
10
	unsigned int		 slot;
197
198
5
	if (tag_files.tfd <= 0)
199
		return;
200
5
	stream = fdopen(tag_files.tfd, "w");
201
5
	entry = ohash_first(&tag_data, &slot);
202
162
	while (entry != NULL) {
203

152
		if (stream != NULL && entry->prio >= 0)
204
302
			for (i = 0; i < entry->nlines; i++)
205
78
				fprintf(stream, "%s %s %zu\n",
206
78
				    entry->s, tag_files.ofn, entry->lines[i]);
207
76
		free(entry->lines);
208
76
		free(entry);
209
76
		entry = ohash_next(&tag_data, &slot);
210
	}
211
5
	ohash_delete(&tag_data);
212
5
	if (stream != NULL)
213
5
		fclose(stream);
214
10
}
215
216
void
217
tag_unlink(void)
218
{
219
	pid_t	 tc_pgid;
220
221
10
	if (tag_files.tcpgid != -1) {
222
5
		tc_pgid = tcgetpgrp(tag_files.ofd);
223

10
		if (tc_pgid == tag_files.pager_pid ||
224
5
		    tc_pgid == getpgid(0) ||
225
5
		    getpgid(tc_pgid) == -1)
226
5
			(void)tcsetpgrp(tag_files.ofd, tag_files.tcpgid);
227
	}
228
5
	if (*tag_files.ofn != '\0')
229
5
		unlink(tag_files.ofn);
230
5
	if (*tag_files.tfn != '\0')
231
5
		unlink(tag_files.tfn);
232
5
}
233
234
static void
235
tag_signal(int signum)
236
{
237
	struct sigaction	 sa;
238
239
	tag_unlink();
240
	memset(&sa, 0, sizeof(sa));
241
	sigemptyset(&sa.sa_mask);
242
	sa.sa_handler = SIG_DFL;
243
	sigaction(signum, &sa, NULL);
244
	kill(getpid(), signum);
245
	/* NOTREACHED */
246
	_exit(1);
247
}