=================================================================== RCS file: /cvs/mandoc/roff.c,v retrieving revision 1.166 retrieving revision 1.171 diff -u -p -r1.166 -r1.171 --- mandoc/roff.c 2011/07/29 09:19:48 1.166 +++ mandoc/roff.c 2011/09/19 08:34:45 1.171 @@ -1,4 +1,4 @@ -/* $Id: roff.c,v 1.166 2011/07/29 09:19:48 kristaps Exp $ */ +/* $Id: roff.c,v 1.171 2011/09/19 08:34:45 schwarze Exp $ */ /* * Copyright (c) 2010, 2011 Kristaps Dzonsons * Copyright (c) 2010, 2011 Ingo Schwarze @@ -31,6 +31,9 @@ /* Maximum number of nested if-else conditionals. */ #define RSTACK_MAX 128 +/* Maximum number of string expansions per line, to break infinite loops. */ +#define EXPAND_LIMIT 1000 + enum rofft { ROFF_ad, ROFF_am, @@ -81,13 +84,16 @@ struct reg { unsigned int u; /* unsigned integer */ }; +/* + * An incredibly-simple string buffer. + */ struct roffstr { - char *p; - size_t sz; + char *p; /* nil-terminated buffer */ + size_t sz; /* saved strlen(p) */ }; /* - * A key-value string pair with lengths. + * A key-value roffstr pair as part of a singly-linked list. */ struct roffkv { struct roffstr key; @@ -102,7 +108,8 @@ struct roff { int rstackpos; /* position in rstack */ struct reg regs[REG__MAX]; struct roffkv *strtab; /* user-defined strings & macros */ - struct roffkv *chrtab; /* user-defined characters */ + struct roffkv *xmbtab; /* multi-byte trans table (`tr') */ + struct roffstr *xtab; /* single-byte trans table (`tr') */ const char *current_string; /* value of last called user macro */ struct tbl_node *first_tbl; /* first table parsed */ struct tbl_node *last_tbl; /* last table parsed */ @@ -169,13 +176,13 @@ static enum rofferr roff_cond_sub(ROFF_ARGS); static enum rofferr roff_ds(ROFF_ARGS); static enum roffrule roff_evalcond(const char *, int *); static void roff_free1(struct roff *); -static void roff_freestr(struct roffkv **); +static void roff_freestr(struct roffkv *); static char *roff_getname(struct roff *, char **, int, int); static const char *roff_getstrn(const struct roff *, const char *, size_t); static enum rofferr roff_line_ignore(ROFF_ARGS); static enum rofferr roff_nr(ROFF_ARGS); -static void roff_openeqn(struct roff *, const char *, +static void roff_openeqn(struct roff *, const char *, int, int, const char *); static enum rofft roff_parse(struct roff *, const char *, int *); static enum rofferr roff_parsetext(char *); @@ -346,6 +353,7 @@ roff_free1(struct roff *r) { struct tbl_node *t; struct eqn_node *e; + int i; while (NULL != (t = r->first_tbl)) { r->first_tbl = t->next; @@ -364,11 +372,19 @@ roff_free1(struct roff *r) while (r->last) roffnode_pop(r); - roff_freestr(&r->strtab); - roff_freestr(&r->chrtab); -} + roff_freestr(r->strtab); + roff_freestr(r->xmbtab); + r->strtab = r->xmbtab = NULL; + if (r->xtab) + for (i = 0; i < 128; i++) + free(r->xtab[i].p); + + free(r->xtab); + r->xtab = NULL; +} + void roff_reset(struct roff *r) { @@ -424,10 +440,12 @@ roff_res(struct roff *r, char **bufp, size_t *szp, int const char *stnam; /* start of the name, after "[(*" */ const char *cp; /* end of the name, e.g. before ']' */ const char *res; /* the string to be substituted */ - int i, maxl; + int i, maxl, expand_count; size_t nsz; char *n; + expand_count = 0; + again: cp = *bufp + pos; while (NULL != (cp = strchr(cp, '\\'))) { @@ -522,7 +540,13 @@ again: *bufp = n; *szp = nsz; - goto again; + + if (EXPAND_LIMIT >= ++expand_count) + goto again; + + /* Just leave the string unexpanded. */ + mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL); + return; } } @@ -532,7 +556,6 @@ again: static enum rofferr roff_parsetext(char *p) { - char l, r; size_t sz; const char *start; enum mandoc_esc esc; @@ -559,14 +582,8 @@ roff_parsetext(char *p) continue; } - l = *(p - 1); - r = *(p + 1); - if ('\\' != l && - '\t' != r && '\t' != l && - ' ' != r && ' ' != l && - '-' != r && '-' != l && - ! isdigit((unsigned char)l) && - ! isdigit((unsigned char)r)) + if (isalpha((unsigned char)p[-1]) && + isalpha((unsigned char)p[1])) *p = ASCII_HYPH; p++; } @@ -1396,7 +1413,19 @@ roff_tr(ROFF_ARGS) p--; } - roff_setstrn(&r->chrtab, first, fsz, second, ssz, 0); + if (fsz > 1) { + roff_setstrn(&r->xmbtab, first, + fsz, second, ssz, 0); + continue; + } + + if (NULL == r->xtab) + r->xtab = mandoc_calloc + (128, sizeof(struct roffstr)); + + free(r->xtab[(int)*first].p); + r->xtab[(int)*first].p = mandoc_strndup(second, ssz); + r->xtab[(int)*first].sz = ssz; } return(ROFF_IGN); @@ -1616,18 +1645,16 @@ roff_getstrn(const struct roff *r, const char *name, s } static void -roff_freestr(struct roffkv **r) +roff_freestr(struct roffkv *r) { struct roffkv *n, *nn; - for (n = *r; n; n = nn) { + for (n = r; n; n = nn) { free(n->key.p); free(n->val.p); nn = n->next; free(n); } - - *r = NULL; } const struct tbl_span * @@ -1644,13 +1671,6 @@ roff_eqn(const struct roff *r) return(r->last_eqn ? &r->last_eqn->eqn : NULL); } -char -roff_eqndelim(const struct roff *r) -{ - - return('\0'); -} - /* * Duplicate an input string, making the appropriate character * conversations (as stipulated by `tr') along the way. @@ -1665,7 +1685,7 @@ roff_strdup(const struct roff *r, const char *p) size_t ssz, sz; enum mandoc_esc esc; - if (NULL == r->chrtab) + if (NULL == r->xmbtab && NULL == r->xtab) return(mandoc_strdup(p)); else if ('\0' == *p) return(mandoc_strdup("")); @@ -1682,8 +1702,21 @@ roff_strdup(const struct roff *r, const char *p) ssz = 0; while ('\0' != *p) { + if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) { + sz = r->xtab[(int)*p].sz; + res = mandoc_realloc(res, ssz + sz + 1); + memcpy(res + ssz, r->xtab[(int)*p].p, sz); + ssz += sz; + p++; + continue; + } else if ('\\' != *p) { + res = mandoc_realloc(res, ssz + 2); + res[ssz++] = *p++; + continue; + } + /* Search for term matches. */ - for (cp = r->chrtab; cp; cp = cp->next) + for (cp = r->xmbtab; cp; cp = cp->next) if (0 == strncmp(p, cp->key.p, cp->key.sz)) break; @@ -1701,36 +1734,29 @@ roff_strdup(const struct roff *r, const char *p) continue; } - if ('\\' == *p) { - /* - * Handle escapes carefully: we need to copy - * over just the escape itself, or else we might - * do replacements within the escape itself. - * Make sure to pass along the bogus string. - */ - pp = p++; - esc = mandoc_escape(&p, NULL, NULL); - if (ESCAPE_ERROR == esc) { - sz = strlen(pp); - res = mandoc_realloc(res, ssz + sz + 1); - memcpy(res + ssz, pp, sz); - break; - } - /* - * We bail out on bad escapes. - * No need to warn: we already did so when - * roff_res() was called. - */ - sz = (int)(p - pp); + /* + * Handle escapes carefully: we need to copy + * over just the escape itself, or else we might + * do replacements within the escape itself. + * Make sure to pass along the bogus string. + */ + pp = p++; + esc = mandoc_escape(&p, NULL, NULL); + if (ESCAPE_ERROR == esc) { + sz = strlen(pp); res = mandoc_realloc(res, ssz + sz + 1); memcpy(res + ssz, pp, sz); - ssz += sz; - continue; + break; } - - /* Just append the charater. */ - res = mandoc_realloc(res, ssz + 2); - res[ssz++] = *p++; + /* + * We bail out on bad escapes. + * No need to warn: we already did so when + * roff_res() was called. + */ + sz = (int)(p - pp); + res = mandoc_realloc(res, ssz + sz + 1); + memcpy(res + ssz, pp, sz); + ssz += sz; } res[(int)ssz] = '\0';