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