=================================================================== RCS file: /cvs/mandoc/roff.c,v retrieving revision 1.359 retrieving revision 1.364 diff -u -p -r1.359 -r1.364 --- mandoc/roff.c 2018/12/31 08:18:12 1.359 +++ mandoc/roff.c 2019/04/21 22:48:58 1.364 @@ -1,7 +1,7 @@ -/* $Id: roff.c,v 1.359 2018/12/31 08:18:12 schwarze Exp $ */ +/* $Id: roff.c,v 1.364 2019/04/21 22:48:58 schwarze Exp $ */ /* * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons - * Copyright (c) 2010-2015, 2017, 2018 Ingo Schwarze + * Copyright (c) 2010-2015, 2017-2019 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -133,15 +133,18 @@ struct roff { char escape; /* escape character */ }; +/* + * A macro definition, condition, or ignored block. + */ struct roffnode { enum roff_tok tok; /* type of node */ struct roffnode *parent; /* up one in stack */ int line; /* parse line */ int col; /* parse col */ char *name; /* node name, e.g. macro name */ - char *end; /* end-rules: custom token */ - int endspan; /* end-rules: next-line or infty */ - int rule; /* current evaluation rule */ + char *end; /* custom end macro of the block */ + int endspan; /* scope to: 1=eol 2=next line -1=\} */ + int rule; /* content is: 1=evaluated 0=skipped */ }; #define ROFF_ARGS struct roff *r, /* parse ctx */ \ @@ -181,6 +184,7 @@ static int roff_als(ROFF_ARGS); static int roff_block(ROFF_ARGS); static int roff_block_text(ROFF_ARGS); static int roff_block_sub(ROFF_ARGS); +static int roff_break(ROFF_ARGS); static int roff_cblock(ROFF_ARGS); static int roff_cc(ROFF_ARGS); static int roff_ccond(struct roff *, int, int); @@ -400,7 +404,7 @@ static struct roffmac roffs[TOKEN_NONE] = { { roff_unsupp, NULL, NULL, 0 }, /* boxa */ { roff_line_ignore, NULL, NULL, 0 }, /* bp */ { roff_unsupp, NULL, NULL, 0 }, /* BP */ - { roff_unsupp, NULL, NULL, 0 }, /* break */ + { roff_break, NULL, NULL, 0 }, /* break */ { roff_line_ignore, NULL, NULL, 0 }, /* breakchar */ { roff_line_ignore, NULL, NULL, 0 }, /* brnl */ { roff_noarg, NULL, NULL, 0 }, /* brp */ @@ -685,7 +689,7 @@ roffhash_find(struct ohash *htab, const char *name, si /* * Pop the current node off of the stack of roff instructions currently - * pending. + * pending. Return 1 if it is a loop or 0 otherwise. */ static int roffnode_pop(struct roff *r) @@ -889,7 +893,7 @@ roff_node_alloc(struct roff_man *man, int line, int po n->flags |= NODE_SYNPRETTY; else n->flags &= ~NODE_SYNPRETTY; - if (man->flags & ROFF_NOFILL) + if ((man->flags & (ROFF_NOFILL | ROFF_NONOFILL)) == ROFF_NOFILL) n->flags |= NODE_NOFILL; else n->flags &= ~NODE_NOFILL; @@ -2002,6 +2006,10 @@ roff_cblock(ROFF_ARGS) } +/* + * Pop all nodes ending at the end of the current input line. + * Return the number of loops ended. + */ static int roffnode_cleanscope(struct roff *r) { @@ -2016,6 +2024,11 @@ roffnode_cleanscope(struct roff *r) return inloop; } +/* + * Handle the closing \} of a conditional block. + * Apart from generating warnings, this only pops nodes. + * Return the number of loops ended. + */ static int roff_ccond(struct roff *r, int ln, int ppos) { @@ -2235,6 +2248,7 @@ roff_block_text(ROFF_ARGS) static int roff_cond_sub(ROFF_ARGS) { + struct roffnode *bl; char *ep; int endloop, irc, rr; enum roff_tok t; @@ -2282,9 +2296,21 @@ roff_cond_sub(ROFF_ARGS) */ t = roff_parse(r, buf->buf, &pos, ln, ppos); - irc |= t != TOKEN_NONE && (rr || roffs[t].flags & ROFFMAC_STRUCT) ? - (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs) : - rr ? ROFF_CONT : ROFF_IGN; + if (t == ROFF_break) { + if (irc & ROFF_LOOPMASK) + irc = ROFF_IGN | ROFF_LOOPEXIT; + else if (rr) { + for (bl = r->last; bl != NULL; bl = bl->parent) { + bl->rule = 0; + if (bl->tok == ROFF_while) + break; + } + } + } else if (t != TOKEN_NONE && + (rr || roffs[t].flags & ROFFMAC_STRUCT)) + irc |= (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs); + else + irc |= rr ? ROFF_CONT : ROFF_IGN; return irc; } @@ -2537,7 +2563,7 @@ roff_evalcond(struct roff *r, int ln, char *v, int *po roff_getstrn(r, name, sz, &deftype); istrue = !!deftype; } - *pos = cp - v; + *pos = (name + sz) - v; return istrue == wanttrue; default: break; @@ -2683,8 +2709,15 @@ roff_ds(ROFF_ARGS) return ROFF_IGN; namesz = roff_getname(r, &string, ln, pos); - if (name[namesz] == '\\') + switch (name[namesz]) { + case '\\': return ROFF_IGN; + case '\t': + string = buf->buf + pos + namesz; + break; + default: + break; + } /* Read past the initial double-quote, if any. */ if (*string == '"') @@ -3060,7 +3093,7 @@ roff_nr(ROFF_ARGS) return ROFF_IGN; keysz = roff_getname(r, &val, ln, pos); - if (key[keysz] == '\\') + if (key[keysz] == '\\' || key[keysz] == '\t') return ROFF_IGN; sign = *val; @@ -3124,7 +3157,7 @@ roff_rm(ROFF_ARGS) namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf)); roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0); roff_setstrn(&r->rentab, name, namesz, NULL, 0, 0); - if (name[namesz] == '\\') + if (name[namesz] == '\\' || name[namesz] == '\t') break; } return ROFF_IGN; @@ -3192,6 +3225,7 @@ roff_Dd(ROFF_ARGS) static int roff_TE(ROFF_ARGS) { + r->man->flags &= ~ROFF_NONOFILL; if (r->tbl == NULL) { mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, ppos, "TE"); return ROFF_IGN; @@ -3336,6 +3370,7 @@ roff_TS(ROFF_ARGS) mandoc_msg(MANDOCERR_BLK_BROKEN, ln, ppos, "TS breaks TS"); tbl_end(r->tbl, 0); } + r->man->flags |= ROFF_NONOFILL; r->tbl = tbl_alloc(ppos, ln, r->last_tbl); if (r->last_tbl == NULL) r->first_tbl = r->tbl; @@ -3457,7 +3492,7 @@ roff_als(ROFF_ARGS) return ROFF_IGN; newsz = roff_getname(r, &oldn, ln, pos); - if (newn[newsz] == '\\' || *oldn == '\0') + if (newn[newsz] == '\\' || newn[newsz] == '\t' || *oldn == '\0') return ROFF_IGN; end = oldn; @@ -3473,7 +3508,18 @@ roff_als(ROFF_ARGS) return ROFF_IGN; } +/* + * The .break request only makes sense inside conditionals, + * and that case is already handled in roff_cond_sub(). + */ static int +roff_break(ROFF_ARGS) +{ + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, pos, "break"); + return ROFF_IGN; +} + +static int roff_cc(ROFF_ARGS) { const char *p; @@ -3687,7 +3733,7 @@ roff_rn(ROFF_ARGS) return ROFF_IGN; oldsz = roff_getname(r, &newn, ln, pos); - if (oldn[oldsz] == '\\' || *newn == '\0') + if (oldn[oldsz] == '\\' || oldn[oldsz] == '\t' || *newn == '\0') return ROFF_IGN; end = newn; @@ -3863,6 +3909,10 @@ roff_renamed(ROFF_ARGS) return ROFF_CONT; } +/* + * Measure the length in bytes of the roff identifier at *cpp + * and advance the pointer to the next word. + */ static size_t roff_getname(struct roff *r, char **cpp, int ln, int pos) { @@ -3870,22 +3920,24 @@ roff_getname(struct roff *r, char **cpp, int ln, int p size_t namesz; name = *cpp; - if ('\0' == *name) + if (*name == '\0') return 0; - /* Read until end of name and terminate it with NUL. */ + /* Advance cp to the byte after the end of the name. */ + for (cp = name; 1; cp++) { - if ('\0' == *cp || ' ' == *cp) { - namesz = cp - name; + namesz = cp - name; + if (*cp == '\0') break; + if (*cp == ' ' || *cp == '\t') { + cp++; + break; } - if ('\\' != *cp) + if (*cp != '\\') continue; - namesz = cp - name; - if ('{' == cp[1] || '}' == cp[1]) + if (cp[1] == '{' || cp[1] == '}') break; - cp++; - if ('\\' == *cp) + if (*++cp == '\\') continue; mandoc_msg(MANDOCERR_NAMESC, ln, pos, "%.*s", (int)(cp - name + 1), name); @@ -3894,7 +3946,8 @@ roff_getname(struct roff *r, char **cpp, int ln, int p } /* Read past spaces. */ - while (' ' == *cp) + + while (*cp == ' ') cp++; *cpp = cp;