1*c1c95addSBrooks Davis /* $Id: read.c,v 1.221 2022/05/19 14:48:56 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 36d38604fSBaptiste Daroussin * Copyright (c) 2010-2020 Ingo Schwarze <schwarze@openbsd.org> 461d06d6bSBaptiste Daroussin * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 561d06d6bSBaptiste Daroussin * Copyright (c) 2010, 2012 Joerg Sonnenberger <joerg@netbsd.org> 661d06d6bSBaptiste Daroussin * 761d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 861d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 961d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 1061d06d6bSBaptiste Daroussin * 1161d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1261d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1361d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 1461d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1561d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1661d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1761d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 186d38604fSBaptiste Daroussin * 196d38604fSBaptiste Daroussin * Top-level functions of the mandoc(3) parser: 206d38604fSBaptiste Daroussin * Parser and input encoding selection, decompression, 216d38604fSBaptiste Daroussin * handling of input bytes, characters, lines, and files, 226d38604fSBaptiste Daroussin * handling of roff(7) loops and file inclusion, 236d38604fSBaptiste Daroussin * and steering of the various parsers. 2461d06d6bSBaptiste Daroussin */ 2561d06d6bSBaptiste Daroussin #include "config.h" 2661d06d6bSBaptiste Daroussin 2761d06d6bSBaptiste Daroussin #include <sys/types.h> 2861d06d6bSBaptiste Daroussin #include <sys/mman.h> 2961d06d6bSBaptiste Daroussin #include <sys/stat.h> 3061d06d6bSBaptiste Daroussin 3161d06d6bSBaptiste Daroussin #include <assert.h> 3261d06d6bSBaptiste Daroussin #include <ctype.h> 3361d06d6bSBaptiste Daroussin #include <errno.h> 3461d06d6bSBaptiste Daroussin #include <fcntl.h> 3561d06d6bSBaptiste Daroussin #include <stdarg.h> 3661d06d6bSBaptiste Daroussin #include <stdio.h> 3761d06d6bSBaptiste Daroussin #include <stdlib.h> 3861d06d6bSBaptiste Daroussin #include <string.h> 3961d06d6bSBaptiste Daroussin #include <unistd.h> 4061d06d6bSBaptiste Daroussin #include <zlib.h> 4161d06d6bSBaptiste Daroussin 4261d06d6bSBaptiste Daroussin #include "mandoc_aux.h" 4361d06d6bSBaptiste Daroussin #include "mandoc.h" 4461d06d6bSBaptiste Daroussin #include "roff.h" 4561d06d6bSBaptiste Daroussin #include "mdoc.h" 4661d06d6bSBaptiste Daroussin #include "man.h" 477295610fSBaptiste Daroussin #include "mandoc_parse.h" 4861d06d6bSBaptiste Daroussin #include "libmandoc.h" 497295610fSBaptiste Daroussin #include "roff_int.h" 506d38604fSBaptiste Daroussin #include "tag.h" 5161d06d6bSBaptiste Daroussin 5261d06d6bSBaptiste Daroussin #define REPARSE_LIMIT 1000 5361d06d6bSBaptiste Daroussin 5461d06d6bSBaptiste Daroussin struct mparse { 5561d06d6bSBaptiste Daroussin struct roff *roff; /* roff parser (!NULL) */ 5661d06d6bSBaptiste Daroussin struct roff_man *man; /* man parser */ 5761d06d6bSBaptiste Daroussin struct buf *primary; /* buffer currently being parsed */ 587295610fSBaptiste Daroussin struct buf *secondary; /* copy of top level input */ 597295610fSBaptiste Daroussin struct buf *loop; /* open .while request line */ 6061d06d6bSBaptiste Daroussin const char *os_s; /* default operating system */ 6161d06d6bSBaptiste Daroussin int options; /* parser options */ 6261d06d6bSBaptiste Daroussin int gzip; /* current input file is gzipped */ 6361d06d6bSBaptiste Daroussin int filenc; /* encoding of the current file */ 6461d06d6bSBaptiste Daroussin int reparse_count; /* finite interp. stack */ 6561d06d6bSBaptiste Daroussin int line; /* line number in the file */ 6661d06d6bSBaptiste Daroussin }; 6761d06d6bSBaptiste Daroussin 6861d06d6bSBaptiste Daroussin static void choose_parser(struct mparse *); 697295610fSBaptiste Daroussin static void free_buf_list(struct buf *); 7061d06d6bSBaptiste Daroussin static void resize_buf(struct buf *, size_t); 7161d06d6bSBaptiste Daroussin static int mparse_buf_r(struct mparse *, struct buf, size_t, int); 727295610fSBaptiste Daroussin static int read_whole_file(struct mparse *, int, struct buf *, int *); 7361d06d6bSBaptiste Daroussin static void mparse_end(struct mparse *); 7461d06d6bSBaptiste Daroussin 7561d06d6bSBaptiste Daroussin 7661d06d6bSBaptiste Daroussin static void 7761d06d6bSBaptiste Daroussin resize_buf(struct buf *buf, size_t initial) 7861d06d6bSBaptiste Daroussin { 7961d06d6bSBaptiste Daroussin 8061d06d6bSBaptiste Daroussin buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial; 8161d06d6bSBaptiste Daroussin buf->buf = mandoc_realloc(buf->buf, buf->sz); 8261d06d6bSBaptiste Daroussin } 8361d06d6bSBaptiste Daroussin 8461d06d6bSBaptiste Daroussin static void 857295610fSBaptiste Daroussin free_buf_list(struct buf *buf) 867295610fSBaptiste Daroussin { 877295610fSBaptiste Daroussin struct buf *tmp; 887295610fSBaptiste Daroussin 897295610fSBaptiste Daroussin while (buf != NULL) { 907295610fSBaptiste Daroussin tmp = buf; 917295610fSBaptiste Daroussin buf = tmp->next; 927295610fSBaptiste Daroussin free(tmp->buf); 937295610fSBaptiste Daroussin free(tmp); 947295610fSBaptiste Daroussin } 957295610fSBaptiste Daroussin } 967295610fSBaptiste Daroussin 977295610fSBaptiste Daroussin static void 9861d06d6bSBaptiste Daroussin choose_parser(struct mparse *curp) 9961d06d6bSBaptiste Daroussin { 10061d06d6bSBaptiste Daroussin char *cp, *ep; 10161d06d6bSBaptiste Daroussin int format; 10261d06d6bSBaptiste Daroussin 10361d06d6bSBaptiste Daroussin /* 10461d06d6bSBaptiste Daroussin * If neither command line arguments -mdoc or -man select 10561d06d6bSBaptiste Daroussin * a parser nor the roff parser found a .Dd or .TH macro 10661d06d6bSBaptiste Daroussin * yet, look ahead in the main input buffer. 10761d06d6bSBaptiste Daroussin */ 10861d06d6bSBaptiste Daroussin 10961d06d6bSBaptiste Daroussin if ((format = roff_getformat(curp->roff)) == 0) { 11061d06d6bSBaptiste Daroussin cp = curp->primary->buf; 11161d06d6bSBaptiste Daroussin ep = cp + curp->primary->sz; 11261d06d6bSBaptiste Daroussin while (cp < ep) { 11361d06d6bSBaptiste Daroussin if (*cp == '.' || *cp == '\'') { 11461d06d6bSBaptiste Daroussin cp++; 11561d06d6bSBaptiste Daroussin if (cp[0] == 'D' && cp[1] == 'd') { 11661d06d6bSBaptiste Daroussin format = MPARSE_MDOC; 11761d06d6bSBaptiste Daroussin break; 11861d06d6bSBaptiste Daroussin } 11961d06d6bSBaptiste Daroussin if (cp[0] == 'T' && cp[1] == 'H') { 12061d06d6bSBaptiste Daroussin format = MPARSE_MAN; 12161d06d6bSBaptiste Daroussin break; 12261d06d6bSBaptiste Daroussin } 12361d06d6bSBaptiste Daroussin } 12461d06d6bSBaptiste Daroussin cp = memchr(cp, '\n', ep - cp); 12561d06d6bSBaptiste Daroussin if (cp == NULL) 12661d06d6bSBaptiste Daroussin break; 12761d06d6bSBaptiste Daroussin cp++; 12861d06d6bSBaptiste Daroussin } 12961d06d6bSBaptiste Daroussin } 13061d06d6bSBaptiste Daroussin 13161d06d6bSBaptiste Daroussin if (format == MPARSE_MDOC) { 1327295610fSBaptiste Daroussin curp->man->meta.macroset = MACROSET_MDOC; 13361d06d6bSBaptiste Daroussin if (curp->man->mdocmac == NULL) 13461d06d6bSBaptiste Daroussin curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX); 13561d06d6bSBaptiste Daroussin } else { 1367295610fSBaptiste Daroussin curp->man->meta.macroset = MACROSET_MAN; 13761d06d6bSBaptiste Daroussin if (curp->man->manmac == NULL) 13861d06d6bSBaptiste Daroussin curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX); 13961d06d6bSBaptiste Daroussin } 1407295610fSBaptiste Daroussin curp->man->meta.first->tok = TOKEN_NONE; 14161d06d6bSBaptiste Daroussin } 14261d06d6bSBaptiste Daroussin 14361d06d6bSBaptiste Daroussin /* 14461d06d6bSBaptiste Daroussin * Main parse routine for a buffer. 14561d06d6bSBaptiste Daroussin * It assumes encoding and line numbering are already set up. 14661d06d6bSBaptiste Daroussin * It can recurse directly (for invocations of user-defined 14761d06d6bSBaptiste Daroussin * macros, inline equations, and input line traps) 14861d06d6bSBaptiste Daroussin * and indirectly (for .so file inclusion). 14961d06d6bSBaptiste Daroussin */ 15061d06d6bSBaptiste Daroussin static int 15161d06d6bSBaptiste Daroussin mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) 15261d06d6bSBaptiste Daroussin { 15361d06d6bSBaptiste Daroussin struct buf ln; 1547295610fSBaptiste Daroussin struct buf *firstln, *lastln, *thisln, *loop; 15561d06d6bSBaptiste Daroussin char *cp; 15661d06d6bSBaptiste Daroussin size_t pos; /* byte number in the ln buffer */ 1576d38604fSBaptiste Daroussin size_t spos; /* at the start of the current line parse */ 1587295610fSBaptiste Daroussin int line_result, result; 15961d06d6bSBaptiste Daroussin int of; 16061d06d6bSBaptiste Daroussin int lnn; /* line number in the real file */ 16161d06d6bSBaptiste Daroussin int fd; 1627295610fSBaptiste Daroussin int inloop; /* Saw .while on this level. */ 16361d06d6bSBaptiste Daroussin unsigned char c; 16461d06d6bSBaptiste Daroussin 1657295610fSBaptiste Daroussin ln.sz = 256; 1667295610fSBaptiste Daroussin ln.buf = mandoc_malloc(ln.sz); 1677295610fSBaptiste Daroussin ln.next = NULL; 16845a5aec3SBaptiste Daroussin firstln = lastln = loop = NULL; 16961d06d6bSBaptiste Daroussin lnn = curp->line; 17061d06d6bSBaptiste Daroussin pos = 0; 1717295610fSBaptiste Daroussin inloop = 0; 1727295610fSBaptiste Daroussin result = ROFF_CONT; 17361d06d6bSBaptiste Daroussin 1747295610fSBaptiste Daroussin while (i < blk.sz && (blk.buf[i] != '\0' || pos != 0)) { 17561d06d6bSBaptiste Daroussin if (start) { 17661d06d6bSBaptiste Daroussin curp->line = lnn; 17761d06d6bSBaptiste Daroussin curp->reparse_count = 0; 17861d06d6bSBaptiste Daroussin 17961d06d6bSBaptiste Daroussin if (lnn < 3 && 18061d06d6bSBaptiste Daroussin curp->filenc & MPARSE_UTF8 && 18161d06d6bSBaptiste Daroussin curp->filenc & MPARSE_LATIN1) 18261d06d6bSBaptiste Daroussin curp->filenc = preconv_cue(&blk, i); 18361d06d6bSBaptiste Daroussin } 1846d38604fSBaptiste Daroussin spos = pos; 18561d06d6bSBaptiste Daroussin 18661d06d6bSBaptiste Daroussin while (i < blk.sz && (start || blk.buf[i] != '\0')) { 18761d06d6bSBaptiste Daroussin 18861d06d6bSBaptiste Daroussin /* 18961d06d6bSBaptiste Daroussin * When finding an unescaped newline character, 19061d06d6bSBaptiste Daroussin * leave the character loop to process the line. 19161d06d6bSBaptiste Daroussin * Skip a preceding carriage return, if any. 19261d06d6bSBaptiste Daroussin */ 19361d06d6bSBaptiste Daroussin 19461d06d6bSBaptiste Daroussin if ('\r' == blk.buf[i] && i + 1 < blk.sz && 19561d06d6bSBaptiste Daroussin '\n' == blk.buf[i + 1]) 19661d06d6bSBaptiste Daroussin ++i; 19761d06d6bSBaptiste Daroussin if ('\n' == blk.buf[i]) { 19861d06d6bSBaptiste Daroussin ++i; 19961d06d6bSBaptiste Daroussin ++lnn; 20061d06d6bSBaptiste Daroussin break; 20161d06d6bSBaptiste Daroussin } 20261d06d6bSBaptiste Daroussin 20361d06d6bSBaptiste Daroussin /* 20461d06d6bSBaptiste Daroussin * Make sure we have space for the worst 2057295610fSBaptiste Daroussin * case of 12 bytes: "\\[u10ffff]\n\0" 20661d06d6bSBaptiste Daroussin */ 20761d06d6bSBaptiste Daroussin 2087295610fSBaptiste Daroussin if (pos + 12 > ln.sz) 20961d06d6bSBaptiste Daroussin resize_buf(&ln, 256); 21061d06d6bSBaptiste Daroussin 21161d06d6bSBaptiste Daroussin /* 21261d06d6bSBaptiste Daroussin * Encode 8-bit input. 21361d06d6bSBaptiste Daroussin */ 21461d06d6bSBaptiste Daroussin 21561d06d6bSBaptiste Daroussin c = blk.buf[i]; 21661d06d6bSBaptiste Daroussin if (c & 0x80) { 21761d06d6bSBaptiste Daroussin if ( ! (curp->filenc && preconv_encode( 21861d06d6bSBaptiste Daroussin &blk, &i, &ln, &pos, &curp->filenc))) { 2197295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_CHAR_BAD, 22061d06d6bSBaptiste Daroussin curp->line, pos, "0x%x", c); 22161d06d6bSBaptiste Daroussin ln.buf[pos++] = '?'; 22261d06d6bSBaptiste Daroussin i++; 22361d06d6bSBaptiste Daroussin } 22461d06d6bSBaptiste Daroussin continue; 22561d06d6bSBaptiste Daroussin } 22661d06d6bSBaptiste Daroussin 22761d06d6bSBaptiste Daroussin /* 22861d06d6bSBaptiste Daroussin * Exclude control characters. 22961d06d6bSBaptiste Daroussin */ 23061d06d6bSBaptiste Daroussin 23161d06d6bSBaptiste Daroussin if (c == 0x7f || (c < 0x20 && c != 0x09)) { 2327295610fSBaptiste Daroussin mandoc_msg(c == 0x00 || c == 0x04 || 23361d06d6bSBaptiste Daroussin c > 0x0a ? MANDOCERR_CHAR_BAD : 23461d06d6bSBaptiste Daroussin MANDOCERR_CHAR_UNSUPP, 2357295610fSBaptiste Daroussin curp->line, pos, "0x%x", c); 23661d06d6bSBaptiste Daroussin i++; 23761d06d6bSBaptiste Daroussin if (c != '\r') 23861d06d6bSBaptiste Daroussin ln.buf[pos++] = '?'; 23961d06d6bSBaptiste Daroussin continue; 24061d06d6bSBaptiste Daroussin } 24161d06d6bSBaptiste Daroussin 24261d06d6bSBaptiste Daroussin ln.buf[pos++] = blk.buf[i++]; 24361d06d6bSBaptiste Daroussin } 2447295610fSBaptiste Daroussin ln.buf[pos] = '\0'; 24561d06d6bSBaptiste Daroussin 2467295610fSBaptiste Daroussin /* 2477295610fSBaptiste Daroussin * Maintain a lookaside buffer of all lines. 2487295610fSBaptiste Daroussin * parsed from this input source. 2497295610fSBaptiste Daroussin */ 25061d06d6bSBaptiste Daroussin 2517295610fSBaptiste Daroussin thisln = mandoc_malloc(sizeof(*thisln)); 2527295610fSBaptiste Daroussin thisln->buf = mandoc_strdup(ln.buf); 2537295610fSBaptiste Daroussin thisln->sz = strlen(ln.buf) + 1; 2547295610fSBaptiste Daroussin thisln->next = NULL; 2557295610fSBaptiste Daroussin if (firstln == NULL) { 2567295610fSBaptiste Daroussin firstln = lastln = thisln; 2577295610fSBaptiste Daroussin if (curp->secondary == NULL) 2587295610fSBaptiste Daroussin curp->secondary = firstln; 2597295610fSBaptiste Daroussin } else { 2607295610fSBaptiste Daroussin lastln->next = thisln; 2617295610fSBaptiste Daroussin lastln = thisln; 2627295610fSBaptiste Daroussin } 2637295610fSBaptiste Daroussin 264*c1c95addSBrooks Davis /* 265*c1c95addSBrooks Davis * XXX Ugly hack to mark the end of the input, 266*c1c95addSBrooks Davis * such that the function roff_parse_comment() 267*c1c95addSBrooks Davis * doesn't attempt to append another line if the 268*c1c95addSBrooks Davis * last input line ends with an escape character. 269*c1c95addSBrooks Davis */ 2707295610fSBaptiste Daroussin 2717295610fSBaptiste Daroussin if (i == blk.sz || blk.buf[i] == '\0') { 27245a5aec3SBaptiste Daroussin if (pos + 2 > ln.sz) 27345a5aec3SBaptiste Daroussin resize_buf(&ln, 256); 27461d06d6bSBaptiste Daroussin ln.buf[pos++] = '\n'; 27561d06d6bSBaptiste Daroussin ln.buf[pos] = '\0'; 2767295610fSBaptiste Daroussin } 27761d06d6bSBaptiste Daroussin 27861d06d6bSBaptiste Daroussin /* 27961d06d6bSBaptiste Daroussin * A significant amount of complexity is contained by 28061d06d6bSBaptiste Daroussin * the roff preprocessor. It's line-oriented but can be 28161d06d6bSBaptiste Daroussin * expressed on one line, so we need at times to 28261d06d6bSBaptiste Daroussin * readjust our starting point and re-run it. The roff 28361d06d6bSBaptiste Daroussin * preprocessor can also readjust the buffers with new 28461d06d6bSBaptiste Daroussin * data, so we pass them in wholesale. 28561d06d6bSBaptiste Daroussin */ 28661d06d6bSBaptiste Daroussin 28761d06d6bSBaptiste Daroussin of = 0; 28861d06d6bSBaptiste Daroussin rerun: 2896d38604fSBaptiste Daroussin line_result = roff_parseln(curp->roff, curp->line, 2906d38604fSBaptiste Daroussin &ln, &of, start && spos == 0 ? pos : 0); 29161d06d6bSBaptiste Daroussin 2927295610fSBaptiste Daroussin /* Process options. */ 2937295610fSBaptiste Daroussin 2947295610fSBaptiste Daroussin if (line_result & ROFF_APPEND) 2957295610fSBaptiste Daroussin assert(line_result == (ROFF_IGN | ROFF_APPEND)); 2967295610fSBaptiste Daroussin 2977295610fSBaptiste Daroussin if (line_result & ROFF_USERCALL) 2987295610fSBaptiste Daroussin assert((line_result & ROFF_MASK) == ROFF_REPARSE); 2997295610fSBaptiste Daroussin 3007295610fSBaptiste Daroussin if (line_result & ROFF_USERRET) { 3017295610fSBaptiste Daroussin assert(line_result == (ROFF_IGN | ROFF_USERRET)); 3027295610fSBaptiste Daroussin if (start == 0) { 3037295610fSBaptiste Daroussin /* Return from the current macro. */ 3047295610fSBaptiste Daroussin result = ROFF_USERRET; 3057295610fSBaptiste Daroussin goto out; 30661d06d6bSBaptiste Daroussin } 3077295610fSBaptiste Daroussin } 3087295610fSBaptiste Daroussin 3097295610fSBaptiste Daroussin switch (line_result & ROFF_LOOPMASK) { 3107295610fSBaptiste Daroussin case ROFF_IGN: 3117295610fSBaptiste Daroussin break; 3127295610fSBaptiste Daroussin case ROFF_WHILE: 3137295610fSBaptiste Daroussin if (curp->loop != NULL) { 3147295610fSBaptiste Daroussin if (loop == curp->loop) 3157295610fSBaptiste Daroussin break; 3167295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_WHILE_NEST, 3177295610fSBaptiste Daroussin curp->line, pos, NULL); 3187295610fSBaptiste Daroussin } 3197295610fSBaptiste Daroussin curp->loop = thisln; 3207295610fSBaptiste Daroussin loop = NULL; 3217295610fSBaptiste Daroussin inloop = 1; 3227295610fSBaptiste Daroussin break; 3237295610fSBaptiste Daroussin case ROFF_LOOPCONT: 3247295610fSBaptiste Daroussin case ROFF_LOOPEXIT: 3257295610fSBaptiste Daroussin if (curp->loop == NULL) { 3267295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_WHILE_FAIL, 3277295610fSBaptiste Daroussin curp->line, pos, NULL); 3287295610fSBaptiste Daroussin break; 3297295610fSBaptiste Daroussin } 3307295610fSBaptiste Daroussin if (inloop == 0) { 3317295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_WHILE_INTO, 3327295610fSBaptiste Daroussin curp->line, pos, NULL); 3337295610fSBaptiste Daroussin curp->loop = loop = NULL; 3347295610fSBaptiste Daroussin break; 3357295610fSBaptiste Daroussin } 3367295610fSBaptiste Daroussin if (line_result & ROFF_LOOPCONT) 3377295610fSBaptiste Daroussin loop = curp->loop; 3387295610fSBaptiste Daroussin else { 3397295610fSBaptiste Daroussin curp->loop = loop = NULL; 3407295610fSBaptiste Daroussin inloop = 0; 3417295610fSBaptiste Daroussin } 3427295610fSBaptiste Daroussin break; 3437295610fSBaptiste Daroussin default: 3447295610fSBaptiste Daroussin abort(); 3457295610fSBaptiste Daroussin } 3467295610fSBaptiste Daroussin 3477295610fSBaptiste Daroussin /* Process the main instruction from the roff parser. */ 3487295610fSBaptiste Daroussin 3497295610fSBaptiste Daroussin switch (line_result & ROFF_MASK) { 3507295610fSBaptiste Daroussin case ROFF_IGN: 3517295610fSBaptiste Daroussin break; 3527295610fSBaptiste Daroussin case ROFF_CONT: 3537295610fSBaptiste Daroussin if (curp->man->meta.macroset == MACROSET_NONE) 3547295610fSBaptiste Daroussin choose_parser(curp); 3557295610fSBaptiste Daroussin if ((curp->man->meta.macroset == MACROSET_MDOC ? 3567295610fSBaptiste Daroussin mdoc_parseln(curp->man, curp->line, ln.buf, of) : 3577295610fSBaptiste Daroussin man_parseln(curp->man, curp->line, ln.buf, of) 3587295610fSBaptiste Daroussin ) == 2) 3597295610fSBaptiste Daroussin goto out; 3607295610fSBaptiste Daroussin break; 36161d06d6bSBaptiste Daroussin case ROFF_RERUN: 36261d06d6bSBaptiste Daroussin goto rerun; 3637295610fSBaptiste Daroussin case ROFF_REPARSE: 3647295610fSBaptiste Daroussin if (++curp->reparse_count > REPARSE_LIMIT) { 3657295610fSBaptiste Daroussin /* Abort and return to the top level. */ 3667295610fSBaptiste Daroussin result = ROFF_IGN; 3677295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ROFFLOOP, 3687295610fSBaptiste Daroussin curp->line, pos, NULL); 3697295610fSBaptiste Daroussin goto out; 3707295610fSBaptiste Daroussin } 3717295610fSBaptiste Daroussin result = mparse_buf_r(curp, ln, of, 0); 3727295610fSBaptiste Daroussin if (line_result & ROFF_USERCALL) { 3737295610fSBaptiste Daroussin roff_userret(curp->roff); 3747295610fSBaptiste Daroussin /* Continue normally. */ 3757295610fSBaptiste Daroussin if (result & ROFF_USERRET) 3767295610fSBaptiste Daroussin result = ROFF_CONT; 3777295610fSBaptiste Daroussin } 3787295610fSBaptiste Daroussin if (start == 0 && result != ROFF_CONT) 3797295610fSBaptiste Daroussin goto out; 3807295610fSBaptiste Daroussin break; 38161d06d6bSBaptiste Daroussin case ROFF_SO: 38261d06d6bSBaptiste Daroussin if ( ! (curp->options & MPARSE_SO) && 38361d06d6bSBaptiste Daroussin (i >= blk.sz || blk.buf[i] == '\0')) { 3847295610fSBaptiste Daroussin curp->man->meta.sodest = 3857295610fSBaptiste Daroussin mandoc_strdup(ln.buf + of); 3867295610fSBaptiste Daroussin goto out; 38761d06d6bSBaptiste Daroussin } 38861d06d6bSBaptiste Daroussin if ((fd = mparse_open(curp, ln.buf + of)) != -1) { 38961d06d6bSBaptiste Daroussin mparse_readfd(curp, fd, ln.buf + of); 39061d06d6bSBaptiste Daroussin close(fd); 39161d06d6bSBaptiste Daroussin } else { 3927295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_SO_FAIL, 3937295610fSBaptiste Daroussin curp->line, of, ".so %s: %s", 3947295610fSBaptiste Daroussin ln.buf + of, strerror(errno)); 39561d06d6bSBaptiste Daroussin ln.sz = mandoc_asprintf(&cp, 39661d06d6bSBaptiste Daroussin ".sp\nSee the file %s.\n.sp", 39761d06d6bSBaptiste Daroussin ln.buf + of); 39861d06d6bSBaptiste Daroussin free(ln.buf); 39961d06d6bSBaptiste Daroussin ln.buf = cp; 40061d06d6bSBaptiste Daroussin of = 0; 40161d06d6bSBaptiste Daroussin mparse_buf_r(curp, ln, of, 0); 40261d06d6bSBaptiste Daroussin } 4037295610fSBaptiste Daroussin break; 40461d06d6bSBaptiste Daroussin default: 4057295610fSBaptiste Daroussin abort(); 40661d06d6bSBaptiste Daroussin } 40761d06d6bSBaptiste Daroussin 40861d06d6bSBaptiste Daroussin /* Start the next input line. */ 40961d06d6bSBaptiste Daroussin 4107295610fSBaptiste Daroussin if (loop != NULL && 4117295610fSBaptiste Daroussin (line_result & ROFF_LOOPMASK) == ROFF_IGN) 4127295610fSBaptiste Daroussin loop = loop->next; 4137295610fSBaptiste Daroussin 4147295610fSBaptiste Daroussin if (loop != NULL) { 4157295610fSBaptiste Daroussin if ((line_result & ROFF_APPEND) == 0) 4167295610fSBaptiste Daroussin *ln.buf = '\0'; 4177295610fSBaptiste Daroussin if (ln.sz < loop->sz) 4187295610fSBaptiste Daroussin resize_buf(&ln, loop->sz); 4197295610fSBaptiste Daroussin (void)strlcat(ln.buf, loop->buf, ln.sz); 4207295610fSBaptiste Daroussin of = 0; 4217295610fSBaptiste Daroussin goto rerun; 42261d06d6bSBaptiste Daroussin } 42361d06d6bSBaptiste Daroussin 4247295610fSBaptiste Daroussin pos = (line_result & ROFF_APPEND) ? strlen(ln.buf) : 0; 4257295610fSBaptiste Daroussin } 4267295610fSBaptiste Daroussin out: 4277295610fSBaptiste Daroussin if (inloop) { 4287295610fSBaptiste Daroussin if (result != ROFF_USERRET) 4297295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_WHILE_OUTOF, 4307295610fSBaptiste Daroussin curp->line, pos, NULL); 4317295610fSBaptiste Daroussin curp->loop = NULL; 4327295610fSBaptiste Daroussin } 43361d06d6bSBaptiste Daroussin free(ln.buf); 4347295610fSBaptiste Daroussin if (firstln != curp->secondary) 4357295610fSBaptiste Daroussin free_buf_list(firstln); 4367295610fSBaptiste Daroussin return result; 43761d06d6bSBaptiste Daroussin } 43861d06d6bSBaptiste Daroussin 43961d06d6bSBaptiste Daroussin static int 4407295610fSBaptiste Daroussin read_whole_file(struct mparse *curp, int fd, struct buf *fb, int *with_mmap) 44161d06d6bSBaptiste Daroussin { 44261d06d6bSBaptiste Daroussin struct stat st; 44361d06d6bSBaptiste Daroussin gzFile gz; 44461d06d6bSBaptiste Daroussin size_t off; 44561d06d6bSBaptiste Daroussin ssize_t ssz; 44661d06d6bSBaptiste Daroussin int gzerrnum, retval; 44761d06d6bSBaptiste Daroussin 44861d06d6bSBaptiste Daroussin if (fstat(fd, &st) == -1) { 44945a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_FSTAT, 0, 0, "%s", strerror(errno)); 45045a5aec3SBaptiste Daroussin return -1; 45161d06d6bSBaptiste Daroussin } 45261d06d6bSBaptiste Daroussin 45361d06d6bSBaptiste Daroussin /* 45461d06d6bSBaptiste Daroussin * If we're a regular file, try just reading in the whole entry 45561d06d6bSBaptiste Daroussin * via mmap(). This is faster than reading it into blocks, and 45661d06d6bSBaptiste Daroussin * since each file is only a few bytes to begin with, I'm not 45761d06d6bSBaptiste Daroussin * concerned that this is going to tank any machines. 45861d06d6bSBaptiste Daroussin */ 45961d06d6bSBaptiste Daroussin 46061d06d6bSBaptiste Daroussin if (curp->gzip == 0 && S_ISREG(st.st_mode)) { 46161d06d6bSBaptiste Daroussin if (st.st_size > 0x7fffffff) { 4627295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL); 46345a5aec3SBaptiste Daroussin return -1; 46461d06d6bSBaptiste Daroussin } 46561d06d6bSBaptiste Daroussin *with_mmap = 1; 46661d06d6bSBaptiste Daroussin fb->sz = (size_t)st.st_size; 46761d06d6bSBaptiste Daroussin fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0); 46861d06d6bSBaptiste Daroussin if (fb->buf != MAP_FAILED) 46945a5aec3SBaptiste Daroussin return 0; 47061d06d6bSBaptiste Daroussin } 47161d06d6bSBaptiste Daroussin 47261d06d6bSBaptiste Daroussin if (curp->gzip) { 47361d06d6bSBaptiste Daroussin /* 47461d06d6bSBaptiste Daroussin * Duplicating the file descriptor is required 47561d06d6bSBaptiste Daroussin * because we will have to call gzclose(3) 47661d06d6bSBaptiste Daroussin * to free memory used internally by zlib, 47761d06d6bSBaptiste Daroussin * but that will also close the file descriptor, 47861d06d6bSBaptiste Daroussin * which this function must not do. 47961d06d6bSBaptiste Daroussin */ 48061d06d6bSBaptiste Daroussin if ((fd = dup(fd)) == -1) { 48145a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_DUP, 0, 0, 48245a5aec3SBaptiste Daroussin "%s", strerror(errno)); 48345a5aec3SBaptiste Daroussin return -1; 48461d06d6bSBaptiste Daroussin } 48561d06d6bSBaptiste Daroussin if ((gz = gzdopen(fd, "rb")) == NULL) { 48645a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_GZDOPEN, 0, 0, 48745a5aec3SBaptiste Daroussin "%s", strerror(errno)); 48861d06d6bSBaptiste Daroussin close(fd); 48945a5aec3SBaptiste Daroussin return -1; 49061d06d6bSBaptiste Daroussin } 49161d06d6bSBaptiste Daroussin } else 49261d06d6bSBaptiste Daroussin gz = NULL; 49361d06d6bSBaptiste Daroussin 49461d06d6bSBaptiste Daroussin /* 49561d06d6bSBaptiste Daroussin * If this isn't a regular file (like, say, stdin), then we must 49661d06d6bSBaptiste Daroussin * go the old way and just read things in bit by bit. 49761d06d6bSBaptiste Daroussin */ 49861d06d6bSBaptiste Daroussin 49961d06d6bSBaptiste Daroussin *with_mmap = 0; 50061d06d6bSBaptiste Daroussin off = 0; 50145a5aec3SBaptiste Daroussin retval = -1; 50261d06d6bSBaptiste Daroussin fb->sz = 0; 50361d06d6bSBaptiste Daroussin fb->buf = NULL; 50461d06d6bSBaptiste Daroussin for (;;) { 50561d06d6bSBaptiste Daroussin if (off == fb->sz) { 50661d06d6bSBaptiste Daroussin if (fb->sz == (1U << 31)) { 5077295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL); 50861d06d6bSBaptiste Daroussin break; 50961d06d6bSBaptiste Daroussin } 51061d06d6bSBaptiste Daroussin resize_buf(fb, 65536); 51161d06d6bSBaptiste Daroussin } 51261d06d6bSBaptiste Daroussin ssz = curp->gzip ? 51361d06d6bSBaptiste Daroussin gzread(gz, fb->buf + (int)off, fb->sz - off) : 51461d06d6bSBaptiste Daroussin read(fd, fb->buf + (int)off, fb->sz - off); 51561d06d6bSBaptiste Daroussin if (ssz == 0) { 51661d06d6bSBaptiste Daroussin fb->sz = off; 51745a5aec3SBaptiste Daroussin retval = 0; 51861d06d6bSBaptiste Daroussin break; 51961d06d6bSBaptiste Daroussin } 52061d06d6bSBaptiste Daroussin if (ssz == -1) { 52161d06d6bSBaptiste Daroussin if (curp->gzip) 52261d06d6bSBaptiste Daroussin (void)gzerror(gz, &gzerrnum); 52345a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_READ, 0, 0, "%s", 52461d06d6bSBaptiste Daroussin curp->gzip && gzerrnum != Z_ERRNO ? 52561d06d6bSBaptiste Daroussin zError(gzerrnum) : strerror(errno)); 52661d06d6bSBaptiste Daroussin break; 52761d06d6bSBaptiste Daroussin } 52861d06d6bSBaptiste Daroussin off += (size_t)ssz; 52961d06d6bSBaptiste Daroussin } 53061d06d6bSBaptiste Daroussin 53161d06d6bSBaptiste Daroussin if (curp->gzip && (gzerrnum = gzclose(gz)) != Z_OK) 53245a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_GZCLOSE, 0, 0, "%s", 53361d06d6bSBaptiste Daroussin gzerrnum == Z_ERRNO ? strerror(errno) : 53461d06d6bSBaptiste Daroussin zError(gzerrnum)); 53545a5aec3SBaptiste Daroussin if (retval == -1) { 53661d06d6bSBaptiste Daroussin free(fb->buf); 53761d06d6bSBaptiste Daroussin fb->buf = NULL; 53861d06d6bSBaptiste Daroussin } 53961d06d6bSBaptiste Daroussin return retval; 54061d06d6bSBaptiste Daroussin } 54161d06d6bSBaptiste Daroussin 54261d06d6bSBaptiste Daroussin static void 54361d06d6bSBaptiste Daroussin mparse_end(struct mparse *curp) 54461d06d6bSBaptiste Daroussin { 5457295610fSBaptiste Daroussin if (curp->man->meta.macroset == MACROSET_NONE) 5467295610fSBaptiste Daroussin curp->man->meta.macroset = MACROSET_MAN; 5477295610fSBaptiste Daroussin if (curp->man->meta.macroset == MACROSET_MDOC) 54861d06d6bSBaptiste Daroussin mdoc_endparse(curp->man); 54961d06d6bSBaptiste Daroussin else 55061d06d6bSBaptiste Daroussin man_endparse(curp->man); 55161d06d6bSBaptiste Daroussin roff_endparse(curp->roff); 55261d06d6bSBaptiste Daroussin } 55361d06d6bSBaptiste Daroussin 5547295610fSBaptiste Daroussin /* 5557295610fSBaptiste Daroussin * Read the whole file into memory and call the parsers. 5567295610fSBaptiste Daroussin * Called recursively when an .so request is encountered. 5577295610fSBaptiste Daroussin */ 5587295610fSBaptiste Daroussin void 5597295610fSBaptiste Daroussin mparse_readfd(struct mparse *curp, int fd, const char *filename) 56061d06d6bSBaptiste Daroussin { 56161d06d6bSBaptiste Daroussin static int recursion_depth; 56261d06d6bSBaptiste Daroussin 5637295610fSBaptiste Daroussin struct buf blk; 5647295610fSBaptiste Daroussin struct buf *save_primary; 5656d38604fSBaptiste Daroussin const char *save_filename, *cp; 5667295610fSBaptiste Daroussin size_t offset; 5677295610fSBaptiste Daroussin int save_filenc, save_lineno; 5687295610fSBaptiste Daroussin int with_mmap; 5697295610fSBaptiste Daroussin 5707295610fSBaptiste Daroussin if (recursion_depth > 64) { 5717295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ROFFLOOP, curp->line, 0, NULL); 57261d06d6bSBaptiste Daroussin return; 5736d38604fSBaptiste Daroussin } else if (recursion_depth == 0 && 5746d38604fSBaptiste Daroussin (cp = strrchr(filename, '.')) != NULL && 5756d38604fSBaptiste Daroussin cp[1] >= '1' && cp[1] <= '9') 5766d38604fSBaptiste Daroussin curp->man->filesec = cp[1]; 5776d38604fSBaptiste Daroussin else 5786d38604fSBaptiste Daroussin curp->man->filesec = '\0'; 5796d38604fSBaptiste Daroussin 58045a5aec3SBaptiste Daroussin if (read_whole_file(curp, fd, &blk, &with_mmap) == -1) 5817295610fSBaptiste Daroussin return; 58261d06d6bSBaptiste Daroussin 5837295610fSBaptiste Daroussin /* 5847295610fSBaptiste Daroussin * Save some properties of the parent file. 5857295610fSBaptiste Daroussin */ 5867295610fSBaptiste Daroussin 5877295610fSBaptiste Daroussin save_primary = curp->primary; 5887295610fSBaptiste Daroussin save_filenc = curp->filenc; 5897295610fSBaptiste Daroussin save_lineno = curp->line; 5907295610fSBaptiste Daroussin save_filename = mandoc_msg_getinfilename(); 5917295610fSBaptiste Daroussin 59261d06d6bSBaptiste Daroussin curp->primary = &blk; 5937295610fSBaptiste Daroussin curp->filenc = curp->options & (MPARSE_UTF8 | MPARSE_LATIN1); 59461d06d6bSBaptiste Daroussin curp->line = 1; 5957295610fSBaptiste Daroussin mandoc_msg_setinfilename(filename); 59661d06d6bSBaptiste Daroussin 59761d06d6bSBaptiste Daroussin /* Skip an UTF-8 byte order mark. */ 59861d06d6bSBaptiste Daroussin if (curp->filenc & MPARSE_UTF8 && blk.sz > 2 && 59961d06d6bSBaptiste Daroussin (unsigned char)blk.buf[0] == 0xef && 60061d06d6bSBaptiste Daroussin (unsigned char)blk.buf[1] == 0xbb && 60161d06d6bSBaptiste Daroussin (unsigned char)blk.buf[2] == 0xbf) { 60261d06d6bSBaptiste Daroussin offset = 3; 60361d06d6bSBaptiste Daroussin curp->filenc &= ~MPARSE_LATIN1; 60461d06d6bSBaptiste Daroussin } else 60561d06d6bSBaptiste Daroussin offset = 0; 60661d06d6bSBaptiste Daroussin 6077295610fSBaptiste Daroussin recursion_depth++; 60861d06d6bSBaptiste Daroussin mparse_buf_r(curp, blk, offset, 1); 60961d06d6bSBaptiste Daroussin if (--recursion_depth == 0) 61061d06d6bSBaptiste Daroussin mparse_end(curp); 61161d06d6bSBaptiste Daroussin 61261d06d6bSBaptiste Daroussin /* 6137295610fSBaptiste Daroussin * Clean up and restore saved parent properties. 61461d06d6bSBaptiste Daroussin */ 61561d06d6bSBaptiste Daroussin 61661d06d6bSBaptiste Daroussin if (with_mmap) 61761d06d6bSBaptiste Daroussin munmap(blk.buf, blk.sz); 61861d06d6bSBaptiste Daroussin else 61961d06d6bSBaptiste Daroussin free(blk.buf); 6207295610fSBaptiste Daroussin 6217295610fSBaptiste Daroussin curp->primary = save_primary; 6227295610fSBaptiste Daroussin curp->filenc = save_filenc; 6237295610fSBaptiste Daroussin curp->line = save_lineno; 6247295610fSBaptiste Daroussin if (save_filename != NULL) 6257295610fSBaptiste Daroussin mandoc_msg_setinfilename(save_filename); 62661d06d6bSBaptiste Daroussin } 62761d06d6bSBaptiste Daroussin 62861d06d6bSBaptiste Daroussin int 62961d06d6bSBaptiste Daroussin mparse_open(struct mparse *curp, const char *file) 63061d06d6bSBaptiste Daroussin { 63161d06d6bSBaptiste Daroussin char *cp; 6327295610fSBaptiste Daroussin int fd, save_errno; 63361d06d6bSBaptiste Daroussin 63461d06d6bSBaptiste Daroussin cp = strrchr(file, '.'); 63561d06d6bSBaptiste Daroussin curp->gzip = (cp != NULL && ! strcmp(cp + 1, "gz")); 63661d06d6bSBaptiste Daroussin 63761d06d6bSBaptiste Daroussin /* First try to use the filename as it is. */ 63861d06d6bSBaptiste Daroussin 63961d06d6bSBaptiste Daroussin if ((fd = open(file, O_RDONLY)) != -1) 64061d06d6bSBaptiste Daroussin return fd; 64161d06d6bSBaptiste Daroussin 64261d06d6bSBaptiste Daroussin /* 64361d06d6bSBaptiste Daroussin * If that doesn't work and the filename doesn't 64461d06d6bSBaptiste Daroussin * already end in .gz, try appending .gz. 64561d06d6bSBaptiste Daroussin */ 64661d06d6bSBaptiste Daroussin 64761d06d6bSBaptiste Daroussin if ( ! curp->gzip) { 6487295610fSBaptiste Daroussin save_errno = errno; 64961d06d6bSBaptiste Daroussin mandoc_asprintf(&cp, "%s.gz", file); 65061d06d6bSBaptiste Daroussin fd = open(cp, O_RDONLY); 65161d06d6bSBaptiste Daroussin free(cp); 6527295610fSBaptiste Daroussin errno = save_errno; 65361d06d6bSBaptiste Daroussin if (fd != -1) { 65461d06d6bSBaptiste Daroussin curp->gzip = 1; 65561d06d6bSBaptiste Daroussin return fd; 65661d06d6bSBaptiste Daroussin } 65761d06d6bSBaptiste Daroussin } 65861d06d6bSBaptiste Daroussin 65961d06d6bSBaptiste Daroussin /* Neither worked, give up. */ 66061d06d6bSBaptiste Daroussin 66161d06d6bSBaptiste Daroussin return -1; 66261d06d6bSBaptiste Daroussin } 66361d06d6bSBaptiste Daroussin 66461d06d6bSBaptiste Daroussin struct mparse * 6657295610fSBaptiste Daroussin mparse_alloc(int options, enum mandoc_os os_e, const char *os_s) 66661d06d6bSBaptiste Daroussin { 66761d06d6bSBaptiste Daroussin struct mparse *curp; 66861d06d6bSBaptiste Daroussin 66961d06d6bSBaptiste Daroussin curp = mandoc_calloc(1, sizeof(struct mparse)); 67061d06d6bSBaptiste Daroussin 67161d06d6bSBaptiste Daroussin curp->options = options; 67261d06d6bSBaptiste Daroussin curp->os_s = os_s; 67361d06d6bSBaptiste Daroussin 6747295610fSBaptiste Daroussin curp->roff = roff_alloc(options); 6757295610fSBaptiste Daroussin curp->man = roff_man_alloc(curp->roff, curp->os_s, 67661d06d6bSBaptiste Daroussin curp->options & MPARSE_QUICK ? 1 : 0); 67761d06d6bSBaptiste Daroussin if (curp->options & MPARSE_MDOC) { 6787295610fSBaptiste Daroussin curp->man->meta.macroset = MACROSET_MDOC; 67961d06d6bSBaptiste Daroussin if (curp->man->mdocmac == NULL) 68061d06d6bSBaptiste Daroussin curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX); 68161d06d6bSBaptiste Daroussin } else if (curp->options & MPARSE_MAN) { 6827295610fSBaptiste Daroussin curp->man->meta.macroset = MACROSET_MAN; 68361d06d6bSBaptiste Daroussin if (curp->man->manmac == NULL) 68461d06d6bSBaptiste Daroussin curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX); 68561d06d6bSBaptiste Daroussin } 6867295610fSBaptiste Daroussin curp->man->meta.first->tok = TOKEN_NONE; 68761d06d6bSBaptiste Daroussin curp->man->meta.os_e = os_e; 6886d38604fSBaptiste Daroussin tag_alloc(); 68961d06d6bSBaptiste Daroussin return curp; 69061d06d6bSBaptiste Daroussin } 69161d06d6bSBaptiste Daroussin 69261d06d6bSBaptiste Daroussin void 69361d06d6bSBaptiste Daroussin mparse_reset(struct mparse *curp) 69461d06d6bSBaptiste Daroussin { 6956d38604fSBaptiste Daroussin tag_free(); 69661d06d6bSBaptiste Daroussin roff_reset(curp->roff); 69761d06d6bSBaptiste Daroussin roff_man_reset(curp->man); 6987295610fSBaptiste Daroussin free_buf_list(curp->secondary); 6997295610fSBaptiste Daroussin curp->secondary = NULL; 70061d06d6bSBaptiste Daroussin curp->gzip = 0; 7016d38604fSBaptiste Daroussin tag_alloc(); 70261d06d6bSBaptiste Daroussin } 70361d06d6bSBaptiste Daroussin 70461d06d6bSBaptiste Daroussin void 70561d06d6bSBaptiste Daroussin mparse_free(struct mparse *curp) 70661d06d6bSBaptiste Daroussin { 7076d38604fSBaptiste Daroussin tag_free(); 70861d06d6bSBaptiste Daroussin roffhash_free(curp->man->mdocmac); 70961d06d6bSBaptiste Daroussin roffhash_free(curp->man->manmac); 71061d06d6bSBaptiste Daroussin roff_man_free(curp->man); 71161d06d6bSBaptiste Daroussin roff_free(curp->roff); 7127295610fSBaptiste Daroussin free_buf_list(curp->secondary); 71361d06d6bSBaptiste Daroussin free(curp); 71461d06d6bSBaptiste Daroussin } 71561d06d6bSBaptiste Daroussin 7167295610fSBaptiste Daroussin struct roff_meta * 7177295610fSBaptiste Daroussin mparse_result(struct mparse *curp) 71861d06d6bSBaptiste Daroussin { 7197295610fSBaptiste Daroussin roff_state_reset(curp->man); 7207295610fSBaptiste Daroussin if (curp->options & MPARSE_VALIDATE) { 7217295610fSBaptiste Daroussin if (curp->man->meta.macroset == MACROSET_MDOC) 7227295610fSBaptiste Daroussin mdoc_validate(curp->man); 7237295610fSBaptiste Daroussin else 7247295610fSBaptiste Daroussin man_validate(curp->man); 7256d38604fSBaptiste Daroussin tag_postprocess(curp->man, curp->man->meta.first); 72661d06d6bSBaptiste Daroussin } 7277295610fSBaptiste Daroussin return &curp->man->meta; 72861d06d6bSBaptiste Daroussin } 72961d06d6bSBaptiste Daroussin 73061d06d6bSBaptiste Daroussin void 7317295610fSBaptiste Daroussin mparse_copy(const struct mparse *p) 73261d06d6bSBaptiste Daroussin { 7337295610fSBaptiste Daroussin struct buf *buf; 73461d06d6bSBaptiste Daroussin 7357295610fSBaptiste Daroussin for (buf = p->secondary; buf != NULL; buf = buf->next) 7367295610fSBaptiste Daroussin puts(buf->buf); 73761d06d6bSBaptiste Daroussin } 738