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