=================================================================== RCS file: /cvs/texi2mdoc/util.c,v retrieving revision 1.16 retrieving revision 1.19 diff -u -p -r1.16 -r1.19 --- texi2mdoc/util.c 2015/02/25 15:50:56 1.16 +++ texi2mdoc/util.c 2015/02/28 13:16:44 1.19 @@ -1,4 +1,4 @@ -/* $Id: util.c,v 1.16 2015/02/25 15:50:56 kristaps Exp $ */ +/* $Id: util.c,v 1.19 2015/02/28 13:16:44 kristaps Exp $ */ /* * Copyright (c) 2015 Kristaps Dzonsons * @@ -330,8 +330,11 @@ advance(struct texi *p, size_t *pos) f->col = 0; } else f->col++; - } else + } else { --f->insplice; + if (0 == f->insplice) + f->depth = 0; + } (*pos)++; } @@ -439,7 +442,7 @@ advanceto(struct texi *p, size_t *pos, size_t end) } static void -texiexecmacro(struct texi *p, struct teximacro *m, size_t *pos) +texiexecmacro(struct texi *p, struct teximacro *m, size_t sv, size_t *pos) { size_t valsz, realsz, aasz, asz, ssz, i, j, k, start, end; @@ -447,17 +450,31 @@ texiexecmacro(struct texi *p, struct teximacro *m, siz char **args; const char *cp; + /* Disregard empty macros. */ + if (0 == (valsz = realsz = strlen(m->value))) + return; + + /* + * This is important: it protect us from macros that invoke more + * macros, possibly going on infinitely. + * We use "sv" instead of the current position because we might + * be invoked at the end of the macro (i.e., insplice == 0). + * The "sv" value was initialised at the start of the macro. + */ + if (sv > 0) + if (++p->files[p->filepos].depth > 64) + texierr(p, "maximium recursive depth"); + args = argparse(p, pos, &asz, m->argsz); if (asz != m->argsz) texiwarn(p, "invalid macro argument length"); aasz = asz < m->argsz ? asz : m->argsz; if (0 == aasz) { - texisplice(p, m->value, strlen(m->value), pos); + texisplice(p, m->value, valsz, pos); return; } - valsz = realsz = strlen(m->value); val = strdup(m->value); for (i = j = 0; i < realsz; i++) { @@ -574,7 +591,7 @@ parseword(struct texi *p, size_t *pos, char extra) * index after the command name. */ enum texicmd -texicmd(struct texi *p, size_t pos, size_t *end, struct teximacro **macro) +texicmd(const struct texi *p, size_t pos, size_t *end, struct teximacro **macro) { size_t i, len, toksz; @@ -653,7 +670,7 @@ texicmd(struct texi *p, size_t pos, size_t *end, struc int parsearg(struct texi *p, size_t *pos, size_t num) { - size_t end; + size_t end, sv; enum texicmd cmd; struct teximacro *macro; @@ -684,10 +701,11 @@ parsearg(struct texi *p, size_t *pos, size_t num) continue; } + sv = p->files[p->filepos - 1].insplice; cmd = texicmd(p, *pos, &end, ¯o); advanceto(p, pos, end); if (NULL != macro) - texiexecmacro(p, macro, pos); + texiexecmacro(p, macro, sv, pos); if (TEXICMD__MAX == cmd) continue; if (NULL != texitoks[cmd].fp) @@ -701,9 +719,9 @@ parsearg(struct texi *p, size_t *pos, size_t num) * This will stop in the event of EOF or if we're not at a bracket. */ void -parsebracket(struct texi *p, size_t *pos) +parsebracket(struct texi *p, size_t *pos, int dostack) { - size_t end; + size_t end, sv, stack; enum texicmd cmd; struct teximacro *macro; @@ -714,12 +732,25 @@ parsebracket(struct texi *p, size_t *pos) return; advance(p, pos); + stack = 0; while ((*pos = advancenext(p, pos)) < BUFSZ(p)) { switch (BUF(p)[*pos]) { case ('}'): + if (stack > 0) { + stack--; + advance(p, pos); + texiputchar(p, '}'); + continue; + } advance(p, pos); return; case ('{'): + if (dostack) { + stack++; + advance(p, pos); + texiputchar(p, '{'); + continue; + } if (0 == p->ign) texiwarn(p, "unexpected \"{\""); advance(p, pos); @@ -731,10 +762,11 @@ parsebracket(struct texi *p, size_t *pos) continue; } + sv = p->files[p->filepos - 1].insplice; cmd = texicmd(p, *pos, &end, ¯o); advanceto(p, pos, end); if (NULL != macro) - texiexecmacro(p, macro, pos); + texiexecmacro(p, macro, sv, pos); if (TEXICMD__MAX == cmd) continue; if (NULL != texitoks[cmd].fp) @@ -750,7 +782,7 @@ parsebracket(struct texi *p, size_t *pos) void parseeoln(struct texi *p, size_t *pos) { - size_t end; + size_t end, sv; enum texicmd cmd; struct teximacro *macro; @@ -779,10 +811,11 @@ parseeoln(struct texi *p, size_t *pos) continue; } + sv = p->files[p->filepos - 1].insplice; cmd = texicmd(p, *pos, &end, ¯o); advanceto(p, pos, end); if (NULL != macro) - texiexecmacro(p, macro, pos); + texiexecmacro(p, macro, sv, pos); if (TEXICMD__MAX == cmd) continue; if (NULL != texitoks[cmd].fp) @@ -794,13 +827,30 @@ parseeoln(struct texi *p, size_t *pos) } /* + * Peek to see if there's a command after subsequent whitespace. + * If so, return the macro identifier. + * This DOES NOT work with user-defined macros. + */ +enum texicmd +peekcmd(const struct texi *p, size_t pos) +{ + size_t end; + + while (pos < BUFSZ(p) && ismspace(BUF(p)[pos])) + pos++; + if (pos == BUFSZ(p) || '@' != BUF(p)[pos]) + return(TEXICMD__MAX); + return(texicmd(p, pos, &end, NULL)); +} + +/* * Parse a single word or command. * This will return immediately at the EOF. */ static void parsesingle(struct texi *p, size_t *pos) { - size_t end; + size_t end, sv; enum texicmd cmd; struct teximacro *macro; @@ -825,10 +875,11 @@ parsesingle(struct texi *p, size_t *pos) return; } + sv = p->files[p->filepos - 1].insplice; cmd = texicmd(p, *pos, &end, ¯o); advanceto(p, pos, end); if (NULL != macro) - texiexecmacro(p, macro, pos); + texiexecmacro(p, macro, sv, pos); if (TEXICMD__MAX == cmd) return; if (NULL != texitoks[cmd].fp) @@ -853,7 +904,7 @@ parselinearg(struct texi *p, size_t *pos) } if (*pos < BUFSZ(p) && '{' == BUF(p)[*pos]) - parsebracket(p, pos); + parsebracket(p, pos, 0); else if (*pos < BUFSZ(p) && '\n' != BUF(p)[*pos]) parsesingle(p, pos); else @@ -905,7 +956,7 @@ texisplice(struct texi *p, const char *buf, size_t sz, void parseto(struct texi *p, size_t *pos, const char *endtoken) { - size_t end; + size_t end, sv; enum texicmd cmd; size_t endtoksz; struct teximacro *macro; @@ -932,6 +983,7 @@ parseto(struct texi *p, size_t *pos, const char *endto continue; } + sv = p->files[p->filepos - 1].insplice; cmd = texicmd(p, *pos, &end, ¯o); advanceto(p, pos, end); if (TEXICMD_END == cmd) { @@ -952,7 +1004,7 @@ parseto(struct texi *p, size_t *pos, const char *endto continue; } if (NULL != macro) - texiexecmacro(p, macro, pos); + texiexecmacro(p, macro, sv, pos); if (TEXICMD__MAX == cmd) continue; if (NULL != texitoks[cmd].fp) @@ -1209,6 +1261,9 @@ argparse(struct texi *p, size_t *pos, size_t *argsz, s args = NULL; *argsz = 0; + if (*pos == BUFSZ(p)) + return(args); + if ('{' != BUF(p)[*pos] && hint) { /* * Special case: if we encounter an unbracketed argument @@ -1234,6 +1289,8 @@ argparse(struct texi *p, size_t *pos, size_t *argsz, s return(args); } else if ('{' != BUF(p)[*pos]) return(args); + + assert('{' == BUF(p)[*pos]); /* Parse til the closing '}', putting into the array. */ advance(p, pos);