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