xref: /illumos-gate/usr/src/cmd/mandoc/tag.c (revision d2a70789)
1 /*      $Id: tag.c,v 1.12 2016/07/08 20:42:15 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 "config.h"
18 
19 #include <sys/types.h>
20 
21 #include <signal.h>
22 #include <stddef.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 
29 #include "mandoc_aux.h"
30 #include "mandoc_ohash.h"
31 #include "tag.h"
32 
33 struct tag_entry {
34 	size_t	 line;
35 	int	 prio;
36 	char	 s[];
37 };
38 
39 static	void	 tag_signal(int);
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 	if (tag_files.tfd <= 0 || strchr(s, ' ') != NULL)
132 		return;
133 	slot = ohash_qlookup(&tag_data, s);
134 	entry = ohash_find(&tag_data, slot);
135 	if (entry == NULL) {
136 		len = strlen(s) + 1;
137 		entry = mandoc_malloc(sizeof(*entry) + len);
138 		memcpy(entry->s, s, len);
139 		ohash_insert(&tag_data, slot, entry);
140 	} else if (entry->prio <= prio)
141 		return;
142 	entry->line = line;
143 	entry->prio = prio;
144 }
145 
146 /*
147  * Write out the tags file using the previously collected
148  * information and clear the ohash table while going along.
149  */
150 void
151 tag_write(void)
152 {
153 	FILE			*stream;
154 	struct tag_entry	*entry;
155 	unsigned int		 slot;
156 
157 	if (tag_files.tfd <= 0)
158 		return;
159 	stream = fdopen(tag_files.tfd, "w");
160 	entry = ohash_first(&tag_data, &slot);
161 	while (entry != NULL) {
162 		if (stream != NULL)
163 			fprintf(stream, "%s %s %zu\n",
164 			    entry->s, tag_files.ofn, entry->line);
165 		free(entry);
166 		entry = ohash_next(&tag_data, &slot);
167 	}
168 	ohash_delete(&tag_data);
169 	if (stream != NULL)
170 		fclose(stream);
171 }
172 
173 void
174 tag_unlink(void)
175 {
176 	pid_t	 tc_pgid;
177 
178 	if (tag_files.tcpgid != -1) {
179 		tc_pgid = tcgetpgrp(STDIN_FILENO);
180 		if (tc_pgid == tag_files.pager_pid ||
181 		    tc_pgid == getpgid(0) ||
182 		    getpgid(tc_pgid) == -1)
183 			(void)tcsetpgrp(STDIN_FILENO, tag_files.tcpgid);
184 	}
185 	if (*tag_files.ofn != '\0')
186 		unlink(tag_files.ofn);
187 	if (*tag_files.tfn != '\0')
188 		unlink(tag_files.tfn);
189 }
190 
191 static void
192 tag_signal(int signum)
193 {
194 	struct sigaction	 sa;
195 
196 	tag_unlink();
197 	memset(&sa, 0, sizeof(sa));
198 	sigemptyset(&sa.sa_mask);
199 	sa.sa_handler = SIG_DFL;
200 	sigaction(signum, &sa, NULL);
201 	kill(getpid(), signum);
202 	/* NOTREACHED */
203 	_exit(1);
204 }
205