xref: /netbsd-src/external/bsd/mdocml/dist/mandocd.c (revision 544c191c349c1704c9d5e679d12ec15cff579663)
1*544c191cSchristos /*	Id: mandocd.c,v 1.11 2019/03/03 13:02:11 schwarze Exp  */
29508192eSchristos /*
39508192eSchristos  * Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
4*544c191cSchristos  * Copyright (c) 2017, 2019 Ingo Schwarze <schwarze@openbsd.org>
59508192eSchristos  *
69508192eSchristos  * Permission to use, copy, modify, and distribute this software for any
79508192eSchristos  * purpose with or without fee is hereby granted, provided that the above
89508192eSchristos  * copyright notice and this permission notice appear in all copies.
99508192eSchristos  *
109508192eSchristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
119508192eSchristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
129508192eSchristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
139508192eSchristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
149508192eSchristos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
159508192eSchristos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
169508192eSchristos  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
179508192eSchristos  */
189508192eSchristos #include "config.h"
199508192eSchristos 
209508192eSchristos #if HAVE_CMSG_XPG42
219508192eSchristos #define _XPG4_2
229508192eSchristos #endif
239508192eSchristos 
249508192eSchristos #include <sys/types.h>
259508192eSchristos #include <sys/socket.h>
269508192eSchristos 
279508192eSchristos #if HAVE_ERR
289508192eSchristos #include <err.h>
299508192eSchristos #endif
309508192eSchristos #include <limits.h>
319508192eSchristos #include <stdint.h>
329508192eSchristos #include <stdio.h>
339508192eSchristos #include <stdlib.h>
349508192eSchristos #include <string.h>
359508192eSchristos #include <unistd.h>
369508192eSchristos 
379508192eSchristos #include "mandoc.h"
389508192eSchristos #include "roff.h"
399508192eSchristos #include "mdoc.h"
409508192eSchristos #include "man.h"
41*544c191cSchristos #include "mandoc_parse.h"
429508192eSchristos #include "main.h"
439508192eSchristos #include "manconf.h"
449508192eSchristos 
459508192eSchristos enum	outt {
469508192eSchristos 	OUTT_ASCII = 0,
479508192eSchristos 	OUTT_UTF8,
489508192eSchristos 	OUTT_HTML
499508192eSchristos };
509508192eSchristos 
519508192eSchristos static	void	  process(struct mparse *, enum outt, void *);
529508192eSchristos static	int	  read_fds(int, int *);
539508192eSchristos static	void	  usage(void) __attribute__((__noreturn__));
549508192eSchristos 
559508192eSchristos 
569508192eSchristos #define NUM_FDS 3
579508192eSchristos static int
read_fds(int clientfd,int * fds)589508192eSchristos read_fds(int clientfd, int *fds)
599508192eSchristos {
609508192eSchristos 	struct msghdr	 msg;
619508192eSchristos 	struct iovec	 iov[1];
629508192eSchristos 	unsigned char	 dummy[1];
639508192eSchristos 	struct cmsghdr	*cmsg;
649508192eSchristos 	int		*walk;
659508192eSchristos 	int		 cnt;
669508192eSchristos 
679508192eSchristos 	/* Union used for alignment. */
689508192eSchristos 	union {
699508192eSchristos 		uint8_t controlbuf[CMSG_SPACE(NUM_FDS * sizeof(int))];
709508192eSchristos 		struct cmsghdr align;
719508192eSchristos 	} u;
729508192eSchristos 
739508192eSchristos 	memset(&msg, '\0', sizeof(msg));
749508192eSchristos 	msg.msg_control = u.controlbuf;
759508192eSchristos 	msg.msg_controllen = sizeof(u.controlbuf);
769508192eSchristos 
779508192eSchristos 	/*
789508192eSchristos 	 * Read a dummy byte - sendmsg cannot send an empty message,
799508192eSchristos 	 * even if we are only interested in the OOB data.
809508192eSchristos 	 */
819508192eSchristos 
829508192eSchristos 	iov[0].iov_base = dummy;
839508192eSchristos 	iov[0].iov_len = sizeof(dummy);
849508192eSchristos 	msg.msg_iov = iov;
859508192eSchristos 	msg.msg_iovlen = 1;
869508192eSchristos 
879508192eSchristos 	switch (recvmsg(clientfd, &msg, 0)) {
889508192eSchristos 	case -1:
899508192eSchristos 		warn("recvmsg");
909508192eSchristos 		return -1;
919508192eSchristos 	case 0:
929508192eSchristos 		return 0;
939508192eSchristos 	default:
949508192eSchristos 		break;
959508192eSchristos 	}
969508192eSchristos 
979508192eSchristos 	if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) {
989508192eSchristos 		warnx("CMSG_FIRSTHDR: missing control message");
999508192eSchristos 		return -1;
1009508192eSchristos 	}
1019508192eSchristos 
1029508192eSchristos 	if (cmsg->cmsg_level != SOL_SOCKET ||
1039508192eSchristos 	    cmsg->cmsg_type != SCM_RIGHTS ||
1049508192eSchristos 	    cmsg->cmsg_len != CMSG_LEN(NUM_FDS * sizeof(int))) {
1059508192eSchristos 		warnx("CMSG_FIRSTHDR: invalid control message");
1069508192eSchristos 		return -1;
1079508192eSchristos 	}
1089508192eSchristos 
1099508192eSchristos 	walk = (int *)CMSG_DATA(cmsg);
1109508192eSchristos 	for (cnt = 0; cnt < NUM_FDS; cnt++)
1119508192eSchristos 		fds[cnt] = *walk++;
1129508192eSchristos 
1139508192eSchristos 	return 1;
1149508192eSchristos }
1159508192eSchristos 
1169508192eSchristos int
main(int argc,char * argv[])1179508192eSchristos main(int argc, char *argv[])
1189508192eSchristos {
1199508192eSchristos 	struct manoutput	 options;
1209508192eSchristos 	struct mparse		*parser;
1219508192eSchristos 	void			*formatter;
1229508192eSchristos 	const char		*defos;
1239508192eSchristos 	const char		*errstr;
1249508192eSchristos 	int			 clientfd;
1259508192eSchristos 	int			 old_stdin;
1269508192eSchristos 	int			 old_stdout;
1279508192eSchristos 	int			 old_stderr;
1289508192eSchristos 	int			 fds[3];
1299508192eSchristos 	int			 state, opt;
1309508192eSchristos 	enum outt		 outtype;
1319508192eSchristos 
1329508192eSchristos 	defos = NULL;
1339508192eSchristos 	outtype = OUTT_ASCII;
1349508192eSchristos 	while ((opt = getopt(argc, argv, "I:T:")) != -1) {
1359508192eSchristos 		switch (opt) {
1369508192eSchristos 		case 'I':
1379508192eSchristos 			if (strncmp(optarg, "os=", 3) == 0)
1389508192eSchristos 				defos = optarg + 3;
1399508192eSchristos 			else {
1409508192eSchristos 				warnx("-I %s: Bad argument", optarg);
1419508192eSchristos 				usage();
1429508192eSchristos 			}
1439508192eSchristos 			break;
1449508192eSchristos 		case 'T':
1459508192eSchristos 			if (strcmp(optarg, "ascii") == 0)
1469508192eSchristos 				outtype = OUTT_ASCII;
1479508192eSchristos 			else if (strcmp(optarg, "utf8") == 0)
1489508192eSchristos 				outtype = OUTT_UTF8;
1499508192eSchristos 			else if (strcmp(optarg, "html") == 0)
1509508192eSchristos 				outtype = OUTT_HTML;
1519508192eSchristos 			else {
1529508192eSchristos 				warnx("-T %s: Bad argument", optarg);
1539508192eSchristos 				usage();
1549508192eSchristos 			}
1559508192eSchristos 			break;
1569508192eSchristos 		default:
1579508192eSchristos 			usage();
1589508192eSchristos 		}
1599508192eSchristos 	}
1609508192eSchristos 
1619508192eSchristos 	if (argc > 0) {
1629508192eSchristos 		argc -= optind;
1639508192eSchristos 		argv += optind;
1649508192eSchristos 	}
1659508192eSchristos 	if (argc != 1)
1669508192eSchristos 		usage();
1679508192eSchristos 
1689508192eSchristos 	errstr = NULL;
1699508192eSchristos 	clientfd = strtonum(argv[0], 3, INT_MAX, &errstr);
1709508192eSchristos 	if (errstr)
1719508192eSchristos 		errx(1, "file descriptor %s %s", argv[1], errstr);
1729508192eSchristos 
1739508192eSchristos 	mchars_alloc();
174*544c191cSchristos 	parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
175*544c191cSchristos 	    MPARSE_VALIDATE, MANDOC_OS_OTHER, defos);
1769508192eSchristos 
1779508192eSchristos 	memset(&options, 0, sizeof(options));
1789508192eSchristos 	switch (outtype) {
1799508192eSchristos 	case OUTT_ASCII:
1809508192eSchristos 		formatter = ascii_alloc(&options);
1819508192eSchristos 		break;
1829508192eSchristos 	case OUTT_UTF8:
1839508192eSchristos 		formatter = utf8_alloc(&options);
1849508192eSchristos 		break;
1859508192eSchristos 	case OUTT_HTML:
1869508192eSchristos 		options.fragment = 1;
1879508192eSchristos 		formatter = html_alloc(&options);
1889508192eSchristos 		break;
1899508192eSchristos 	}
1909508192eSchristos 
1919508192eSchristos 	state = 1;  /* work to do */
1929508192eSchristos 	fflush(stdout);
1939508192eSchristos 	fflush(stderr);
1949508192eSchristos 	if ((old_stdin = dup(STDIN_FILENO)) == -1 ||
1959508192eSchristos 	    (old_stdout = dup(STDOUT_FILENO)) == -1 ||
1969508192eSchristos 	    (old_stderr = dup(STDERR_FILENO)) == -1) {
1979508192eSchristos 		warn("dup");
1989508192eSchristos 		state = -1;  /* error */
1999508192eSchristos 	}
2009508192eSchristos 
2019508192eSchristos 	while (state == 1 && (state = read_fds(clientfd, fds)) == 1) {
2029508192eSchristos 		if (dup2(fds[0], STDIN_FILENO) == -1 ||
2039508192eSchristos 		    dup2(fds[1], STDOUT_FILENO) == -1 ||
2049508192eSchristos 		    dup2(fds[2], STDERR_FILENO) == -1) {
2059508192eSchristos 			warn("dup2");
2069508192eSchristos 			state = -1;
2079508192eSchristos 			break;
2089508192eSchristos 		}
2099508192eSchristos 
2109508192eSchristos 		close(fds[0]);
2119508192eSchristos 		close(fds[1]);
2129508192eSchristos 		close(fds[2]);
2139508192eSchristos 
2149508192eSchristos 		process(parser, outtype, formatter);
2159508192eSchristos 		mparse_reset(parser);
216*544c191cSchristos 		if (outtype == OUTT_HTML)
217*544c191cSchristos 			html_reset(formatter);
2189508192eSchristos 
2199508192eSchristos 		fflush(stdout);
2209508192eSchristos 		fflush(stderr);
2219508192eSchristos 		/* Close file descriptors by restoring the old ones. */
2229508192eSchristos 		if (dup2(old_stderr, STDERR_FILENO) == -1 ||
2239508192eSchristos 		    dup2(old_stdout, STDOUT_FILENO) == -1 ||
2249508192eSchristos 		    dup2(old_stdin, STDIN_FILENO) == -1) {
2259508192eSchristos 			warn("dup2");
2269508192eSchristos 			state = -1;
2279508192eSchristos 			break;
2289508192eSchristos 		}
2299508192eSchristos 	}
2309508192eSchristos 
2319508192eSchristos 	close(clientfd);
2329508192eSchristos 	switch (outtype) {
2339508192eSchristos 	case OUTT_ASCII:
2349508192eSchristos 	case OUTT_UTF8:
2359508192eSchristos 		ascii_free(formatter);
2369508192eSchristos 		break;
2379508192eSchristos 	case OUTT_HTML:
2389508192eSchristos 		html_free(formatter);
2399508192eSchristos 		break;
2409508192eSchristos 	}
2419508192eSchristos 	mparse_free(parser);
2429508192eSchristos 	mchars_free();
2439508192eSchristos 	return state == -1 ? 1 : 0;
2449508192eSchristos }
2459508192eSchristos 
2469508192eSchristos static void
process(struct mparse * parser,enum outt outtype,void * formatter)2479508192eSchristos process(struct mparse *parser, enum outt outtype, void *formatter)
2489508192eSchristos {
249*544c191cSchristos 	struct roff_meta *meta;
2509508192eSchristos 
2519508192eSchristos 	mparse_readfd(parser, STDIN_FILENO, "<unixfd>");
252*544c191cSchristos 	meta = mparse_result(parser);
253*544c191cSchristos 	if (meta->macroset == MACROSET_MDOC) {
2549508192eSchristos 		switch (outtype) {
2559508192eSchristos 		case OUTT_ASCII:
2569508192eSchristos 		case OUTT_UTF8:
257*544c191cSchristos 			terminal_mdoc(formatter, meta);
2589508192eSchristos 			break;
2599508192eSchristos 		case OUTT_HTML:
260*544c191cSchristos 			html_mdoc(formatter, meta);
2619508192eSchristos 			break;
2629508192eSchristos 		}
2639508192eSchristos 	}
264*544c191cSchristos 	if (meta->macroset == MACROSET_MAN) {
2659508192eSchristos 		switch (outtype) {
2669508192eSchristos 		case OUTT_ASCII:
2679508192eSchristos 		case OUTT_UTF8:
268*544c191cSchristos 			terminal_man(formatter, meta);
2699508192eSchristos 			break;
2709508192eSchristos 		case OUTT_HTML:
271*544c191cSchristos 			html_man(formatter, meta);
2729508192eSchristos 			break;
2739508192eSchristos 		}
2749508192eSchristos 	}
2759508192eSchristos }
2769508192eSchristos 
2779508192eSchristos void
usage(void)2789508192eSchristos usage(void)
2799508192eSchristos {
2809508192eSchristos 	fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n");
2819508192eSchristos 	exit(1);
2829508192eSchristos }
283