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 } 205