]>
git.cameronkatri.com Git - mandoc.git/blob - tag.c
1 /* $Id: tag.c,v 1.27 2020/01/20 10:37:15 schwarze Exp $ */
3 * Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze <schwarze@openbsd.org>
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.
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.
19 #include <sys/types.h>
32 #include "mandoc_aux.h"
33 #include "mandoc_ohash.h"
45 static void tag_signal(int) __attribute__((__noreturn__
));
47 static struct ohash tag_data
;
48 static struct tag_files tag_files
;
52 * Prepare for using a pager.
53 * Not all pagers are capable of using a tag file,
54 * but for simplicity, create it anyway.
57 tag_init(char *tagname
)
64 tag_files
.tcpgid
= -1;
65 tag_files
.tagname
= tagname
;
67 /* Clean up when dying from a signal. */
69 memset(&sa
, 0, sizeof(sa
));
70 sigfillset(&sa
.sa_mask
);
71 sa
.sa_handler
= tag_signal
;
72 sigaction(SIGHUP
, &sa
, NULL
);
73 sigaction(SIGINT
, &sa
, NULL
);
74 sigaction(SIGTERM
, &sa
, NULL
);
77 * POSIX requires that a process calling tcsetpgrp(3)
78 * from the background gets a SIGTTOU signal.
79 * In that case, do not stop.
82 sa
.sa_handler
= SIG_IGN
;
83 sigaction(SIGTTOU
, &sa
, NULL
);
85 /* Save the original standard output for use by the pager. */
87 if ((tag_files
.ofd
= dup(STDOUT_FILENO
)) == -1) {
88 mandoc_msg(MANDOCERR_DUP
, 0, 0, "%s", strerror(errno
));
92 /* Create both temporary output files. */
94 (void)strlcpy(tag_files
.ofn
, "/tmp/man.XXXXXXXXXX",
95 sizeof(tag_files
.ofn
));
96 (void)strlcpy(tag_files
.tfn
, "/tmp/man.XXXXXXXXXX",
97 sizeof(tag_files
.tfn
));
98 if ((ofd
= mkstemp(tag_files
.ofn
)) == -1) {
99 mandoc_msg(MANDOCERR_MKSTEMP
, 0, 0,
100 "%s: %s", tag_files
.ofn
, strerror(errno
));
103 if ((tag_files
.tfd
= mkstemp(tag_files
.tfn
)) == -1) {
104 mandoc_msg(MANDOCERR_MKSTEMP
, 0, 0,
105 "%s: %s", tag_files
.tfn
, strerror(errno
));
108 if (dup2(ofd
, STDOUT_FILENO
) == -1) {
109 mandoc_msg(MANDOCERR_DUP
, 0, 0, "%s", strerror(errno
));
115 * Set up the ohash table to collect output line numbers
116 * where various marked-up terms are documented.
119 mandoc_ohash_init(&tag_data
, 4, offsetof(struct tag_entry
, s
));
126 if (tag_files
.ofd
!= -1)
127 close(tag_files
.ofd
);
128 if (tag_files
.tfd
!= -1)
129 close(tag_files
.tfd
);
130 *tag_files
.ofn
= '\0';
131 *tag_files
.tfn
= '\0';
134 tag_files
.tagname
= NULL
;
139 * Set the line number where a term is defined,
140 * unless it is already defined at a lower priority.
143 tag_put(const char *s
, int prio
, size_t line
)
145 struct tag_entry
*entry
;
150 assert(prio
<= TAG_FALLBACK
);
151 if (tag_files
.tfd
<= 0)
154 if (s
[0] == '\\' && (s
[1] == '&' || s
[1] == 'e'))
158 * Skip whitespace and escapes and whatever follows,
159 * and if there is any, downgrade the priority.
162 len
= strcspn(s
, " \t\\");
167 if (*se
!= '\0' && prio
< TAG_WEAK
)
170 slot
= ohash_qlookupi(&tag_data
, s
, &se
);
171 entry
= ohash_find(&tag_data
, slot
);
175 /* Build a new entry. */
177 entry
= mandoc_malloc(sizeof(*entry
) + len
+ 1);
178 memcpy(entry
->s
, s
, len
);
179 entry
->s
[len
] = '\0';
181 entry
->maxlines
= entry
->nlines
= 0;
182 ohash_insert(&tag_data
, slot
, entry
);
187 * Lower priority numbers take precedence,
188 * but TAG_FALLBACK is special.
189 * A tag with priority TAG_FALLBACK is only used
190 * if the tag occurs exactly once.
193 if (prio
== TAG_FALLBACK
) {
194 if (entry
->prio
== TAG_FALLBACK
)
195 entry
->prio
= TAG_DELETE
;
199 /* A better entry is already present, ignore the new one. */
201 if (entry
->prio
< prio
)
204 /* The existing entry is worse, clear it. */
206 if (entry
->prio
> prio
)
210 /* Remember the new line. */
212 if (entry
->maxlines
== entry
->nlines
) {
213 entry
->maxlines
+= 4;
214 entry
->lines
= mandoc_reallocarray(entry
->lines
,
215 entry
->maxlines
, sizeof(*entry
->lines
));
217 entry
->lines
[entry
->nlines
++] = line
;
222 * Write out the tags file using the previously collected
223 * information and clear the ohash table while going along.
229 struct tag_entry
*entry
;
234 if (tag_files
.tfd
<= 0)
236 if (tag_files
.tagname
!= NULL
&& ohash_find(&tag_data
,
237 ohash_qlookup(&tag_data
, tag_files
.tagname
)) == NULL
) {
238 mandoc_msg(MANDOCERR_TAG
, 0, 0, "%s", tag_files
.tagname
);
239 tag_files
.tagname
= NULL
;
241 if ((stream
= fdopen(tag_files
.tfd
, "w")) == NULL
)
242 mandoc_msg(MANDOCERR_FDOPEN
, 0, 0, "%s", strerror(errno
));
244 entry
= ohash_first(&tag_data
, &slot
);
245 while (entry
!= NULL
) {
246 if (stream
!= NULL
&& entry
->prio
< TAG_DELETE
) {
247 for (i
= 0; i
< entry
->nlines
; i
++) {
248 fprintf(stream
, "%s %s %zu\n",
249 entry
->s
, tag_files
.ofn
, entry
->lines
[i
]);
255 entry
= ohash_next(&tag_data
, &slot
);
257 ohash_delete(&tag_data
);
261 close(tag_files
.tfd
);
264 unlink(tag_files
.tfn
);
265 *tag_files
.tfn
= '\0';
274 if (tag_files
.tcpgid
!= -1) {
275 tc_pgid
= tcgetpgrp(tag_files
.ofd
);
276 if (tc_pgid
== tag_files
.pager_pid
||
277 tc_pgid
== getpgid(0) ||
278 getpgid(tc_pgid
) == -1)
279 (void)tcsetpgrp(tag_files
.ofd
, tag_files
.tcpgid
);
281 if (*tag_files
.ofn
!= '\0')
282 unlink(tag_files
.ofn
);
283 if (*tag_files
.tfn
!= '\0')
284 unlink(tag_files
.tfn
);
288 tag_signal(int signum
)
293 memset(&sa
, 0, sizeof(sa
));
294 sigemptyset(&sa
.sa_mask
);
295 sa
.sa_handler
= SIG_DFL
;
296 sigaction(signum
, &sa
, NULL
);
297 kill(getpid(), signum
);