GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/tag.c Lines: 3 96 3.1 %
Date: 2017-11-13 Branches: 1 66 1.5 %

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
	struct sigaction	 sa;
54
	int			 ofd;
55
56
	ofd = -1;
57
	tag_files.tfd = -1;
58
	tag_files.tcpgid = -1;
59
60
	/* Clean up when dying from a signal. */
61
62
	memset(&sa, 0, sizeof(sa));
63
	sigfillset(&sa.sa_mask);
64
	sa.sa_handler = tag_signal;
65
	sigaction(SIGHUP, &sa, NULL);
66
	sigaction(SIGINT, &sa, NULL);
67
	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
	sa.sa_handler = SIG_IGN;
76
	sigaction(SIGTTOU, &sa, NULL);
77
78
	/* Save the original standard output for use by the pager. */
79
80
	if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1)
81
		goto fail;
82
83
	/* Create both temporary output files. */
84
85
	(void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX",
86
	    sizeof(tag_files.ofn));
87
	(void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX",
88
	    sizeof(tag_files.tfn));
89
	if ((ofd = mkstemp(tag_files.ofn)) == -1)
90
		goto fail;
91
	if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1)
92
		goto fail;
93
	if (dup2(ofd, STDOUT_FILENO) == -1)
94
		goto fail;
95
	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
	mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s));
103
	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
}
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
1650
	if (tag_files.tfd <= 0)
134
825
		return;
135
	if (s[0] == '\\' && (s[1] == '&' || s[1] == 'e'))
136
		s += 2;
137
	if (*s == '\0' || strchr(s, ' ') != NULL)
138
		return;
139
140
	slot = ohash_qlookup(&tag_data, s);
141
	entry = ohash_find(&tag_data, slot);
142
143
	if (entry == NULL) {
144
145
		/* Build a new entry. */
146
147
		len = strlen(s) + 1;
148
		entry = mandoc_malloc(sizeof(*entry) + len);
149
		memcpy(entry->s, s, len);
150
		entry->lines = NULL;
151
		entry->maxlines = entry->nlines = 0;
152
		ohash_insert(&tag_data, slot, entry);
153
154
	} else {
155
156
		/* Handle priority 0 entries. */
157
158
		if (prio == 0) {
159
			if (entry->prio == 0)
160
				entry->prio = -1;
161
			return;
162
		}
163
164
		/* A better entry is already present, ignore the new one. */
165
166
		if (entry->prio > 0 && entry->prio < prio)
167
			return;
168
169
		/* The existing entry is worse, clear it. */
170
171
		if (entry->prio < 1 || entry->prio > prio)
172
			entry->nlines = 0;
173
	}
174
175
	/* Remember the new line. */
176
177
	if (entry->maxlines == entry->nlines) {
178
		entry->maxlines += 4;
179
		entry->lines = mandoc_reallocarray(entry->lines,
180
		    entry->maxlines, sizeof(*entry->lines));
181
	}
182
	entry->lines[entry->nlines++] = line;
183
	entry->prio = prio;
184
825
}
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
	unsigned int		 slot;
197
198
	if (tag_files.tfd <= 0)
199
		return;
200
	stream = fdopen(tag_files.tfd, "w");
201
	entry = ohash_first(&tag_data, &slot);
202
	while (entry != NULL) {
203
		if (stream != NULL && entry->prio >= 0)
204
			for (i = 0; i < entry->nlines; i++)
205
				fprintf(stream, "%s %s %zu\n",
206
				    entry->s, tag_files.ofn, entry->lines[i]);
207
		free(entry->lines);
208
		free(entry);
209
		entry = ohash_next(&tag_data, &slot);
210
	}
211
	ohash_delete(&tag_data);
212
	if (stream != NULL)
213
		fclose(stream);
214
}
215
216
void
217
tag_unlink(void)
218
{
219
	pid_t	 tc_pgid;
220
221
	if (tag_files.tcpgid != -1) {
222
		tc_pgid = tcgetpgrp(tag_files.ofd);
223
		if (tc_pgid == tag_files.pager_pid ||
224
		    tc_pgid == getpgid(0) ||
225
		    getpgid(tc_pgid) == -1)
226
			(void)tcsetpgrp(tag_files.ofd, tag_files.tcpgid);
227
	}
228
	if (*tag_files.ofn != '\0')
229
		unlink(tag_files.ofn);
230
	if (*tag_files.tfn != '\0')
231
		unlink(tag_files.tfn);
232
}
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
}