Annotation of src/usr.bin/mandoc/tag.c, Revision 1.13
1.13 ! schwarze 1: /* $OpenBSD: tag.c,v 1.12 2016/07/08 20:41:13 schwarze Exp $ */
1.1 schwarze 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 <sys/types.h>
18:
1.2 schwarze 19: #include <signal.h>
1.1 schwarze 20: #include <stddef.h>
1.7 schwarze 21: #include <stdint.h>
1.1 schwarze 22: #include <stdio.h>
23: #include <stdlib.h>
24: #include <string.h>
25: #include <unistd.h>
26:
27: #include "mandoc_aux.h"
1.10 schwarze 28: #include "mandoc_ohash.h"
1.1 schwarze 29: #include "tag.h"
30:
31: struct tag_entry {
32: size_t line;
1.4 schwarze 33: int prio;
1.1 schwarze 34: char s[];
35: };
36:
1.13 ! schwarze 37: static void tag_signal(int) __attribute__((noreturn));
1.1 schwarze 38:
39: static struct ohash tag_data;
1.6 schwarze 40: static struct tag_files tag_files;
1.1 schwarze 41:
42:
43: /*
1.6 schwarze 44: * Prepare for using a pager.
45: * Not all pagers are capable of using a tag file,
46: * but for simplicity, create it anyway.
1.1 schwarze 47: */
1.6 schwarze 48: struct tag_files *
1.1 schwarze 49: tag_init(void)
50: {
1.11 schwarze 51: struct sigaction sa;
1.6 schwarze 52: int ofd;
1.1 schwarze 53:
1.6 schwarze 54: ofd = -1;
55: tag_files.tfd = -1;
1.11 schwarze 56: tag_files.tcpgid = -1;
1.6 schwarze 57:
1.12 schwarze 58: /* Clean up when dying from a signal. */
59:
60: memset(&sa, 0, sizeof(sa));
61: sigfillset(&sa.sa_mask);
62: sa.sa_handler = tag_signal;
63: sigaction(SIGHUP, &sa, NULL);
64: sigaction(SIGINT, &sa, NULL);
65: sigaction(SIGTERM, &sa, NULL);
66:
67: /*
68: * POSIX requires that a process calling tcsetpgrp(3)
69: * from the background gets a SIGTTOU signal.
70: * In that case, do not stop.
71: */
72:
73: sa.sa_handler = SIG_IGN;
74: sigaction(SIGTTOU, &sa, NULL);
75:
1.6 schwarze 76: /* Save the original standard output for use by the pager. */
77:
78: if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1)
79: goto fail;
80:
81: /* Create both temporary output files. */
82:
83: (void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX",
84: sizeof(tag_files.ofn));
85: (void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX",
86: sizeof(tag_files.tfn));
87: if ((ofd = mkstemp(tag_files.ofn)) == -1)
88: goto fail;
89: if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1)
90: goto fail;
91: if (dup2(ofd, STDOUT_FILENO) == -1)
92: goto fail;
93: close(ofd);
94:
95: /*
96: * Set up the ohash table to collect output line numbers
97: * where various marked-up terms are documented.
98: */
1.1 schwarze 99:
1.10 schwarze 100: mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s));
1.8 schwarze 101: return &tag_files;
1.6 schwarze 102:
103: fail:
104: tag_unlink();
105: if (ofd != -1)
106: close(ofd);
107: if (tag_files.ofd != -1)
108: close(tag_files.ofd);
109: if (tag_files.tfd != -1)
110: close(tag_files.tfd);
111: *tag_files.ofn = '\0';
112: *tag_files.tfn = '\0';
113: tag_files.ofd = -1;
114: tag_files.tfd = -1;
1.8 schwarze 115: return NULL;
1.1 schwarze 116: }
117:
118: /*
1.5 schwarze 119: * Set the line number where a term is defined,
120: * unless it is already defined at a higher priority.
1.1 schwarze 121: */
122: void
1.5 schwarze 123: tag_put(const char *s, int prio, size_t line)
1.1 schwarze 124: {
125: struct tag_entry *entry;
1.5 schwarze 126: size_t len;
1.1 schwarze 127: unsigned int slot;
128:
1.9 schwarze 129: if (tag_files.tfd <= 0 || strchr(s, ' ') != NULL)
1.1 schwarze 130: return;
1.5 schwarze 131: slot = ohash_qlookup(&tag_data, s);
1.1 schwarze 132: entry = ohash_find(&tag_data, slot);
133: if (entry == NULL) {
1.5 schwarze 134: len = strlen(s) + 1;
135: entry = mandoc_malloc(sizeof(*entry) + len);
1.1 schwarze 136: memcpy(entry->s, s, len);
137: ohash_insert(&tag_data, slot, entry);
1.5 schwarze 138: } else if (entry->prio <= prio)
139: return;
1.1 schwarze 140: entry->line = line;
1.4 schwarze 141: entry->prio = prio;
1.1 schwarze 142: }
143:
144: /*
145: * Write out the tags file using the previously collected
146: * information and clear the ohash table while going along.
147: */
148: void
149: tag_write(void)
150: {
151: FILE *stream;
152: struct tag_entry *entry;
153: unsigned int slot;
154:
1.6 schwarze 155: if (tag_files.tfd <= 0)
1.1 schwarze 156: return;
1.6 schwarze 157: stream = fdopen(tag_files.tfd, "w");
1.1 schwarze 158: entry = ohash_first(&tag_data, &slot);
159: while (entry != NULL) {
160: if (stream != NULL)
1.6 schwarze 161: fprintf(stream, "%s %s %zu\n",
162: entry->s, tag_files.ofn, entry->line);
1.1 schwarze 163: free(entry);
164: entry = ohash_next(&tag_data, &slot);
165: }
166: ohash_delete(&tag_data);
167: if (stream != NULL)
168: fclose(stream);
169: }
170:
171: void
172: tag_unlink(void)
173: {
1.11 schwarze 174: pid_t tc_pgid;
1.1 schwarze 175:
1.11 schwarze 176: if (tag_files.tcpgid != -1) {
177: tc_pgid = tcgetpgrp(STDIN_FILENO);
178: if (tc_pgid == tag_files.pager_pid ||
179: tc_pgid == getpgid(0) ||
180: getpgid(tc_pgid) == -1)
181: (void)tcsetpgrp(STDIN_FILENO, tag_files.tcpgid);
182: }
1.6 schwarze 183: if (*tag_files.ofn != '\0')
184: unlink(tag_files.ofn);
185: if (*tag_files.tfn != '\0')
186: unlink(tag_files.tfn);
1.2 schwarze 187: }
188:
189: static void
190: tag_signal(int signum)
191: {
1.11 schwarze 192: struct sigaction sa;
1.2 schwarze 193:
194: tag_unlink();
1.11 schwarze 195: memset(&sa, 0, sizeof(sa));
196: sigemptyset(&sa.sa_mask);
197: sa.sa_handler = SIG_DFL;
198: sigaction(signum, &sa, NULL);
1.2 schwarze 199: kill(getpid(), signum);
200: /* NOTREACHED */
201: _exit(1);
1.1 schwarze 202: }