1*45a5aec3SBaptiste Daroussin /* $Id: read.c,v 1.214 2019/07/10 19:39:01 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 361d06d6bSBaptiste Daroussin * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 47295610fSBaptiste Daroussin * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org> 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. 1861d06d6bSBaptiste Daroussin */ 1961d06d6bSBaptiste Daroussin #include "config.h" 2061d06d6bSBaptiste Daroussin 2161d06d6bSBaptiste Daroussin #include <sys/types.h> 2261d06d6bSBaptiste Daroussin #include <sys/mman.h> 2361d06d6bSBaptiste Daroussin #include <sys/stat.h> 2461d06d6bSBaptiste Daroussin 2561d06d6bSBaptiste Daroussin #include <assert.h> 2661d06d6bSBaptiste Daroussin #include <ctype.h> 2761d06d6bSBaptiste Daroussin #include <errno.h> 2861d06d6bSBaptiste Daroussin #include <fcntl.h> 2961d06d6bSBaptiste Daroussin #include <stdarg.h> 3061d06d6bSBaptiste Daroussin #include <stdio.h> 3161d06d6bSBaptiste Daroussin #include <stdlib.h> 3261d06d6bSBaptiste Daroussin #include <string.h> 3361d06d6bSBaptiste Daroussin #include <unistd.h> 3461d06d6bSBaptiste Daroussin #include <zlib.h> 3561d06d6bSBaptiste Daroussin 3661d06d6bSBaptiste Daroussin #include "mandoc_aux.h" 3761d06d6bSBaptiste Daroussin #include "mandoc.h" 3861d06d6bSBaptiste Daroussin #include "roff.h" 3961d06d6bSBaptiste Daroussin #include "mdoc.h" 4061d06d6bSBaptiste Daroussin #include "man.h" 417295610fSBaptiste Daroussin #include "mandoc_parse.h" 4261d06d6bSBaptiste Daroussin #include "libmandoc.h" 437295610fSBaptiste Daroussin #include "roff_int.h" 4461d06d6bSBaptiste Daroussin 4561d06d6bSBaptiste Daroussin #define REPARSE_LIMIT 1000 4661d06d6bSBaptiste Daroussin 4761d06d6bSBaptiste Daroussin struct mparse { 4861d06d6bSBaptiste Daroussin struct roff *roff; /* roff parser (!NULL) */ 4961d06d6bSBaptiste Daroussin struct roff_man *man; /* man parser */ 5061d06d6bSBaptiste Daroussin struct buf *primary; /* buffer currently being parsed */ 517295610fSBaptiste Daroussin struct buf *secondary; /* copy of top level input */ 527295610fSBaptiste Daroussin struct buf *loop; /* open .while request line */ 5361d06d6bSBaptiste Daroussin const char *os_s; /* default operating system */ 5461d06d6bSBaptiste Daroussin int options; /* parser options */ 5561d06d6bSBaptiste Daroussin int gzip; /* current input file is gzipped */ 5661d06d6bSBaptiste Daroussin int filenc; /* encoding of the current file */ 5761d06d6bSBaptiste Daroussin int reparse_count; /* finite interp. stack */ 5861d06d6bSBaptiste Daroussin int line; /* line number in the file */ 5961d06d6bSBaptiste Daroussin }; 6061d06d6bSBaptiste Daroussin 6161d06d6bSBaptiste Daroussin static void choose_parser(struct mparse *); 627295610fSBaptiste Daroussin static void free_buf_list(struct buf *); 6361d06d6bSBaptiste Daroussin static void resize_buf(struct buf *, size_t); 6461d06d6bSBaptiste Daroussin static int mparse_buf_r(struct mparse *, struct buf, size_t, int); 657295610fSBaptiste Daroussin static int read_whole_file(struct mparse *, int, struct buf *, int *); 6661d06d6bSBaptiste Daroussin static void mparse_end(struct mparse *); 6761d06d6bSBaptiste Daroussin 6861d06d6bSBaptiste Daroussin 6961d06d6bSBaptiste Daroussin static void 7061d06d6bSBaptiste Daroussin resize_buf(struct buf *buf, size_t initial) 7161d06d6bSBaptiste Daroussin { 7261d06d6bSBaptiste Daroussin 7361d06d6bSBaptiste Daroussin buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial; 7461d06d6bSBaptiste Daroussin buf->buf = mandoc_realloc(buf->buf, buf->sz); 7561d06d6bSBaptiste Daroussin } 7661d06d6bSBaptiste Daroussin 7761d06d6bSBaptiste Daroussin static void 787295610fSBaptiste Daroussin free_buf_list(struct buf *buf) 797295610fSBaptiste Daroussin { 807295610fSBaptiste Daroussin struct buf *tmp; 817295610fSBaptiste Daroussin 827295610fSBaptiste Daroussin while (buf != NULL) { 837295610fSBaptiste Daroussin tmp = buf; 847295610fSBaptiste Daroussin buf = tmp->next; 857295610fSBaptiste Daroussin free(tmp->buf); 867295610fSBaptiste Daroussin free(tmp); 877295610fSBaptiste Daroussin } 887295610fSBaptiste Daroussin } 897295610fSBaptiste Daroussin 907295610fSBaptiste Daroussin static void 9161d06d6bSBaptiste Daroussin choose_parser(struct mparse *curp) 9261d06d6bSBaptiste Daroussin { 9361d06d6bSBaptiste Daroussin char *cp, *ep; 9461d06d6bSBaptiste Daroussin int format; 9561d06d6bSBaptiste Daroussin 9661d06d6bSBaptiste Daroussin /* 9761d06d6bSBaptiste Daroussin * If neither command line arguments -mdoc or -man select 9861d06d6bSBaptiste Daroussin * a parser nor the roff parser found a .Dd or .TH macro 9961d06d6bSBaptiste Daroussin * yet, look ahead in the main input buffer. 10061d06d6bSBaptiste Daroussin */ 10161d06d6bSBaptiste Daroussin 10261d06d6bSBaptiste Daroussin if ((format = roff_getformat(curp->roff)) == 0) { 10361d06d6bSBaptiste Daroussin cp = curp->primary->buf; 10461d06d6bSBaptiste Daroussin ep = cp + curp->primary->sz; 10561d06d6bSBaptiste Daroussin while (cp < ep) { 10661d06d6bSBaptiste Daroussin if (*cp == '.' || *cp == '\'') { 10761d06d6bSBaptiste Daroussin cp++; 10861d06d6bSBaptiste Daroussin if (cp[0] == 'D' && cp[1] == 'd') { 10961d06d6bSBaptiste Daroussin format = MPARSE_MDOC; 11061d06d6bSBaptiste Daroussin break; 11161d06d6bSBaptiste Daroussin } 11261d06d6bSBaptiste Daroussin if (cp[0] == 'T' && cp[1] == 'H') { 11361d06d6bSBaptiste Daroussin format = MPARSE_MAN; 11461d06d6bSBaptiste Daroussin break; 11561d06d6bSBaptiste Daroussin } 11661d06d6bSBaptiste Daroussin } 11761d06d6bSBaptiste Daroussin cp = memchr(cp, '\n', ep - cp); 11861d06d6bSBaptiste Daroussin if (cp == NULL) 11961d06d6bSBaptiste Daroussin break; 12061d06d6bSBaptiste Daroussin cp++; 12161d06d6bSBaptiste Daroussin } 12261d06d6bSBaptiste Daroussin } 12361d06d6bSBaptiste Daroussin 12461d06d6bSBaptiste Daroussin if (format == MPARSE_MDOC) { 1257295610fSBaptiste Daroussin curp->man->meta.macroset = MACROSET_MDOC; 12661d06d6bSBaptiste Daroussin if (curp->man->mdocmac == NULL) 12761d06d6bSBaptiste Daroussin curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX); 12861d06d6bSBaptiste Daroussin } else { 1297295610fSBaptiste Daroussin curp->man->meta.macroset = MACROSET_MAN; 13061d06d6bSBaptiste Daroussin if (curp->man->manmac == NULL) 13161d06d6bSBaptiste Daroussin curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX); 13261d06d6bSBaptiste Daroussin } 1337295610fSBaptiste Daroussin curp->man->meta.first->tok = TOKEN_NONE; 13461d06d6bSBaptiste Daroussin } 13561d06d6bSBaptiste Daroussin 13661d06d6bSBaptiste Daroussin /* 13761d06d6bSBaptiste Daroussin * Main parse routine for a buffer. 13861d06d6bSBaptiste Daroussin * It assumes encoding and line numbering are already set up. 13961d06d6bSBaptiste Daroussin * It can recurse directly (for invocations of user-defined 14061d06d6bSBaptiste Daroussin * macros, inline equations, and input line traps) 14161d06d6bSBaptiste Daroussin * and indirectly (for .so file inclusion). 14261d06d6bSBaptiste Daroussin */ 14361d06d6bSBaptiste Daroussin static int 14461d06d6bSBaptiste Daroussin mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) 14561d06d6bSBaptiste Daroussin { 14661d06d6bSBaptiste Daroussin struct buf ln; 1477295610fSBaptiste Daroussin struct buf *firstln, *lastln, *thisln, *loop; 14861d06d6bSBaptiste Daroussin char *cp; 14961d06d6bSBaptiste Daroussin size_t pos; /* byte number in the ln buffer */ 1507295610fSBaptiste Daroussin int line_result, result; 15161d06d6bSBaptiste Daroussin int of; 15261d06d6bSBaptiste Daroussin int lnn; /* line number in the real file */ 15361d06d6bSBaptiste Daroussin int fd; 1547295610fSBaptiste Daroussin int inloop; /* Saw .while on this level. */ 15561d06d6bSBaptiste Daroussin unsigned char c; 15661d06d6bSBaptiste Daroussin 1577295610fSBaptiste Daroussin ln.sz = 256; 1587295610fSBaptiste Daroussin ln.buf = mandoc_malloc(ln.sz); 1597295610fSBaptiste Daroussin ln.next = NULL; 160*45a5aec3SBaptiste Daroussin firstln = lastln = loop = NULL; 16161d06d6bSBaptiste Daroussin lnn = curp->line; 16261d06d6bSBaptiste Daroussin pos = 0; 1637295610fSBaptiste Daroussin inloop = 0; 1647295610fSBaptiste Daroussin result = ROFF_CONT; 16561d06d6bSBaptiste Daroussin 1667295610fSBaptiste Daroussin while (i < blk.sz && (blk.buf[i] != '\0' || pos != 0)) { 16761d06d6bSBaptiste Daroussin if (start) { 16861d06d6bSBaptiste Daroussin curp->line = lnn; 16961d06d6bSBaptiste Daroussin curp->reparse_count = 0; 17061d06d6bSBaptiste Daroussin 17161d06d6bSBaptiste Daroussin if (lnn < 3 && 17261d06d6bSBaptiste Daroussin curp->filenc & MPARSE_UTF8 && 17361d06d6bSBaptiste Daroussin curp->filenc & MPARSE_LATIN1) 17461d06d6bSBaptiste Daroussin curp->filenc = preconv_cue(&blk, i); 17561d06d6bSBaptiste Daroussin } 17661d06d6bSBaptiste Daroussin 17761d06d6bSBaptiste Daroussin while (i < blk.sz && (start || blk.buf[i] != '\0')) { 17861d06d6bSBaptiste Daroussin 17961d06d6bSBaptiste Daroussin /* 18061d06d6bSBaptiste Daroussin * When finding an unescaped newline character, 18161d06d6bSBaptiste Daroussin * leave the character loop to process the line. 18261d06d6bSBaptiste Daroussin * Skip a preceding carriage return, if any. 18361d06d6bSBaptiste Daroussin */ 18461d06d6bSBaptiste Daroussin 18561d06d6bSBaptiste Daroussin if ('\r' == blk.buf[i] && i + 1 < blk.sz && 18661d06d6bSBaptiste Daroussin '\n' == blk.buf[i + 1]) 18761d06d6bSBaptiste Daroussin ++i; 18861d06d6bSBaptiste Daroussin if ('\n' == blk.buf[i]) { 18961d06d6bSBaptiste Daroussin ++i; 19061d06d6bSBaptiste Daroussin ++lnn; 19161d06d6bSBaptiste Daroussin break; 19261d06d6bSBaptiste Daroussin } 19361d06d6bSBaptiste Daroussin 19461d06d6bSBaptiste Daroussin /* 19561d06d6bSBaptiste Daroussin * Make sure we have space for the worst 1967295610fSBaptiste Daroussin * case of 12 bytes: "\\[u10ffff]\n\0" 19761d06d6bSBaptiste Daroussin */ 19861d06d6bSBaptiste Daroussin 1997295610fSBaptiste Daroussin if (pos + 12 > ln.sz) 20061d06d6bSBaptiste Daroussin resize_buf(&ln, 256); 20161d06d6bSBaptiste Daroussin 20261d06d6bSBaptiste Daroussin /* 20361d06d6bSBaptiste Daroussin * Encode 8-bit input. 20461d06d6bSBaptiste Daroussin */ 20561d06d6bSBaptiste Daroussin 20661d06d6bSBaptiste Daroussin c = blk.buf[i]; 20761d06d6bSBaptiste Daroussin if (c & 0x80) { 20861d06d6bSBaptiste Daroussin if ( ! (curp->filenc && preconv_encode( 20961d06d6bSBaptiste Daroussin &blk, &i, &ln, &pos, &curp->filenc))) { 2107295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_CHAR_BAD, 21161d06d6bSBaptiste Daroussin curp->line, pos, "0x%x", c); 21261d06d6bSBaptiste Daroussin ln.buf[pos++] = '?'; 21361d06d6bSBaptiste Daroussin i++; 21461d06d6bSBaptiste Daroussin } 21561d06d6bSBaptiste Daroussin continue; 21661d06d6bSBaptiste Daroussin } 21761d06d6bSBaptiste Daroussin 21861d06d6bSBaptiste Daroussin /* 21961d06d6bSBaptiste Daroussin * Exclude control characters. 22061d06d6bSBaptiste Daroussin */ 22161d06d6bSBaptiste Daroussin 22261d06d6bSBaptiste Daroussin if (c == 0x7f || (c < 0x20 && c != 0x09)) { 2237295610fSBaptiste Daroussin mandoc_msg(c == 0x00 || c == 0x04 || 22461d06d6bSBaptiste Daroussin c > 0x0a ? MANDOCERR_CHAR_BAD : 22561d06d6bSBaptiste Daroussin MANDOCERR_CHAR_UNSUPP, 2267295610fSBaptiste Daroussin curp->line, pos, "0x%x", c); 22761d06d6bSBaptiste Daroussin i++; 22861d06d6bSBaptiste Daroussin if (c != '\r') 22961d06d6bSBaptiste Daroussin ln.buf[pos++] = '?'; 23061d06d6bSBaptiste Daroussin continue; 23161d06d6bSBaptiste Daroussin } 23261d06d6bSBaptiste Daroussin 23361d06d6bSBaptiste Daroussin ln.buf[pos++] = blk.buf[i++]; 23461d06d6bSBaptiste Daroussin } 2357295610fSBaptiste Daroussin ln.buf[pos] = '\0'; 23661d06d6bSBaptiste Daroussin 2377295610fSBaptiste Daroussin /* 2387295610fSBaptiste Daroussin * Maintain a lookaside buffer of all lines. 2397295610fSBaptiste Daroussin * parsed from this input source. 2407295610fSBaptiste Daroussin */ 24161d06d6bSBaptiste Daroussin 2427295610fSBaptiste Daroussin thisln = mandoc_malloc(sizeof(*thisln)); 2437295610fSBaptiste Daroussin thisln->buf = mandoc_strdup(ln.buf); 2447295610fSBaptiste Daroussin thisln->sz = strlen(ln.buf) + 1; 2457295610fSBaptiste Daroussin thisln->next = NULL; 2467295610fSBaptiste Daroussin if (firstln == NULL) { 2477295610fSBaptiste Daroussin firstln = lastln = thisln; 2487295610fSBaptiste Daroussin if (curp->secondary == NULL) 2497295610fSBaptiste Daroussin curp->secondary = firstln; 2507295610fSBaptiste Daroussin } else { 2517295610fSBaptiste Daroussin lastln->next = thisln; 2527295610fSBaptiste Daroussin lastln = thisln; 2537295610fSBaptiste Daroussin } 2547295610fSBaptiste Daroussin 2557295610fSBaptiste Daroussin /* XXX Ugly hack to mark the end of the input. */ 2567295610fSBaptiste Daroussin 2577295610fSBaptiste Daroussin if (i == blk.sz || blk.buf[i] == '\0') { 258*45a5aec3SBaptiste Daroussin if (pos + 2 > ln.sz) 259*45a5aec3SBaptiste Daroussin resize_buf(&ln, 256); 26061d06d6bSBaptiste Daroussin ln.buf[pos++] = '\n'; 26161d06d6bSBaptiste Daroussin ln.buf[pos] = '\0'; 2627295610fSBaptiste Daroussin } 26361d06d6bSBaptiste Daroussin 26461d06d6bSBaptiste Daroussin /* 26561d06d6bSBaptiste Daroussin * A significant amount of complexity is contained by 26661d06d6bSBaptiste Daroussin * the roff preprocessor. It's line-oriented but can be 26761d06d6bSBaptiste Daroussin * expressed on one line, so we need at times to 26861d06d6bSBaptiste Daroussin * readjust our starting point and re-run it. The roff 26961d06d6bSBaptiste Daroussin * preprocessor can also readjust the buffers with new 27061d06d6bSBaptiste Daroussin * data, so we pass them in wholesale. 27161d06d6bSBaptiste Daroussin */ 27261d06d6bSBaptiste Daroussin 27361d06d6bSBaptiste Daroussin of = 0; 27461d06d6bSBaptiste Daroussin rerun: 2757295610fSBaptiste Daroussin line_result = roff_parseln(curp->roff, curp->line, &ln, &of); 27661d06d6bSBaptiste Daroussin 2777295610fSBaptiste Daroussin /* Process options. */ 2787295610fSBaptiste Daroussin 2797295610fSBaptiste Daroussin if (line_result & ROFF_APPEND) 2807295610fSBaptiste Daroussin assert(line_result == (ROFF_IGN | ROFF_APPEND)); 2817295610fSBaptiste Daroussin 2827295610fSBaptiste Daroussin if (line_result & ROFF_USERCALL) 2837295610fSBaptiste Daroussin assert((line_result & ROFF_MASK) == ROFF_REPARSE); 2847295610fSBaptiste Daroussin 2857295610fSBaptiste Daroussin if (line_result & ROFF_USERRET) { 2867295610fSBaptiste Daroussin assert(line_result == (ROFF_IGN | ROFF_USERRET)); 2877295610fSBaptiste Daroussin if (start == 0) { 2887295610fSBaptiste Daroussin /* Return from the current macro. */ 2897295610fSBaptiste Daroussin result = ROFF_USERRET; 2907295610fSBaptiste Daroussin goto out; 29161d06d6bSBaptiste Daroussin } 2927295610fSBaptiste Daroussin } 2937295610fSBaptiste Daroussin 2947295610fSBaptiste Daroussin switch (line_result & ROFF_LOOPMASK) { 2957295610fSBaptiste Daroussin case ROFF_IGN: 2967295610fSBaptiste Daroussin break; 2977295610fSBaptiste Daroussin case ROFF_WHILE: 2987295610fSBaptiste Daroussin if (curp->loop != NULL) { 2997295610fSBaptiste Daroussin if (loop == curp->loop) 3007295610fSBaptiste Daroussin break; 3017295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_WHILE_NEST, 3027295610fSBaptiste Daroussin curp->line, pos, NULL); 3037295610fSBaptiste Daroussin } 3047295610fSBaptiste Daroussin curp->loop = thisln; 3057295610fSBaptiste Daroussin loop = NULL; 3067295610fSBaptiste Daroussin inloop = 1; 3077295610fSBaptiste Daroussin break; 3087295610fSBaptiste Daroussin case ROFF_LOOPCONT: 3097295610fSBaptiste Daroussin case ROFF_LOOPEXIT: 3107295610fSBaptiste Daroussin if (curp->loop == NULL) { 3117295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_WHILE_FAIL, 3127295610fSBaptiste Daroussin curp->line, pos, NULL); 3137295610fSBaptiste Daroussin break; 3147295610fSBaptiste Daroussin } 3157295610fSBaptiste Daroussin if (inloop == 0) { 3167295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_WHILE_INTO, 3177295610fSBaptiste Daroussin curp->line, pos, NULL); 3187295610fSBaptiste Daroussin curp->loop = loop = NULL; 3197295610fSBaptiste Daroussin break; 3207295610fSBaptiste Daroussin } 3217295610fSBaptiste Daroussin if (line_result & ROFF_LOOPCONT) 3227295610fSBaptiste Daroussin loop = curp->loop; 3237295610fSBaptiste Daroussin else { 3247295610fSBaptiste Daroussin curp->loop = loop = NULL; 3257295610fSBaptiste Daroussin inloop = 0; 3267295610fSBaptiste Daroussin } 3277295610fSBaptiste Daroussin break; 3287295610fSBaptiste Daroussin default: 3297295610fSBaptiste Daroussin abort(); 3307295610fSBaptiste Daroussin } 3317295610fSBaptiste Daroussin 3327295610fSBaptiste Daroussin /* Process the main instruction from the roff parser. */ 3337295610fSBaptiste Daroussin 3347295610fSBaptiste Daroussin switch (line_result & ROFF_MASK) { 3357295610fSBaptiste Daroussin case ROFF_IGN: 3367295610fSBaptiste Daroussin break; 3377295610fSBaptiste Daroussin case ROFF_CONT: 3387295610fSBaptiste Daroussin if (curp->man->meta.macroset == MACROSET_NONE) 3397295610fSBaptiste Daroussin choose_parser(curp); 3407295610fSBaptiste Daroussin if ((curp->man->meta.macroset == MACROSET_MDOC ? 3417295610fSBaptiste Daroussin mdoc_parseln(curp->man, curp->line, ln.buf, of) : 3427295610fSBaptiste Daroussin man_parseln(curp->man, curp->line, ln.buf, of) 3437295610fSBaptiste Daroussin ) == 2) 3447295610fSBaptiste Daroussin goto out; 3457295610fSBaptiste Daroussin break; 34661d06d6bSBaptiste Daroussin case ROFF_RERUN: 34761d06d6bSBaptiste Daroussin goto rerun; 3487295610fSBaptiste Daroussin case ROFF_REPARSE: 3497295610fSBaptiste Daroussin if (++curp->reparse_count > REPARSE_LIMIT) { 3507295610fSBaptiste Daroussin /* Abort and return to the top level. */ 3517295610fSBaptiste Daroussin result = ROFF_IGN; 3527295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ROFFLOOP, 3537295610fSBaptiste Daroussin curp->line, pos, NULL); 3547295610fSBaptiste Daroussin goto out; 3557295610fSBaptiste Daroussin } 3567295610fSBaptiste Daroussin result = mparse_buf_r(curp, ln, of, 0); 3577295610fSBaptiste Daroussin if (line_result & ROFF_USERCALL) { 3587295610fSBaptiste Daroussin roff_userret(curp->roff); 3597295610fSBaptiste Daroussin /* Continue normally. */ 3607295610fSBaptiste Daroussin if (result & ROFF_USERRET) 3617295610fSBaptiste Daroussin result = ROFF_CONT; 3627295610fSBaptiste Daroussin } 3637295610fSBaptiste Daroussin if (start == 0 && result != ROFF_CONT) 3647295610fSBaptiste Daroussin goto out; 3657295610fSBaptiste Daroussin break; 36661d06d6bSBaptiste Daroussin case ROFF_SO: 36761d06d6bSBaptiste Daroussin if ( ! (curp->options & MPARSE_SO) && 36861d06d6bSBaptiste Daroussin (i >= blk.sz || blk.buf[i] == '\0')) { 3697295610fSBaptiste Daroussin curp->man->meta.sodest = 3707295610fSBaptiste Daroussin mandoc_strdup(ln.buf + of); 3717295610fSBaptiste Daroussin goto out; 37261d06d6bSBaptiste Daroussin } 37361d06d6bSBaptiste Daroussin if ((fd = mparse_open(curp, ln.buf + of)) != -1) { 37461d06d6bSBaptiste Daroussin mparse_readfd(curp, fd, ln.buf + of); 37561d06d6bSBaptiste Daroussin close(fd); 37661d06d6bSBaptiste Daroussin } else { 3777295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_SO_FAIL, 3787295610fSBaptiste Daroussin curp->line, of, ".so %s: %s", 3797295610fSBaptiste Daroussin ln.buf + of, strerror(errno)); 38061d06d6bSBaptiste Daroussin ln.sz = mandoc_asprintf(&cp, 38161d06d6bSBaptiste Daroussin ".sp\nSee the file %s.\n.sp", 38261d06d6bSBaptiste Daroussin ln.buf + of); 38361d06d6bSBaptiste Daroussin free(ln.buf); 38461d06d6bSBaptiste Daroussin ln.buf = cp; 38561d06d6bSBaptiste Daroussin of = 0; 38661d06d6bSBaptiste Daroussin mparse_buf_r(curp, ln, of, 0); 38761d06d6bSBaptiste Daroussin } 3887295610fSBaptiste Daroussin break; 38961d06d6bSBaptiste Daroussin default: 3907295610fSBaptiste Daroussin abort(); 39161d06d6bSBaptiste Daroussin } 39261d06d6bSBaptiste Daroussin 39361d06d6bSBaptiste Daroussin /* Start the next input line. */ 39461d06d6bSBaptiste Daroussin 3957295610fSBaptiste Daroussin if (loop != NULL && 3967295610fSBaptiste Daroussin (line_result & ROFF_LOOPMASK) == ROFF_IGN) 3977295610fSBaptiste Daroussin loop = loop->next; 3987295610fSBaptiste Daroussin 3997295610fSBaptiste Daroussin if (loop != NULL) { 4007295610fSBaptiste Daroussin if ((line_result & ROFF_APPEND) == 0) 4017295610fSBaptiste Daroussin *ln.buf = '\0'; 4027295610fSBaptiste Daroussin if (ln.sz < loop->sz) 4037295610fSBaptiste Daroussin resize_buf(&ln, loop->sz); 4047295610fSBaptiste Daroussin (void)strlcat(ln.buf, loop->buf, ln.sz); 4057295610fSBaptiste Daroussin of = 0; 4067295610fSBaptiste Daroussin goto rerun; 40761d06d6bSBaptiste Daroussin } 40861d06d6bSBaptiste Daroussin 4097295610fSBaptiste Daroussin pos = (line_result & ROFF_APPEND) ? strlen(ln.buf) : 0; 4107295610fSBaptiste Daroussin } 4117295610fSBaptiste Daroussin out: 4127295610fSBaptiste Daroussin if (inloop) { 4137295610fSBaptiste Daroussin if (result != ROFF_USERRET) 4147295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_WHILE_OUTOF, 4157295610fSBaptiste Daroussin curp->line, pos, NULL); 4167295610fSBaptiste Daroussin curp->loop = NULL; 4177295610fSBaptiste Daroussin } 41861d06d6bSBaptiste Daroussin free(ln.buf); 4197295610fSBaptiste Daroussin if (firstln != curp->secondary) 4207295610fSBaptiste Daroussin free_buf_list(firstln); 4217295610fSBaptiste Daroussin return result; 42261d06d6bSBaptiste Daroussin } 42361d06d6bSBaptiste Daroussin 42461d06d6bSBaptiste Daroussin static int 4257295610fSBaptiste Daroussin read_whole_file(struct mparse *curp, int fd, struct buf *fb, int *with_mmap) 42661d06d6bSBaptiste Daroussin { 42761d06d6bSBaptiste Daroussin struct stat st; 42861d06d6bSBaptiste Daroussin gzFile gz; 42961d06d6bSBaptiste Daroussin size_t off; 43061d06d6bSBaptiste Daroussin ssize_t ssz; 43161d06d6bSBaptiste Daroussin int gzerrnum, retval; 43261d06d6bSBaptiste Daroussin 43361d06d6bSBaptiste Daroussin if (fstat(fd, &st) == -1) { 434*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_FSTAT, 0, 0, "%s", strerror(errno)); 435*45a5aec3SBaptiste Daroussin return -1; 43661d06d6bSBaptiste Daroussin } 43761d06d6bSBaptiste Daroussin 43861d06d6bSBaptiste Daroussin /* 43961d06d6bSBaptiste Daroussin * If we're a regular file, try just reading in the whole entry 44061d06d6bSBaptiste Daroussin * via mmap(). This is faster than reading it into blocks, and 44161d06d6bSBaptiste Daroussin * since each file is only a few bytes to begin with, I'm not 44261d06d6bSBaptiste Daroussin * concerned that this is going to tank any machines. 44361d06d6bSBaptiste Daroussin */ 44461d06d6bSBaptiste Daroussin 44561d06d6bSBaptiste Daroussin if (curp->gzip == 0 && S_ISREG(st.st_mode)) { 44661d06d6bSBaptiste Daroussin if (st.st_size > 0x7fffffff) { 4477295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL); 448*45a5aec3SBaptiste Daroussin return -1; 44961d06d6bSBaptiste Daroussin } 45061d06d6bSBaptiste Daroussin *with_mmap = 1; 45161d06d6bSBaptiste Daroussin fb->sz = (size_t)st.st_size; 45261d06d6bSBaptiste Daroussin fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0); 45361d06d6bSBaptiste Daroussin if (fb->buf != MAP_FAILED) 454*45a5aec3SBaptiste Daroussin return 0; 45561d06d6bSBaptiste Daroussin } 45661d06d6bSBaptiste Daroussin 45761d06d6bSBaptiste Daroussin if (curp->gzip) { 45861d06d6bSBaptiste Daroussin /* 45961d06d6bSBaptiste Daroussin * Duplicating the file descriptor is required 46061d06d6bSBaptiste Daroussin * because we will have to call gzclose(3) 46161d06d6bSBaptiste Daroussin * to free memory used internally by zlib, 46261d06d6bSBaptiste Daroussin * but that will also close the file descriptor, 46361d06d6bSBaptiste Daroussin * which this function must not do. 46461d06d6bSBaptiste Daroussin */ 46561d06d6bSBaptiste Daroussin if ((fd = dup(fd)) == -1) { 466*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_DUP, 0, 0, 467*45a5aec3SBaptiste Daroussin "%s", strerror(errno)); 468*45a5aec3SBaptiste Daroussin return -1; 46961d06d6bSBaptiste Daroussin } 47061d06d6bSBaptiste Daroussin if ((gz = gzdopen(fd, "rb")) == NULL) { 471*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_GZDOPEN, 0, 0, 472*45a5aec3SBaptiste Daroussin "%s", strerror(errno)); 47361d06d6bSBaptiste Daroussin close(fd); 474*45a5aec3SBaptiste Daroussin return -1; 47561d06d6bSBaptiste Daroussin } 47661d06d6bSBaptiste Daroussin } else 47761d06d6bSBaptiste Daroussin gz = NULL; 47861d06d6bSBaptiste Daroussin 47961d06d6bSBaptiste Daroussin /* 48061d06d6bSBaptiste Daroussin * If this isn't a regular file (like, say, stdin), then we must 48161d06d6bSBaptiste Daroussin * go the old way and just read things in bit by bit. 48261d06d6bSBaptiste Daroussin */ 48361d06d6bSBaptiste Daroussin 48461d06d6bSBaptiste Daroussin *with_mmap = 0; 48561d06d6bSBaptiste Daroussin off = 0; 486*45a5aec3SBaptiste Daroussin retval = -1; 48761d06d6bSBaptiste Daroussin fb->sz = 0; 48861d06d6bSBaptiste Daroussin fb->buf = NULL; 48961d06d6bSBaptiste Daroussin for (;;) { 49061d06d6bSBaptiste Daroussin if (off == fb->sz) { 49161d06d6bSBaptiste Daroussin if (fb->sz == (1U << 31)) { 4927295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL); 49361d06d6bSBaptiste Daroussin break; 49461d06d6bSBaptiste Daroussin } 49561d06d6bSBaptiste Daroussin resize_buf(fb, 65536); 49661d06d6bSBaptiste Daroussin } 49761d06d6bSBaptiste Daroussin ssz = curp->gzip ? 49861d06d6bSBaptiste Daroussin gzread(gz, fb->buf + (int)off, fb->sz - off) : 49961d06d6bSBaptiste Daroussin read(fd, fb->buf + (int)off, fb->sz - off); 50061d06d6bSBaptiste Daroussin if (ssz == 0) { 50161d06d6bSBaptiste Daroussin fb->sz = off; 502*45a5aec3SBaptiste Daroussin retval = 0; 50361d06d6bSBaptiste Daroussin break; 50461d06d6bSBaptiste Daroussin } 50561d06d6bSBaptiste Daroussin if (ssz == -1) { 50661d06d6bSBaptiste Daroussin if (curp->gzip) 50761d06d6bSBaptiste Daroussin (void)gzerror(gz, &gzerrnum); 508*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_READ, 0, 0, "%s", 50961d06d6bSBaptiste Daroussin curp->gzip && gzerrnum != Z_ERRNO ? 51061d06d6bSBaptiste Daroussin zError(gzerrnum) : strerror(errno)); 51161d06d6bSBaptiste Daroussin break; 51261d06d6bSBaptiste Daroussin } 51361d06d6bSBaptiste Daroussin off += (size_t)ssz; 51461d06d6bSBaptiste Daroussin } 51561d06d6bSBaptiste Daroussin 51661d06d6bSBaptiste Daroussin if (curp->gzip && (gzerrnum = gzclose(gz)) != Z_OK) 517*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_GZCLOSE, 0, 0, "%s", 51861d06d6bSBaptiste Daroussin gzerrnum == Z_ERRNO ? strerror(errno) : 51961d06d6bSBaptiste Daroussin zError(gzerrnum)); 520*45a5aec3SBaptiste Daroussin if (retval == -1) { 52161d06d6bSBaptiste Daroussin free(fb->buf); 52261d06d6bSBaptiste Daroussin fb->buf = NULL; 52361d06d6bSBaptiste Daroussin } 52461d06d6bSBaptiste Daroussin return retval; 52561d06d6bSBaptiste Daroussin } 52661d06d6bSBaptiste Daroussin 52761d06d6bSBaptiste Daroussin static void 52861d06d6bSBaptiste Daroussin mparse_end(struct mparse *curp) 52961d06d6bSBaptiste Daroussin { 5307295610fSBaptiste Daroussin if (curp->man->meta.macroset == MACROSET_NONE) 5317295610fSBaptiste Daroussin curp->man->meta.macroset = MACROSET_MAN; 5327295610fSBaptiste Daroussin if (curp->man->meta.macroset == MACROSET_MDOC) 53361d06d6bSBaptiste Daroussin mdoc_endparse(curp->man); 53461d06d6bSBaptiste Daroussin else 53561d06d6bSBaptiste Daroussin man_endparse(curp->man); 53661d06d6bSBaptiste Daroussin roff_endparse(curp->roff); 53761d06d6bSBaptiste Daroussin } 53861d06d6bSBaptiste Daroussin 5397295610fSBaptiste Daroussin /* 5407295610fSBaptiste Daroussin * Read the whole file into memory and call the parsers. 5417295610fSBaptiste Daroussin * Called recursively when an .so request is encountered. 5427295610fSBaptiste Daroussin */ 5437295610fSBaptiste Daroussin void 5447295610fSBaptiste Daroussin mparse_readfd(struct mparse *curp, int fd, const char *filename) 54561d06d6bSBaptiste Daroussin { 54661d06d6bSBaptiste Daroussin static int recursion_depth; 54761d06d6bSBaptiste Daroussin 5487295610fSBaptiste Daroussin struct buf blk; 5497295610fSBaptiste Daroussin struct buf *save_primary; 5507295610fSBaptiste Daroussin const char *save_filename; 5517295610fSBaptiste Daroussin size_t offset; 5527295610fSBaptiste Daroussin int save_filenc, save_lineno; 5537295610fSBaptiste Daroussin int with_mmap; 5547295610fSBaptiste Daroussin 5557295610fSBaptiste Daroussin if (recursion_depth > 64) { 5567295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_ROFFLOOP, curp->line, 0, NULL); 55761d06d6bSBaptiste Daroussin return; 55861d06d6bSBaptiste Daroussin } 559*45a5aec3SBaptiste Daroussin if (read_whole_file(curp, fd, &blk, &with_mmap) == -1) 5607295610fSBaptiste Daroussin return; 56161d06d6bSBaptiste Daroussin 5627295610fSBaptiste Daroussin /* 5637295610fSBaptiste Daroussin * Save some properties of the parent file. 5647295610fSBaptiste Daroussin */ 5657295610fSBaptiste Daroussin 5667295610fSBaptiste Daroussin save_primary = curp->primary; 5677295610fSBaptiste Daroussin save_filenc = curp->filenc; 5687295610fSBaptiste Daroussin save_lineno = curp->line; 5697295610fSBaptiste Daroussin save_filename = mandoc_msg_getinfilename(); 5707295610fSBaptiste Daroussin 57161d06d6bSBaptiste Daroussin curp->primary = &blk; 5727295610fSBaptiste Daroussin curp->filenc = curp->options & (MPARSE_UTF8 | MPARSE_LATIN1); 57361d06d6bSBaptiste Daroussin curp->line = 1; 5747295610fSBaptiste Daroussin mandoc_msg_setinfilename(filename); 57561d06d6bSBaptiste Daroussin 57661d06d6bSBaptiste Daroussin /* Skip an UTF-8 byte order mark. */ 57761d06d6bSBaptiste Daroussin if (curp->filenc & MPARSE_UTF8 && blk.sz > 2 && 57861d06d6bSBaptiste Daroussin (unsigned char)blk.buf[0] == 0xef && 57961d06d6bSBaptiste Daroussin (unsigned char)blk.buf[1] == 0xbb && 58061d06d6bSBaptiste Daroussin (unsigned char)blk.buf[2] == 0xbf) { 58161d06d6bSBaptiste Daroussin offset = 3; 58261d06d6bSBaptiste Daroussin curp->filenc &= ~MPARSE_LATIN1; 58361d06d6bSBaptiste Daroussin } else 58461d06d6bSBaptiste Daroussin offset = 0; 58561d06d6bSBaptiste Daroussin 5867295610fSBaptiste Daroussin recursion_depth++; 58761d06d6bSBaptiste Daroussin mparse_buf_r(curp, blk, offset, 1); 58861d06d6bSBaptiste Daroussin if (--recursion_depth == 0) 58961d06d6bSBaptiste Daroussin mparse_end(curp); 59061d06d6bSBaptiste Daroussin 59161d06d6bSBaptiste Daroussin /* 5927295610fSBaptiste Daroussin * Clean up and restore saved parent properties. 59361d06d6bSBaptiste Daroussin */ 59461d06d6bSBaptiste Daroussin 59561d06d6bSBaptiste Daroussin if (with_mmap) 59661d06d6bSBaptiste Daroussin munmap(blk.buf, blk.sz); 59761d06d6bSBaptiste Daroussin else 59861d06d6bSBaptiste Daroussin free(blk.buf); 5997295610fSBaptiste Daroussin 6007295610fSBaptiste Daroussin curp->primary = save_primary; 6017295610fSBaptiste Daroussin curp->filenc = save_filenc; 6027295610fSBaptiste Daroussin curp->line = save_lineno; 6037295610fSBaptiste Daroussin if (save_filename != NULL) 6047295610fSBaptiste Daroussin mandoc_msg_setinfilename(save_filename); 60561d06d6bSBaptiste Daroussin } 60661d06d6bSBaptiste Daroussin 60761d06d6bSBaptiste Daroussin int 60861d06d6bSBaptiste Daroussin mparse_open(struct mparse *curp, const char *file) 60961d06d6bSBaptiste Daroussin { 61061d06d6bSBaptiste Daroussin char *cp; 6117295610fSBaptiste Daroussin int fd, save_errno; 61261d06d6bSBaptiste Daroussin 61361d06d6bSBaptiste Daroussin cp = strrchr(file, '.'); 61461d06d6bSBaptiste Daroussin curp->gzip = (cp != NULL && ! strcmp(cp + 1, "gz")); 61561d06d6bSBaptiste Daroussin 61661d06d6bSBaptiste Daroussin /* First try to use the filename as it is. */ 61761d06d6bSBaptiste Daroussin 61861d06d6bSBaptiste Daroussin if ((fd = open(file, O_RDONLY)) != -1) 61961d06d6bSBaptiste Daroussin return fd; 62061d06d6bSBaptiste Daroussin 62161d06d6bSBaptiste Daroussin /* 62261d06d6bSBaptiste Daroussin * If that doesn't work and the filename doesn't 62361d06d6bSBaptiste Daroussin * already end in .gz, try appending .gz. 62461d06d6bSBaptiste Daroussin */ 62561d06d6bSBaptiste Daroussin 62661d06d6bSBaptiste Daroussin if ( ! curp->gzip) { 6277295610fSBaptiste Daroussin save_errno = errno; 62861d06d6bSBaptiste Daroussin mandoc_asprintf(&cp, "%s.gz", file); 62961d06d6bSBaptiste Daroussin fd = open(cp, O_RDONLY); 63061d06d6bSBaptiste Daroussin free(cp); 6317295610fSBaptiste Daroussin errno = save_errno; 63261d06d6bSBaptiste Daroussin if (fd != -1) { 63361d06d6bSBaptiste Daroussin curp->gzip = 1; 63461d06d6bSBaptiste Daroussin return fd; 63561d06d6bSBaptiste Daroussin } 63661d06d6bSBaptiste Daroussin } 63761d06d6bSBaptiste Daroussin 63861d06d6bSBaptiste Daroussin /* Neither worked, give up. */ 63961d06d6bSBaptiste Daroussin 64061d06d6bSBaptiste Daroussin return -1; 64161d06d6bSBaptiste Daroussin } 64261d06d6bSBaptiste Daroussin 64361d06d6bSBaptiste Daroussin struct mparse * 6447295610fSBaptiste Daroussin mparse_alloc(int options, enum mandoc_os os_e, const char *os_s) 64561d06d6bSBaptiste Daroussin { 64661d06d6bSBaptiste Daroussin struct mparse *curp; 64761d06d6bSBaptiste Daroussin 64861d06d6bSBaptiste Daroussin curp = mandoc_calloc(1, sizeof(struct mparse)); 64961d06d6bSBaptiste Daroussin 65061d06d6bSBaptiste Daroussin curp->options = options; 65161d06d6bSBaptiste Daroussin curp->os_s = os_s; 65261d06d6bSBaptiste Daroussin 6537295610fSBaptiste Daroussin curp->roff = roff_alloc(options); 6547295610fSBaptiste Daroussin curp->man = roff_man_alloc(curp->roff, curp->os_s, 65561d06d6bSBaptiste Daroussin curp->options & MPARSE_QUICK ? 1 : 0); 65661d06d6bSBaptiste Daroussin if (curp->options & MPARSE_MDOC) { 6577295610fSBaptiste Daroussin curp->man->meta.macroset = MACROSET_MDOC; 65861d06d6bSBaptiste Daroussin if (curp->man->mdocmac == NULL) 65961d06d6bSBaptiste Daroussin curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX); 66061d06d6bSBaptiste Daroussin } else if (curp->options & MPARSE_MAN) { 6617295610fSBaptiste Daroussin curp->man->meta.macroset = MACROSET_MAN; 66261d06d6bSBaptiste Daroussin if (curp->man->manmac == NULL) 66361d06d6bSBaptiste Daroussin curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX); 66461d06d6bSBaptiste Daroussin } 6657295610fSBaptiste Daroussin curp->man->meta.first->tok = TOKEN_NONE; 66661d06d6bSBaptiste Daroussin curp->man->meta.os_e = os_e; 66761d06d6bSBaptiste Daroussin return curp; 66861d06d6bSBaptiste Daroussin } 66961d06d6bSBaptiste Daroussin 67061d06d6bSBaptiste Daroussin void 67161d06d6bSBaptiste Daroussin mparse_reset(struct mparse *curp) 67261d06d6bSBaptiste Daroussin { 67361d06d6bSBaptiste Daroussin roff_reset(curp->roff); 67461d06d6bSBaptiste Daroussin roff_man_reset(curp->man); 6757295610fSBaptiste Daroussin free_buf_list(curp->secondary); 6767295610fSBaptiste Daroussin curp->secondary = NULL; 67761d06d6bSBaptiste Daroussin curp->gzip = 0; 67861d06d6bSBaptiste Daroussin } 67961d06d6bSBaptiste Daroussin 68061d06d6bSBaptiste Daroussin void 68161d06d6bSBaptiste Daroussin mparse_free(struct mparse *curp) 68261d06d6bSBaptiste Daroussin { 68361d06d6bSBaptiste Daroussin roffhash_free(curp->man->mdocmac); 68461d06d6bSBaptiste Daroussin roffhash_free(curp->man->manmac); 68561d06d6bSBaptiste Daroussin roff_man_free(curp->man); 68661d06d6bSBaptiste Daroussin roff_free(curp->roff); 6877295610fSBaptiste Daroussin free_buf_list(curp->secondary); 68861d06d6bSBaptiste Daroussin free(curp); 68961d06d6bSBaptiste Daroussin } 69061d06d6bSBaptiste Daroussin 6917295610fSBaptiste Daroussin struct roff_meta * 6927295610fSBaptiste Daroussin mparse_result(struct mparse *curp) 69361d06d6bSBaptiste Daroussin { 6947295610fSBaptiste Daroussin roff_state_reset(curp->man); 6957295610fSBaptiste Daroussin if (curp->options & MPARSE_VALIDATE) { 6967295610fSBaptiste Daroussin if (curp->man->meta.macroset == MACROSET_MDOC) 6977295610fSBaptiste Daroussin mdoc_validate(curp->man); 6987295610fSBaptiste Daroussin else 6997295610fSBaptiste Daroussin man_validate(curp->man); 70061d06d6bSBaptiste Daroussin } 7017295610fSBaptiste Daroussin return &curp->man->meta; 70261d06d6bSBaptiste Daroussin } 70361d06d6bSBaptiste Daroussin 70461d06d6bSBaptiste Daroussin void 7057295610fSBaptiste Daroussin mparse_copy(const struct mparse *p) 70661d06d6bSBaptiste Daroussin { 7077295610fSBaptiste Daroussin struct buf *buf; 70861d06d6bSBaptiste Daroussin 7097295610fSBaptiste Daroussin for (buf = p->secondary; buf != NULL; buf = buf->next) 7107295610fSBaptiste Daroussin puts(buf->buf); 71161d06d6bSBaptiste Daroussin } 712