From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from localhost (fantadrom.bsd.lv [local]) by fantadrom.bsd.lv (OpenSMTPD) with ESMTPA id d975e8d0 for ; Fri, 24 Aug 2018 18:13:07 -0500 (EST) Date: Fri, 24 Aug 2018 18:13:07 -0500 (EST) X-Mailinglist: mandoc-source Reply-To: source@mandoc.bsd.lv MIME-Version: 1.0 From: schwarze@mandoc.bsd.lv To: source@mandoc.bsd.lv Subject: mandoc: Rudimentary implementation of the roff(7) .while request. X-Mailer: activitymail 1.26, http://search.cpan.org/dist/activitymail/ Content-Type: text/plain; charset=utf-8 Message-Id: Log Message: ----------- Rudimentary implementation of the roff(7) .while request. Needed for example by groff_hdtbl(7). There are two limitations: It does not support nested .while requests yet, and each .while loop must start and end in the same scope. The roff_parseln() return codes are now more flexible and allow OR'ing options. Modified Files: -------------- mandoc: TODO libmandoc.h mandoc.h mandoc_headers.3 read.c roff.7 roff.c tbl.3 mandoc/regress/roff: Makefile Added Files: ----------- mandoc/regress/roff/while: Makefile badargs.in badargs.out_ascii badargs.out_lint basic.in basic.out_ascii into.in into.out_ascii into.out_lint nesting.in nesting.out_ascii nesting.out_lint outof.in outof.out_ascii outof.out_lint Revision Data ------------- Index: read.c =================================================================== RCS file: /home/cvs/mandoc/mandoc/read.c,v retrieving revision 1.198 retrieving revision 1.199 diff -Lread.c -Lread.c -u -p -r1.198 -r1.199 --- read.c +++ read.c @@ -49,6 +49,7 @@ struct mparse { const char *file; /* filename of current input file */ struct buf *primary; /* buffer currently being parsed */ struct buf *secondary; /* copy of top level input */ + struct buf *loop; /* open .while request line */ const char *os_s; /* default operating system */ mandocmsg mmsg; /* warning/error message handler */ enum mandoclevel file_status; /* status of current parse */ @@ -63,7 +64,7 @@ struct mparse { static void choose_parser(struct mparse *); static void free_buf_list(struct buf *); static void resize_buf(struct buf *, size_t); -static enum rofferr mparse_buf_r(struct mparse *, struct buf, size_t, int); +static int mparse_buf_r(struct mparse *, struct buf, size_t, int); static int read_whole_file(struct mparse *, const char *, int, struct buf *, int *); static void mparse_end(struct mparse *); @@ -266,6 +267,10 @@ static const char * const mandocerrs[MAN "input too large", "unsupported control character", "unsupported roff request", + "nested .while loops", + "end of scope with open .while loop", + "end of .while loop in inner scope", + "cannot continue this .while loop", "eqn delim option in tbl", "unsupported tbl layout modifier", "ignoring macro in table", @@ -356,32 +361,31 @@ choose_parser(struct mparse *curp) * macros, inline equations, and input line traps) * and indirectly (for .so file inclusion). */ -static enum rofferr +static int mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) { struct buf ln; - struct buf *firstln, *lastln, *thisln; + struct buf *firstln, *lastln, *thisln, *loop; const char *save_file; char *cp; size_t pos; /* byte number in the ln buffer */ - enum rofferr line_result, result; + int line_result, result; int of; int lnn; /* line number in the real file */ int fd; + int inloop; /* Saw .while on this level. */ unsigned char c; ln.sz = 256; ln.buf = mandoc_malloc(ln.sz); ln.next = NULL; - firstln = NULL; + firstln = loop = NULL; lnn = curp->line; pos = 0; + inloop = 0; result = ROFF_CONT; - while (i < blk.sz) { - if (0 == pos && '\0' == blk.buf[i]) - break; - + while (i < blk.sz && (blk.buf[i] != '\0' || pos != 0)) { if (start) { curp->line = lnn; curp->reparse_count = 0; @@ -490,41 +494,95 @@ mparse_buf_r(struct mparse *curp, struct rerun: line_result = roff_parseln(curp->roff, curp->line, &ln, &of); - switch (line_result) { + /* Process options. */ + + if (line_result & ROFF_APPEND) + assert(line_result == (ROFF_IGN | ROFF_APPEND)); + + if (line_result & ROFF_USERCALL) + assert((line_result & ROFF_MASK) == ROFF_REPARSE); + + if (line_result & ROFF_USERRET) { + assert(line_result == (ROFF_IGN | ROFF_USERRET)); + if (start == 0) { + /* Return from the current macro. */ + result = ROFF_USERRET; + goto out; + } + } + + switch (line_result & ROFF_LOOPMASK) { + case ROFF_IGN: + break; + case ROFF_WHILE: + if (curp->loop != NULL) { + if (loop == curp->loop) + break; + mandoc_msg(MANDOCERR_WHILE_NEST, + curp, curp->line, pos, NULL); + } + curp->loop = thisln; + loop = NULL; + inloop = 1; + break; + case ROFF_LOOPCONT: + case ROFF_LOOPEXIT: + if (curp->loop == NULL) { + mandoc_msg(MANDOCERR_WHILE_FAIL, + curp, curp->line, pos, NULL); + break; + } + if (inloop == 0) { + mandoc_msg(MANDOCERR_WHILE_INTO, + curp, curp->line, pos, NULL); + curp->loop = loop = NULL; + break; + } + if (line_result & ROFF_LOOPCONT) + loop = curp->loop; + else { + curp->loop = loop = NULL; + inloop = 0; + } + break; + default: + abort(); + } + + /* Process the main instruction from the roff parser. */ + + switch (line_result & ROFF_MASK) { + case ROFF_IGN: + break; + case ROFF_CONT: + if (curp->man->macroset == MACROSET_NONE) + choose_parser(curp); + if ((curp->man->macroset == MACROSET_MDOC ? + mdoc_parseln(curp->man, curp->line, ln.buf, of) : + man_parseln(curp->man, curp->line, ln.buf, of) + ) == 2) + goto out; + break; + case ROFF_RERUN: + goto rerun; case ROFF_REPARSE: - case ROFF_USERCALL: if (++curp->reparse_count > REPARSE_LIMIT) { + /* Abort and return to the top level. */ result = ROFF_IGN; mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, pos, NULL); - } else { - result = mparse_buf_r(curp, ln, of, 0); - if (line_result == ROFF_USERCALL) { - if (result == ROFF_USERRET) - result = ROFF_CONT; - roff_userret(curp->roff); - } - if (start || result == ROFF_CONT) { - pos = 0; - continue; - } + goto out; } - goto out; - case ROFF_USERRET: - if (start) { - pos = 0; - continue; + result = mparse_buf_r(curp, ln, of, 0); + if (line_result & ROFF_USERCALL) { + roff_userret(curp->roff); + /* Continue normally. */ + if (result & ROFF_USERRET) + result = ROFF_CONT; } - result = ROFF_USERRET; - goto out; - case ROFF_APPEND: - pos = strlen(ln.buf); - continue; - case ROFF_RERUN: - goto rerun; - case ROFF_IGN: - pos = 0; - continue; + if (start == 0 && result != ROFF_CONT) + goto out; + break; case ROFF_SO: if ( ! (curp->options & MPARSE_SO) && (i >= blk.sz || blk.buf[i] == '\0')) { @@ -549,30 +607,36 @@ rerun: of = 0; mparse_buf_r(curp, ln, of, 0); } - pos = 0; - continue; - default: break; + default: + abort(); } - if (curp->man->macroset == MACROSET_NONE) - choose_parser(curp); - - if ((curp->man->macroset == MACROSET_MDOC ? - mdoc_parseln(curp->man, curp->line, ln.buf, of) : - man_parseln(curp->man, curp->line, ln.buf, of)) == 2) - break; - - /* Temporary buffers typically are not full. */ - - if (0 == start && '\0' == blk.buf[i]) - break; - /* Start the next input line. */ - pos = 0; + if (loop != NULL && + (line_result & ROFF_LOOPMASK) == ROFF_IGN) + loop = loop->next; + + if (loop != NULL) { + if ((line_result & ROFF_APPEND) == 0) + *ln.buf = '\0'; + if (ln.sz < loop->sz) + resize_buf(&ln, loop->sz); + (void)strlcat(ln.buf, loop->buf, ln.sz); + of = 0; + goto rerun; + } + + pos = (line_result & ROFF_APPEND) ? strlen(ln.buf) : 0; } out: + if (inloop) { + if (result != ROFF_USERRET) + mandoc_msg(MANDOCERR_WHILE_OUTOF, curp, + curp->line, pos, NULL); + curp->loop = NULL; + } free(ln.buf); if (firstln != curp->secondary) free_buf_list(firstln); Index: roff.c =================================================================== RCS file: /home/cvs/mandoc/mandoc/roff.c,v retrieving revision 1.339 retrieving revision 1.340 diff -Lroff.c -Lroff.c -u -p -r1.339 -r1.340 --- roff.c +++ roff.c @@ -143,7 +143,7 @@ struct roffnode { int pos, /* current pos in buffer */ \ int *offs /* reset offset of buffer data */ -typedef enum rofferr (*roffproc)(ROFF_ARGS); +typedef int (*roffproc)(ROFF_ARGS); struct roffmac { roffproc proc; /* process new macro */ @@ -163,26 +163,26 @@ struct predef { /* --- function prototypes ------------------------------------------------ */ -static void roffnode_cleanscope(struct roff *); -static void roffnode_pop(struct roff *); +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 enum rofferr roff_als(ROFF_ARGS); -static enum rofferr roff_block(ROFF_ARGS); -static enum rofferr roff_block_text(ROFF_ARGS); -static enum rofferr roff_block_sub(ROFF_ARGS); -static enum rofferr roff_br(ROFF_ARGS); -static enum rofferr roff_cblock(ROFF_ARGS); -static enum rofferr roff_cc(ROFF_ARGS); -static void roff_ccond(struct roff *, int, int); -static enum rofferr roff_cond(ROFF_ARGS); -static enum rofferr roff_cond_text(ROFF_ARGS); -static enum rofferr roff_cond_sub(ROFF_ARGS); -static enum rofferr roff_ds(ROFF_ARGS); -static enum rofferr roff_ec(ROFF_ARGS); -static enum rofferr roff_eo(ROFF_ARGS); -static enum rofferr roff_eqndelim(struct roff *, struct buf *, int); +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_br(ROFF_ARGS); +static int roff_cblock(ROFF_ARGS); +static int roff_cc(ROFF_ARGS); +static int roff_ccond(struct roff *, int, int); +static int roff_cond(ROFF_ARGS); +static int roff_cond_text(ROFF_ARGS); +static int roff_cond_sub(ROFF_ARGS); +static int roff_ds(ROFF_ARGS); +static int roff_ec(ROFF_ARGS); +static int roff_eo(ROFF_ARGS); +static int roff_eqndelim(struct roff *, struct buf *, int); static int roff_evalcond(struct roff *r, int, char *, int *); static int roff_evalnum(struct roff *, int, const char *, int *, int *, int); @@ -203,42 +203,42 @@ static const char *roff_getstrn(struct r const char *, size_t, int *); static int roff_hasregn(const struct roff *, const char *, size_t); -static enum rofferr roff_insec(ROFF_ARGS); -static enum rofferr roff_it(ROFF_ARGS); -static enum rofferr roff_line_ignore(ROFF_ARGS); +static int roff_insec(ROFF_ARGS); +static int roff_it(ROFF_ARGS); +static int roff_line_ignore(ROFF_ARGS); static void roff_man_alloc1(struct roff_man *); static void roff_man_free1(struct roff_man *); -static enum rofferr roff_manyarg(ROFF_ARGS); -static enum rofferr roff_nop(ROFF_ARGS); -static enum rofferr roff_nr(ROFF_ARGS); -static enum rofferr roff_onearg(ROFF_ARGS); +static int roff_manyarg(ROFF_ARGS); +static int roff_nop(ROFF_ARGS); +static int roff_nr(ROFF_ARGS); +static int roff_onearg(ROFF_ARGS); static enum roff_tok roff_parse(struct roff *, char *, int *, int, int); -static enum rofferr roff_parsetext(struct roff *, struct buf *, +static int roff_parsetext(struct roff *, struct buf *, int, int *); -static enum rofferr roff_renamed(ROFF_ARGS); -static enum rofferr roff_res(struct roff *, struct buf *, int, int); -static enum rofferr roff_return(ROFF_ARGS); -static enum rofferr roff_rm(ROFF_ARGS); -static enum rofferr roff_rn(ROFF_ARGS); -static enum rofferr roff_rr(ROFF_ARGS); +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); +static int roff_rr(ROFF_ARGS); static void roff_setregn(struct roff *, const char *, size_t, int, char, int); static void roff_setstr(struct roff *, const char *, const char *, int); static void roff_setstrn(struct roffkv **, const char *, size_t, const char *, size_t, int); -static enum rofferr roff_shift(ROFF_ARGS); -static enum rofferr roff_so(ROFF_ARGS); -static enum rofferr roff_tr(ROFF_ARGS); -static enum rofferr roff_Dd(ROFF_ARGS); -static enum rofferr roff_TE(ROFF_ARGS); -static enum rofferr roff_TS(ROFF_ARGS); -static enum rofferr roff_EQ(ROFF_ARGS); -static enum rofferr roff_EN(ROFF_ARGS); -static enum rofferr roff_T_(ROFF_ARGS); -static enum rofferr roff_unsupp(ROFF_ARGS); -static enum rofferr roff_userdef(ROFF_ARGS); +static int roff_shift(ROFF_ARGS); +static int roff_so(ROFF_ARGS); +static int roff_tr(ROFF_ARGS); +static int roff_Dd(ROFF_ARGS); +static int roff_TE(ROFF_ARGS); +static int roff_TS(ROFF_ARGS); +static int roff_EQ(ROFF_ARGS); +static int roff_EN(ROFF_ARGS); +static int roff_T_(ROFF_ARGS); +static int roff_unsupp(ROFF_ARGS); +static int roff_userdef(ROFF_ARGS); /* --- constant data ------------------------------------------------------ */ @@ -590,7 +590,7 @@ static struct roffmac roffs[TOKEN_NONE] { roff_line_ignore, NULL, NULL, 0 }, /* watchlength */ { roff_line_ignore, NULL, NULL, 0 }, /* watchn */ { roff_unsupp, NULL, NULL, 0 }, /* wh */ - { roff_unsupp, NULL, NULL, 0 }, /* while */ + { roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT }, /*while*/ { roff_insec, NULL, NULL, 0 }, /* write */ { roff_insec, NULL, NULL, 0 }, /* writec */ { roff_insec, NULL, NULL, 0 }, /* writem */ @@ -674,18 +674,19 @@ roffhash_find(struct ohash *htab, const * Pop the current node off of the stack of roff instructions currently * pending. */ -static void +static int roffnode_pop(struct roff *r) { struct roffnode *p; + int inloop; - assert(r->last); p = r->last; - - r->last = r->last->parent; + inloop = p->tok == ROFF_while; + r->last = p->parent; free(p->name); free(p->end); free(p); + return inloop; } /* @@ -1144,7 +1145,7 @@ deroff(char **dest, const struct roff_no * used in numerical expressions and conditional requests. * Also check the syntax of the remaining escape sequences. */ -static enum rofferr +static int roff_res(struct roff *r, struct buf *buf, int ln, int pos) { struct mctx *ctx; /* current macro call context */ @@ -1236,7 +1237,7 @@ roff_res(struct roff *r, struct buf *buf if (stesc[1] == '#') { *stesc = '\0'; - return ROFF_APPEND; + return ROFF_IGN | ROFF_APPEND; } /* Discard normal comments. */ @@ -1294,7 +1295,7 @@ roff_res(struct roff *r, struct buf *buf if (done) continue; else - return ROFF_APPEND; + return ROFF_IGN | ROFF_APPEND; } /* Decide whether to expand or to check only. */ @@ -1529,7 +1530,7 @@ roff_res(struct roff *r, struct buf *buf /* * Process text streams. */ -static enum rofferr +static int roff_parsetext(struct roff *r, struct buf *buf, int pos, int *offs) { size_t sz; @@ -1595,11 +1596,11 @@ roff_parsetext(struct roff *r, struct bu return ROFF_CONT; } -enum rofferr +int roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs) { enum roff_tok t; - enum rofferr e; + int e; int pos; /* parse point */ int spos; /* saved parse point for messages */ int ppos; /* original offset in buf->buf */ @@ -1621,7 +1622,7 @@ roff_parseln(struct roff *r, int ln, str /* Expand some escape sequences. */ e = roff_res(r, buf, ln, pos); - if (e == ROFF_IGN || e == ROFF_APPEND) + if ((e & ROFF_MASK) == ROFF_IGN) return e; assert(e == ROFF_CONT); @@ -1638,21 +1639,22 @@ roff_parseln(struct roff *r, int ln, str if (r->last != NULL && ! ctl) { t = r->last->tok; e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs); - if (e == ROFF_IGN) + if ((e & ROFF_MASK) == ROFF_IGN) return e; - assert(e == ROFF_CONT); - } + e &= ~ROFF_MASK; + } else + e = ROFF_IGN; if (r->eqn != NULL && strncmp(buf->buf + ppos, ".EN", 3)) { eqn_read(r->eqn, buf->buf + ppos); - return ROFF_IGN; + return e; } if (r->tbl != NULL && (ctl == 0 || buf->buf[pos] == '\0')) { tbl_read(r->tbl, ln, buf->buf, ppos); roff_addtbl(r->man, r->tbl); - return ROFF_IGN; + return e; } if ( ! ctl) - return roff_parsetext(r, buf, pos, offs); + return roff_parsetext(r, buf, pos, offs) | e; /* Skip empty request lines. */ @@ -1808,7 +1810,7 @@ roff_parse(struct roff *r, char *buf, in /* --- handling of request blocks ----------------------------------------- */ -static enum rofferr +static int roff_cblock(ROFF_ARGS) { @@ -1848,50 +1850,51 @@ roff_cblock(ROFF_ARGS) } -static void +static int roffnode_cleanscope(struct roff *r) { + int inloop; - while (r->last) { + inloop = 0; + while (r->last != NULL) { if (--r->last->endspan != 0) break; - roffnode_pop(r); + inloop += roffnode_pop(r); } + return inloop; } -static void +static int roff_ccond(struct roff *r, int ln, int ppos) { - if (NULL == r->last) { mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "\\}"); - return; + return 0; } switch (r->last->tok) { case ROFF_el: case ROFF_ie: case ROFF_if: + case ROFF_while: break; default: mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "\\}"); - return; + return 0; } if (r->last->endspan > -1) { mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "\\}"); - return; + return 0; } - roffnode_pop(r); - roffnode_cleanscope(r); - return; + return roffnode_pop(r) + roffnode_cleanscope(r); } -static enum rofferr +static int roff_block(ROFF_ARGS) { const char *name, *value; @@ -2016,7 +2019,7 @@ roff_block(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_block_sub(ROFF_ARGS) { enum roff_tok t; @@ -2070,7 +2073,7 @@ roff_block_sub(ROFF_ARGS) return (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs); } -static enum rofferr +static int roff_block_text(ROFF_ARGS) { @@ -2080,15 +2083,19 @@ roff_block_text(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_cond_sub(ROFF_ARGS) { - enum roff_tok t; char *ep; - int rr; + int endloop, irc, rr; + enum roff_tok t; + irc = ROFF_IGN; rr = r->last->rule; - roffnode_cleanscope(r); + endloop = tok != ROFF_while ? ROFF_IGN : + rr ? ROFF_LOOPCONT : ROFF_LOOPEXIT; + if (roffnode_cleanscope(r)) + irc |= endloop; /* * If `\}' occurs on a macro line without a preceding macro, @@ -2105,7 +2112,8 @@ roff_cond_sub(ROFF_ARGS) switch (ep[1]) { case '}': memmove(ep, ep + 2, strlen(ep + 2) + 1); - roff_ccond(r, ln, ep - buf->buf); + if (roff_ccond(r, ln, ep - buf->buf)) + irc |= endloop; break; case '\0': ++ep; @@ -2122,30 +2130,38 @@ roff_cond_sub(ROFF_ARGS) */ t = roff_parse(r, buf->buf, &pos, ln, ppos); - return t != TOKEN_NONE && (rr || roffs[t].flags & ROFFMAC_STRUCT) - ? (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs) : rr - ? ROFF_CONT : ROFF_IGN; + 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; + return irc; } -static enum rofferr +static int roff_cond_text(ROFF_ARGS) { char *ep; - int rr; + int endloop, irc, rr; + irc = ROFF_IGN; rr = r->last->rule; - roffnode_cleanscope(r); + endloop = tok != ROFF_while ? ROFF_IGN : + rr ? ROFF_LOOPCONT : ROFF_LOOPEXIT; + if (roffnode_cleanscope(r)) + irc |= endloop; ep = buf->buf + pos; while ((ep = strchr(ep, '\\')) != NULL) { if (*(++ep) == '}') { *ep = '&'; - roff_ccond(r, ln, ep - buf->buf - 1); + if (roff_ccond(r, ln, ep - buf->buf - 1)) + irc |= endloop; } if (*ep != '\0') ++ep; } - return rr ? ROFF_CONT : ROFF_IGN; + if (rr) + irc |= ROFF_CONT; + return irc; } /* --- handling of numeric and conditional expressions -------------------- */ @@ -2365,14 +2381,14 @@ roff_evalcond(struct roff *r, int ln, ch return 0; } -static enum rofferr +static int roff_line_ignore(ROFF_ARGS) { return ROFF_IGN; } -static enum rofferr +static int roff_insec(ROFF_ARGS) { @@ -2381,7 +2397,7 @@ roff_insec(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_unsupp(ROFF_ARGS) { @@ -2390,9 +2406,10 @@ roff_unsupp(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_cond(ROFF_ARGS) { + int irc; roffnode_push(r, tok, NULL, ln, ppos); @@ -2431,9 +2448,10 @@ roff_cond(ROFF_ARGS) * Determine scope. * If there is nothing on the line after the conditional, * not even whitespace, use next-line scope. + * Except that .while does not support next-line scope. */ - if (buf->buf[pos] == '\0') { + if (buf->buf[pos] == '\0' && tok != ROFF_while) { r->last->endspan = 2; goto out; } @@ -2465,10 +2483,13 @@ roff_cond(ROFF_ARGS) out: *offs = pos; - return ROFF_RERUN; + irc = ROFF_RERUN; + if (tok == ROFF_while) + irc |= ROFF_WHILE; + return irc; } -static enum rofferr +static int roff_ds(ROFF_ARGS) { char *string; @@ -2857,7 +2878,7 @@ roff_freereg(struct roffreg *reg) } } -static enum rofferr +static int roff_nr(ROFF_ARGS) { char *key, *val, *step; @@ -2891,7 +2912,7 @@ roff_nr(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_rr(ROFF_ARGS) { struct roffreg *reg, **prev; @@ -2921,7 +2942,7 @@ roff_rr(ROFF_ARGS) /* --- handler functions for roff requests -------------------------------- */ -static enum rofferr +static int roff_rm(ROFF_ARGS) { const char *name; @@ -2940,7 +2961,7 @@ roff_rm(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_it(ROFF_ARGS) { int iv; @@ -2969,7 +2990,7 @@ roff_it(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_Dd(ROFF_ARGS) { int mask; @@ -2999,7 +3020,7 @@ roff_Dd(ROFF_ARGS) return ROFF_CONT; } -static enum rofferr +static int roff_TE(ROFF_ARGS) { if (r->tbl == NULL) { @@ -3019,7 +3040,7 @@ roff_TE(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_T_(ROFF_ARGS) { @@ -3035,7 +3056,7 @@ roff_T_(ROFF_ARGS) /* * Handle in-line equation delimiters. */ -static enum rofferr +static int roff_eqndelim(struct roff *r, struct buf *buf, int pos) { char *cp1, *cp2; @@ -3098,7 +3119,7 @@ roff_eqndelim(struct roff *r, struct buf return ROFF_REPARSE; } -static enum rofferr +static int roff_EQ(ROFF_ARGS) { struct roff_node *n; @@ -3128,7 +3149,7 @@ roff_EQ(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_EN(ROFF_ARGS) { if (r->eqn != NULL) { @@ -3142,7 +3163,7 @@ roff_EN(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_TS(ROFF_ARGS) { if (r->tbl != NULL) { @@ -3159,7 +3180,7 @@ roff_TS(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_onearg(ROFF_ARGS) { struct roff_node *n; @@ -3219,7 +3240,7 @@ roff_onearg(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_manyarg(ROFF_ARGS) { struct roff_node *n; @@ -3242,7 +3263,7 @@ roff_manyarg(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_als(ROFF_ARGS) { char *oldn, *newn, *end, *value; @@ -3269,7 +3290,7 @@ roff_als(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_br(ROFF_ARGS) { if (r->man->flags & (MAN_BLINE | MAN_ELINE)) @@ -3283,7 +3304,7 @@ roff_br(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_cc(ROFF_ARGS) { const char *p; @@ -3300,7 +3321,7 @@ roff_cc(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_ec(ROFF_ARGS) { const char *p; @@ -3317,7 +3338,7 @@ roff_ec(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_eo(ROFF_ARGS) { r->escape = '\0'; @@ -3327,7 +3348,7 @@ roff_eo(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_nop(ROFF_ARGS) { while (buf->buf[pos] == ' ') @@ -3336,7 +3357,7 @@ roff_nop(ROFF_ARGS) return ROFF_RERUN; } -static enum rofferr +static int roff_tr(ROFF_ARGS) { const char *p, *first, *second; @@ -3404,17 +3425,17 @@ roff_tr(ROFF_ARGS) * The read module will call that after rewinding the reader stack * to the place from where the current macro was called. */ -static enum rofferr +static int roff_return(ROFF_ARGS) { if (r->mstackpos >= 0) - return ROFF_USERRET; + return ROFF_IGN | ROFF_USERRET; mandoc_msg(MANDOCERR_REQ_NOMAC, r->parse, ln, ppos, "return"); return ROFF_IGN; } -static enum rofferr +static int roff_rn(ROFF_ARGS) { const char *value; @@ -3464,7 +3485,7 @@ roff_rn(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_shift(ROFF_ARGS) { struct mctx *ctx; @@ -3497,7 +3518,7 @@ roff_shift(ROFF_ARGS) return ROFF_IGN; } -static enum rofferr +static int roff_so(ROFF_ARGS) { char *name, *cp; @@ -3529,7 +3550,7 @@ roff_so(ROFF_ARGS) /* --- user defined strings and macros ------------------------------------ */ -static enum rofferr +static int roff_userdef(ROFF_ARGS) { struct mctx *ctx; @@ -3583,14 +3604,14 @@ roff_userdef(ROFF_ARGS) *offs = 0; return buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ? - ROFF_USERCALL : ROFF_APPEND; + ROFF_REPARSE | ROFF_USERCALL : ROFF_IGN | ROFF_APPEND; } /* * Calling a high-level macro that was renamed with .rn. * r->current_string has already been set up by roff_parse(). */ -static enum rofferr +static int roff_renamed(ROFF_ARGS) { char *nbuf; Index: mandoc.h =================================================================== RCS file: /home/cvs/mandoc/mandoc/mandoc.h,v retrieving revision 1.251 retrieving revision 1.252 diff -Lmandoc.h -Lmandoc.h -u -p -r1.251 -r1.252 --- mandoc.h +++ mandoc.h @@ -228,6 +228,10 @@ enum mandocerr { MANDOCERR_TOOLARGE, /* input too large */ MANDOCERR_CHAR_UNSUPP, /* unsupported control character: number */ MANDOCERR_REQ_UNSUPP, /* unsupported roff request: request */ + MANDOCERR_WHILE_NEST, /* nested .while loops */ + MANDOCERR_WHILE_OUTOF, /* end of scope with open .while loop */ + MANDOCERR_WHILE_INTO, /* end of .while loop in inner scope */ + MANDOCERR_WHILE_FAIL, /* cannot continue this .while loop */ MANDOCERR_TBLOPT_EQN, /* eqn delim option in tbl: arg */ MANDOCERR_TBLLAYOUT_MOD, /* unsupported tbl layout modifier: m */ MANDOCERR_TBLMACRO, /* ignoring macro in table: macro */ Index: TODO =================================================================== RCS file: /home/cvs/mandoc/mandoc/TODO,v retrieving revision 1.268 retrieving revision 1.269 diff -LTODO -LTODO -u -p -r1.268 -r1.269 --- TODO +++ TODO @@ -57,10 +57,6 @@ are mere guesses, and some may be wrong. reported by brad@ Sat, 15 Jan 2011 15:45:23 -0500 loc *** exist *** algo *** size ** imp * -- .while - found by jca@ in ratpoison(1) Sun, 30 Jun 2013 12:01:09 +0200 - loc * exist ** algo ** size ** imp ** - - \w'' improve width measurements would not be very useful without an expression parser, see below needed for Tcl_NewStringObj(3) via wiz@ Wed, 5 Mar 2014 22:27:43 +0100 Index: tbl.3 =================================================================== RCS file: /home/cvs/mandoc/mandoc/tbl.3,v retrieving revision 1.2 retrieving revision 1.3 diff -Ltbl.3 -Ltbl.3 -u -p -r1.2 -r1.3 --- tbl.3 +++ tbl.3 @@ -35,7 +35,7 @@ .Fa "int line" .Fa "struct mparse *parse" .Fc -.Ft enum rofferr +.Ft void .Fo tbl_read .Fa "struct tbl_node *tbl" .Fa "int ln" Index: libmandoc.h =================================================================== RCS file: /home/cvs/mandoc/mandoc/libmandoc.h,v retrieving revision 1.73 retrieving revision 1.74 diff -Llibmandoc.h -Llibmandoc.h -u -p -r1.73 -r1.74 --- libmandoc.h +++ libmandoc.h @@ -16,16 +16,27 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -enum rofferr { - ROFF_CONT, /* continue processing line */ - ROFF_RERUN, /* re-run roff interpreter with offset */ - ROFF_APPEND, /* re-run main parser, appending next line */ - ROFF_REPARSE, /* re-run main parser on the result */ - ROFF_USERCALL, /* dto., calling a user-defined macro */ - ROFF_USERRET, /* abort parsing of user-defined macro */ - ROFF_SO, /* include another file */ - ROFF_IGN, /* ignore current line */ -}; +/* + * Return codes passed from the roff parser to the main parser. + */ + +/* Main instruction: what to do with the returned line. */ +#define ROFF_IGN 0x000 /* Don't do anything with it. */ +#define ROFF_CONT 0x001 /* Give it to the high-level parser. */ +#define ROFF_RERUN 0x002 /* Re-run the roff parser with an offset. */ +#define ROFF_REPARSE 0x004 /* Recursively run the main parser on it. */ +#define ROFF_SO 0x008 /* Include the named file. */ +#define ROFF_MASK 0x00f /* Only one of these bits should be set. */ + +/* Options for further parsing, to be OR'ed with the above. */ +#define ROFF_APPEND 0x010 /* Append the next line to this one. */ +#define ROFF_USERCALL 0x020 /* Start execution of a new macro. */ +#define ROFF_USERRET 0x040 /* Abort execution of the current macro. */ +#define ROFF_WHILE 0x100 /* Start a new .while loop. */ +#define ROFF_LOOPCONT 0x200 /* Iterate the current .while loop. */ +#define ROFF_LOOPEXIT 0x400 /* Exit the current .while loop. */ +#define ROFF_LOOPMASK 0xf00 + struct buf { char *buf; @@ -66,7 +77,7 @@ void roff_man_free(struct roff_man *); struct roff_man *roff_man_alloc(struct roff *, struct mparse *, const char *, int); void roff_man_reset(struct roff_man *); -enum rofferr roff_parseln(struct roff *, int, struct buf *, int *); +int roff_parseln(struct roff *, int, struct buf *, int *); void roff_userret(struct roff *); void roff_endparse(struct roff *); void roff_setreg(struct roff *, const char *, int, char sign); Index: mandoc_headers.3 =================================================================== RCS file: /home/cvs/mandoc/mandoc/mandoc_headers.3,v retrieving revision 1.18 retrieving revision 1.19 diff -Lmandoc_headers.3 -Lmandoc_headers.3 -u -p -r1.18 -r1.19 --- mandoc_headers.3 +++ mandoc_headers.3 @@ -250,7 +250,6 @@ for .Vt enum mandocerr . .Pp Provides -.Vt enum rofferr , .Vt struct buf , utility functions needed by multiple parsers, and the top-level functions to call the parsers. Index: roff.7 =================================================================== RCS file: /home/cvs/mandoc/mandoc/roff.7,v retrieving revision 1.103 retrieving revision 1.104 diff -Lroff.7 -Lroff.7 -u -p -r1.103 -r1.104 --- roff.7 +++ roff.7 @@ -1202,7 +1202,7 @@ While evaluating the the unit suffixes described below .Sx Scaling Widths are ignored. -.It Ic \&it Ar expression macro +.It Ic \&itc Ar expression macro Set an input line trap, not counting lines ending with \ec. Currently unsupported. .It Ic \&IX Ar class keystring @@ -1715,8 +1715,12 @@ This is a Heirloom extension and current Set a page location trap. Currently unsupported. .It Ic \&while Ar condition body -Repeated execution while a condition is true. -Currently unsupported. +Repeated execution while a +.Ar condition +is true, with syntax similar to +.Ic \&if . +Currently implemented with two restrictions: cannot nest, +and each loop must start and end in the same scope. .It Ic \&write Oo \(dq Oc Ns Ar string Write to an open file. Ignored because insecure. @@ -2149,10 +2153,6 @@ macro control character does not suppres .It Diversions are not implemented, and support for traps is very incomplete. -.It -While recursion is supported, -.Sx \&while -loops are not. .El .Pp The special semantics of the Index: Makefile =================================================================== RCS file: /home/cvs/mandoc/mandoc/regress/roff/Makefile,v retrieving revision 1.6 retrieving revision 1.7 diff -Lregress/roff/Makefile -Lregress/roff/Makefile -u -p -r1.6 -r1.7 --- regress/roff/Makefile +++ regress/roff/Makefile @@ -1,7 +1,8 @@ -# $OpenBSD: Makefile,v 1.25 2018/08/23 14:16:12 schwarze Exp $ +# $OpenBSD: Makefile,v 1.26 2018/08/24 22:56:37 schwarze Exp $ SUBDIR = args cond esc scale string -SUBDIR += br cc de ds ft ig it ll na nr po ps return rm rn shift sp ta ti tr +SUBDIR += br cc de ds ft ig it ll na nr po ps +SUBDIR += return rm rn shift sp ta ti tr while .include "../Makefile.sub" .include --- /dev/null +++ regress/roff/while/outof.out_ascii @@ -0,0 +1,9 @@ +WHILE-OUTOF(1) General Commands Manual WHILE-OUTOF(1) + +NNAAMMEE + wwhhiillee--oouuttooff - while request starting in a macro + +DDEESSCCRRIIPPTTIIOONN + initial text 10 final text + +OpenBSD August 24, 2018 OpenBSD --- /dev/null +++ regress/roff/while/Makefile @@ -0,0 +1,13 @@ +# $OpenBSD: Makefile,v 1.1 2018/08/24 22:56:37 schwarze Exp $ + +REGRESS_TARGETS = basic badargs into nesting outof +LINT_TARGETS = badargs into nesting outof + +# mandoc defects: +# - if a while loop extends into a scope, mandoc may close it there +# - mandoc does not support nested .while loops +# - mandoc does not support .while loops extending out of the current scope + +SKIP_GROFF = into nesting outof + +.include --- /dev/null +++ regress/roff/while/basic.out_ascii @@ -0,0 +1,16 @@ +WHILE-BASIC(1) General Commands Manual WHILE-BASIC(1) + +NNAAMMEE + wwhhiillee--bbaassiicc - the while request + +DDEESSCCRRIIPPTTIIOONN + Loop with single-line scope: 10 9 8 7 6 5 4 3 2 1 0 + + Loop with multi-line scope, text line closure: 10, 9, 8, 7, 6, 5, 4, 3, + 2, 1, 0, boom. + + Loop with multi-line scope, macro line closure: 10 9 8 7 6 5 4 3 2 1 0 + + final text + +OpenBSD August 24, 2018 OpenBSD --- /dev/null +++ regress/roff/while/nesting.in @@ -0,0 +1,19 @@ +.\" $OpenBSD: nesting.in,v 1.1 2018/08/24 22:56:37 schwarze Exp $ +.Dd $Mdocdate: August 24 2018 $ +.Dt WHILE-NESTING 1 +.Os +.Sh NAME +.Nm while-nesting +.Nd nested while requests +.Sh DESCRIPTION +initial text +.nr c1 3 +.while \n(c1 \{\ +. nr c2 3 +. while \n(c2 \{\ +. nop \n(c1\n(c2 +. nr c2 -1 +. \} +. nr c1 -1 +.\} +final text --- /dev/null +++ regress/roff/while/into.out_lint @@ -0,0 +1,2 @@ +mandoc: into.in:17:5: UNSUPP: end of .while loop in inner scope +mandoc: into.in:20:1: UNSUPP: end of scope with open .while loop --- /dev/null +++ regress/roff/while/badargs.in @@ -0,0 +1,14 @@ +.\" $OpenBSD: badargs.in,v 1.1 2018/08/24 22:56:37 schwarze Exp $ +.Dd $Mdocdate: August 24 2018 $ +.Dt WHILE-BADARGS 1 +.Os +.Sh NAME +.Nm while-badargs +.Nd dubious arguments for the while request +.Sh DESCRIPTION +while does not support next line scope: +.nr cnt 2 1 +.while \n-[cnt] +\n[cnt] +.Pp +final text --- /dev/null +++ regress/roff/while/badargs.out_lint @@ -0,0 +1,3 @@ +mandoc: badargs.in:11:2: WARNING: conditional request controls empty scope: while +mandoc: badargs.in:11:9: WARNING: blank line in fill mode, using .sp +mandoc: badargs.in:11:2: WARNING: conditional request controls empty scope: while --- /dev/null +++ regress/roff/while/outof.out_lint @@ -0,0 +1,2 @@ +mandoc: outof.in:15:1: UNSUPP: end of scope with open .while loop +mandoc: outof.in:17:4: UNSUPP: cannot continue this .while loop --- /dev/null +++ regress/roff/while/nesting.out_ascii @@ -0,0 +1,9 @@ +WHILE-NESTING(1) General Commands Manual WHILE-NESTING(1) + +NNAAMMEE + wwhhiillee--nneessttiinngg - nested while requests + +DDEESSCCRRIIPPTTIIOONN + initial text 33 32 31 final text + +OpenBSD August 24, 2018 OpenBSD --- /dev/null +++ regress/roff/while/nesting.out_lint @@ -0,0 +1,2 @@ +mandoc: nesting.in:14:37: UNSUPP: nested .while loops +mandoc: nesting.in:18:4: UNSUPP: cannot continue this .while loop --- /dev/null +++ regress/roff/while/outof.in @@ -0,0 +1,18 @@ +.\" $OpenBSD: outof.in,v 1.1 2018/08/24 22:56:37 schwarze Exp $ +.Dd $Mdocdate: August 24 2018 $ +.Dt WHILE-OUTOF 1 +.Os +.Sh NAME +.Nm while-outof +.Nd while request starting in a macro +.Sh DESCRIPTION +.nr cnt 10 +.de mym +. while \\n[cnt] \{\ +. nop \\n[cnt] +.. +initial text +.mym +. nr cnt -1 +.\} +final text --- /dev/null +++ regress/roff/while/badargs.out_ascii @@ -0,0 +1,13 @@ +WHILE-BADARGS(1) General Commands Manual WHILE-BADARGS(1) + +NNAAMMEE + wwhhiillee--bbaaddaarrggss - dubious arguments for the while request + +DDEESSCCRRIIPPTTIIOONN + while does not support next line scope: + + 0 + + final text + +OpenBSD August 24, 2018 OpenBSD --- /dev/null +++ regress/roff/while/basic.in @@ -0,0 +1,30 @@ +.\" $OpenBSD: basic.in,v 1.1 2018/08/24 22:56:37 schwarze Exp $ +.Dd $Mdocdate: August 24 2018 $ +.Dt WHILE-BASIC 1 +.Os +.Sh NAME +.Nm while-basic +.Nd the while request +.Sh DESCRIPTION +Loop with single-line scope: +.nr cnt 11 1 +.de mym +\\n-[cnt] +.. +.while \n[cnt] .mym +.Pp +Loop with multi-line scope, text line closure: +.nr cnt 11 +.while \n[cnt] \{\ +.nr cnt -1 +\n[cnt]\}, +boom. +.Pp +Loop with multi-line scope, macro line closure: +.nr cnt 11 +.while \n[cnt] \{\ +.nr cnt -1 +\n[cnt] +.\} +.Pp +final text --- /dev/null +++ regress/roff/while/into.in @@ -0,0 +1,20 @@ +.\" $OpenBSD: into.in,v 1.1 2018/08/24 22:56:37 schwarze Exp $ +.Dd $Mdocdate: August 24 2018 $ +.Dt WHILE-INTO 1 +.Os +.Sh NAME +.Nm while-into +.Nd while request extending into a macro +.Sh DESCRIPTION +.nr cnt 10 +.de closeloop +.nr cnt -1 +.\} +.. +initial text +.while \n[cnt] \{\ +\n[cnt] +.closeloop +after macro +.\} +final text --- /dev/null +++ regress/roff/while/into.out_ascii @@ -0,0 +1,9 @@ +WHILE-INTO(1) General Commands Manual WHILE-INTO(1) + +NNAAMMEE + wwhhiillee--iinnttoo - while request extending into a macro + +DDEESSCCRRIIPPTTIIOONN + initial text 10 after macro final text + +OpenBSD August 24, 2018 OpenBSD -- To unsubscribe send an email to source+unsubscribe@mandoc.bsd.lv