xref: /dragonfly/contrib/mdocml/mandocd.c (revision 1e4d43f9c96723e4e55543d240f182e1aac9a4c2)
1 /*        $Id: mandocd.c,v 1.12 2020/06/14 23:40:31 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 NEED_XPG4_2
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
read_fds(int clientfd,int * fds)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
main(int argc,char * argv[])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
process(struct mparse * parser,enum outt outtype,void * formatter)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
usage(void)278 usage(void)
279 {
280           fprintf(stderr, "usage: mandocd [-I os=name] [-T output] socket_fd\n");
281           exit(1);
282 }
283