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