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