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