]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mandoc/tag.c
readelf: add newline after dumping dynamic FLAGS / FLAGS_1
[FreeBSD/FreeBSD.git] / contrib / mandoc / tag.c
1 /*      $Id: tag.c,v 1.19 2018/02/23 16:47:10 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 "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  *lines;
35         size_t   maxlines;
36         size_t   nlines;
37         int      prio;
38         char     s[];
39 };
40
41 static  void     tag_signal(int) __attribute__((__noreturn__));
42
43 static struct ohash      tag_data;
44 static struct tag_files  tag_files;
45
46
47 /*
48  * Prepare for using a pager.
49  * Not all pagers are capable of using a tag file,
50  * but for simplicity, create it anyway.
51  */
52 struct tag_files *
53 tag_init(void)
54 {
55         struct sigaction         sa;
56         int                      ofd;
57
58         ofd = -1;
59         tag_files.tfd = -1;
60         tag_files.tcpgid = -1;
61
62         /* Clean up when dying from a signal. */
63
64         memset(&sa, 0, sizeof(sa));
65         sigfillset(&sa.sa_mask);
66         sa.sa_handler = tag_signal;
67         sigaction(SIGHUP, &sa, NULL);
68         sigaction(SIGINT, &sa, NULL);
69         sigaction(SIGTERM, &sa, NULL);
70
71         /*
72          * POSIX requires that a process calling tcsetpgrp(3)
73          * from the background gets a SIGTTOU signal.
74          * In that case, do not stop.
75          */
76
77         sa.sa_handler = SIG_IGN;
78         sigaction(SIGTTOU, &sa, NULL);
79
80         /* Save the original standard output for use by the pager. */
81
82         if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1)
83                 goto fail;
84
85         /* Create both temporary output files. */
86
87         (void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX",
88             sizeof(tag_files.ofn));
89         (void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX",
90             sizeof(tag_files.tfn));
91         if ((ofd = mkstemp(tag_files.ofn)) == -1)
92                 goto fail;
93         if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1)
94                 goto fail;
95         if (dup2(ofd, STDOUT_FILENO) == -1)
96                 goto fail;
97         close(ofd);
98
99         /*
100          * Set up the ohash table to collect output line numbers
101          * where various marked-up terms are documented.
102          */
103
104         mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s));
105         return &tag_files;
106
107 fail:
108         tag_unlink();
109         if (ofd != -1)
110                 close(ofd);
111         if (tag_files.ofd != -1)
112                 close(tag_files.ofd);
113         if (tag_files.tfd != -1)
114                 close(tag_files.tfd);
115         *tag_files.ofn = '\0';
116         *tag_files.tfn = '\0';
117         tag_files.ofd = -1;
118         tag_files.tfd = -1;
119         return NULL;
120 }
121
122 /*
123  * Set the line number where a term is defined,
124  * unless it is already defined at a higher priority.
125  */
126 void
127 tag_put(const char *s, int prio, size_t line)
128 {
129         struct tag_entry        *entry;
130         size_t                   len;
131         unsigned int             slot;
132
133         /* Sanity checks. */
134
135         if (tag_files.tfd <= 0)
136                 return;
137         if (s[0] == '\\' && (s[1] == '&' || s[1] == 'e'))
138                 s += 2;
139         if (*s == '\0' || strchr(s, ' ') != NULL)
140                 return;
141
142         slot = ohash_qlookup(&tag_data, s);
143         entry = ohash_find(&tag_data, slot);
144
145         if (entry == NULL) {
146
147                 /* Build a new entry. */
148
149                 len = strlen(s) + 1;
150                 entry = mandoc_malloc(sizeof(*entry) + len);
151                 memcpy(entry->s, s, len);
152                 entry->lines = NULL;
153                 entry->maxlines = entry->nlines = 0;
154                 ohash_insert(&tag_data, slot, entry);
155
156         } else {
157
158                 /* Handle priority 0 entries. */
159
160                 if (prio == 0) {
161                         if (entry->prio == 0)
162                                 entry->prio = -1;
163                         return;
164                 }
165
166                 /* A better entry is already present, ignore the new one. */
167
168                 if (entry->prio > 0 && entry->prio < prio)
169                         return;
170
171                 /* The existing entry is worse, clear it. */
172
173                 if (entry->prio < 1 || entry->prio > prio)
174                         entry->nlines = 0;
175         }
176
177         /* Remember the new line. */
178
179         if (entry->maxlines == entry->nlines) {
180                 entry->maxlines += 4;
181                 entry->lines = mandoc_reallocarray(entry->lines,
182                     entry->maxlines, sizeof(*entry->lines));
183         }
184         entry->lines[entry->nlines++] = line;
185         entry->prio = prio;
186 }
187
188 /*
189  * Write out the tags file using the previously collected
190  * information and clear the ohash table while going along.
191  */
192 void
193 tag_write(void)
194 {
195         FILE                    *stream;
196         struct tag_entry        *entry;
197         size_t                   i;
198         unsigned int             slot;
199
200         if (tag_files.tfd <= 0)
201                 return;
202         stream = fdopen(tag_files.tfd, "w");
203         entry = ohash_first(&tag_data, &slot);
204         while (entry != NULL) {
205                 if (stream != NULL && entry->prio >= 0)
206                         for (i = 0; i < entry->nlines; i++)
207                                 fprintf(stream, "%s %s %zu\n",
208                                     entry->s, tag_files.ofn, entry->lines[i]);
209                 free(entry->lines);
210                 free(entry);
211                 entry = ohash_next(&tag_data, &slot);
212         }
213         ohash_delete(&tag_data);
214         if (stream != NULL)
215                 fclose(stream);
216         else
217                 close(tag_files.tfd);
218         tag_files.tfd = -1;
219 }
220
221 void
222 tag_unlink(void)
223 {
224         pid_t    tc_pgid;
225
226         if (tag_files.tcpgid != -1) {
227                 tc_pgid = tcgetpgrp(tag_files.ofd);
228                 if (tc_pgid == tag_files.pager_pid ||
229                     tc_pgid == getpgid(0) ||
230                     getpgid(tc_pgid) == -1)
231                         (void)tcsetpgrp(tag_files.ofd, tag_files.tcpgid);
232         }
233         if (*tag_files.ofn != '\0')
234                 unlink(tag_files.ofn);
235         if (*tag_files.tfn != '\0')
236                 unlink(tag_files.tfn);
237 }
238
239 static void
240 tag_signal(int signum)
241 {
242         struct sigaction         sa;
243
244         tag_unlink();
245         memset(&sa, 0, sizeof(sa));
246         sigemptyset(&sa.sa_mask);
247         sa.sa_handler = SIG_DFL;
248         sigaction(signum, &sa, NULL);
249         kill(getpid(), signum);
250         /* NOTREACHED */
251         _exit(1);
252 }