1 /* $OpenBSD: tag.c,v 1.13 2016/07/20 13:02:44 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 <sys/types.h> 18 19 #include <signal.h> 20 #include <stddef.h> 21 #include <stdint.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 #include "mandoc_aux.h" 28 #include "mandoc_ohash.h" 29 #include "tag.h" 30 31 struct tag_entry { 32 size_t line; 33 int prio; 34 char s[]; 35 }; 36 37 static void tag_signal(int) __attribute__((noreturn)); 38 39 static struct ohash tag_data; 40 static struct tag_files tag_files; 41 42 43 /* 44 * Prepare for using a pager. 45 * Not all pagers are capable of using a tag file, 46 * but for simplicity, create it anyway. 47 */ 48 struct tag_files * 49 tag_init(void) 50 { 51 struct sigaction sa; 52 int ofd; 53 54 ofd = -1; 55 tag_files.tfd = -1; 56 tag_files.tcpgid = -1; 57 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 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 */ 99 100 mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s)); 101 return &tag_files; 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; 115 return NULL; 116 } 117 118 /* 119 * Set the line number where a term is defined, 120 * unless it is already defined at a higher priority. 121 */ 122 void 123 tag_put(const char *s, int prio, size_t line) 124 { 125 struct tag_entry *entry; 126 size_t len; 127 unsigned int slot; 128 129 if (tag_files.tfd <= 0 || strchr(s, ' ') != NULL) 130 return; 131 slot = ohash_qlookup(&tag_data, s); 132 entry = ohash_find(&tag_data, slot); 133 if (entry == NULL) { 134 len = strlen(s) + 1; 135 entry = mandoc_malloc(sizeof(*entry) + len); 136 memcpy(entry->s, s, len); 137 ohash_insert(&tag_data, slot, entry); 138 } else if (entry->prio <= prio) 139 return; 140 entry->line = line; 141 entry->prio = prio; 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 155 if (tag_files.tfd <= 0) 156 return; 157 stream = fdopen(tag_files.tfd, "w"); 158 entry = ohash_first(&tag_data, &slot); 159 while (entry != NULL) { 160 if (stream != NULL) 161 fprintf(stream, "%s %s %zu\n", 162 entry->s, tag_files.ofn, entry->line); 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 { 174 pid_t tc_pgid; 175 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 } 183 if (*tag_files.ofn != '\0') 184 unlink(tag_files.ofn); 185 if (*tag_files.tfn != '\0') 186 unlink(tag_files.tfn); 187 } 188 189 static void 190 tag_signal(int signum) 191 { 192 struct sigaction sa; 193 194 tag_unlink(); 195 memset(&sa, 0, sizeof(sa)); 196 sigemptyset(&sa.sa_mask); 197 sa.sa_handler = SIG_DFL; 198 sigaction(signum, &sa, NULL); 199 kill(getpid(), signum); 200 /* NOTREACHED */ 201 _exit(1); 202 } 203