]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/mdocml/read.c
Increase the buffer size to keep the list of programm names when
[FreeBSD/FreeBSD.git] / contrib / mdocml / read.c
1 /*      $Id: read.c,v 1.101 2014/11/28 18:09:01 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
5  * Copyright (c) 2010, 2012 Joerg Sonnenberger <joerg@netbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 #include "config.h"
20
21 #include <sys/types.h>
22 #if HAVE_MMAP
23 #include <sys/mman.h>
24 #include <sys/stat.h>
25 #endif
26 #include <sys/wait.h>
27
28 #include <assert.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdarg.h>
33 #include <stdint.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 #include "mandoc.h"
40 #include "mandoc_aux.h"
41 #include "libmandoc.h"
42 #include "mdoc.h"
43 #include "man.h"
44 #include "main.h"
45
46 #define REPARSE_LIMIT   1000
47
48 struct  mparse {
49         struct man       *pman; /* persistent man parser */
50         struct mdoc      *pmdoc; /* persistent mdoc parser */
51         struct man       *man; /* man parser */
52         struct mdoc      *mdoc; /* mdoc parser */
53         struct roff      *roff; /* roff parser (!NULL) */
54         const struct mchars *mchars; /* character table */
55         char             *sodest; /* filename pointed to by .so */
56         const char       *file; /* filename of current input file */
57         struct buf       *primary; /* buffer currently being parsed */
58         struct buf       *secondary; /* preprocessed copy of input */
59         const char       *defos; /* default operating system */
60         mandocmsg         mmsg; /* warning/error message handler */
61         enum mandoclevel  file_status; /* status of current parse */
62         enum mandoclevel  wlevel; /* ignore messages below this */
63         int               options; /* parser options */
64         int               filenc; /* encoding of the current file */
65         int               reparse_count; /* finite interp. stack */
66         int               line; /* line number in the file */
67         pid_t             child; /* the gunzip(1) process */
68 };
69
70 static  void      choose_parser(struct mparse *);
71 static  void      resize_buf(struct buf *, size_t);
72 static  void      mparse_buf_r(struct mparse *, struct buf, size_t, int);
73 static  int       read_whole_file(struct mparse *, const char *, int,
74                                 struct buf *, int *);
75 static  void      mparse_end(struct mparse *);
76 static  void      mparse_parse_buffer(struct mparse *, struct buf,
77                         const char *);
78
79 static  const enum mandocerr    mandoclimits[MANDOCLEVEL_MAX] = {
80         MANDOCERR_OK,
81         MANDOCERR_WARNING,
82         MANDOCERR_WARNING,
83         MANDOCERR_ERROR,
84         MANDOCERR_FATAL,
85         MANDOCERR_MAX,
86         MANDOCERR_MAX
87 };
88
89 static  const char * const      mandocerrs[MANDOCERR_MAX] = {
90         "ok",
91
92         "generic warning",
93
94         /* related to the prologue */
95         "missing manual title, using UNTITLED",
96         "missing manual title, using \"\"",
97         "lower case character in document title",
98         "missing manual section, using \"\"",
99         "unknown manual section",
100         "missing date, using today's date",
101         "cannot parse date, using it verbatim",
102         "missing Os macro, using \"\"",
103         "duplicate prologue macro",
104         "late prologue macro",
105         "skipping late title macro",
106         "prologue macros out of order",
107
108         /* related to document structure */
109         ".so is fragile, better use ln(1)",
110         "no document body",
111         "content before first section header",
112         "first section is not \"NAME\"",
113         "bad NAME section contents",
114         "sections out of conventional order",
115         "duplicate section title",
116         "unexpected section",
117         "unusual Xr order",
118         "unusual Xr punctuation",
119         "AUTHORS section without An macro",
120
121         /* related to macros and nesting */
122         "obsolete macro",
123         "skipping paragraph macro",
124         "moving paragraph macro out of list",
125         "skipping no-space macro",
126         "blocks badly nested",
127         "nested displays are not portable",
128         "moving content out of list",
129         ".Vt block has child macro",
130         "fill mode already enabled, skipping",
131         "fill mode already disabled, skipping",
132         "line scope broken",
133
134         /* related to missing macro arguments */
135         "skipping empty request",
136         "conditional request controls empty scope",
137         "skipping empty macro",
138         "empty argument, using 0n",
139         "argument count wrong",
140         "missing display type, using -ragged",
141         "list type is not the first argument",
142         "missing -width in -tag list, using 8n",
143         "missing utility name, using \"\"",
144         "empty head in list item",
145         "empty list item",
146         "missing font type, using \\fR",
147         "unknown font type, using \\fR",
148         "missing -std argument, adding it",
149         "missing eqn box, using \"\"",
150
151         /* related to bad macro arguments */
152         "unterminated quoted argument",
153         "duplicate argument",
154         "skipping duplicate argument",
155         "skipping duplicate display type",
156         "skipping duplicate list type",
157         "skipping -width argument",
158         "unknown AT&T UNIX version",
159         "comma in function argument",
160         "parenthesis in function name",
161         "invalid content in Rs block",
162         "invalid Boolean argument",
163         "unknown font, skipping request",
164
165         /* related to plain text */
166         "blank line in fill mode, using .sp",
167         "tab in filled text",
168         "whitespace at end of input line",
169         "bad comment style",
170         "invalid escape sequence",
171         "undefined string, using \"\"",
172
173         "generic error",
174
175         /* related to equations */
176         "unexpected equation scope closure",
177         "equation scope open on exit",
178         "overlapping equation scopes",
179         "unexpected end of equation",
180
181         /* related to tables */
182         "bad table syntax",
183         "bad table option",
184         "bad table layout",
185         "no table layout cells specified",
186         "no table data cells specified",
187         "ignore data in cell",
188         "data block still open",
189         "ignoring extra data cells",
190
191         /* related to document structure and macros */
192         "input stack limit exceeded, infinite loop?",
193         "skipping bad character",
194         "skipping unknown macro",
195         "skipping item outside list",
196         "skipping column outside column list",
197         "skipping end of block that is not open",
198         "inserting missing end of block",
199         "appending missing end of block",
200
201         /* related to request and macro arguments */
202         "escaped character not allowed in a name",
203         "argument count wrong",
204         "NOT IMPLEMENTED: Bd -file",
205         "missing list type, using -item",
206         "missing manual name, using \"\"",
207         "uname(3) system call failed, using UNKNOWN",
208         "unknown standard specifier",
209         "skipping request without numeric argument",
210         "skipping all arguments",
211         "skipping excess arguments",
212         "divide by zero",
213
214         "generic fatal error",
215
216         "input too large",
217         "NOT IMPLEMENTED: .so with absolute path or \"..\"",
218         ".so request failed",
219
220         /* system errors */
221         "cannot dup file descriptor",
222         "cannot exec",
223         "gunzip failed with code",
224         "cannot fork",
225         NULL,
226         "cannot open pipe",
227         "cannot read file",
228         "gunzip died from signal",
229         "cannot stat file",
230         "wait failed",
231 };
232
233 static  const char * const      mandoclevels[MANDOCLEVEL_MAX] = {
234         "SUCCESS",
235         "RESERVED",
236         "WARNING",
237         "ERROR",
238         "FATAL",
239         "BADARG",
240         "SYSERR"
241 };
242
243
244 static void
245 resize_buf(struct buf *buf, size_t initial)
246 {
247
248         buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial;
249         buf->buf = mandoc_realloc(buf->buf, buf->sz);
250 }
251
252 static void
253 choose_parser(struct mparse *curp)
254 {
255         char            *cp, *ep;
256         int              format;
257
258         /*
259          * If neither command line arguments -mdoc or -man select
260          * a parser nor the roff parser found a .Dd or .TH macro
261          * yet, look ahead in the main input buffer.
262          */
263
264         if ((format = roff_getformat(curp->roff)) == 0) {
265                 cp = curp->primary->buf;
266                 ep = cp + curp->primary->sz;
267                 while (cp < ep) {
268                         if (*cp == '.' || *cp == '\'') {
269                                 cp++;
270                                 if (cp[0] == 'D' && cp[1] == 'd') {
271                                         format = MPARSE_MDOC;
272                                         break;
273                                 }
274                                 if (cp[0] == 'T' && cp[1] == 'H') {
275                                         format = MPARSE_MAN;
276                                         break;
277                                 }
278                         }
279                         cp = memchr(cp, '\n', ep - cp);
280                         if (cp == NULL)
281                                 break;
282                         cp++;
283                 }
284         }
285
286         if (format == MPARSE_MDOC) {
287                 if (NULL == curp->pmdoc)
288                         curp->pmdoc = mdoc_alloc(
289                             curp->roff, curp, curp->defos,
290                             MPARSE_QUICK & curp->options ? 1 : 0);
291                 assert(curp->pmdoc);
292                 curp->mdoc = curp->pmdoc;
293                 return;
294         }
295
296         /* Fall back to man(7) as a last resort. */
297
298         if (NULL == curp->pman)
299                 curp->pman = man_alloc(curp->roff, curp,
300                     MPARSE_QUICK & curp->options ? 1 : 0);
301         assert(curp->pman);
302         curp->man = curp->pman;
303 }
304
305 /*
306  * Main parse routine for a buffer.
307  * It assumes encoding and line numbering are already set up.
308  * It can recurse directly (for invocations of user-defined
309  * macros, inline equations, and input line traps)
310  * and indirectly (for .so file inclusion).
311  */
312 static void
313 mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start)
314 {
315         const struct tbl_span   *span;
316         struct buf       ln;
317         size_t           pos; /* byte number in the ln buffer */
318         enum rofferr     rr;
319         int              of;
320         int              lnn; /* line number in the real file */
321         unsigned char    c;
322
323         memset(&ln, 0, sizeof(ln));
324
325         lnn = curp->line;
326         pos = 0;
327
328         while (i < blk.sz) {
329                 if (0 == pos && '\0' == blk.buf[i])
330                         break;
331
332                 if (start) {
333                         curp->line = lnn;
334                         curp->reparse_count = 0;
335
336                         if (lnn < 3 &&
337                             curp->filenc & MPARSE_UTF8 &&
338                             curp->filenc & MPARSE_LATIN1)
339                                 curp->filenc = preconv_cue(&blk, i);
340                 }
341
342                 while (i < blk.sz && (start || blk.buf[i] != '\0')) {
343
344                         /*
345                          * When finding an unescaped newline character,
346                          * leave the character loop to process the line.
347                          * Skip a preceding carriage return, if any.
348                          */
349
350                         if ('\r' == blk.buf[i] && i + 1 < blk.sz &&
351                             '\n' == blk.buf[i + 1])
352                                 ++i;
353                         if ('\n' == blk.buf[i]) {
354                                 ++i;
355                                 ++lnn;
356                                 break;
357                         }
358
359                         /*
360                          * Make sure we have space for the worst
361                          * case of 11 bytes: "\\[u10ffff]\0"
362                          */
363
364                         if (pos + 11 > ln.sz)
365                                 resize_buf(&ln, 256);
366
367                         /*
368                          * Encode 8-bit input.
369                          */
370
371                         c = blk.buf[i];
372                         if (c & 0x80) {
373                                 if ( ! (curp->filenc && preconv_encode(
374                                     &blk, &i, &ln, &pos, &curp->filenc))) {
375                                         mandoc_vmsg(MANDOCERR_BADCHAR,
376                                             curp, curp->line, pos,
377                                             "0x%x", c);
378                                         ln.buf[pos++] = '?';
379                                         i++;
380                                 }
381                                 continue;
382                         }
383
384                         /*
385                          * Exclude control characters.
386                          */
387
388                         if (c == 0x7f || (c < 0x20 && c != 0x09)) {
389                                 mandoc_vmsg(MANDOCERR_BADCHAR, curp,
390                                     curp->line, pos, "0x%x", c);
391                                 i++;
392                                 ln.buf[pos++] = '?';
393                                 continue;
394                         }
395
396                         /* Trailing backslash = a plain char. */
397
398                         if (blk.buf[i] != '\\' || i + 1 == blk.sz) {
399                                 ln.buf[pos++] = blk.buf[i++];
400                                 continue;
401                         }
402
403                         /*
404                          * Found escape and at least one other character.
405                          * When it's a newline character, skip it.
406                          * When there is a carriage return in between,
407                          * skip that one as well.
408                          */
409
410                         if ('\r' == blk.buf[i + 1] && i + 2 < blk.sz &&
411                             '\n' == blk.buf[i + 2])
412                                 ++i;
413                         if ('\n' == blk.buf[i + 1]) {
414                                 i += 2;
415                                 ++lnn;
416                                 continue;
417                         }
418
419                         if ('"' == blk.buf[i + 1] || '#' == blk.buf[i + 1]) {
420                                 i += 2;
421                                 /* Comment, skip to end of line */
422                                 for (; i < blk.sz; ++i) {
423                                         if ('\n' == blk.buf[i]) {
424                                                 ++i;
425                                                 ++lnn;
426                                                 break;
427                                         }
428                                 }
429
430                                 /* Backout trailing whitespaces */
431                                 for (; pos > 0; --pos) {
432                                         if (ln.buf[pos - 1] != ' ')
433                                                 break;
434                                         if (pos > 2 && ln.buf[pos - 2] == '\\')
435                                                 break;
436                                 }
437                                 break;
438                         }
439
440                         /* Catch escaped bogus characters. */
441
442                         c = (unsigned char) blk.buf[i+1];
443
444                         if ( ! (isascii(c) &&
445                             (isgraph(c) || isblank(c)))) {
446                                 mandoc_vmsg(MANDOCERR_BADCHAR, curp,
447                                     curp->line, pos, "0x%x", c);
448                                 i += 2;
449                                 ln.buf[pos++] = '?';
450                                 continue;
451                         }
452
453                         /* Some other escape sequence, copy & cont. */
454
455                         ln.buf[pos++] = blk.buf[i++];
456                         ln.buf[pos++] = blk.buf[i++];
457                 }
458
459                 if (pos >= ln.sz)
460                         resize_buf(&ln, 256);
461
462                 ln.buf[pos] = '\0';
463
464                 /*
465                  * A significant amount of complexity is contained by
466                  * the roff preprocessor.  It's line-oriented but can be
467                  * expressed on one line, so we need at times to
468                  * readjust our starting point and re-run it.  The roff
469                  * preprocessor can also readjust the buffers with new
470                  * data, so we pass them in wholesale.
471                  */
472
473                 of = 0;
474
475                 /*
476                  * Maintain a lookaside buffer of all parsed lines.  We
477                  * only do this if mparse_keep() has been invoked (the
478                  * buffer may be accessed with mparse_getkeep()).
479                  */
480
481                 if (curp->secondary) {
482                         curp->secondary->buf = mandoc_realloc(
483                             curp->secondary->buf,
484                             curp->secondary->sz + pos + 2);
485                         memcpy(curp->secondary->buf +
486                             curp->secondary->sz,
487                             ln.buf, pos);
488                         curp->secondary->sz += pos;
489                         curp->secondary->buf
490                                 [curp->secondary->sz] = '\n';
491                         curp->secondary->sz++;
492                         curp->secondary->buf
493                                 [curp->secondary->sz] = '\0';
494                 }
495 rerun:
496                 rr = roff_parseln(curp->roff, curp->line, &ln, &of);
497
498                 switch (rr) {
499                 case ROFF_REPARSE:
500                         if (REPARSE_LIMIT >= ++curp->reparse_count)
501                                 mparse_buf_r(curp, ln, of, 0);
502                         else
503                                 mandoc_msg(MANDOCERR_ROFFLOOP, curp,
504                                     curp->line, pos, NULL);
505                         pos = 0;
506                         continue;
507                 case ROFF_APPEND:
508                         pos = strlen(ln.buf);
509                         continue;
510                 case ROFF_RERUN:
511                         goto rerun;
512                 case ROFF_IGN:
513                         pos = 0;
514                         continue;
515                 case ROFF_ERR:
516                         assert(MANDOCLEVEL_FATAL <= curp->file_status);
517                         break;
518                 case ROFF_SO:
519                         if ( ! (curp->options & MPARSE_SO) &&
520                             (i >= blk.sz || blk.buf[i] == '\0')) {
521                                 curp->sodest = mandoc_strdup(ln.buf + of);
522                                 free(ln.buf);
523                                 return;
524                         }
525                         /*
526                          * We remove `so' clauses from our lookaside
527                          * buffer because we're going to descend into
528                          * the file recursively.
529                          */
530                         if (curp->secondary)
531                                 curp->secondary->sz -= pos + 1;
532                         mparse_readfd(curp, -1, ln.buf + of);
533                         if (MANDOCLEVEL_FATAL <= curp->file_status) {
534                                 mandoc_vmsg(MANDOCERR_SO_FAIL,
535                                     curp, curp->line, pos,
536                                     ".so %s", ln.buf + of);
537                                 break;
538                         }
539                         pos = 0;
540                         continue;
541                 default:
542                         break;
543                 }
544
545                 /*
546                  * If we encounter errors in the recursive parse, make
547                  * sure we don't continue parsing.
548                  */
549
550                 if (MANDOCLEVEL_FATAL <= curp->file_status)
551                         break;
552
553                 /*
554                  * If input parsers have not been allocated, do so now.
555                  * We keep these instanced between parsers, but set them
556                  * locally per parse routine since we can use different
557                  * parsers with each one.
558                  */
559
560                 if ( ! (curp->man || curp->mdoc))
561                         choose_parser(curp);
562
563                 /*
564                  * Lastly, push down into the parsers themselves.
565                  * If libroff returns ROFF_TBL, then add it to the
566                  * currently open parse.  Since we only get here if
567                  * there does exist data (see tbl_data.c), we're
568                  * guaranteed that something's been allocated.
569                  * Do the same for ROFF_EQN.
570                  */
571
572                 if (rr == ROFF_TBL) {
573                         while ((span = roff_span(curp->roff)) != NULL)
574                                 if (curp->man == NULL)
575                                         mdoc_addspan(curp->mdoc, span);
576                                 else
577                                         man_addspan(curp->man, span);
578                 } else if (rr == ROFF_EQN) {
579                         if (curp->man == NULL)
580                                 mdoc_addeqn(curp->mdoc, roff_eqn(curp->roff));
581                         else
582                                 man_addeqn(curp->man, roff_eqn(curp->roff));
583                 } else if ((curp->man == NULL ?
584                     mdoc_parseln(curp->mdoc, curp->line, ln.buf, of) :
585                     man_parseln(curp->man, curp->line, ln.buf, of)) == 2)
586                                 break;
587
588                 /* Temporary buffers typically are not full. */
589
590                 if (0 == start && '\0' == blk.buf[i])
591                         break;
592
593                 /* Start the next input line. */
594
595                 pos = 0;
596         }
597
598         free(ln.buf);
599 }
600
601 static int
602 read_whole_file(struct mparse *curp, const char *file, int fd,
603                 struct buf *fb, int *with_mmap)
604 {
605         size_t           off;
606         ssize_t          ssz;
607
608 #if HAVE_MMAP
609         struct stat      st;
610         if (-1 == fstat(fd, &st)) {
611                 curp->file_status = MANDOCLEVEL_SYSERR;
612                 if (curp->mmsg)
613                         (*curp->mmsg)(MANDOCERR_SYSSTAT, curp->file_status,
614                             file, 0, 0, strerror(errno));
615                 return(0);
616         }
617
618         /*
619          * If we're a regular file, try just reading in the whole entry
620          * via mmap().  This is faster than reading it into blocks, and
621          * since each file is only a few bytes to begin with, I'm not
622          * concerned that this is going to tank any machines.
623          */
624
625         if (S_ISREG(st.st_mode)) {
626                 if (st.st_size >= (1U << 31)) {
627                         curp->file_status = MANDOCLEVEL_FATAL;
628                         if (curp->mmsg)
629                                 (*curp->mmsg)(MANDOCERR_TOOLARGE,
630                                     curp->file_status, file, 0, 0, NULL);
631                         return(0);
632                 }
633                 *with_mmap = 1;
634                 fb->sz = (size_t)st.st_size;
635                 fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0);
636                 if (fb->buf != MAP_FAILED)
637                         return(1);
638         }
639 #endif
640
641         /*
642          * If this isn't a regular file (like, say, stdin), then we must
643          * go the old way and just read things in bit by bit.
644          */
645
646         *with_mmap = 0;
647         off = 0;
648         fb->sz = 0;
649         fb->buf = NULL;
650         for (;;) {
651                 if (off == fb->sz) {
652                         if (fb->sz == (1U << 31)) {
653                                 curp->file_status = MANDOCLEVEL_FATAL;
654                                 if (curp->mmsg)
655                                         (*curp->mmsg)(MANDOCERR_TOOLARGE,
656                                             curp->file_status,
657                                             file, 0, 0, NULL);
658                                 break;
659                         }
660                         resize_buf(fb, 65536);
661                 }
662                 ssz = read(fd, fb->buf + (int)off, fb->sz - off);
663                 if (ssz == 0) {
664                         fb->sz = off;
665                         return(1);
666                 }
667                 if (ssz == -1) {
668                         curp->file_status = MANDOCLEVEL_SYSERR;
669                         if (curp->mmsg)
670                                 (*curp->mmsg)(MANDOCERR_SYSREAD,
671                                     curp->file_status, file, 0, 0,
672                                     strerror(errno));
673                         break;
674                 }
675                 off += (size_t)ssz;
676         }
677
678         free(fb->buf);
679         fb->buf = NULL;
680         return(0);
681 }
682
683 static void
684 mparse_end(struct mparse *curp)
685 {
686
687         if (MANDOCLEVEL_FATAL <= curp->file_status)
688                 return;
689
690         if (curp->mdoc == NULL &&
691             curp->man == NULL &&
692             curp->sodest == NULL) {
693                 if (curp->options & MPARSE_MDOC)
694                         curp->mdoc = curp->pmdoc;
695                 else {
696                         if (curp->pman == NULL)
697                                 curp->pman = man_alloc(curp->roff, curp,
698                                     curp->options & MPARSE_QUICK ? 1 : 0);
699                         curp->man = curp->pman;
700                 }
701         }
702
703         if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) {
704                 assert(MANDOCLEVEL_FATAL <= curp->file_status);
705                 return;
706         }
707
708         if (curp->man && ! man_endparse(curp->man)) {
709                 assert(MANDOCLEVEL_FATAL <= curp->file_status);
710                 return;
711         }
712
713         roff_endparse(curp->roff);
714 }
715
716 static void
717 mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file)
718 {
719         struct buf      *svprimary;
720         const char      *svfile;
721         size_t           offset;
722         static int       recursion_depth;
723
724         if (64 < recursion_depth) {
725                 mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, 0, NULL);
726                 return;
727         }
728
729         /* Line number is per-file. */
730         svfile = curp->file;
731         curp->file = file;
732         svprimary = curp->primary;
733         curp->primary = &blk;
734         curp->line = 1;
735         recursion_depth++;
736
737         /* Skip an UTF-8 byte order mark. */
738         if (curp->filenc & MPARSE_UTF8 && blk.sz > 2 &&
739             (unsigned char)blk.buf[0] == 0xef &&
740             (unsigned char)blk.buf[1] == 0xbb &&
741             (unsigned char)blk.buf[2] == 0xbf) {
742                 offset = 3;
743                 curp->filenc &= ~MPARSE_LATIN1;
744         } else
745                 offset = 0;
746
747         mparse_buf_r(curp, blk, offset, 1);
748
749         if (0 == --recursion_depth && MANDOCLEVEL_FATAL > curp->file_status)
750                 mparse_end(curp);
751
752         curp->primary = svprimary;
753         curp->file = svfile;
754 }
755
756 enum mandoclevel
757 mparse_readmem(struct mparse *curp, const void *buf, size_t len,
758                 const char *file)
759 {
760         struct buf blk;
761
762         blk.buf = UNCONST(buf);
763         blk.sz = len;
764
765         mparse_parse_buffer(curp, blk, file);
766         return(curp->file_status);
767 }
768
769 /*
770  * If a file descriptor is given, use it and assume it points
771  * to the named file.  Otherwise, open the named file.
772  * Read the whole file into memory and call the parsers.
773  * Called recursively when an .so request is encountered.
774  */
775 enum mandoclevel
776 mparse_readfd(struct mparse *curp, int fd, const char *file)
777 {
778         struct buf       blk;
779         int              with_mmap;
780         int              save_filenc;
781         pid_t            save_child;
782
783         save_child = curp->child;
784         if (fd != -1)
785                 curp->child = 0;
786         else if (mparse_open(curp, &fd, file) >= MANDOCLEVEL_SYSERR)
787                 goto out;
788
789         if (read_whole_file(curp, file, fd, &blk, &with_mmap)) {
790                 save_filenc = curp->filenc;
791                 curp->filenc = curp->options &
792                     (MPARSE_UTF8 | MPARSE_LATIN1);
793                 mparse_parse_buffer(curp, blk, file);
794                 curp->filenc = save_filenc;
795 #if HAVE_MMAP
796                 if (with_mmap)
797                         munmap(blk.buf, blk.sz);
798                 else
799 #endif
800                         free(blk.buf);
801         }
802
803         if (fd != STDIN_FILENO && close(fd) == -1)
804                 perror(file);
805
806         mparse_wait(curp);
807 out:
808         curp->child = save_child;
809         return(curp->file_status);
810 }
811
812 enum mandoclevel
813 mparse_open(struct mparse *curp, int *fd, const char *file)
814 {
815         int               pfd[2];
816         int               save_errno;
817         char             *cp;
818         enum mandocerr    err;
819
820         pfd[1] = -1;
821         curp->file = file;
822
823         /* Unless zipped, try to just open the file. */
824
825         if ((cp = strrchr(file, '.')) == NULL ||
826             strcmp(cp + 1, "gz")) {
827                 curp->child = 0;
828                 if ((*fd = open(file, O_RDONLY)) != -1)
829                         return(MANDOCLEVEL_OK);
830
831                 /* Open failed; try to append ".gz". */
832
833                 mandoc_asprintf(&cp, "%s.gz", file);
834                 file = cp;
835         } else
836                 cp = NULL;
837
838         /* Before forking, make sure the file can be read. */
839
840         save_errno = errno;
841         if (access(file, R_OK) == -1) {
842                 if (cp != NULL)
843                         errno = save_errno;
844                 err = MANDOCERR_SYSOPEN;
845                 goto out;
846         }
847
848         /* Run gunzip(1). */
849
850         if (pipe(pfd) == -1) {
851                 err = MANDOCERR_SYSPIPE;
852                 goto out;
853         }
854
855         switch (curp->child = fork()) {
856         case -1:
857                 err = MANDOCERR_SYSFORK;
858                 close(pfd[0]);
859                 close(pfd[1]);
860                 pfd[1] = -1;
861                 break;
862         case 0:
863                 close(pfd[0]);
864                 if (dup2(pfd[1], STDOUT_FILENO) == -1) {
865                         err = MANDOCERR_SYSDUP;
866                         break;
867                 }
868                 execlp("gunzip", "gunzip", "-c", file, NULL);
869                 err = MANDOCERR_SYSEXEC;
870                 break;
871         default:
872                 close(pfd[1]);
873                 *fd = pfd[0];
874                 return(MANDOCLEVEL_OK);
875         }
876
877 out:
878         free(cp);
879         *fd = -1;
880         curp->child = 0;
881         curp->file_status = MANDOCLEVEL_SYSERR;
882         if (curp->mmsg)
883                 (*curp->mmsg)(err, curp->file_status, curp->file,
884                     0, 0, strerror(errno));
885         if (pfd[1] != -1)
886                 exit(1);
887         return(curp->file_status);
888 }
889
890 enum mandoclevel
891 mparse_wait(struct mparse *curp)
892 {
893         int       status;
894
895         if (curp->child == 0)
896                 return(MANDOCLEVEL_OK);
897
898         if (waitpid(curp->child, &status, 0) == -1) {
899                 mandoc_msg(MANDOCERR_SYSWAIT, curp, 0, 0,
900                     strerror(errno));
901                 curp->file_status = MANDOCLEVEL_SYSERR;
902                 return(curp->file_status);
903         }
904         if (WIFSIGNALED(status)) {
905                 mandoc_vmsg(MANDOCERR_SYSSIG, curp, 0, 0,
906                     "%d", WTERMSIG(status));
907                 curp->file_status = MANDOCLEVEL_SYSERR;
908                 return(curp->file_status);
909         }
910         if (WEXITSTATUS(status)) {
911                 mandoc_vmsg(MANDOCERR_SYSEXIT, curp, 0, 0,
912                     "%d", WEXITSTATUS(status));
913                 curp->file_status = MANDOCLEVEL_SYSERR;
914                 return(curp->file_status);
915         }
916         return(MANDOCLEVEL_OK);
917 }
918
919 struct mparse *
920 mparse_alloc(int options, enum mandoclevel wlevel, mandocmsg mmsg,
921     const struct mchars *mchars, const char *defos)
922 {
923         struct mparse   *curp;
924
925         assert(wlevel <= MANDOCLEVEL_FATAL);
926
927         curp = mandoc_calloc(1, sizeof(struct mparse));
928
929         curp->options = options;
930         curp->wlevel = wlevel;
931         curp->mmsg = mmsg;
932         curp->defos = defos;
933
934         curp->mchars = mchars;
935         curp->roff = roff_alloc(curp, curp->mchars, options);
936         if (curp->options & MPARSE_MDOC)
937                 curp->pmdoc = mdoc_alloc(
938                     curp->roff, curp, curp->defos,
939                     curp->options & MPARSE_QUICK ? 1 : 0);
940         if (curp->options & MPARSE_MAN)
941                 curp->pman = man_alloc(curp->roff, curp,
942                     curp->options & MPARSE_QUICK ? 1 : 0);
943
944         return(curp);
945 }
946
947 void
948 mparse_reset(struct mparse *curp)
949 {
950
951         roff_reset(curp->roff);
952
953         if (curp->mdoc)
954                 mdoc_reset(curp->mdoc);
955         if (curp->man)
956                 man_reset(curp->man);
957         if (curp->secondary)
958                 curp->secondary->sz = 0;
959
960         curp->file_status = MANDOCLEVEL_OK;
961         curp->mdoc = NULL;
962         curp->man = NULL;
963
964         free(curp->sodest);
965         curp->sodest = NULL;
966 }
967
968 void
969 mparse_free(struct mparse *curp)
970 {
971
972         if (curp->pmdoc)
973                 mdoc_free(curp->pmdoc);
974         if (curp->pman)
975                 man_free(curp->pman);
976         if (curp->roff)
977                 roff_free(curp->roff);
978         if (curp->secondary)
979                 free(curp->secondary->buf);
980
981         free(curp->secondary);
982         free(curp->sodest);
983         free(curp);
984 }
985
986 void
987 mparse_result(struct mparse *curp,
988         struct mdoc **mdoc, struct man **man, char **sodest)
989 {
990
991         if (sodest && NULL != (*sodest = curp->sodest)) {
992                 *mdoc = NULL;
993                 *man = NULL;
994                 return;
995         }
996         if (mdoc)
997                 *mdoc = curp->mdoc;
998         if (man)
999                 *man = curp->man;
1000 }
1001
1002 void
1003 mandoc_vmsg(enum mandocerr t, struct mparse *m,
1004                 int ln, int pos, const char *fmt, ...)
1005 {
1006         char             buf[256];
1007         va_list          ap;
1008
1009         va_start(ap, fmt);
1010         (void)vsnprintf(buf, sizeof(buf), fmt, ap);
1011         va_end(ap);
1012
1013         mandoc_msg(t, m, ln, pos, buf);
1014 }
1015
1016 void
1017 mandoc_msg(enum mandocerr er, struct mparse *m,
1018                 int ln, int col, const char *msg)
1019 {
1020         enum mandoclevel level;
1021
1022         level = MANDOCLEVEL_FATAL;
1023         while (er < mandoclimits[level])
1024                 level--;
1025
1026         if (level < m->wlevel)
1027                 return;
1028
1029         if (m->mmsg)
1030                 (*m->mmsg)(er, level, m->file, ln, col, msg);
1031
1032         if (m->file_status < level)
1033                 m->file_status = level;
1034 }
1035
1036 const char *
1037 mparse_strerror(enum mandocerr er)
1038 {
1039
1040         return(mandocerrs[er]);
1041 }
1042
1043 const char *
1044 mparse_strlevel(enum mandoclevel lvl)
1045 {
1046         return(mandoclevels[lvl]);
1047 }
1048
1049 void
1050 mparse_keep(struct mparse *p)
1051 {
1052
1053         assert(NULL == p->secondary);
1054         p->secondary = mandoc_calloc(1, sizeof(struct buf));
1055 }
1056
1057 const char *
1058 mparse_getkeep(const struct mparse *p)
1059 {
1060
1061         assert(p->secondary);
1062         return(p->secondary->sz ? p->secondary->buf : NULL);
1063 }