xref: /freebsd/contrib/mandoc/mandocd.c (revision 6d38604fc532a3fc060788e3ce40464b46047eaf)
1*6d38604fSBaptiste Daroussin /*	$Id: mandocd.c,v 1.12 2020/06/14 23:40:31 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
361d06d6bSBaptiste Daroussin  * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
47295610fSBaptiste Daroussin  * Copyright (c) 2017, 2019 Ingo Schwarze <schwarze@openbsd.org>
561d06d6bSBaptiste Daroussin  *
661d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
761d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
861d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
961d06d6bSBaptiste Daroussin  *
1061d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1161d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1261d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1361d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1461d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1561d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1661d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1761d06d6bSBaptiste Daroussin  */
1861d06d6bSBaptiste Daroussin #include "config.h"
1961d06d6bSBaptiste Daroussin 
20*6d38604fSBaptiste Daroussin #if NEED_XPG4_2
2161d06d6bSBaptiste Daroussin #define _XPG4_2
2261d06d6bSBaptiste Daroussin #endif
2361d06d6bSBaptiste Daroussin 
2461d06d6bSBaptiste Daroussin #include <sys/types.h>
2561d06d6bSBaptiste Daroussin #include <sys/socket.h>
2661d06d6bSBaptiste Daroussin 
2761d06d6bSBaptiste Daroussin #if HAVE_ERR
2861d06d6bSBaptiste Daroussin #include <err.h>
2961d06d6bSBaptiste Daroussin #endif
3061d06d6bSBaptiste Daroussin #include <limits.h>
3161d06d6bSBaptiste Daroussin #include <stdint.h>
3261d06d6bSBaptiste Daroussin #include <stdio.h>
3361d06d6bSBaptiste Daroussin #include <stdlib.h>
3461d06d6bSBaptiste Daroussin #include <string.h>
3561d06d6bSBaptiste Daroussin #include <unistd.h>
3661d06d6bSBaptiste Daroussin 
3761d06d6bSBaptiste Daroussin #include "mandoc.h"
3861d06d6bSBaptiste Daroussin #include "roff.h"
3961d06d6bSBaptiste Daroussin #include "mdoc.h"
4061d06d6bSBaptiste Daroussin #include "man.h"
417295610fSBaptiste Daroussin #include "mandoc_parse.h"
4261d06d6bSBaptiste Daroussin #include "main.h"
4361d06d6bSBaptiste Daroussin #include "manconf.h"
4461d06d6bSBaptiste Daroussin 
4561d06d6bSBaptiste Daroussin enum	outt {
4661d06d6bSBaptiste Daroussin 	OUTT_ASCII = 0,
4761d06d6bSBaptiste Daroussin 	OUTT_UTF8,
4861d06d6bSBaptiste Daroussin 	OUTT_HTML
4961d06d6bSBaptiste Daroussin };
5061d06d6bSBaptiste Daroussin 
5161d06d6bSBaptiste Daroussin static	void	  process(struct mparse *, enum outt, void *);
5261d06d6bSBaptiste Daroussin static	int	  read_fds(int, int *);
5361d06d6bSBaptiste Daroussin static	void	  usage(void) __attribute__((__noreturn__));
5461d06d6bSBaptiste Daroussin 
5561d06d6bSBaptiste Daroussin 
5661d06d6bSBaptiste Daroussin #define NUM_FDS 3
5761d06d6bSBaptiste Daroussin static int
5861d06d6bSBaptiste Daroussin read_fds(int clientfd, int *fds)
5961d06d6bSBaptiste Daroussin {
6061d06d6bSBaptiste Daroussin 	struct msghdr	 msg;
6161d06d6bSBaptiste Daroussin 	struct iovec	 iov[1];
6261d06d6bSBaptiste Daroussin 	unsigned char	 dummy[1];
6361d06d6bSBaptiste Daroussin 	struct cmsghdr	*cmsg;
6461d06d6bSBaptiste Daroussin 	int		*walk;
6561d06d6bSBaptiste Daroussin 	int		 cnt;
6661d06d6bSBaptiste Daroussin 
6761d06d6bSBaptiste Daroussin 	/* Union used for alignment. */
6861d06d6bSBaptiste Daroussin 	union {
6961d06d6bSBaptiste Daroussin 		uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))];
7061d06d6bSBaptiste Daroussin 		struct cmsghdr align;
7161d06d6bSBaptiste Daroussin 	} u;
7261d06d6bSBaptiste Daroussin 
7361d06d6bSBaptiste Daroussin 	memset(&msg, '\0', sizeof(msg));
7461d06d6bSBaptiste Daroussin 	msg.msg_control = u.controlbuf;
7561d06d6bSBaptiste Daroussin 	msg.msg_controllen = sizeof(u.controlbuf);
7661d06d6bSBaptiste Daroussin 
7761d06d6bSBaptiste Daroussin 	/*
7861d06d6bSBaptiste Daroussin 	 * Read a dummy byte - sendmsg cannot send an empty message,
7961d06d6bSBaptiste Daroussin 	 * even if we are only interested in the OOB data.
8061d06d6bSBaptiste Daroussin 	 */
8161d06d6bSBaptiste Daroussin 
8261d06d6bSBaptiste Daroussin 	iov[0].iov_base = dummy;
8361d06d6bSBaptiste Daroussin 	iov[0].iov_len = sizeof(dummy);
8461d06d6bSBaptiste Daroussin 	msg.msg_iov = iov;
8561d06d6bSBaptiste Daroussin 	msg.msg_iovlen = 1;
8661d06d6bSBaptiste Daroussin 
8761d06d6bSBaptiste Daroussin 	switch (recvmsg(clientfd, &msg, 0)) {
8861d06d6bSBaptiste Daroussin 	case -1:
8961d06d6bSBaptiste Daroussin 		warn("recvmsg");
9061d06d6bSBaptiste Daroussin 		return -1;
9161d06d6bSBaptiste Daroussin 	case 0:
9261d06d6bSBaptiste Daroussin 		return 0;
9361d06d6bSBaptiste Daroussin 	default:
9461d06d6bSBaptiste Daroussin 		break;
9561d06d6bSBaptiste Daroussin 	}
9661d06d6bSBaptiste Daroussin 
9761d06d6bSBaptiste Daroussin 	if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) {
9861d06d6bSBaptiste Daroussin 		warnx("CMSG_FIRSTHDR: missing control message");
9961d06d6bSBaptiste Daroussin 		return -1;
10061d06d6bSBaptiste Daroussin 	}
10161d06d6bSBaptiste Daroussin 
10261d06d6bSBaptiste Daroussin 	if (cmsg->cmsg_level != SOL_SOCKET ||
10361d06d6bSBaptiste Daroussin 	    cmsg->cmsg_type != SCM_RIGHTS ||
10461d06d6bSBaptiste Daroussin 	    cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) {
10561d06d6bSBaptiste Daroussin 		warnx("CMSG_FIRSTHDR: invalid control message");
10661d06d6bSBaptiste Daroussin 		return -1;
10761d06d6bSBaptiste Daroussin 	}
10861d06d6bSBaptiste Daroussin 
10961d06d6bSBaptiste Daroussin 	walk = (int *)CMSG_DATA(cmsg);
11061d06d6bSBaptiste Daroussin 	for (cnt = 0; cnt < NUM_FDS; cnt++)
11161d06d6bSBaptiste Daroussin 		fds[cnt] = *walk++;
11261d06d6bSBaptiste Daroussin 
11361d06d6bSBaptiste Daroussin 	return 1;
11461d06d6bSBaptiste Daroussin }
11561d06d6bSBaptiste Daroussin 
11661d06d6bSBaptiste Daroussin int
11761d06d6bSBaptiste Daroussin main(int argc, char *argv[])
11861d06d6bSBaptiste Daroussin {
11961d06d6bSBaptiste Daroussin 	struct manoutput	 options;
12061d06d6bSBaptiste Daroussin 	struct mparse		*parser;
12161d06d6bSBaptiste Daroussin 	void			*formatter;
12261d06d6bSBaptiste Daroussin 	const char		*defos;
12361d06d6bSBaptiste Daroussin 	const char		*errstr;
12461d06d6bSBaptiste Daroussin 	int			 clientfd;
12561d06d6bSBaptiste Daroussin 	int			 old_stdin;
12661d06d6bSBaptiste Daroussin 	int			 old_stdout;
12761d06d6bSBaptiste Daroussin 	int			 old_stderr;
12861d06d6bSBaptiste Daroussin 	int			 fds[3];
12961d06d6bSBaptiste Daroussin 	int			 state, opt;
13061d06d6bSBaptiste Daroussin 	enum outt		 outtype;
13161d06d6bSBaptiste Daroussin 
13261d06d6bSBaptiste Daroussin 	defos = NULL;
13361d06d6bSBaptiste Daroussin 	outtype = OUTT_ASCII;
13461d06d6bSBaptiste Daroussin 	while ((opt = getopt(argc, argv, "I:T:")) != -1) {
13561d06d6bSBaptiste Daroussin 		switch (opt) {
13661d06d6bSBaptiste Daroussin 		case 'I':
13761d06d6bSBaptiste Daroussin 			if (strncmp(optarg, "os=", 3) == 0)
13861d06d6bSBaptiste Daroussin 				defos = optarg + 3;
13961d06d6bSBaptiste Daroussin 			else {
14061d06d6bSBaptiste Daroussin 				warnx("-I %s: Bad argument", optarg);
14161d06d6bSBaptiste Daroussin 				usage();
14261d06d6bSBaptiste Daroussin 			}
14361d06d6bSBaptiste Daroussin 			break;
14461d06d6bSBaptiste Daroussin 		case 'T':
14561d06d6bSBaptiste Daroussin 			if (strcmp(optarg, "ascii") == 0)
14661d06d6bSBaptiste Daroussin 				outtype = OUTT_ASCII;
14761d06d6bSBaptiste Daroussin 			else if (strcmp(optarg, "utf8") == 0)
14861d06d6bSBaptiste Daroussin 				outtype = OUTT_UTF8;
14961d06d6bSBaptiste Daroussin 			else if (strcmp(optarg, "html") == 0)
15061d06d6bSBaptiste Daroussin 				outtype = OUTT_HTML;
15161d06d6bSBaptiste Daroussin 			else {
15261d06d6bSBaptiste Daroussin 				warnx("-T %s: Bad argument", optarg);
15361d06d6bSBaptiste Daroussin 				usage();
15461d06d6bSBaptiste Daroussin 			}
15561d06d6bSBaptiste Daroussin 			break;
15661d06d6bSBaptiste Daroussin 		default:
15761d06d6bSBaptiste Daroussin 			usage();
15861d06d6bSBaptiste Daroussin 		}
15961d06d6bSBaptiste Daroussin 	}
16061d06d6bSBaptiste Daroussin 
16161d06d6bSBaptiste Daroussin 	if (argc > 0) {
16261d06d6bSBaptiste Daroussin 		argc -= optind;
16361d06d6bSBaptiste Daroussin 		argv += optind;
16461d06d6bSBaptiste Daroussin 	}
16561d06d6bSBaptiste Daroussin 	if (argc != 1)
16661d06d6bSBaptiste Daroussin 		usage();
16761d06d6bSBaptiste Daroussin 
16861d06d6bSBaptiste Daroussin 	errstr = NULL;
16961d06d6bSBaptiste Daroussin 	clientfd = strtonum(argv[0], 3, INT_MAX, &errstr);
17061d06d6bSBaptiste Daroussin 	if (errstr)
17161d06d6bSBaptiste Daroussin 		errx(1, "file descriptor %s %s", argv[1], errstr);
17261d06d6bSBaptiste Daroussin 
17361d06d6bSBaptiste Daroussin 	mchars_alloc();
1747295610fSBaptiste Daroussin 	parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
1757295610fSBaptiste Daroussin 	    MPARSE_VALIDATE, MANDOC_OS_OTHER, defos);
17661d06d6bSBaptiste Daroussin 
17761d06d6bSBaptiste Daroussin 	memset(&options, 0, sizeof(options));
17861d06d6bSBaptiste Daroussin 	switch (outtype) {
17961d06d6bSBaptiste Daroussin 	case OUTT_ASCII:
18061d06d6bSBaptiste Daroussin 		formatter = ascii_alloc(&options);
18161d06d6bSBaptiste Daroussin 		break;
18261d06d6bSBaptiste Daroussin 	case OUTT_UTF8:
18361d06d6bSBaptiste Daroussin 		formatter = utf8_alloc(&options);
18461d06d6bSBaptiste Daroussin 		break;
18561d06d6bSBaptiste Daroussin 	case OUTT_HTML:
18661d06d6bSBaptiste Daroussin 		options.fragment = 1;
18761d06d6bSBaptiste Daroussin 		formatter = html_alloc(&options);
18861d06d6bSBaptiste Daroussin 		break;
18961d06d6bSBaptiste Daroussin 	}
19061d06d6bSBaptiste Daroussin 
19161d06d6bSBaptiste Daroussin 	state = 1;  /* work to do */
19261d06d6bSBaptiste Daroussin 	fflush(stdout);
19361d06d6bSBaptiste Daroussin 	fflush(stderr);
19461d06d6bSBaptiste Daroussin 	if ((old_stdin = dup(STDIN_FILENO)) == -1 ||
19561d06d6bSBaptiste Daroussin 	    (old_stdout = dup(STDOUT_FILENO)) == -1 ||
19661d06d6bSBaptiste Daroussin 	    (old_stderr = dup(STDERR_FILENO)) == -1) {
19761d06d6bSBaptiste Daroussin 		warn("dup");
19861d06d6bSBaptiste Daroussin 		state = -1;  /* error */
19961d06d6bSBaptiste Daroussin 	}
20061d06d6bSBaptiste Daroussin 
20161d06d6bSBaptiste Daroussin 	while (state == 1 && (state = read_fds(clientfd, fds)) == 1) {
20261d06d6bSBaptiste Daroussin 		if (dup2(fds[0], STDIN_FILENO) == -1 ||
20361d06d6bSBaptiste Daroussin 		    dup2(fds[1], STDOUT_FILENO) == -1 ||
20461d06d6bSBaptiste Daroussin 		    dup2(fds[2], STDERR_FILENO) == -1) {
20561d06d6bSBaptiste Daroussin 			warn("dup2");
20661d06d6bSBaptiste Daroussin 			state = -1;
20761d06d6bSBaptiste Daroussin 			break;
20861d06d6bSBaptiste Daroussin 		}
20961d06d6bSBaptiste Daroussin 
21061d06d6bSBaptiste Daroussin 		close(fds[0]);
21161d06d6bSBaptiste Daroussin 		close(fds[1]);
21261d06d6bSBaptiste Daroussin 		close(fds[2]);
21361d06d6bSBaptiste Daroussin 
21461d06d6bSBaptiste Daroussin 		process(parser, outtype, formatter);
21561d06d6bSBaptiste Daroussin 		mparse_reset(parser);
2167295610fSBaptiste Daroussin 		if (outtype == OUTT_HTML)
2177295610fSBaptiste Daroussin 			html_reset(formatter);
21861d06d6bSBaptiste Daroussin 
21961d06d6bSBaptiste Daroussin 		fflush(stdout);
22061d06d6bSBaptiste Daroussin 		fflush(stderr);
22161d06d6bSBaptiste Daroussin 		/* Close file descriptors by restoring the old ones. */
22261d06d6bSBaptiste Daroussin 		if (dup2(old_stderr, STDERR_FILENO) == -1 ||
22361d06d6bSBaptiste Daroussin 		    dup2(old_stdout, STDOUT_FILENO) == -1 ||
22461d06d6bSBaptiste Daroussin 		    dup2(old_stdin, STDIN_FILENO) == -1) {
22561d06d6bSBaptiste Daroussin 			warn("dup2");
22661d06d6bSBaptiste Daroussin 			state = -1;
22761d06d6bSBaptiste Daroussin 			break;
22861d06d6bSBaptiste Daroussin 		}
22961d06d6bSBaptiste Daroussin 	}
23061d06d6bSBaptiste Daroussin 
23161d06d6bSBaptiste Daroussin 	close(clientfd);
23261d06d6bSBaptiste Daroussin 	switch (outtype) {
23361d06d6bSBaptiste Daroussin 	case OUTT_ASCII:
23461d06d6bSBaptiste Daroussin 	case OUTT_UTF8:
23561d06d6bSBaptiste Daroussin 		ascii_free(formatter);
23661d06d6bSBaptiste Daroussin 		break;
23761d06d6bSBaptiste Daroussin 	case OUTT_HTML:
23861d06d6bSBaptiste Daroussin 		html_free(formatter);
23961d06d6bSBaptiste Daroussin 		break;
24061d06d6bSBaptiste Daroussin 	}
24161d06d6bSBaptiste Daroussin 	mparse_free(parser);
24261d06d6bSBaptiste Daroussin 	mchars_free();
24361d06d6bSBaptiste Daroussin 	return state == -1 ? 1 : 0;
24461d06d6bSBaptiste Daroussin }
24561d06d6bSBaptiste Daroussin 
24661d06d6bSBaptiste Daroussin static void
24761d06d6bSBaptiste Daroussin process(struct mparse *parser, enum outt outtype, void *formatter)
24861d06d6bSBaptiste Daroussin {
2497295610fSBaptiste Daroussin 	struct roff_meta *meta;
25061d06d6bSBaptiste Daroussin 
25161d06d6bSBaptiste Daroussin 	mparse_readfd(parser, STDIN_FILENO, "<unixfd>");
2527295610fSBaptiste Daroussin 	meta = mparse_result(parser);
2537295610fSBaptiste Daroussin 	if (meta->macroset == MACROSET_MDOC) {
25461d06d6bSBaptiste Daroussin 		switch (outtype) {
25561d06d6bSBaptiste Daroussin 		case OUTT_ASCII:
25661d06d6bSBaptiste Daroussin 		case OUTT_UTF8:
2577295610fSBaptiste Daroussin 			terminal_mdoc(formatter, meta);
25861d06d6bSBaptiste Daroussin 			break;
25961d06d6bSBaptiste Daroussin 		case OUTT_HTML:
2607295610fSBaptiste Daroussin 			html_mdoc(formatter, meta);
26161d06d6bSBaptiste Daroussin 			break;
26261d06d6bSBaptiste Daroussin 		}
26361d06d6bSBaptiste Daroussin 	}
2647295610fSBaptiste Daroussin 	if (meta->macroset == MACROSET_MAN) {
26561d06d6bSBaptiste Daroussin 		switch (outtype) {
26661d06d6bSBaptiste Daroussin 		case OUTT_ASCII:
26761d06d6bSBaptiste Daroussin 		case OUTT_UTF8:
2687295610fSBaptiste Daroussin 			terminal_man(formatter, meta);
26961d06d6bSBaptiste Daroussin 			break;
27061d06d6bSBaptiste Daroussin 		case OUTT_HTML:
2717295610fSBaptiste Daroussin 			html_man(formatter, meta);
27261d06d6bSBaptiste Daroussin 			break;
27361d06d6bSBaptiste Daroussin 		}
27461d06d6bSBaptiste Daroussin 	}
27561d06d6bSBaptiste Daroussin }
27661d06d6bSBaptiste Daroussin 
27761d06d6bSBaptiste Daroussin void
27861d06d6bSBaptiste Daroussin usage(void)
27961d06d6bSBaptiste Daroussin {
28061d06d6bSBaptiste Daroussin 	fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n");
28161d06d6bSBaptiste Daroussin 	exit(1);
28261d06d6bSBaptiste Daroussin }
283