]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/tag.c
Import Concurrency Kit in the kernel.
[FreeBSD/FreeBSD.git] / contrib / mdocml / 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 }