=================================================================== RCS file: /cvs/mandoc/roff.c,v retrieving revision 1.67 retrieving revision 1.73 diff -u -p -r1.67 -r1.73 --- mandoc/roff.c 2010/05/15 18:35:14 1.67 +++ mandoc/roff.c 2010/05/15 22:28:22 1.73 @@ -1,4 +1,4 @@ -/* $Id: roff.c,v 1.67 2010/05/15 18:35:14 kristaps Exp $ */ +/* $Id: roff.c,v 1.73 2010/05/15 22:28:22 kristaps Exp $ */ /* * Copyright (c) 2010 Kristaps Dzonsons * @@ -44,11 +44,13 @@ struct roff { struct roffnode { enum rofft tok; /* type of node */ struct roffnode *parent; /* up one in stack */ + char *end; /* custom end-token */ int line; /* parse line */ int col; /* parse col */ }; #define ROFF_ARGS struct roff *r, /* parse ctx */ \ + enum rofft tok, /* tok of macro */ \ char **bufp, /* input buffer */ \ size_t *szp, /* size of input buffer */ \ int ln, /* parse line */ \ @@ -62,21 +64,19 @@ struct roffmac { roffproc new; /* root of stack (type = ROFF_MAX) */ }; -static enum rofferr roff_ignore(ROFF_ARGS); static enum rofferr roff_new_close(ROFF_ARGS); static enum rofferr roff_new_ig(ROFF_ARGS); static enum rofferr roff_sub_ig(ROFF_ARGS); const struct roffmac roffs[ROFF_MAX] = { - { "de", NULL, roff_ignore }, - { "dei", NULL, roff_ignore }, - { "am", NULL, roff_ignore }, - { "ami", NULL, roff_ignore }, + { "de", roff_sub_ig, roff_new_ig }, + { "dei", roff_sub_ig, roff_new_ig }, + { "am", roff_sub_ig, roff_new_ig }, + { "ami", roff_sub_ig, roff_new_ig }, { "ig", roff_sub_ig, roff_new_ig }, { ".", NULL, roff_new_close }, }; -static void roff_alloc1(struct roff *); static void roff_free1(struct roff *); static enum rofft roff_hash_find(const char *); static int roffnode_push(struct roff *, @@ -153,20 +153,11 @@ roff_free1(struct roff *r) } -static void -roff_alloc1(struct roff *r) -{ - - memset(r, 0, sizeof(struct roff)); -} - - void roff_reset(struct roff *r) { roff_free1(r); - roff_alloc1(r); } @@ -208,7 +199,7 @@ roff_parseln(struct roff *r, int ln, char **bufp, size */ t = r->last->tok; assert(roffs[t].sub); - return((*roffs[t].sub)(r, bufp, szp, ln, 0)); + return((*roffs[t].sub)(r, t, bufp, szp, ln, 0)); } else if ('.' != (*bufp)[0] && NULL == r->last) /* Return when in free text without a context. */ return(ROFF_CONT); @@ -219,7 +210,7 @@ roff_parseln(struct roff *r, int ln, char **bufp, size return(ROFF_CONT); assert(roffs[t].new); - return((*roffs[t].new)(r, bufp, szp, ln, ppos)); + return((*roffs[t].new)(r, t, bufp, szp, ln, ppos)); } @@ -266,32 +257,43 @@ roff_parse(const char *buf, int *pos) /* ARGSUSED */ static enum rofferr -roff_ignore(ROFF_ARGS) -{ - - return(ROFF_IGN); -} - - -/* ARGSUSED */ -static enum rofferr roff_sub_ig(ROFF_ARGS) { - enum rofft t; - int pos; + int i, j; /* Ignore free-text lines. */ if ('.' != (*bufp)[ppos]) return(ROFF_IGN); - /* Ignore macros unless it's a closing macro. */ + if (r->last->end) { + i = ppos + 1; - t = roff_parse(*bufp, &pos); - if (ROFF_close != t) + while ((*bufp)[i] && ' ' == (*bufp)[i]) + i++; + + for (j = 0; r->last->end[j]; i++, j++) + if ((*bufp)[i] != r->last->end[j]) + return(ROFF_IGN); + + if (r->last->end[j]) + return(ROFF_IGN); + if ((*bufp)[i] && ' ' != (*bufp)[i]) + return(ROFF_IGN); + + while (' ' == (*bufp)[i]) + i++; + + } else if (ROFF_close != roff_parse(*bufp, &i)) return(ROFF_IGN); roffnode_pop(r); + + if ('\0' == (*bufp)[i]) + return(ROFF_IGN); + if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, i, NULL)) + return(ROFF_ERR); + return(ROFF_IGN); } @@ -303,6 +305,7 @@ roff_new_close(ROFF_ARGS) if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL)) return(ROFF_ERR); + return(ROFF_IGN); } @@ -311,9 +314,51 @@ roff_new_close(ROFF_ARGS) static enum rofferr roff_new_ig(ROFF_ARGS) { + int i; - return(roffnode_push(r, ROFF_ig, ln, ppos) ? - ROFF_IGN : ROFF_ERR); + if ( ! roffnode_push(r, tok, ln, ppos)) + return(ROFF_ERR); + + if (ROFF_ig != tok) { + while ((*bufp)[ppos] && ' ' != (*bufp)[ppos]) + ppos++; + while (' ' == (*bufp)[ppos]) + ppos++; + } + + i = (int)ppos; + + while ((*bufp)[i] && ' ' != (*bufp)[i]) + i++; + + if (i == (int)ppos) + return(ROFF_IGN); + + if ((*bufp)[i]) + if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, i, NULL)) + return(ROFF_ERR); + + /* + * If the macro has arguments, the first argument (up to the + * next whitespace) is interpreted as an argument marking the + * macro close. Thus, `.ig foo' will close at `.foo'. + * + * NOTE: the closing macro `.foo' in the above case is not + * allowed to have leading spaces with old groff! Thus `.foo' + * != `. foo'. Oh yeah, everything after the `.foo' is lost. + * Merry fucking Christmas. + */ + + r->last->end = malloc((size_t)(i - ppos) + 1); + if (NULL == r->last->end) { + (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL); + return(ROFF_ERR); + } + + memcpy(r->last->end, &(*bufp)[ppos], (size_t)(i - ppos)); + r->last->end[i - ppos] = '\0'; + + return(ROFF_IGN); }