1 /* $OpenBSD: term_tag.c,v 1.4 2020/04/18 20:28:46 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2015,2016,2018,2019,2020 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 * Functions to write a ctags(1) file. 18 * For use by the mandoc(1) ASCII and UTF-8 formatters only. 19 */ 20 #include <sys/types.h> 21 22 #include <errno.h> 23 #include <signal.h> 24 #include <stddef.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 #include "mandoc.h" 31 #include "roff.h" 32 #include "roff_int.h" 33 #include "tag.h" 34 #include "term_tag.h" 35 36 static void tag_signal(int) __attribute__((__noreturn__)); 37 38 static struct tag_files tag_files; 39 40 41 /* 42 * Prepare for using a pager. 43 * Not all pagers are capable of using a tag file, 44 * but for simplicity, create it anyway. 45 */ 46 struct tag_files * 47 term_tag_init(void) 48 { 49 struct sigaction sa; 50 int ofd; /* In /tmp/, dup(2)ed to stdout. */ 51 int tfd; 52 53 ofd = tfd = -1; 54 tag_files.tfs = NULL; 55 tag_files.tcpgid = -1; 56 57 /* Clean up when dying from a signal. */ 58 59 memset(&sa, 0, sizeof(sa)); 60 sigfillset(&sa.sa_mask); 61 sa.sa_handler = tag_signal; 62 sigaction(SIGHUP, &sa, NULL); 63 sigaction(SIGINT, &sa, NULL); 64 sigaction(SIGTERM, &sa, NULL); 65 66 /* 67 * POSIX requires that a process calling tcsetpgrp(3) 68 * from the background gets a SIGTTOU signal. 69 * In that case, do not stop. 70 */ 71 72 sa.sa_handler = SIG_IGN; 73 sigaction(SIGTTOU, &sa, NULL); 74 75 /* Save the original standard output for use by the pager. */ 76 77 if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1) { 78 mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); 79 goto fail; 80 } 81 82 /* Create both temporary output files. */ 83 84 (void)strlcpy(tag_files.ofn, "/tmp/man.XXXXXXXXXX", 85 sizeof(tag_files.ofn)); 86 (void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX", 87 sizeof(tag_files.tfn)); 88 if ((ofd = mkstemp(tag_files.ofn)) == -1) { 89 mandoc_msg(MANDOCERR_MKSTEMP, 0, 0, 90 "%s: %s", tag_files.ofn, strerror(errno)); 91 goto fail; 92 } 93 if ((tfd = mkstemp(tag_files.tfn)) == -1) { 94 mandoc_msg(MANDOCERR_MKSTEMP, 0, 0, 95 "%s: %s", tag_files.tfn, strerror(errno)); 96 goto fail; 97 } 98 if ((tag_files.tfs = fdopen(tfd, "w")) == NULL) { 99 mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno)); 100 goto fail; 101 } 102 tfd = -1; 103 if (dup2(ofd, STDOUT_FILENO) == -1) { 104 mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); 105 goto fail; 106 } 107 close(ofd); 108 return &tag_files; 109 110 fail: 111 term_tag_unlink(); 112 if (ofd != -1) 113 close(ofd); 114 if (tfd != -1) 115 close(tfd); 116 if (tag_files.ofd != -1) { 117 close(tag_files.ofd); 118 tag_files.ofd = -1; 119 } 120 return NULL; 121 } 122 123 void 124 term_tag_write(struct roff_node *n, size_t line) 125 { 126 const char *cp; 127 int len; 128 129 if (tag_files.tfs == NULL) 130 return; 131 cp = n->tag == NULL ? n->child->string : n->tag; 132 if (cp[0] == '\\' && (cp[1] == '&' || cp[1] == 'e')) 133 cp += 2; 134 len = strcspn(cp, " \t\\"); 135 fprintf(tag_files.tfs, "%.*s %s %zu\n", 136 len, cp, tag_files.ofn, line); 137 } 138 139 /* 140 * Close both output files and restore the original standard output 141 * to the terminal. In the unlikely case that the latter fails, 142 * trying to start a pager would be useless, so report the failure 143 * to the main program. 144 */ 145 int 146 term_tag_close(void) 147 { 148 int irc = 0; 149 150 if (tag_files.tfs != NULL) { 151 fclose(tag_files.tfs); 152 tag_files.tfs = NULL; 153 } 154 if (tag_files.ofd != -1) { 155 fflush(stdout); 156 if ((irc = dup2(tag_files.ofd, STDOUT_FILENO)) == -1) 157 mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); 158 close(tag_files.ofd); 159 tag_files.ofd = -1; 160 } 161 return irc; 162 } 163 164 void 165 term_tag_unlink(void) 166 { 167 pid_t tc_pgid; 168 169 if (tag_files.tcpgid != -1) { 170 tc_pgid = tcgetpgrp(STDOUT_FILENO); 171 if (tc_pgid == tag_files.pager_pid || 172 tc_pgid == getpgid(0) || 173 getpgid(tc_pgid) == -1) 174 (void)tcsetpgrp(STDOUT_FILENO, tag_files.tcpgid); 175 } 176 if (*tag_files.ofn != '\0') { 177 unlink(tag_files.ofn); 178 *tag_files.ofn = '\0'; 179 } 180 if (*tag_files.tfn != '\0') { 181 unlink(tag_files.tfn); 182 *tag_files.tfn = '\0'; 183 } 184 } 185 186 static void 187 tag_signal(int signum) 188 { 189 struct sigaction sa; 190 191 term_tag_unlink(); 192 memset(&sa, 0, sizeof(sa)); 193 sigemptyset(&sa.sa_mask); 194 sa.sa_handler = SIG_DFL; 195 sigaction(signum, &sa, NULL); 196 kill(getpid(), signum); 197 /* NOTREACHED */ 198 _exit(1); 199 } 200