]> git.cameronkatri.com Git - mandoc.git/blob - mandocd.c
For accessibility, label the last two widgets in the search form.
[mandoc.git] / mandocd.c
1 /* $Id: mandocd.c,v 1.13 2022/04/14 16:43:44 schwarze Exp $ */
2 /*
3 * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
4 * Copyright (c) 2017, 2019, 2021 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #include "config.h"
19
20 #if NEED_XPG4_2
21 #define _XPG4_2
22 #endif
23
24 #include <sys/types.h>
25 #include <sys/socket.h>
26
27 #if HAVE_ERR
28 #include <err.h>
29 #endif
30 #include <limits.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 #include "mandoc.h"
38 #if DEBUG_MEMORY
39 #define DEBUG_NODEF 1
40 #include "mandoc_dbg.h"
41 #endif
42 #include "roff.h"
43 #include "mdoc.h"
44 #include "man.h"
45 #include "mandoc_parse.h"
46 #include "main.h"
47 #include "manconf.h"
48
49 enum outt {
50 OUTT_ASCII = 0,
51 OUTT_UTF8,
52 OUTT_HTML
53 };
54
55 static void process(struct mparse *, enum outt, void *);
56 static int read_fds(int, int *);
57 static void usage(void) __attribute__((__noreturn__));
58
59
60 #define NUM_FDS 3
61 static int
62 read_fds(int clientfd, int *fds)
63 {
64 struct msghdr msg;
65 struct iovec iov[1];
66 unsigned char dummy[1];
67 struct cmsghdr *cmsg;
68 int *walk;
69 int cnt;
70
71 /* Union used for alignment. */
72 union {
73 uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))];
74 struct cmsghdr align;
75 } u;
76
77 memset(&msg, '\0', sizeof(msg));
78 msg.msg_control = u.controlbuf;
79 msg.msg_controllen = sizeof(u.controlbuf);
80
81 /*
82 * Read a dummy byte - sendmsg cannot send an empty message,
83 * even if we are only interested in the OOB data.
84 */
85
86 iov[0].iov_base = dummy;
87 iov[0].iov_len = sizeof(dummy);
88 msg.msg_iov = iov;
89 msg.msg_iovlen = 1;
90
91 switch (recvmsg(clientfd, &msg, 0)) {
92 case -1:
93 warn("recvmsg");
94 return -1;
95 case 0:
96 return 0;
97 default:
98 break;
99 }
100
101 if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) {
102 warnx("CMSG_FIRSTHDR: missing control message");
103 return -1;
104 }
105
106 if (cmsg->cmsg_level != SOL_SOCKET ||
107 cmsg->cmsg_type != SCM_RIGHTS ||
108 cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) {
109 warnx("CMSG_FIRSTHDR: invalid control message");
110 return -1;
111 }
112
113 walk = (int *)CMSG_DATA(cmsg);
114 for (cnt = 0; cnt < NUM_FDS; cnt++)
115 fds[cnt] = *walk++;
116
117 return 1;
118 }
119
120 int
121 main(int argc, char *argv[])
122 {
123 struct manoutput options;
124 struct mparse *parser;
125 void *formatter;
126 const char *defos;
127 const char *errstr;
128 int clientfd;
129 int old_stdin;
130 int old_stdout;
131 int old_stderr;
132 int fds[3];
133 int state, opt;
134 enum outt outtype;
135
136 #if DEBUG_MEMORY
137 mandoc_dbg_init(argc, argv);
138 #endif
139
140 defos = NULL;
141 outtype = OUTT_ASCII;
142 while ((opt = getopt(argc, argv, "I:T:")) != -1) {
143 switch (opt) {
144 case 'I':
145 if (strncmp(optarg, "os=", 3) == 0)
146 defos = optarg + 3;
147 else {
148 warnx("-I %s: Bad argument", optarg);
149 usage();
150 }
151 break;
152 case 'T':
153 if (strcmp(optarg, "ascii") == 0)
154 outtype = OUTT_ASCII;
155 else if (strcmp(optarg, "utf8") == 0)
156 outtype = OUTT_UTF8;
157 else if (strcmp(optarg, "html") == 0)
158 outtype = OUTT_HTML;
159 else {
160 warnx("-T %s: Bad argument", optarg);
161 usage();
162 }
163 break;
164 default:
165 usage();
166 }
167 }
168
169 if (argc > 0) {
170 argc -= optind;
171 argv += optind;
172 }
173 if (argc != 1)
174 usage();
175
176 errstr = NULL;
177 clientfd = strtonum(argv[0], 3, INT_MAX, &errstr);
178 if (errstr)
179 errx(1, "file descriptor %s %s", argv[1], errstr);
180
181 mchars_alloc();
182 parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
183 MPARSE_VALIDATE, MANDOC_OS_OTHER, defos);
184
185 memset(&options, 0, sizeof(options));
186 switch (outtype) {
187 case OUTT_ASCII:
188 formatter = ascii_alloc(&options);
189 break;
190 case OUTT_UTF8:
191 formatter = utf8_alloc(&options);
192 break;
193 case OUTT_HTML:
194 options.fragment = 1;
195 formatter = html_alloc(&options);
196 break;
197 }
198
199 state = 1; /* work to do */
200 fflush(stdout);
201 fflush(stderr);
202 if ((old_stdin = dup(STDIN_FILENO)) == -1 ||
203 (old_stdout = dup(STDOUT_FILENO)) == -1 ||
204 (old_stderr = dup(STDERR_FILENO)) == -1) {
205 warn("dup");
206 state = -1; /* error */
207 }
208
209 while (state == 1 && (state = read_fds(clientfd, fds)) == 1) {
210 if (dup2(fds[0], STDIN_FILENO) == -1 ||
211 dup2(fds[1], STDOUT_FILENO) == -1 ||
212 dup2(fds[2], STDERR_FILENO) == -1) {
213 warn("dup2");
214 state = -1;
215 break;
216 }
217
218 close(fds[0]);
219 close(fds[1]);
220 close(fds[2]);
221
222 process(parser, outtype, formatter);
223 mparse_reset(parser);
224 if (outtype == OUTT_HTML)
225 html_reset(formatter);
226
227 fflush(stdout);
228 fflush(stderr);
229 /* Close file descriptors by restoring the old ones. */
230 if (dup2(old_stderr, STDERR_FILENO) == -1 ||
231 dup2(old_stdout, STDOUT_FILENO) == -1 ||
232 dup2(old_stdin, STDIN_FILENO) == -1) {
233 warn("dup2");
234 state = -1;
235 break;
236 }
237 }
238
239 close(clientfd);
240 switch (outtype) {
241 case OUTT_ASCII:
242 case OUTT_UTF8:
243 ascii_free(formatter);
244 break;
245 case OUTT_HTML:
246 html_free(formatter);
247 break;
248 }
249 mparse_free(parser);
250 mchars_free();
251 #if DEBUG_MEMORY
252 mandoc_dbg_finish();
253 #endif
254 return state == -1 ? 1 : 0;
255 }
256
257 static void
258 process(struct mparse *parser, enum outt outtype, void *formatter)
259 {
260 struct roff_meta *meta;
261
262 mparse_readfd(parser, STDIN_FILENO, "<unixfd>");
263 meta = mparse_result(parser);
264 if (meta->macroset == MACROSET_MDOC) {
265 switch (outtype) {
266 case OUTT_ASCII:
267 case OUTT_UTF8:
268 terminal_mdoc(formatter, meta);
269 break;
270 case OUTT_HTML:
271 html_mdoc(formatter, meta);
272 break;
273 }
274 }
275 if (meta->macroset == MACROSET_MAN) {
276 switch (outtype) {
277 case OUTT_ASCII:
278 case OUTT_UTF8:
279 terminal_man(formatter, meta);
280 break;
281 case OUTT_HTML:
282 html_man(formatter, meta);
283 break;
284 }
285 }
286 }
287
288 void
289 usage(void)
290 {
291 fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n");
292 exit(1);
293 }