]> git.cameronkatri.com Git - mandoc.git/blob - tag.c
Now that our man.conf(5) format is mature and extremely simple,
[mandoc.git] / tag.c
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 }