]> git.cameronkatri.com Git - mandoc.git/blob - mandocd.c
Since resetting of offsets works quite differently in man(7) and mdoc(7),
[mandoc.git] / mandocd.c
1 /* $Id: mandocd.c,v 1.9 2018/12/30 00:49:55 schwarze Exp $ */
2 /*
3 * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
4 * Copyright (c) 2017 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 HAVE_CMSG_XPG42
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 #include "roff.h"
39 #include "mdoc.h"
40 #include "man.h"
41 #include "mandoc_parse.h"
42 #include "main.h"
43 #include "manconf.h"
44
45 enum outt {
46 OUTT_ASCII = 0,
47 OUTT_UTF8,
48 OUTT_HTML
49 };
50
51 static void process(struct mparse *, enum outt, void *);
52 static int read_fds(int, int *);
53 static void usage(void) __attribute__((__noreturn__));
54
55
56 #define NUM_FDS 3
57 static int
58 read_fds(int clientfd, int *fds)
59 {
60 struct msghdr msg;
61 struct iovec iov[1];
62 unsigned char dummy[1];
63 struct cmsghdr *cmsg;
64 int *walk;
65 int cnt;
66
67 /* Union used for alignment. */
68 union {
69 uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))];
70 struct cmsghdr align;
71 } u;
72
73 memset(&msg, '\0', sizeof(msg));
74 msg.msg_control = u.controlbuf;
75 msg.msg_controllen = sizeof(u.controlbuf);
76
77 /*
78 * Read a dummy byte - sendmsg cannot send an empty message,
79 * even if we are only interested in the OOB data.
80 */
81
82 iov[0].iov_base = dummy;
83 iov[0].iov_len = sizeof(dummy);
84 msg.msg_iov = iov;
85 msg.msg_iovlen = 1;
86
87 switch (recvmsg(clientfd, &msg, 0)) {
88 case -1:
89 warn("recvmsg");
90 return -1;
91 case 0:
92 return 0;
93 default:
94 break;
95 }
96
97 if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) {
98 warnx("CMSG_FIRSTHDR: missing control message");
99 return -1;
100 }
101
102 if (cmsg->cmsg_level != SOL_SOCKET ||
103 cmsg->cmsg_type != SCM_RIGHTS ||
104 cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) {
105 warnx("CMSG_FIRSTHDR: invalid control message");
106 return -1;
107 }
108
109 walk = (int *)CMSG_DATA(cmsg);
110 for (cnt = 0; cnt < NUM_FDS; cnt++)
111 fds[cnt] = *walk++;
112
113 return 1;
114 }
115
116 int
117 main(int argc, char *argv[])
118 {
119 struct manoutput options;
120 struct mparse *parser;
121 void *formatter;
122 const char *defos;
123 const char *errstr;
124 int clientfd;
125 int old_stdin;
126 int old_stdout;
127 int old_stderr;
128 int fds[3];
129 int state, opt;
130 enum outt outtype;
131
132 defos = NULL;
133 outtype = OUTT_ASCII;
134 while ((opt = getopt(argc, argv, "I:T:")) != -1) {
135 switch (opt) {
136 case 'I':
137 if (strncmp(optarg, "os=", 3) == 0)
138 defos = optarg + 3;
139 else {
140 warnx("-I %s: Bad argument", optarg);
141 usage();
142 }
143 break;
144 case 'T':
145 if (strcmp(optarg, "ascii") == 0)
146 outtype = OUTT_ASCII;
147 else if (strcmp(optarg, "utf8") == 0)
148 outtype = OUTT_UTF8;
149 else if (strcmp(optarg, "html") == 0)
150 outtype = OUTT_HTML;
151 else {
152 warnx("-T %s: Bad argument", optarg);
153 usage();
154 }
155 break;
156 default:
157 usage();
158 }
159 }
160
161 if (argc > 0) {
162 argc -= optind;
163 argv += optind;
164 }
165 if (argc != 1)
166 usage();
167
168 errstr = NULL;
169 clientfd = strtonum(argv[0], 3, INT_MAX, &errstr);
170 if (errstr)
171 errx(1, "file descriptor %s %s", argv[1], errstr);
172
173 mchars_alloc();
174 parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1,
175 MANDOC_OS_OTHER, defos);
176
177 memset(&options, 0, sizeof(options));
178 switch (outtype) {
179 case OUTT_ASCII:
180 formatter = ascii_alloc(&options);
181 break;
182 case OUTT_UTF8:
183 formatter = utf8_alloc(&options);
184 break;
185 case OUTT_HTML:
186 options.fragment = 1;
187 formatter = html_alloc(&options);
188 break;
189 }
190
191 state = 1; /* work to do */
192 fflush(stdout);
193 fflush(stderr);
194 if ((old_stdin = dup(STDIN_FILENO)) == -1 ||
195 (old_stdout = dup(STDOUT_FILENO)) == -1 ||
196 (old_stderr = dup(STDERR_FILENO)) == -1) {
197 warn("dup");
198 state = -1; /* error */
199 }
200
201 while (state == 1 && (state = read_fds(clientfd, fds)) == 1) {
202 if (dup2(fds[0], STDIN_FILENO) == -1 ||
203 dup2(fds[1], STDOUT_FILENO) == -1 ||
204 dup2(fds[2], STDERR_FILENO) == -1) {
205 warn("dup2");
206 state = -1;
207 break;
208 }
209
210 close(fds[0]);
211 close(fds[1]);
212 close(fds[2]);
213
214 process(parser, outtype, formatter);
215 mparse_reset(parser);
216
217 fflush(stdout);
218 fflush(stderr);
219 /* Close file descriptors by restoring the old ones. */
220 if (dup2(old_stderr, STDERR_FILENO) == -1 ||
221 dup2(old_stdout, STDOUT_FILENO) == -1 ||
222 dup2(old_stdin, STDIN_FILENO) == -1) {
223 warn("dup2");
224 state = -1;
225 break;
226 }
227 }
228
229 close(clientfd);
230 switch (outtype) {
231 case OUTT_ASCII:
232 case OUTT_UTF8:
233 ascii_free(formatter);
234 break;
235 case OUTT_HTML:
236 html_free(formatter);
237 break;
238 }
239 mparse_free(parser);
240 mchars_free();
241 return state == -1 ? 1 : 0;
242 }
243
244 static void
245 process(struct mparse *parser, enum outt outtype, void *formatter)
246 {
247 struct roff_meta *meta;
248
249 mparse_readfd(parser, STDIN_FILENO, "<unixfd>");
250 meta = mparse_result(parser);
251 if (meta->macroset == MACROSET_MDOC) {
252 switch (outtype) {
253 case OUTT_ASCII:
254 case OUTT_UTF8:
255 terminal_mdoc(formatter, meta);
256 break;
257 case OUTT_HTML:
258 html_mdoc(formatter, meta);
259 break;
260 }
261 }
262 if (meta->macroset == MACROSET_MAN) {
263 switch (outtype) {
264 case OUTT_ASCII:
265 case OUTT_UTF8:
266 terminal_man(formatter, meta);
267 break;
268 case OUTT_HTML:
269 html_man(formatter, meta);
270 break;
271 }
272 }
273 }
274
275 void
276 usage(void)
277 {
278 fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n");
279 exit(1);
280 }