]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/tag.c
MFV r300961:
[FreeBSD/FreeBSD.git] / contrib / mdocml / tag.c
1 /*      $Id: tag.c,v 1.11 2015/11/20 21:59:54 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         /* Save the original standard output for use by the pager. */
61
62         if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1)
63                 goto fail;
64
65         /* Create both temporary output files. */
66
67         (void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX",
68             sizeof(tag_files.ofn));
69         (void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX",
70             sizeof(tag_files.tfn));
71         memset(&sa, 0, sizeof(sa));
72         sigfillset(&sa.sa_mask);
73         sa.sa_handler = tag_signal;
74         sigaction(SIGHUP, &sa, NULL);
75         sigaction(SIGINT, &sa, NULL);
76         sigaction(SIGTERM, &sa, NULL);
77         if ((ofd = mkstemp(tag_files.ofn)) == -1)
78                 goto fail;
79         if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1)
80                 goto fail;
81         if (dup2(ofd, STDOUT_FILENO) == -1)
82                 goto fail;
83         close(ofd);
84
85         /*
86          * Set up the ohash table to collect output line numbers
87          * where various marked-up terms are documented.
88          */
89
90         mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s));
91         return &tag_files;
92
93 fail:
94         tag_unlink();
95         if (ofd != -1)
96                 close(ofd);
97         if (tag_files.ofd != -1)
98                 close(tag_files.ofd);
99         if (tag_files.tfd != -1)
100                 close(tag_files.tfd);
101         *tag_files.ofn = '\0';
102         *tag_files.tfn = '\0';
103         tag_files.ofd = -1;
104         tag_files.tfd = -1;
105         return NULL;
106 }
107
108 /*
109  * Set the line number where a term is defined,
110  * unless it is already defined at a higher priority.
111  */
112 void
113 tag_put(const char *s, int prio, size_t line)
114 {
115         struct tag_entry        *entry;
116         size_t                   len;
117         unsigned int             slot;
118
119         if (tag_files.tfd <= 0 || strchr(s, ' ') != NULL)
120                 return;
121         slot = ohash_qlookup(&tag_data, s);
122         entry = ohash_find(&tag_data, slot);
123         if (entry == NULL) {
124                 len = strlen(s) + 1;
125                 entry = mandoc_malloc(sizeof(*entry) + len);
126                 memcpy(entry->s, s, len);
127                 ohash_insert(&tag_data, slot, entry);
128         } else if (entry->prio <= prio)
129                 return;
130         entry->line = line;
131         entry->prio = prio;
132 }
133
134 /*
135  * Write out the tags file using the previously collected
136  * information and clear the ohash table while going along.
137  */
138 void
139 tag_write(void)
140 {
141         FILE                    *stream;
142         struct tag_entry        *entry;
143         unsigned int             slot;
144
145         if (tag_files.tfd <= 0)
146                 return;
147         stream = fdopen(tag_files.tfd, "w");
148         entry = ohash_first(&tag_data, &slot);
149         while (entry != NULL) {
150                 if (stream != NULL)
151                         fprintf(stream, "%s %s %zu\n",
152                             entry->s, tag_files.ofn, entry->line);
153                 free(entry);
154                 entry = ohash_next(&tag_data, &slot);
155         }
156         ohash_delete(&tag_data);
157         if (stream != NULL)
158                 fclose(stream);
159 }
160
161 void
162 tag_unlink(void)
163 {
164         pid_t    tc_pgid;
165
166         if (tag_files.tcpgid != -1) {
167                 tc_pgid = tcgetpgrp(STDIN_FILENO);
168                 if (tc_pgid == tag_files.pager_pid ||
169                     tc_pgid == getpgid(0) ||
170                     getpgid(tc_pgid) == -1)
171                         (void)tcsetpgrp(STDIN_FILENO, tag_files.tcpgid);
172         }
173         if (*tag_files.ofn != '\0')
174                 unlink(tag_files.ofn);
175         if (*tag_files.tfn != '\0')
176                 unlink(tag_files.tfn);
177 }
178
179 static void
180 tag_signal(int signum)
181 {
182         struct sigaction         sa;
183
184         tag_unlink();
185         memset(&sa, 0, sizeof(sa));
186         sigemptyset(&sa.sa_mask);
187         sa.sa_handler = SIG_DFL;
188         sigaction(signum, &sa, NULL);
189         kill(getpid(), signum);
190         /* NOTREACHED */
191         _exit(1);
192 }