=================================================================== RCS file: /cvs/mandoc/roff.c,v retrieving revision 1.180 retrieving revision 1.193 diff -u -p -r1.180 -r1.193 --- mandoc/roff.c 2013/10/05 22:08:12 1.180 +++ mandoc/roff.c 2014/02/14 23:05:20 1.193 @@ -1,7 +1,7 @@ -/* $Id: roff.c,v 1.180 2013/10/05 22:08:12 schwarze Exp $ */ +/* $Id: roff.c,v 1.193 2014/02/14 23:05:20 schwarze Exp $ */ /* * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons - * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze + * Copyright (c) 2010-2014 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 @@ -40,12 +40,15 @@ enum rofft { ROFF_am, ROFF_ami, ROFF_am1, + ROFF_as, ROFF_cc, ROFF_de, ROFF_dei, ROFF_de1, ROFF_ds, ROFF_el, + ROFF_fam, + ROFF_hw, ROFF_hy, ROFF_ie, ROFF_if, @@ -74,8 +77,8 @@ enum rofft { }; enum roffrule { - ROFFRULE_ALLOW, - ROFFRULE_DENY + ROFFRULE_DENY, + ROFFRULE_ALLOW }; /* @@ -97,17 +100,17 @@ struct roffkv { /* * A single number register as part of a singly-linked list. - * Registers are assumed to be unsigned ints for now. */ struct roffreg { struct roffstr key; - unsigned int u; + int val; struct roffreg *next; }; struct roff { enum mparset parsetype; /* requested parse type */ struct mparse *parse; /* parse point */ + int quick; /* skip standard macro deletion */ struct roffnode *last; /* leaf of stack */ enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */ char control; /* control character */ @@ -186,6 +189,11 @@ static void roff_free1(struct roff *); static void roff_freereg(struct roffreg *); static void roff_freestr(struct roffkv *); static char *roff_getname(struct roff *, char **, int, int); +static int roff_getnum(const char *, int *, int *); +static int roff_getop(const char *, int *, char *); +static int roff_getregn(const struct roff *, + const char *, size_t); +static int roff_getregro(const char *name); static const char *roff_getstrn(const struct roff *, const char *, size_t); static enum rofferr roff_it(ROFF_ARGS); @@ -226,12 +234,15 @@ static struct roffmac roffs[ROFF_MAX] = { { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, + { "as", roff_ds, NULL, NULL, 0, NULL }, { "cc", roff_cc, NULL, NULL, 0, NULL }, { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL }, { "ds", roff_ds, NULL, NULL, 0, NULL }, { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, + { "fam", roff_line_ignore, NULL, NULL, 0, NULL }, + { "hw", roff_line_ignore, NULL, NULL, 0, NULL }, { "hy", roff_line_ignore, NULL, NULL, 0, NULL }, { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL }, @@ -440,14 +451,9 @@ roff_free1(struct roff *r) void roff_reset(struct roff *r) { - int i; roff_free1(r); - r->control = 0; - - for (i = 0; i < PREDEFS_MAX; i++) - roff_setstr(r, predefs[i].name, predefs[i].str, 0); } @@ -461,41 +467,39 @@ roff_free(struct roff *r) struct roff * -roff_alloc(enum mparset type, struct mparse *parse) +roff_alloc(enum mparset type, struct mparse *parse, int quick) { struct roff *r; - int i; r = mandoc_calloc(1, sizeof(struct roff)); r->parsetype = type; r->parse = parse; + r->quick = quick; r->rstackpos = -1; roffhash_init(); - for (i = 0; i < PREDEFS_MAX; i++) - roff_setstr(r, predefs[i].name, predefs[i].str, 0); - return(r); } /* - * Pre-filter each and every line for reserved words (one beginning with - * `\*', e.g., `\*(ab'). These must be handled before the actual line - * is processed. - * This also checks the syntax of regular escapes. + * In the current line, expand user-defined strings ("\*") + * and references to number registers ("\n"). + * Also check the syntax of other escape sequences. */ static enum rofferr roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos) { - enum mandoc_esc esc; + char ubuf[12]; /* buffer to print the number */ const char *stesc; /* start of an escape sequence ('\\') */ 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, expand_count; - size_t nsz; - char *n; + char *nbuf; /* new buffer to copy bufp to */ + size_t nsz; /* size of the new buffer */ + size_t maxl; /* expected length of the escape name */ + size_t naml; /* actual length of the escape name */ + int expand_count; /* to avoid infinite loops */ expand_count = 0; @@ -505,7 +509,7 @@ again: stesc = cp++; /* - * The second character must be an asterisk. + * The second character must be an asterisk or an n. * If it isn't, skip it anyway: It is escaped, * so it can't start another escape sequence. */ @@ -513,12 +517,16 @@ again: if ('\0' == *cp) return(ROFF_CONT); - if ('*' != *cp) { - res = cp; - esc = mandoc_escape(&cp, NULL, NULL); - if (ESCAPE_ERROR != esc) + switch (*cp) { + case ('*'): + res = NULL; + break; + case ('n'): + res = ubuf; + break; + default: + if (ESCAPE_ERROR != mandoc_escape(&cp, NULL, NULL)) continue; - cp = res; mandoc_msg (MANDOCERR_BADESCAPE, r->parse, ln, (int)(stesc - *bufp), NULL); @@ -529,7 +537,7 @@ again: /* * The third character decides the length - * of the name of the string. + * of the name of the string or register. * Save a pointer to the name. */ @@ -552,7 +560,7 @@ again: /* Advance to the end of the name. */ - for (i = 0; 0 == maxl || i < maxl; i++, cp++) { + for (naml = 0; 0 == maxl || naml < maxl; naml++, cp++) { if ('\0' == *cp) { mandoc_msg (MANDOCERR_BADESCAPE, @@ -569,7 +577,11 @@ again: * undefined, resume searching for escapes. */ - res = roff_getstrn(r, stnam, (size_t)i); + if (NULL == res) + res = roff_getstrn(r, stnam, naml); + else + snprintf(ubuf, sizeof(ubuf), "%d", + roff_getregn(r, stnam, naml)); if (NULL == res) { mandoc_msg @@ -583,15 +595,15 @@ again: pos = stesc - *bufp; nsz = *szp + strlen(res) + 1; - n = mandoc_malloc(nsz); + nbuf = mandoc_malloc(nsz); - strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1)); - strlcat(n, res, nsz); - strlcat(n, cp + (maxl ? 0 : 1), nsz); + strlcpy(nbuf, *bufp, (size_t)(stesc - *bufp + 1)); + strlcat(nbuf, res, nsz); + strlcat(nbuf, cp + (maxl ? 0 : 1), nsz); free(*bufp); - *bufp = n; + *bufp = nbuf; *szp = nsz; if (EXPAND_LIMIT >= ++expand_count) @@ -630,8 +642,7 @@ roff_parsetext(char **bufp, size_t *szp, int pos, int if ('\\' == *p) { /* Skip over escapes. */ p++; - esc = mandoc_escape - ((const char const **)&p, NULL, NULL); + esc = mandoc_escape((const char **)&p, NULL, NULL); if (ESCAPE_ERROR == esc) break; continue; @@ -702,19 +713,14 @@ roff_parseln(struct roff *r, int ln, char **bufp, assert(ROFF_IGN == e || ROFF_CONT == e); if (ROFF_CONT != e) return(e); - if (r->eqn) - return(eqn_read(&r->eqn, ln, *bufp, pos, offs)); + } + if (r->eqn) + return(eqn_read(&r->eqn, ln, *bufp, ppos, offs)); + if ( ! ctl) { if (r->tbl) return(tbl_read(r->tbl, ln, *bufp, pos)); return(roff_parsetext(bufp, szp, pos, offs)); - } else if ( ! ctl) { - if (r->eqn) - return(eqn_read(&r->eqn, ln, *bufp, pos, offs)); - if (r->tbl) - return(tbl_read(r->tbl, ln, *bufp, pos)); - return(roff_parsetext(bufp, szp, pos, offs)); - } else if (r->eqn) - return(eqn_read(&r->eqn, ln, *bufp, ppos, offs)); + } /* * If a scope is open, go to the child handler for that macro, @@ -935,7 +941,7 @@ roff_block(ROFF_ARGS) /* * At the beginning of a `de' macro, clear the existing string * with the same name, if there is one. New content will be - * added from roff_block_text() in multiline mode. + * appended from roff_block_text() in multiline mode. */ if (ROFF_de == tok) @@ -1025,7 +1031,7 @@ roff_block_sub(ROFF_ARGS) */ if (ROFF_cblock != t) { if (ROFF_de == tok) - roff_setstr(r, r->last->name, *bufp + ppos, 1); + roff_setstr(r, r->last->name, *bufp + ppos, 2); return(ROFF_IGN); } @@ -1041,7 +1047,7 @@ roff_block_text(ROFF_ARGS) { if (ROFF_de == tok) - roff_setstr(r, r->last->name, *bufp + pos, 1); + roff_setstr(r, r->last->name, *bufp + pos, 2); return(ROFF_IGN); } @@ -1120,9 +1126,61 @@ roff_cond_text(ROFF_ARGS) return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); } +static int +roff_getnum(const char *v, int *pos, int *res) +{ + int p, n; + + p = *pos; + n = v[p] == '-'; + if (n) + p++; + + for (*res = 0; isdigit((unsigned char)v[p]); p++) + *res += 10 * *res + v[p] - '0'; + if (p == *pos + n) + return 0; + + if (n) + *res = -*res; + + *pos = p; + return 1; +} + +static int +roff_getop(const char *v, int *pos, char *res) +{ + int e; + + *res = v[*pos]; + e = v[*pos + 1] == '='; + + switch (*res) { + case '=': + break; + case '>': + if (e) + *res = 'g'; + break; + case '<': + if (e) + *res = 'l'; + break; + default: + return(0); + } + + *pos += 1 + e; + + return(*res); +} + static enum roffrule roff_evalcond(const char *v, int *pos) { + int not, lh, rh; + char op; switch (v[*pos]) { case ('n'): @@ -1135,13 +1193,47 @@ roff_evalcond(const char *v, int *pos) case ('t'): (*pos)++; return(ROFFRULE_DENY); + case ('!'): + (*pos)++; + not = 1; + break; default: + not = 0; break; } - while (v[*pos] && ' ' != v[*pos]) - (*pos)++; - return(ROFFRULE_DENY); + if (!roff_getnum(v, pos, &lh)) + return ROFFRULE_DENY; + if (!roff_getop(v, pos, &op)) { + if (lh < 0) + lh = 0; + goto out; + } + if (!roff_getnum(v, pos, &rh)) + return ROFFRULE_DENY; + switch (op) { + case 'g': + lh = lh >= rh; + break; + case 'l': + lh = lh <= rh; + break; + case '=': + lh = lh == rh; + break; + case '>': + lh = lh > rh; + break; + case '<': + lh = lh < rh; + break; + default: + return ROFFRULE_DENY; + } +out: + if (not) + lh = !lh; + return lh ? ROFFRULE_ALLOW : ROFFRULE_DENY; } /* ARGSUSED */ @@ -1258,12 +1350,12 @@ roff_ds(ROFF_ARGS) string++; /* The rest is the value. */ - roff_setstr(r, name, string, 0); + roff_setstr(r, name, string, ROFF_as == tok); return(ROFF_IGN); } void -roff_setreg(struct roff *r, const char *name, unsigned int val) +roff_setreg(struct roff *r, const char *name, int val, char sign) { struct roffreg *reg; @@ -1278,25 +1370,86 @@ roff_setreg(struct roff *r, const char *name, unsigned reg = mandoc_malloc(sizeof(struct roffreg)); reg->key.p = mandoc_strdup(name); reg->key.sz = strlen(name); + reg->val = 0; reg->next = r->regtab; r->regtab = reg; } - reg->u = val; + if ('+' == sign) + reg->val += val; + else if ('-' == sign) + reg->val -= val; + else + reg->val = val; } -unsigned int +/* + * Handle some predefined read-only number registers. + * For now, return -1 if the requested register is not predefined; + * in case a predefined read-only register having the value -1 + * were to turn up, another special value would have to be chosen. + */ +static int +roff_getregro(const char *name) +{ + + switch (*name) { + case ('A'): /* ASCII approximation mode is always off. */ + return(0); + case ('g'): /* Groff compatibility mode is always on. */ + return(1); + case ('H'): /* Fixed horizontal resolution. */ + return (24); + case ('j'): /* Always adjust left margin only. */ + return(0); + case ('T'): /* Some output device is always defined. */ + return(1); + case ('V'): /* Fixed vertical resolution. */ + return (40); + default: + return (-1); + } +} + +int roff_getreg(const struct roff *r, const char *name) { struct roffreg *reg; + int val; + if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) { + val = roff_getregro(name + 1); + if (-1 != val) + return (val); + } + for (reg = r->regtab; reg; reg = reg->next) if (0 == strcmp(name, reg->key.p)) - return(reg->u); + return(reg->val); return(0); } +static int +roff_getregn(const struct roff *r, const char *name, size_t len) +{ + struct roffreg *reg; + int val; + + if ('.' == name[0] && 2 == len) { + val = roff_getregro(name + 1); + if (-1 != val) + return (val); + } + + for (reg = r->regtab; reg; reg = reg->next) + if (len == reg->key.sz && + 0 == strncmp(name, reg->key.p, len)) + return(reg->val); + + return(0); +} + static void roff_freereg(struct roffreg *reg) { @@ -1316,17 +1469,22 @@ roff_nr(ROFF_ARGS) { const char *key; char *val; + size_t sz; int iv; + char sign; val = *bufp + pos; key = roff_getname(r, &val, ln, pos); - iv = mandoc_strntoi(val, strlen(val), 10); - if (0 > iv) - iv = 0; + sign = *val; + if ('+' == sign || '-' == sign) + val++; - roff_setreg(r, key, (unsigned)iv); + sz = strspn(val, "0123456789"); + iv = sz ? mandoc_strntoi(val, sz, 10) : 0; + roff_setreg(r, key, iv, sign); + return(ROFF_IGN); } @@ -1377,7 +1535,7 @@ roff_Dd(ROFF_ARGS) { const char *const *cp; - if (MPARSE_MDOC != r->parsetype) + if (0 == r->quick && MPARSE_MDOC != r->parsetype) for (cp = __mdoc_reserved; *cp; cp++) roff_setstr(r, *cp, NULL, 0); @@ -1390,7 +1548,7 @@ roff_TH(ROFF_ARGS) { const char *const *cp; - if (MPARSE_MDOC != r->parsetype) + if (0 == r->quick && MPARSE_MDOC != r->parsetype) for (cp = __man_reserved; *cp; cp++) roff_setstr(r, *cp, NULL, 0); @@ -1612,7 +1770,7 @@ roff_userdef(ROFF_ARGS) /* * Collect pointers to macro argument strings - * and null-terminate them. + * and NUL-terminate them. */ cp = *bufp + pos; for (i = 0; i < 9; i++) @@ -1692,22 +1850,23 @@ roff_getname(struct roff *r, char **cpp, int ln, int p /* * Store *string into the user-defined string called *name. - * In multiline mode, append to an existing entry and append '\n'; - * else replace the existing entry, if there is one. * To clear an existing entry, call with (*r, *name, NULL, 0). + * append == 0: replace mode + * append == 1: single-line append mode + * append == 2: multiline append mode, append '\n' after each call */ static void roff_setstr(struct roff *r, const char *name, const char *string, - int multiline) + int append) { roff_setstrn(&r->strtab, name, strlen(name), string, - string ? strlen(string) : 0, multiline); + string ? strlen(string) : 0, append); } static void roff_setstrn(struct roffkv **r, const char *name, size_t namesz, - const char *string, size_t stringsz, int multiline) + const char *string, size_t stringsz, int append) { struct roffkv *n; char *c; @@ -1729,8 +1888,7 @@ roff_setstrn(struct roffkv **r, const char *name, size n->val.sz = 0; n->next = *r; *r = n; - } else if (0 == multiline) { - /* In multiline mode, append; else replace. */ + } else if (0 == append) { free(n->val.p); n->val.p = NULL; n->val.sz = 0; @@ -1743,7 +1901,7 @@ roff_setstrn(struct roffkv **r, const char *name, size * One additional byte for the '\n' in multiline mode, * and one for the terminating '\0'. */ - newch = stringsz + (multiline ? 2u : 1u); + newch = stringsz + (1 < append ? 2u : 1u); if (NULL == n->val.p) { n->val.p = mandoc_malloc(newch); @@ -1770,7 +1928,7 @@ roff_setstrn(struct roffkv **r, const char *name, size } /* Append terminating bytes. */ - if (multiline) + if (1 < append) *c++ = '\n'; *c = '\0'; @@ -1781,11 +1939,17 @@ static const char * roff_getstrn(const struct roff *r, const char *name, size_t len) { const struct roffkv *n; + int i; for (n = r->strtab; n; n = n->next) if (0 == strncmp(name, n->key.p, len) && '\0' == n->key.p[(int)len]) return(n->val.p); + + for (i = 0; i < PREDEFS_MAX; i++) + if (0 == strncmp(name, predefs[i].name, len) && + '\0' == predefs[i].name[(int)len]) + return(predefs[i].str); return(NULL); }