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