GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/mandoc/tag.c Lines: 0 76 0.0 %
Date: 2016-12-06 Branches: 0 42 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: tag.c,v 1.13 2016/07/20 13:02:44 schwarze Exp $ */
2
/*
3
 * Copyright (c) 2015 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	 line;
33
	int	 prio;
34
	char	 s[];
35
};
36
37
static	void	 tag_signal(int) __attribute__((noreturn));
38
39
static struct ohash	 tag_data;
40
static struct tag_files	 tag_files;
41
42
43
/*
44
 * Prepare for using a pager.
45
 * Not all pagers are capable of using a tag file,
46
 * but for simplicity, create it anyway.
47
 */
48
struct tag_files *
49
tag_init(void)
50
{
51
	struct sigaction	 sa;
52
	int			 ofd;
53
54
	ofd = -1;
55
	tag_files.tfd = -1;
56
	tag_files.tcpgid = -1;
57
58
	/* Clean up when dying from a signal. */
59
60
	memset(&sa, 0, sizeof(sa));
61
	sigfillset(&sa.sa_mask);
62
	sa.sa_handler = tag_signal;
63
	sigaction(SIGHUP, &sa, NULL);
64
	sigaction(SIGINT, &sa, NULL);
65
	sigaction(SIGTERM, &sa, NULL);
66
67
	/*
68
	 * POSIX requires that a process calling tcsetpgrp(3)
69
	 * from the background gets a SIGTTOU signal.
70
	 * In that case, do not stop.
71
	 */
72
73
	sa.sa_handler = SIG_IGN;
74
	sigaction(SIGTTOU, &sa, NULL);
75
76
	/* Save the original standard output for use by the pager. */
77
78
	if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1)
79
		goto fail;
80
81
	/* Create both temporary output files. */
82
83
	(void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX",
84
	    sizeof(tag_files.ofn));
85
	(void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX",
86
	    sizeof(tag_files.tfn));
87
	if ((ofd = mkstemp(tag_files.ofn)) == -1)
88
		goto fail;
89
	if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1)
90
		goto fail;
91
	if (dup2(ofd, STDOUT_FILENO) == -1)
92
		goto fail;
93
	close(ofd);
94
95
	/*
96
	 * Set up the ohash table to collect output line numbers
97
	 * where various marked-up terms are documented.
98
	 */
99
100
	mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s));
101
	return &tag_files;
102
103
fail:
104
	tag_unlink();
105
	if (ofd != -1)
106
		close(ofd);
107
	if (tag_files.ofd != -1)
108
		close(tag_files.ofd);
109
	if (tag_files.tfd != -1)
110
		close(tag_files.tfd);
111
	*tag_files.ofn = '\0';
112
	*tag_files.tfn = '\0';
113
	tag_files.ofd = -1;
114
	tag_files.tfd = -1;
115
	return NULL;
116
}
117
118
/*
119
 * Set the line number where a term is defined,
120
 * unless it is already defined at a higher priority.
121
 */
122
void
123
tag_put(const char *s, int prio, size_t line)
124
{
125
	struct tag_entry	*entry;
126
	size_t			 len;
127
	unsigned int		 slot;
128
129
	if (tag_files.tfd <= 0 || strchr(s, ' ') != NULL)
130
		return;
131
	slot = ohash_qlookup(&tag_data, s);
132
	entry = ohash_find(&tag_data, slot);
133
	if (entry == NULL) {
134
		len = strlen(s) + 1;
135
		entry = mandoc_malloc(sizeof(*entry) + len);
136
		memcpy(entry->s, s, len);
137
		ohash_insert(&tag_data, slot, entry);
138
	} else if (entry->prio <= prio)
139
		return;
140
	entry->line = line;
141
	entry->prio = prio;
142
}
143
144
/*
145
 * Write out the tags file using the previously collected
146
 * information and clear the ohash table while going along.
147
 */
148
void
149
tag_write(void)
150
{
151
	FILE			*stream;
152
	struct tag_entry	*entry;
153
	unsigned int		 slot;
154
155
	if (tag_files.tfd <= 0)
156
		return;
157
	stream = fdopen(tag_files.tfd, "w");
158
	entry = ohash_first(&tag_data, &slot);
159
	while (entry != NULL) {
160
		if (stream != NULL)
161
			fprintf(stream, "%s %s %zu\n",
162
			    entry->s, tag_files.ofn, entry->line);
163
		free(entry);
164
		entry = ohash_next(&tag_data, &slot);
165
	}
166
	ohash_delete(&tag_data);
167
	if (stream != NULL)
168
		fclose(stream);
169
}
170
171
void
172
tag_unlink(void)
173
{
174
	pid_t	 tc_pgid;
175
176
	if (tag_files.tcpgid != -1) {
177
		tc_pgid = tcgetpgrp(STDIN_FILENO);
178
		if (tc_pgid == tag_files.pager_pid ||
179
		    tc_pgid == getpgid(0) ||
180
		    getpgid(tc_pgid) == -1)
181
			(void)tcsetpgrp(STDIN_FILENO, tag_files.tcpgid);
182
	}
183
	if (*tag_files.ofn != '\0')
184
		unlink(tag_files.ofn);
185
	if (*tag_files.tfn != '\0')
186
		unlink(tag_files.tfn);
187
}
188
189
static void
190
tag_signal(int signum)
191
{
192
	struct sigaction	 sa;
193
194
	tag_unlink();
195
	memset(&sa, 0, sizeof(sa));
196
	sigemptyset(&sa.sa_mask);
197
	sa.sa_handler = SIG_DFL;
198
	sigaction(signum, &sa, NULL);
199
	kill(getpid(), signum);
200
	/* NOTREACHED */
201
	_exit(1);
202
}