=================================================================== RCS file: /cvs/mandoc/roff.c,v retrieving revision 1.341 retrieving revision 1.355 diff -u -p -r1.341 -r1.355 --- mandoc/roff.c 2018/08/25 16:53:39 1.341 +++ mandoc/roff.c 2018/12/21 17:15:19 1.355 @@ -1,4 +1,4 @@ -/* $Id: roff.c,v 1.341 2018/08/25 16:53:39 schwarze Exp $ */ +/* $Id: roff.c,v 1.355 2018/12/21 17:15:19 schwarze Exp $ */ /* * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons * Copyright (c) 2010-2015, 2017, 2018 Ingo Schwarze @@ -28,14 +28,24 @@ #include #include -#include "mandoc.h" #include "mandoc_aux.h" #include "mandoc_ohash.h" +#include "mandoc.h" #include "roff.h" +#include "mandoc_parse.h" #include "libmandoc.h" #include "roff_int.h" -#include "libroff.h" +#include "tbl_parse.h" +#include "eqn_parse.h" +/* + * ASCII_ESC is used to signal from roff_getarg() to roff_expand() + * that an escape sequence resulted from copy-in processing and + * needs to be checked or interpolated. As it is used nowhere + * else, it is defined here rather than in a header file. + */ +#define ASCII_ESC 27 + /* Maximum number of string expansions per line, to break infinite loops. */ #define EXPAND_LIMIT 1000 @@ -96,7 +106,6 @@ struct mctx { }; struct roff { - struct mparse *parse; /* parse point */ struct roff_man *man; /* mdoc or man parser */ struct roffnode *last; /* leaf of stack */ struct mctx *mstack; /* stack of macro contexts */ @@ -167,7 +176,7 @@ static int roffnode_cleanscope(struct roff *); static int roffnode_pop(struct roff *); static void roffnode_push(struct roff *, enum roff_tok, const char *, int, int); -static void roff_addtbl(struct roff_man *, struct tbl_node *); +static void roff_addtbl(struct roff_man *, int, struct tbl_node *); static int roff_als(ROFF_ARGS); static int roff_block(ROFF_ARGS); static int roff_block_text(ROFF_ARGS); @@ -190,6 +199,8 @@ static int roff_evalnum(struct roff *, int, static int roff_evalpar(struct roff *, int, const char *, int *, int *, int); static int roff_evalstrcond(const char *, int *); +static int roff_expand(struct roff *, struct buf *, + int, int, char); static void roff_free1(struct roff *); static void roff_freereg(struct roffreg *); static void roff_freestr(struct roffkv *); @@ -218,7 +229,6 @@ static enum roff_tok roff_parse(struct roff *, char * static int roff_parsetext(struct roff *, struct buf *, int, int *); static int roff_renamed(ROFF_ARGS); -static int roff_res(struct roff *, struct buf *, int, int); static int roff_return(ROFF_ARGS); static int roff_rm(ROFF_ARGS); static int roff_rn(ROFF_ARGS); @@ -717,17 +727,12 @@ roffnode_push(struct roff *r, enum roff_tok tok, const static void roff_free1(struct roff *r) { - struct tbl_node *tbl; int i; - while (NULL != (tbl = r->first_tbl)) { - r->first_tbl = tbl->next; - tbl_free(tbl); - } + tbl_free(r->first_tbl); r->first_tbl = r->last_tbl = r->tbl = NULL; - if (r->last_eqn != NULL) - eqn_free(r->last_eqn); + eqn_free(r->last_eqn); r->last_eqn = r->eqn = NULL; while (r->mstackpos >= 0) @@ -783,12 +788,11 @@ roff_free(struct roff *r) } struct roff * -roff_alloc(struct mparse *parse, int options) +roff_alloc(int options) { struct roff *r; r = mandoc_calloc(1, sizeof(struct roff)); - r->parse = parse; r->reqtab = roffhash_alloc(0, ROFF_RENAMED); r->options = options; r->format = options & (MPARSE_MDOC | MPARSE_MAN); @@ -847,13 +851,11 @@ roff_man_free(struct roff_man *man) } struct roff_man * -roff_man_alloc(struct roff *roff, struct mparse *parse, - const char *os_s, int quick) +roff_man_alloc(struct roff *roff, const char *os_s, int quick) { struct roff_man *man; man = mandoc_calloc(1, sizeof(*man)); - man->parse = parse; man->roff = roff; man->os_s = os_s; man->quick = quick; @@ -1013,15 +1015,15 @@ roff_body_alloc(struct roff_man *man, int line, int po } static void -roff_addtbl(struct roff_man *man, struct tbl_node *tbl) +roff_addtbl(struct roff_man *man, int line, struct tbl_node *tbl) { struct roff_node *n; - const struct tbl_span *span; + struct tbl_span *span; if (man->macroset == MACROSET_MAN) man_breakscope(man, ROFF_TS); while ((span = tbl_span(tbl)) != NULL) { - n = roff_node_alloc(man, tbl->line, 0, ROFFT_TBL, TOKEN_NONE); + n = roff_node_alloc(man, line, 0, ROFFT_TBL, TOKEN_NONE); n->span = span; roff_node_append(man, n); n->flags |= NODE_VALID | NODE_ENDED; @@ -1067,6 +1069,14 @@ roff_node_unlink(struct roff_man *man, struct roff_nod } void +roff_node_relink(struct roff_man *man, struct roff_node *n) +{ + roff_node_unlink(man, n); + n->prev = n->next = NULL; + roff_node_append(man, n); +} + +void roff_node_free(struct roff_node *n) { @@ -1074,8 +1084,7 @@ roff_node_free(struct roff_node *n) mdoc_argv_free(n->args); if (n->type == ROFFT_BLOCK || n->type == ROFFT_ELEM) free(n->norm); - if (n->eqn != NULL) - eqn_box_free(n->eqn); + eqn_box_free(n->eqn); free(n->string); free(n); } @@ -1142,18 +1151,19 @@ deroff(char **dest, const struct roff_node *n) /* --- main functions of the roff parser ---------------------------------- */ /* - * In the current line, expand escape sequences that tend to get - * used in numerical expressions and conditional requests. - * Also check the syntax of the remaining escape sequences. + * In the current line, expand escape sequences that produce parsable + * input text. Also check the syntax of the remaining escape sequences, + * which typically produce output glyphs or change formatter state. */ static int -roff_res(struct roff *r, struct buf *buf, int ln, int pos) +roff_expand(struct roff *r, struct buf *buf, int ln, int pos, char newesc) { struct mctx *ctx; /* current macro call context */ char ubuf[24]; /* buffer to print the number */ struct roff_node *n; /* used for header comments */ const char *start; /* start of the string to process */ char *stesc; /* start of an escape sequence ('\\') */ + const char *esct; /* type of esccape sequence */ char *ep; /* end of comment string */ const char *stnam; /* start of the name, after "[(*" */ const char *cp; /* end of the name, e.g. before ']' */ @@ -1163,7 +1173,6 @@ roff_res(struct roff *r, struct buf *buf, int ln, int size_t naml; /* actual length of the escape name */ size_t asz; /* length of the replacement */ size_t rsz; /* length of the rest of the string */ - enum mandoc_esc esc; /* type of the escape sequence */ int inaml; /* length returned from mandoc_escape() */ int expand_count; /* to avoid infinite loops */ int npos; /* position in numeric expression */ @@ -1172,6 +1181,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int int done; /* no more input available */ int deftype; /* type of definition to paste */ int rcsid; /* kind of RCS id seen */ + enum mandocerr err; /* for escape sequence problems */ char sign; /* increment number register */ char term; /* character terminating the escape */ @@ -1180,7 +1190,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int done = 0; start = buf->buf + pos; for (stesc = buf->buf + pos; *stesc != '\0'; stesc++) { - if (stesc[0] != r->escape || stesc[1] == '\0') + if (stesc[0] != newesc || stesc[1] == '\0') continue; stesc++; if (*stesc != '"' && *stesc != '#') @@ -1200,8 +1210,9 @@ roff_res(struct roff *r, struct buf *buf, int ln, int isalnum((unsigned char)*cp) == 0 && strchr(cp, '$') != NULL) { if (r->man->meta.rcsids & rcsid) - mandoc_msg(MANDOCERR_RCS_REP, r->parse, - ln, stesc + 1 - buf->buf, stesc + 1); + mandoc_msg(MANDOCERR_RCS_REP, ln, + (int)(stesc - buf->buf) + 1, + "%s", stesc + 1); r->man->meta.rcsids |= rcsid; } @@ -1213,15 +1224,15 @@ roff_res(struct roff *r, struct buf *buf, int ln, int ep--; } if (*ep == ' ' || *ep == '\t') - mandoc_msg(MANDOCERR_SPACE_EOL, r->parse, - ln, ep - buf->buf, NULL); + mandoc_msg(MANDOCERR_SPACE_EOL, + ln, (int)(ep - buf->buf), NULL); /* * Save comments preceding the title macro * in the syntax tree. */ - if (r->format == 0) { + if (newesc != ASCII_ESC && r->format == 0) { while (*ep == ' ' || *ep == '\t') ep--; ep[1] = '\0'; @@ -1262,11 +1273,16 @@ roff_res(struct roff *r, struct buf *buf, int ln, int expand_count = 0; while (stesc >= start) { + if (*stesc != newesc) { - /* Search backwards for the next backslash. */ + /* + * If we have a non-standard escape character, + * escape literal backslashes because all + * processing in subsequent functions uses + * the standard escaping rules. + */ - if (*stesc != r->escape) { - if (*stesc == '\\') { + if (newesc != ASCII_ESC && *stesc == '\\') { *stesc = '\0'; buf->sz = mandoc_asprintf(&nbuf, "%s\\e%s", buf->buf, stesc + 1) + 1; @@ -1275,6 +1291,9 @@ roff_res(struct roff *r, struct buf *buf, int ln, int free(buf->buf); buf->buf = nbuf; } + + /* Search backwards for the next escape. */ + stesc--; continue; } @@ -1303,7 +1322,10 @@ roff_res(struct roff *r, struct buf *buf, int ln, int term = '\0'; cp = stesc + 1; - switch (*cp) { + if (*cp == 'E') + cp++; + esct = cp; + switch (*esct) { case '*': case '$': res = NULL; @@ -1319,19 +1341,33 @@ roff_res(struct roff *r, struct buf *buf, int ln, int res = ubuf; break; default: - esc = mandoc_escape(&cp, &stnam, &inaml); - if (esc == ESCAPE_ERROR || - (esc == ESCAPE_SPECIAL && - mchars_spec2cp(stnam, inaml) < 0)) - mandoc_vmsg(MANDOCERR_ESC_BAD, - r->parse, ln, (int)(stesc - buf->buf), + err = MANDOCERR_OK; + switch(mandoc_escape(&cp, &stnam, &inaml)) { + case ESCAPE_SPECIAL: + if (mchars_spec2cp(stnam, inaml) >= 0) + break; + /* FALLTHROUGH */ + case ESCAPE_ERROR: + err = MANDOCERR_ESC_BAD; + break; + case ESCAPE_UNDEF: + err = MANDOCERR_ESC_UNDEF; + break; + case ESCAPE_UNSUPP: + err = MANDOCERR_ESC_UNSUPP; + break; + default: + break; + } + if (err != MANDOCERR_OK) + mandoc_msg(err, ln, (int)(stesc - buf->buf), "%.*s", (int)(cp - stesc), stesc); stesc--; continue; } if (EXPAND_LIMIT < ++expand_count) { - mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, + mandoc_msg(MANDOCERR_ROFFLOOP, ln, (int)(stesc - buf->buf), NULL); return ROFF_IGN; } @@ -1372,8 +1408,8 @@ roff_res(struct roff *r, struct buf *buf, int ln, int arg_complete = 1; while (maxl == 0 || naml < maxl) { if (*cp == '\0') { - mandoc_msg(MANDOCERR_ESC_BAD, r->parse, - ln, (int)(stesc - buf->buf), stesc); + mandoc_msg(MANDOCERR_ESC_BAD, ln, + (int)(stesc - buf->buf), "%s", stesc); arg_complete = 0; break; } @@ -1381,7 +1417,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int cp++; break; } - if (*cp++ != '\\' || stesc[1] != 'w') { + if (*cp++ != '\\' || *esct != 'w') { naml++; continue; } @@ -1389,6 +1425,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int case ESCAPE_SPECIAL: case ESCAPE_UNICODE: case ESCAPE_NUMBERED: + case ESCAPE_UNDEF: case ESCAPE_OVERSTRIKE: naml++; break; @@ -1402,7 +1439,7 @@ roff_res(struct roff *r, struct buf *buf, int ln, int * undefined, resume searching for escapes. */ - switch (stesc[1]) { + switch (*esct) { case '*': if (arg_complete) { deftype = ROFFDEF_USER | ROFFDEF_PRE; @@ -1424,26 +1461,24 @@ roff_res(struct roff *r, struct buf *buf, int ln, int break; case '$': if (r->mstackpos < 0) { - mandoc_vmsg(MANDOCERR_ARG_UNDEF, - r->parse, ln, (int)(stesc - buf->buf), - "%.3s", stesc); + mandoc_msg(MANDOCERR_ARG_UNDEF, ln, + (int)(stesc - buf->buf), "%.3s", stesc); break; } ctx = r->mstack + r->mstackpos; - npos = stesc[2] - '1'; + npos = esct[1] - '1'; if (npos >= 0 && npos <= 8) { res = npos < ctx->argc ? ctx->argv[npos] : ""; break; } - if (stesc[2] == '*') + if (esct[1] == '*') quote_args = 0; - else if (stesc[2] == '@') + else if (esct[1] == '@') quote_args = 1; else { - mandoc_vmsg(MANDOCERR_ARG_NONUM, - r->parse, ln, (int)(stesc - buf->buf), - "%.3s", stesc); + mandoc_msg(MANDOCERR_ARG_NONUM, ln, + (int)(stesc - buf->buf), "%.3s", stesc); break; } asz = 0; @@ -1501,13 +1536,13 @@ roff_res(struct roff *r, struct buf *buf, int ln, int } if (res == NULL) { - if (stesc[1] == '*') - mandoc_vmsg(MANDOCERR_STR_UNDEF, - r->parse, ln, (int)(stesc - buf->buf), + if (*esct == '*') + mandoc_msg(MANDOCERR_STR_UNDEF, + ln, (int)(stesc - buf->buf), "%.*s", (int)naml, stnam); res = ""; } else if (buf->sz + strlen(res) > SHRT_MAX) { - mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, + mandoc_msg(MANDOCERR_ROFFLOOP, ln, (int)(stesc - buf->buf), NULL); return ROFF_IGN; } @@ -1529,6 +1564,118 @@ roff_res(struct roff *r, struct buf *buf, int ln, int } /* + * Parse a quoted or unquoted roff-style request or macro argument. + * Return a pointer to the parsed argument, which is either the original + * pointer or advanced by one byte in case the argument is quoted. + * NUL-terminate the argument in place. + * Collapse pairs of quotes inside quoted arguments. + * Advance the argument pointer to the next argument, + * or to the NUL byte terminating the argument line. + */ +char * +roff_getarg(struct roff *r, char **cpp, int ln, int *pos) +{ + struct buf buf; + char *cp, *start; + int newesc, pairs, quoted, white; + + /* Quoting can only start with a new word. */ + start = *cpp; + quoted = 0; + if ('"' == *start) { + quoted = 1; + start++; + } + + newesc = pairs = white = 0; + for (cp = start; '\0' != *cp; cp++) { + + /* + * Move the following text left + * after quoted quotes and after "\\" and "\t". + */ + if (pairs) + cp[-pairs] = cp[0]; + + if ('\\' == cp[0]) { + /* + * In copy mode, translate double to single + * backslashes and backslash-t to literal tabs. + */ + switch (cp[1]) { + case 'a': + case 't': + cp[-pairs] = '\t'; + pairs++; + cp++; + break; + case '\\': + newesc = 1; + cp[-pairs] = ASCII_ESC; + pairs++; + cp++; + break; + case ' ': + /* Skip escaped blanks. */ + if (0 == quoted) + cp++; + break; + default: + break; + } + } else if (0 == quoted) { + if (' ' == cp[0]) { + /* Unescaped blanks end unquoted args. */ + white = 1; + break; + } + } else if ('"' == cp[0]) { + if ('"' == cp[1]) { + /* Quoted quotes collapse. */ + pairs++; + cp++; + } else { + /* Unquoted quotes end quoted args. */ + quoted = 2; + break; + } + } + } + + /* Quoted argument without a closing quote. */ + if (1 == quoted) + mandoc_msg(MANDOCERR_ARG_QUOTE, ln, *pos, NULL); + + /* NUL-terminate this argument and move to the next one. */ + if (pairs) + cp[-pairs] = '\0'; + if ('\0' != *cp) { + *cp++ = '\0'; + while (' ' == *cp) + cp++; + } + *pos += (int)(cp - start) + (quoted ? 1 : 0); + *cpp = cp; + + if ('\0' == *cp && (white || ' ' == cp[-1])) + mandoc_msg(MANDOCERR_SPACE_EOL, ln, *pos, NULL); + + start = mandoc_strdup(start); + if (newesc == 0) + return start; + + buf.buf = start; + buf.sz = strlen(start) + 1; + buf.next = NULL; + if (roff_expand(r, &buf, ln, 0, ASCII_ESC) & ROFF_IGN) { + free(buf.buf); + buf.buf = mandoc_strdup(""); + } + return buf.buf; +} + + +/* * Process text streams. */ static int @@ -1622,7 +1769,7 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, /* Expand some escape sequences. */ - e = roff_res(r, buf, ln, pos); + e = roff_expand(r, buf, ln, pos, r->escape); if ((e & ROFF_MASK) == ROFF_IGN) return e; assert(e == ROFF_CONT); @@ -1651,7 +1798,7 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, } if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) { tbl_read(r->tbl, ln, buf->buf, ppos); - roff_addtbl(r->man, r->tbl); + roff_addtbl(r->man, ln, r->tbl); return e; } if ( ! ctl) @@ -1660,8 +1807,7 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, /* Skip empty request lines. */ if (buf->buf[pos] == '"') { - mandoc_msg(MANDOCERR_COMMENT_BAD, r->parse, - ln, pos, NULL); + mandoc_msg(MANDOCERR_COMMENT_BAD, ln, pos, NULL); return ROFF_IGN; } else if (buf->buf[pos] == '\0') return ROFF_IGN; @@ -1686,8 +1832,8 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, if (r->tbl != NULL && (t == TOKEN_NONE || t == ROFF_TS || t == ROFF_br || t == ROFF_ce || t == ROFF_rj || t == ROFF_sp)) { - mandoc_msg(MANDOCERR_TBLMACRO, r->parse, - ln, pos, buf->buf + spos); + mandoc_msg(MANDOCERR_TBLMACRO, + ln, pos, "%s", buf->buf + spos); if (t != TOKEN_NONE) return ROFF_IGN; while (buf->buf[pos] != '\0' && buf->buf[pos] != ' ') @@ -1695,7 +1841,7 @@ roff_parseln(struct roff *r, int ln, struct buf *buf, while (buf->buf[pos] == ' ') pos++; tbl_read(r->tbl, ln, buf->buf, pos); - roff_addtbl(r->man, r->tbl); + roff_addtbl(r->man, ln, r->tbl); return ROFF_IGN; } @@ -1746,21 +1892,18 @@ void roff_endparse(struct roff *r) { if (r->last != NULL) - mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, - r->last->line, r->last->col, - roff_name[r->last->tok]); + mandoc_msg(MANDOCERR_BLK_NOEND, r->last->line, + r->last->col, "%s", roff_name[r->last->tok]); if (r->eqn != NULL) { - mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, + mandoc_msg(MANDOCERR_BLK_NOEND, r->eqn->node->line, r->eqn->node->pos, "EQ"); eqn_parse(r->eqn); r->eqn = NULL; } if (r->tbl != NULL) { - mandoc_msg(MANDOCERR_BLK_NOEND, r->parse, - r->tbl->line, r->tbl->pos, "TS"); - tbl_end(r->tbl); + tbl_end(r->tbl, 1); r->tbl = NULL; } } @@ -1821,8 +1964,7 @@ roff_cblock(ROFF_ARGS) */ if (r->last == NULL) { - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, ".."); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, ".."); return ROFF_IGN; } @@ -1836,13 +1978,12 @@ roff_cblock(ROFF_ARGS) case ROFF_ig: break; default: - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, ".."); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, ".."); return ROFF_IGN; } if (buf->buf[pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, + mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, ".. %s", buf->buf + pos); roffnode_pop(r); @@ -1869,8 +2010,7 @@ static int roff_ccond(struct roff *r, int ln, int ppos) { if (NULL == r->last) { - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, "\\}"); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}"); return 0; } @@ -1881,14 +2021,12 @@ roff_ccond(struct roff *r, int ln, int ppos) case ROFF_while: break; default: - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, "\\}"); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}"); return 0; } if (r->last->endspan > -1) { - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, "\\}"); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "\\}"); return 0; } @@ -1932,8 +2070,8 @@ roff_block(ROFF_ARGS) deftype = ROFFDEF_USER; name = roff_getstrn(r, iname, namesz, &deftype); if (name == NULL) { - mandoc_vmsg(MANDOCERR_STR_UNDEF, - r->parse, ln, (int)(iname - buf->buf), + mandoc_msg(MANDOCERR_STR_UNDEF, + ln, (int)(iname - buf->buf), "%.*s", (int)namesz, iname); namesz = 0; } else @@ -1942,8 +2080,8 @@ roff_block(ROFF_ARGS) name = iname; if (namesz == 0 && tok != ROFF_ig) { - mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, - ln, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_REQ_EMPTY, + ln, ppos, "%s", roff_name[tok]); return ROFF_IGN; } @@ -2001,8 +2139,8 @@ roff_block(ROFF_ARGS) deftype = ROFFDEF_USER; name = roff_getstrn(r, iname, namesz, &deftype); if (name == NULL) { - mandoc_vmsg(MANDOCERR_STR_UNDEF, - r->parse, ln, (int)(iname - buf->buf), + mandoc_msg(MANDOCERR_STR_UNDEF, + ln, (int)(iname - buf->buf), "%.*s", (int)namesz, iname); namesz = 0; } else @@ -2014,7 +2152,7 @@ roff_block(ROFF_ARGS) r->last->end = mandoc_strndup(name, namesz); if (*cp != '\0') - mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, + mandoc_msg(MANDOCERR_ARG_EXCESS, ln, pos, ".%s ... %s", roff_name[tok], cp); return ROFF_IGN; @@ -2107,7 +2245,10 @@ roff_cond_sub(ROFF_ARGS) if (ep[0] == '\\' && ep[1] == '}') rr = 0; - /* Always check for the closing delimiter `\}'. */ + /* + * The closing delimiter `\}' rewinds the conditional scope + * but is otherwise ignored when interpreting the line. + */ while ((ep = strchr(ep, '\\')) != NULL) { switch (ep[1]) { @@ -2150,15 +2291,34 @@ roff_cond_text(ROFF_ARGS) if (roffnode_cleanscope(r)) irc |= endloop; + /* + * If `\}' occurs on a text line with neither preceding + * nor following characters, drop the line completely. + */ + ep = buf->buf + pos; + if (strcmp(ep, "\\}") == 0) + rr = 0; + + /* + * The closing delimiter `\}' rewinds the conditional scope + * but is otherwise ignored when interpreting the line. + */ + while ((ep = strchr(ep, '\\')) != NULL) { - if (*(++ep) == '}') { - *ep = '&'; - if (roff_ccond(r, ln, ep - buf->buf - 1)) + switch (ep[1]) { + case '}': + memmove(ep, ep + 2, strlen(ep + 2) + 1); + if (roff_ccond(r, ln, ep - buf->buf)) irc |= endloop; - } - if (*ep != '\0') + break; + case '\0': ++ep; + break; + default: + ep += 2; + break; + } } if (rr) irc |= ROFF_CONT; @@ -2393,8 +2553,7 @@ static int roff_insec(ROFF_ARGS) { - mandoc_msg(MANDOCERR_REQ_INSEC, r->parse, - ln, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_REQ_INSEC, ln, ppos, "%s", roff_name[tok]); return ROFF_IGN; } @@ -2402,8 +2561,7 @@ static int roff_unsupp(ROFF_ARGS) { - mandoc_msg(MANDOCERR_REQ_UNSUPP, r->parse, - ln, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_REQ_UNSUPP, ln, ppos, "%s", roff_name[tok]); return ROFF_IGN; } @@ -2477,8 +2635,8 @@ roff_cond(ROFF_ARGS) */ if (buf->buf[pos] == '\0') - mandoc_msg(MANDOCERR_COND_EMPTY, r->parse, - ln, ppos, roff_name[tok]); + mandoc_msg(MANDOCERR_COND_EMPTY, + ln, ppos, "%s", roff_name[tok]); r->last->endspan = 1; @@ -2681,7 +2839,7 @@ roff_evalnum(struct roff *r, int ln, const char *v, case '/': if (operand2 == 0) { mandoc_msg(MANDOCERR_DIVZERO, - r->parse, ln, *pos, v); + ln, *pos, "%s", v); *res = 0; break; } @@ -2690,7 +2848,7 @@ roff_evalnum(struct roff *r, int ln, const char *v, case '%': if (operand2 == 0) { mandoc_msg(MANDOCERR_DIVZERO, - r->parse, ln, *pos, v); + ln, *pos, "%s", v); *res = 0; break; } @@ -2970,8 +3128,8 @@ roff_it(ROFF_ARGS) /* Parse the number of lines. */ if ( ! roff_evalnum(r, ln, buf->buf, &pos, &iv, 0)) { - mandoc_msg(MANDOCERR_IT_NONUM, r->parse, - ln, ppos, buf->buf + 1); + mandoc_msg(MANDOCERR_IT_NONUM, + ln, ppos, "%s", buf->buf + 1); return ROFF_IGN; } @@ -3025,11 +3183,10 @@ static int roff_TE(ROFF_ARGS) { if (r->tbl == NULL) { - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, "TE"); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "TE"); return ROFF_IGN; } - if (tbl_end(r->tbl) == 0) { + if (tbl_end(r->tbl, 0) == 0) { r->tbl = NULL; free(buf->buf); buf->buf = mandoc_strdup(".sp"); @@ -3046,8 +3203,7 @@ roff_T_(ROFF_ARGS) { if (NULL == r->tbl) - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, - ln, ppos, "T&"); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "T&"); else tbl_restart(ln, ppos, r->tbl); @@ -3130,21 +3286,20 @@ roff_EQ(ROFF_ARGS) n = roff_node_alloc(r->man, ln, ppos, ROFFT_EQN, TOKEN_NONE); if (ln > r->man->last->line) n->flags |= NODE_LINE; - n->eqn = mandoc_calloc(1, sizeof(*n->eqn)); - n->eqn->expectargs = UINT_MAX; + n->eqn = eqn_box_new(); roff_node_append(r->man, n); r->man->next = ROFF_NEXT_SIBLING; assert(r->eqn == NULL); if (r->last_eqn == NULL) - r->last_eqn = eqn_alloc(r->parse); + r->last_eqn = eqn_alloc(); else eqn_reset(r->last_eqn); r->eqn = r->last_eqn; r->eqn->node = n; if (buf->buf[pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, + mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, ".EQ %s", buf->buf + pos); return ROFF_IGN; @@ -3157,9 +3312,9 @@ roff_EN(ROFF_ARGS) eqn_parse(r->eqn); r->eqn = NULL; } else - mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN"); + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "EN"); if (buf->buf[pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, + mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, "EN %s", buf->buf + pos); return ROFF_IGN; } @@ -3168,14 +3323,11 @@ static int roff_TS(ROFF_ARGS) { if (r->tbl != NULL) { - mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse, - ln, ppos, "TS breaks TS"); - tbl_end(r->tbl); + mandoc_msg(MANDOCERR_BLK_BROKEN, ln, ppos, "TS breaks TS"); + tbl_end(r->tbl, 0); } - r->tbl = tbl_alloc(ppos, ln, r->parse); - if (r->last_tbl) - r->last_tbl->next = r->tbl; - else + r->tbl = tbl_alloc(ppos, ln, r->last_tbl); + if (r->last_tbl == NULL) r->first_tbl = r->tbl; r->last_tbl = r->tbl; return ROFF_IGN; @@ -3208,8 +3360,8 @@ roff_onearg(ROFF_ARGS) while (*cp == ' ') *cp++ = '\0'; if (*cp != '\0') - mandoc_vmsg(MANDOCERR_ARG_EXCESS, - r->parse, ln, cp - buf->buf, + mandoc_msg(MANDOCERR_ARG_EXCESS, + ln, (int)(cp - buf->buf), "%s ... %s", roff_name[tok], cp); roff_word_alloc(r->man, ln, pos, buf->buf + pos); } @@ -3222,8 +3374,8 @@ roff_onearg(ROFF_ARGS) npos = 0; if (roff_evalnum(r, ln, r->man->last->string, &npos, &roffce_lines, 0) == 0) { - mandoc_vmsg(MANDOCERR_CE_NONUM, - r->parse, ln, pos, "ce %s", buf->buf + pos); + mandoc_msg(MANDOCERR_CE_NONUM, + ln, pos, "ce %s", buf->buf + pos); roffce_lines = 1; } if (roffce_lines < 1) { @@ -3298,7 +3450,7 @@ roff_br(ROFF_ARGS) man_breakscope(r->man, ROFF_br); roff_elem_alloc(r->man, ln, ppos, ROFF_br); if (buf->buf[pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos, + mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, "%s %s", roff_name[tok], buf->buf + pos); r->man->last->flags |= NODE_LINE | NODE_VALID | NODE_ENDED; r->man->next = ROFF_NEXT_SIBLING; @@ -3316,7 +3468,7 @@ roff_cc(ROFF_ARGS) r->control = '\0'; if (*p != '\0') - mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, + mandoc_msg(MANDOCERR_ARG_EXCESS, ln, p - buf->buf, "cc ... %s", p); return ROFF_IGN; @@ -3336,8 +3488,7 @@ roff_char(ROFF_ARGS) if (*kp == '\0' || (*kp == '\\' && mandoc_escape(&p, NULL, NULL) != ESCAPE_SPECIAL) || (*p != ' ' && *p != '\0')) { - mandoc_vmsg(MANDOCERR_CHAR_ARG, r->parse, - ln, pos, "char %s", kp); + mandoc_msg(MANDOCERR_CHAR_ARG, ln, pos, "char %s", kp); return ROFF_IGN; } ksz = p - kp; @@ -3361,6 +3512,7 @@ roff_char(ROFF_ARGS) case ESCAPE_FONTITALIC: case ESCAPE_FONTBOLD: case ESCAPE_FONTBI: + case ESCAPE_FONTCW: case ESCAPE_FONTPREV: font++; break; @@ -3369,8 +3521,8 @@ roff_char(ROFF_ARGS) } } if (font > 1) - mandoc_msg(MANDOCERR_CHAR_FONT, r->parse, - ln, vp - buf->buf, vp); + mandoc_msg(MANDOCERR_CHAR_FONT, + ln, (int)(vp - buf->buf), "%s", vp); /* * Approximate the effect of .char using the .tr tables. @@ -3403,8 +3555,8 @@ roff_ec(ROFF_ARGS) else { r->escape = *p; if (*++p != '\0') - mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse, - ln, p - buf->buf, "ec ... %s", p); + mandoc_msg(MANDOCERR_ARG_EXCESS, ln, + (int)(p - buf->buf), "ec ... %s", p); } return ROFF_IGN; } @@ -3414,7 +3566,7 @@ roff_eo(ROFF_ARGS) { r->escape = '\0'; if (buf->buf[pos] != '\0') - mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, + mandoc_msg(MANDOCERR_ARG_SKIP, ln, pos, "eo %s", buf->buf + pos); return ROFF_IGN; } @@ -3438,7 +3590,7 @@ roff_tr(ROFF_ARGS) p = buf->buf + pos; if (*p == '\0') { - mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse, ln, ppos, "tr"); + mandoc_msg(MANDOCERR_REQ_EMPTY, ln, ppos, "tr"); return ROFF_IGN; } @@ -3449,8 +3601,8 @@ roff_tr(ROFF_ARGS) if (*first == '\\') { esc = mandoc_escape(&p, NULL, NULL); if (esc == ESCAPE_ERROR) { - mandoc_msg(MANDOCERR_ESC_BAD, r->parse, - ln, (int)(p - buf->buf), first); + mandoc_msg(MANDOCERR_ESC_BAD, ln, + (int)(p - buf->buf), "%s", first); return ROFF_IGN; } fsz = (size_t)(p - first); @@ -3460,14 +3612,14 @@ roff_tr(ROFF_ARGS) if (*second == '\\') { esc = mandoc_escape(&p, NULL, NULL); if (esc == ESCAPE_ERROR) { - mandoc_msg(MANDOCERR_ESC_BAD, r->parse, - ln, (int)(p - buf->buf), second); + mandoc_msg(MANDOCERR_ESC_BAD, ln, + (int)(p - buf->buf), "%s", second); return ROFF_IGN; } ssz = (size_t)(p - second); } else if (*second == '\0') { - mandoc_vmsg(MANDOCERR_TR_ODD, r->parse, - ln, first - buf->buf, "tr %s", first); + mandoc_msg(MANDOCERR_TR_ODD, ln, + (int)(first - buf->buf), "tr %s", first); second = " "; p--; } @@ -3502,7 +3654,7 @@ roff_return(ROFF_ARGS) if (r->mstackpos >= 0) return ROFF_IGN | ROFF_USERRET; - mandoc_msg(MANDOCERR_REQ_NOMAC, r->parse, ln, ppos, "return"); + mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "return"); return ROFF_IGN; } @@ -3565,17 +3717,17 @@ roff_shift(ROFF_ARGS) levels = 1; if (buf->buf[pos] != '\0' && roff_evalnum(r, ln, buf->buf, &pos, &levels, 0) == 0) { - mandoc_vmsg(MANDOCERR_CE_NONUM, r->parse, + mandoc_msg(MANDOCERR_CE_NONUM, ln, pos, "shift %s", buf->buf + pos); levels = 1; } if (r->mstackpos < 0) { - mandoc_msg(MANDOCERR_REQ_NOMAC, r->parse, ln, ppos, "shift"); + mandoc_msg(MANDOCERR_REQ_NOMAC, ln, ppos, "shift"); return ROFF_IGN; } ctx = r->mstack + r->mstackpos; if (levels > ctx->argc) { - mandoc_vmsg(MANDOCERR_SHIFT, r->parse, + mandoc_msg(MANDOCERR_SHIFT, ln, pos, "%d, but max is %d", levels, ctx->argc); levels = ctx->argc; } @@ -3595,7 +3747,7 @@ roff_so(ROFF_ARGS) char *name, *cp; name = buf->buf + pos; - mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name); + mandoc_msg(MANDOCERR_SO, ln, ppos, "so %s", name); /* * Handle `so'. Be EXTREMELY careful, as we shouldn't be @@ -3605,8 +3757,7 @@ roff_so(ROFF_ARGS) */ if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) { - mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos, - ".so %s", name); + mandoc_msg(MANDOCERR_SO_PATH, ln, ppos, ".so %s", name); buf->sz = mandoc_asprintf(&cp, ".sp\nSee the file %s.\n.sp", name) + 1; free(buf->buf); @@ -3652,7 +3803,7 @@ roff_userdef(ROFF_ARGS) ctx->argv = mandoc_reallocarray(ctx->argv, ctx->argsz, sizeof(*ctx->argv)); } - arg = mandoc_getarg(r->parse, &src, ln, &pos); + arg = roff_getarg(r, &src, ln, &pos); sz = 1; /* For the terminating NUL. */ for (ap = arg; *ap != '\0'; ap++) sz += *ap == '"' ? 4 : 1; @@ -3665,6 +3816,7 @@ roff_userdef(ROFF_ARGS) *dst++ = *ap; } *dst = '\0'; + free(arg); } /* Replace the macro invocation by the macro definition. */ @@ -3719,7 +3871,7 @@ roff_getname(struct roff *r, char **cpp, int ln, int p cp++; if ('\\' == *cp) continue; - mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos, + mandoc_msg(MANDOCERR_NAMESC, ln, pos, "%.*s", (int)(cp - name + 1), name); mandoc_escape((const char **)&cp, NULL, NULL); break; @@ -4014,7 +4166,7 @@ roff_strdup(const struct roff *r, const char *p) /* * We bail out on bad escapes. * No need to warn: we already did so when - * roff_res() was called. + * roff_expand() was called. */ sz = (int)(p - pp); res = mandoc_realloc(res, ssz + sz + 1);