xref: /freebsd/contrib/mandoc/mandocd.c (revision c1c95add8c80843ba15d784f95c361d795b1f593)
1*c1c95addSBrooks Davis /* $Id: mandocd.c,v 1.13 2022/04/14 16:43:44 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
361d06d6bSBaptiste Daroussin  * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
4*c1c95addSBrooks Davis  * Copyright (c) 2017, 2019, 2021 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 
206d38604fSBaptiste 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"
38*c1c95addSBrooks Davis #if DEBUG_MEMORY
39*c1c95addSBrooks Davis #define DEBUG_NODEF 1
40*c1c95addSBrooks Davis #include "mandoc_dbg.h"
41*c1c95addSBrooks Davis #endif
4261d06d6bSBaptiste Daroussin #include "roff.h"
4361d06d6bSBaptiste Daroussin #include "mdoc.h"
4461d06d6bSBaptiste Daroussin #include "man.h"
457295610fSBaptiste Daroussin #include "mandoc_parse.h"
4661d06d6bSBaptiste Daroussin #include "main.h"
4761d06d6bSBaptiste Daroussin #include "manconf.h"
4861d06d6bSBaptiste Daroussin 
4961d06d6bSBaptiste Daroussin enum	outt {
5061d06d6bSBaptiste Daroussin 	OUTT_ASCII = 0,
5161d06d6bSBaptiste Daroussin 	OUTT_UTF8,
5261d06d6bSBaptiste Daroussin 	OUTT_HTML
5361d06d6bSBaptiste Daroussin };
5461d06d6bSBaptiste Daroussin 
5561d06d6bSBaptiste Daroussin static	void	  process(struct mparse *, enum outt, void *);
5661d06d6bSBaptiste Daroussin static	int	  read_fds(int, int *);
5761d06d6bSBaptiste Daroussin static	void	  usage(void) __attribute__((__noreturn__));
5861d06d6bSBaptiste Daroussin 
5961d06d6bSBaptiste Daroussin 
6061d06d6bSBaptiste Daroussin #define NUM_FDS 3
6161d06d6bSBaptiste Daroussin static int
read_fds(int clientfd,int * fds)6261d06d6bSBaptiste Daroussin read_fds(int clientfd, int *fds)
6361d06d6bSBaptiste Daroussin {
6461d06d6bSBaptiste Daroussin 	struct msghdr	 msg;
6561d06d6bSBaptiste Daroussin 	struct iovec	 iov[1];
6661d06d6bSBaptiste Daroussin 	unsigned char	 dummy[1];
6761d06d6bSBaptiste Daroussin 	struct cmsghdr	*cmsg;
6861d06d6bSBaptiste Daroussin 	int		*walk;
6961d06d6bSBaptiste Daroussin 	int		 cnt;
7061d06d6bSBaptiste Daroussin 
7161d06d6bSBaptiste Daroussin 	/* Union used for alignment. */
7261d06d6bSBaptiste Daroussin 	union {
7361d06d6bSBaptiste Daroussin 		uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))];
7461d06d6bSBaptiste Daroussin 		struct cmsghdr align;
7561d06d6bSBaptiste Daroussin 	} u;
7661d06d6bSBaptiste Daroussin 
7761d06d6bSBaptiste Daroussin 	memset(&msg, '\0', sizeof(msg));
7861d06d6bSBaptiste Daroussin 	msg.msg_control = u.controlbuf;
7961d06d6bSBaptiste Daroussin 	msg.msg_controllen = sizeof(u.controlbuf);
8061d06d6bSBaptiste Daroussin 
8161d06d6bSBaptiste Daroussin 	/*
8261d06d6bSBaptiste Daroussin 	 * Read a dummy byte - sendmsg cannot send an empty message,
8361d06d6bSBaptiste Daroussin 	 * even if we are only interested in the OOB data.
8461d06d6bSBaptiste Daroussin 	 */
8561d06d6bSBaptiste Daroussin 
8661d06d6bSBaptiste Daroussin 	iov[0].iov_base = dummy;
8761d06d6bSBaptiste Daroussin 	iov[0].iov_len = sizeof(dummy);
8861d06d6bSBaptiste Daroussin 	msg.msg_iov = iov;
8961d06d6bSBaptiste Daroussin 	msg.msg_iovlen = 1;
9061d06d6bSBaptiste Daroussin 
9161d06d6bSBaptiste Daroussin 	switch (recvmsg(clientfd, &msg, 0)) {
9261d06d6bSBaptiste Daroussin 	case -1:
9361d06d6bSBaptiste Daroussin 		warn("recvmsg");
9461d06d6bSBaptiste Daroussin 		return -1;
9561d06d6bSBaptiste Daroussin 	case 0:
9661d06d6bSBaptiste Daroussin 		return 0;
9761d06d6bSBaptiste Daroussin 	default:
9861d06d6bSBaptiste Daroussin 		break;
9961d06d6bSBaptiste Daroussin 	}
10061d06d6bSBaptiste Daroussin 
10161d06d6bSBaptiste Daroussin 	if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) {
10261d06d6bSBaptiste Daroussin 		warnx("CMSG_FIRSTHDR: missing control message");
10361d06d6bSBaptiste Daroussin 		return -1;
10461d06d6bSBaptiste Daroussin 	}
10561d06d6bSBaptiste Daroussin 
10661d06d6bSBaptiste Daroussin 	if (cmsg->cmsg_level != SOL_SOCKET ||
10761d06d6bSBaptiste Daroussin 	    cmsg->cmsg_type != SCM_RIGHTS ||
10861d06d6bSBaptiste Daroussin 	    cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) {
10961d06d6bSBaptiste Daroussin 		warnx("CMSG_FIRSTHDR: invalid control message");
11061d06d6bSBaptiste Daroussin 		return -1;
11161d06d6bSBaptiste Daroussin 	}
11261d06d6bSBaptiste Daroussin 
11361d06d6bSBaptiste Daroussin 	walk = (int *)CMSG_DATA(cmsg);
11461d06d6bSBaptiste Daroussin 	for (cnt = 0; cnt < NUM_FDS; cnt++)
11561d06d6bSBaptiste Daroussin 		fds[cnt] = *walk++;
11661d06d6bSBaptiste Daroussin 
11761d06d6bSBaptiste Daroussin 	return 1;
11861d06d6bSBaptiste Daroussin }
11961d06d6bSBaptiste Daroussin 
12061d06d6bSBaptiste Daroussin int
main(int argc,char * argv[])12161d06d6bSBaptiste Daroussin main(int argc, char *argv[])
12261d06d6bSBaptiste Daroussin {
12361d06d6bSBaptiste Daroussin 	struct manoutput	 options;
12461d06d6bSBaptiste Daroussin 	struct mparse		*parser;
12561d06d6bSBaptiste Daroussin 	void			*formatter;
12661d06d6bSBaptiste Daroussin 	const char		*defos;
12761d06d6bSBaptiste Daroussin 	const char		*errstr;
12861d06d6bSBaptiste Daroussin 	int			 clientfd;
12961d06d6bSBaptiste Daroussin 	int			 old_stdin;
13061d06d6bSBaptiste Daroussin 	int			 old_stdout;
13161d06d6bSBaptiste Daroussin 	int			 old_stderr;
13261d06d6bSBaptiste Daroussin 	int			 fds[3];
13361d06d6bSBaptiste Daroussin 	int			 state, opt;
13461d06d6bSBaptiste Daroussin 	enum outt		 outtype;
13561d06d6bSBaptiste Daroussin 
136*c1c95addSBrooks Davis #if DEBUG_MEMORY
137*c1c95addSBrooks Davis 	mandoc_dbg_init(argc, argv);
138*c1c95addSBrooks Davis #endif
139*c1c95addSBrooks Davis 
14061d06d6bSBaptiste Daroussin 	defos = NULL;
14161d06d6bSBaptiste Daroussin 	outtype = OUTT_ASCII;
14261d06d6bSBaptiste Daroussin 	while ((opt = getopt(argc, argv, "I:T:")) != -1) {
14361d06d6bSBaptiste Daroussin 		switch (opt) {
14461d06d6bSBaptiste Daroussin 		case 'I':
14561d06d6bSBaptiste Daroussin 			if (strncmp(optarg, "os=", 3) == 0)
14661d06d6bSBaptiste Daroussin 				defos = optarg + 3;
14761d06d6bSBaptiste Daroussin 			else {
14861d06d6bSBaptiste Daroussin 				warnx("-I %s: Bad argument", optarg);
14961d06d6bSBaptiste Daroussin 				usage();
15061d06d6bSBaptiste Daroussin 			}
15161d06d6bSBaptiste Daroussin 			break;
15261d06d6bSBaptiste Daroussin 		case 'T':
15361d06d6bSBaptiste Daroussin 			if (strcmp(optarg, "ascii") == 0)
15461d06d6bSBaptiste Daroussin 				outtype = OUTT_ASCII;
15561d06d6bSBaptiste Daroussin 			else if (strcmp(optarg, "utf8") == 0)
15661d06d6bSBaptiste Daroussin 				outtype = OUTT_UTF8;
15761d06d6bSBaptiste Daroussin 			else if (strcmp(optarg, "html") == 0)
15861d06d6bSBaptiste Daroussin 				outtype = OUTT_HTML;
15961d06d6bSBaptiste Daroussin 			else {
16061d06d6bSBaptiste Daroussin 				warnx("-T %s: Bad argument", optarg);
16161d06d6bSBaptiste Daroussin 				usage();
16261d06d6bSBaptiste Daroussin 			}
16361d06d6bSBaptiste Daroussin 			break;
16461d06d6bSBaptiste Daroussin 		default:
16561d06d6bSBaptiste Daroussin 			usage();
16661d06d6bSBaptiste Daroussin 		}
16761d06d6bSBaptiste Daroussin 	}
16861d06d6bSBaptiste Daroussin 
16961d06d6bSBaptiste Daroussin 	if (argc > 0) {
17061d06d6bSBaptiste Daroussin 		argc -= optind;
17161d06d6bSBaptiste Daroussin 		argv += optind;
17261d06d6bSBaptiste Daroussin 	}
17361d06d6bSBaptiste Daroussin 	if (argc != 1)
17461d06d6bSBaptiste Daroussin 		usage();
17561d06d6bSBaptiste Daroussin 
17661d06d6bSBaptiste Daroussin 	errstr = NULL;
17761d06d6bSBaptiste Daroussin 	clientfd = strtonum(argv[0], 3, INT_MAX, &errstr);
17861d06d6bSBaptiste Daroussin 	if (errstr)
17961d06d6bSBaptiste Daroussin 		errx(1, "file descriptor %s %s", argv[1], errstr);
18061d06d6bSBaptiste Daroussin 
18161d06d6bSBaptiste Daroussin 	mchars_alloc();
1827295610fSBaptiste Daroussin 	parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
1837295610fSBaptiste Daroussin 	    MPARSE_VALIDATE, MANDOC_OS_OTHER, defos);
18461d06d6bSBaptiste Daroussin 
18561d06d6bSBaptiste Daroussin 	memset(&options, 0, sizeof(options));
18661d06d6bSBaptiste Daroussin 	switch (outtype) {
18761d06d6bSBaptiste Daroussin 	case OUTT_ASCII:
18861d06d6bSBaptiste Daroussin 		formatter = ascii_alloc(&options);
18961d06d6bSBaptiste Daroussin 		break;
19061d06d6bSBaptiste Daroussin 	case OUTT_UTF8:
19161d06d6bSBaptiste Daroussin 		formatter = utf8_alloc(&options);
19261d06d6bSBaptiste Daroussin 		break;
19361d06d6bSBaptiste Daroussin 	case OUTT_HTML:
19461d06d6bSBaptiste Daroussin 		options.fragment = 1;
19561d06d6bSBaptiste Daroussin 		formatter = html_alloc(&options);
19661d06d6bSBaptiste Daroussin 		break;
19761d06d6bSBaptiste Daroussin 	}
19861d06d6bSBaptiste Daroussin 
19961d06d6bSBaptiste Daroussin 	state = 1;  /* work to do */
20061d06d6bSBaptiste Daroussin 	fflush(stdout);
20161d06d6bSBaptiste Daroussin 	fflush(stderr);
20261d06d6bSBaptiste Daroussin 	if ((old_stdin = dup(STDIN_FILENO)) == -1 ||
20361d06d6bSBaptiste Daroussin 	    (old_stdout = dup(STDOUT_FILENO)) == -1 ||
20461d06d6bSBaptiste Daroussin 	    (old_stderr = dup(STDERR_FILENO)) == -1) {
20561d06d6bSBaptiste Daroussin 		warn("dup");
20661d06d6bSBaptiste Daroussin 		state = -1;  /* error */
20761d06d6bSBaptiste Daroussin 	}
20861d06d6bSBaptiste Daroussin 
20961d06d6bSBaptiste Daroussin 	while (state == 1 && (state = read_fds(clientfd, fds)) == 1) {
21061d06d6bSBaptiste Daroussin 		if (dup2(fds[0], STDIN_FILENO) == -1 ||
21161d06d6bSBaptiste Daroussin 		    dup2(fds[1], STDOUT_FILENO) == -1 ||
21261d06d6bSBaptiste Daroussin 		    dup2(fds[2], STDERR_FILENO) == -1) {
21361d06d6bSBaptiste Daroussin 			warn("dup2");
21461d06d6bSBaptiste Daroussin 			state = -1;
21561d06d6bSBaptiste Daroussin 			break;
21661d06d6bSBaptiste Daroussin 		}
21761d06d6bSBaptiste Daroussin 
21861d06d6bSBaptiste Daroussin 		close(fds[0]);
21961d06d6bSBaptiste Daroussin 		close(fds[1]);
22061d06d6bSBaptiste Daroussin 		close(fds[2]);
22161d06d6bSBaptiste Daroussin 
22261d06d6bSBaptiste Daroussin 		process(parser, outtype, formatter);
22361d06d6bSBaptiste Daroussin 		mparse_reset(parser);
2247295610fSBaptiste Daroussin 		if (outtype == OUTT_HTML)
2257295610fSBaptiste Daroussin 			html_reset(formatter);
22661d06d6bSBaptiste Daroussin 
22761d06d6bSBaptiste Daroussin 		fflush(stdout);
22861d06d6bSBaptiste Daroussin 		fflush(stderr);
22961d06d6bSBaptiste Daroussin 		/* Close file descriptors by restoring the old ones. */
23061d06d6bSBaptiste Daroussin 		if (dup2(old_stderr, STDERR_FILENO) == -1 ||
23161d06d6bSBaptiste Daroussin 		    dup2(old_stdout, STDOUT_FILENO) == -1 ||
23261d06d6bSBaptiste Daroussin 		    dup2(old_stdin, STDIN_FILENO) == -1) {
23361d06d6bSBaptiste Daroussin 			warn("dup2");
23461d06d6bSBaptiste Daroussin 			state = -1;
23561d06d6bSBaptiste Daroussin 			break;
23661d06d6bSBaptiste Daroussin 		}
23761d06d6bSBaptiste Daroussin 	}
23861d06d6bSBaptiste Daroussin 
23961d06d6bSBaptiste Daroussin 	close(clientfd);
24061d06d6bSBaptiste Daroussin 	switch (outtype) {
24161d06d6bSBaptiste Daroussin 	case OUTT_ASCII:
24261d06d6bSBaptiste Daroussin 	case OUTT_UTF8:
24361d06d6bSBaptiste Daroussin 		ascii_free(formatter);
24461d06d6bSBaptiste Daroussin 		break;
24561d06d6bSBaptiste Daroussin 	case OUTT_HTML:
24661d06d6bSBaptiste Daroussin 		html_free(formatter);
24761d06d6bSBaptiste Daroussin 		break;
24861d06d6bSBaptiste Daroussin 	}
24961d06d6bSBaptiste Daroussin 	mparse_free(parser);
25061d06d6bSBaptiste Daroussin 	mchars_free();
251*c1c95addSBrooks Davis #if DEBUG_MEMORY
252*c1c95addSBrooks Davis 	mandoc_dbg_finish();
253*c1c95addSBrooks Davis #endif
25461d06d6bSBaptiste Daroussin 	return state == -1 ? 1 : 0;
25561d06d6bSBaptiste Daroussin }
25661d06d6bSBaptiste Daroussin 
25761d06d6bSBaptiste Daroussin static void
process(struct mparse * parser,enum outt outtype,void * formatter)25861d06d6bSBaptiste Daroussin process(struct mparse *parser, enum outt outtype, void *formatter)
25961d06d6bSBaptiste Daroussin {
2607295610fSBaptiste Daroussin 	struct roff_meta *meta;
26161d06d6bSBaptiste Daroussin 
26261d06d6bSBaptiste Daroussin 	mparse_readfd(parser, STDIN_FILENO, "<unixfd>");
2637295610fSBaptiste Daroussin 	meta = mparse_result(parser);
2647295610fSBaptiste Daroussin 	if (meta->macroset == MACROSET_MDOC) {
26561d06d6bSBaptiste Daroussin 		switch (outtype) {
26661d06d6bSBaptiste Daroussin 		case OUTT_ASCII:
26761d06d6bSBaptiste Daroussin 		case OUTT_UTF8:
2687295610fSBaptiste Daroussin 			terminal_mdoc(formatter, meta);
26961d06d6bSBaptiste Daroussin 			break;
27061d06d6bSBaptiste Daroussin 		case OUTT_HTML:
2717295610fSBaptiste Daroussin 			html_mdoc(formatter, meta);
27261d06d6bSBaptiste Daroussin 			break;
27361d06d6bSBaptiste Daroussin 		}
27461d06d6bSBaptiste Daroussin 	}
2757295610fSBaptiste Daroussin 	if (meta->macroset == MACROSET_MAN) {
27661d06d6bSBaptiste Daroussin 		switch (outtype) {
27761d06d6bSBaptiste Daroussin 		case OUTT_ASCII:
27861d06d6bSBaptiste Daroussin 		case OUTT_UTF8:
2797295610fSBaptiste Daroussin 			terminal_man(formatter, meta);
28061d06d6bSBaptiste Daroussin 			break;
28161d06d6bSBaptiste Daroussin 		case OUTT_HTML:
2827295610fSBaptiste Daroussin 			html_man(formatter, meta);
28361d06d6bSBaptiste Daroussin 			break;
28461d06d6bSBaptiste Daroussin 		}
28561d06d6bSBaptiste Daroussin 	}
28661d06d6bSBaptiste Daroussin }
28761d06d6bSBaptiste Daroussin 
28861d06d6bSBaptiste Daroussin void
usage(void)28961d06d6bSBaptiste Daroussin usage(void)
29061d06d6bSBaptiste Daroussin {
29161d06d6bSBaptiste Daroussin 	fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n");
29261d06d6bSBaptiste Daroussin 	exit(1);
29361d06d6bSBaptiste Daroussin }
294