=================================================================== RCS file: /cvs/mandoc/roff.c,v retrieving revision 1.187 retrieving revision 1.200 diff -u -p -r1.187 -r1.200 --- mandoc/roff.c 2013/12/15 21:23:52 1.187 +++ mandoc/roff.c 2014/03/20 02:57:28 1.200 @@ -1,7 +1,7 @@ -/* $Id: roff.c,v 1.187 2013/12/15 21:23:52 schwarze Exp $ */ +/* $Id: roff.c,v 1.200 2014/03/20 02:57:28 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,7 +40,9 @@ enum rofft { ROFF_am, ROFF_ami, ROFF_am1, + ROFF_as, ROFF_cc, + ROFF_ce, ROFF_de, ROFF_dei, ROFF_de1, @@ -70,16 +72,10 @@ enum rofft { ROFF_EQ, ROFF_EN, ROFF_cblock, - ROFF_ccond, ROFF_USERDEF, ROFF_MAX }; -enum roffrule { - ROFFRULE_DENY, - ROFFRULE_ALLOW -}; - /* * An incredibly-simple string buffer. */ @@ -107,10 +103,10 @@ struct roffreg { }; struct roff { - enum mparset parsetype; /* requested parse type */ struct mparse *parse; /* parse point */ + int options; /* parse options */ struct roffnode *last; /* leaf of stack */ - enum roffrule rstack[RSTACK_MAX]; /* stack of !`ie' rules */ + int rstack[RSTACK_MAX]; /* stack of !`ie' rules */ char control; /* control character */ int rstackpos; /* position in rstack */ struct roffreg *regtab; /* number registers */ @@ -134,7 +130,7 @@ struct roffnode { char *name; /* node name, e.g. macro name */ char *end; /* end-rules: custom token */ int endspan; /* end-rules: next-line or infty */ - enum roffrule rule; /* current evaluation rule */ + int rule; /* current evaluation rule */ }; #define ROFF_ARGS struct roff *r, /* parse ctx */ \ @@ -177,12 +173,13 @@ static enum rofferr roff_block_text(ROFF_ARGS); static enum rofferr roff_block_sub(ROFF_ARGS); static enum rofferr roff_cblock(ROFF_ARGS); static enum rofferr roff_cc(ROFF_ARGS); -static enum rofferr roff_ccond(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 roffrule roff_evalcond(const char *, int *); +static int roff_evalcond(const char *, int *); +static int roff_evalstrcond(const char *, int *); static void roff_free1(struct roff *); static void roff_freereg(struct roffreg *); static void roff_freestr(struct roffkv *); @@ -191,6 +188,7 @@ 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); @@ -231,7 +229,9 @@ 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 }, + { "ce", roff_line_ignore, 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 }, @@ -261,38 +261,39 @@ static struct roffmac roffs[ROFF_MAX] = { { "EQ", roff_EQ, NULL, NULL, 0, NULL }, { "EN", roff_EN, NULL, NULL, 0, NULL }, { ".", roff_cblock, NULL, NULL, 0, NULL }, - { "\\}", roff_ccond, NULL, NULL, 0, NULL }, { NULL, roff_userdef, NULL, NULL, 0, NULL }, }; +/* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */ const char *const __mdoc_reserved[] = { "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At", "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq", "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx", "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq", - "Ds", "Dt", "Dv", "Dx", "D1", - "Ec", "Ed", "Ef", "Ek", "El", "Em", "em", - "En", "Eo", "Eq", "Er", "Es", "Ev", "Ex", + "Dt", "Dv", "Dx", "D1", + "Ec", "Ed", "Ef", "Ek", "El", "Em", + "En", "Eo", "Er", "Es", "Ev", "Ex", "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx", - "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp", "LP", - "Me", "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx", + "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp", + "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx", "Oc", "Oo", "Op", "Os", "Ot", "Ox", - "Pa", "Pc", "Pf", "Po", "Pp", "PP", "pp", "Pq", - "Qc", "Ql", "Qo", "Qq", "Or", "Rd", "Re", "Rs", "Rv", - "Sc", "Sf", "Sh", "SH", "Sm", "So", "Sq", + "Pa", "Pc", "Pf", "Po", "Pp", "Pq", + "Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv", + "Sc", "Sh", "Sm", "So", "Sq", "Ss", "St", "Sx", "Sy", "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr", - "%A", "%B", "%D", "%I", "%J", "%N", "%O", + "%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O", "%P", "%Q", "%R", "%T", "%U", "%V", NULL }; +/* not currently implemented: BT DE DS ME MT PT SY TQ YS */ const char *const __man_reserved[] = { - "AT", "B", "BI", "BR", "BT", "DE", "DS", "DT", - "EE", "EN", "EQ", "EX", "HF", "HP", "I", "IB", "IP", "IR", - "LP", "ME", "MT", "OP", "P", "PD", "PP", "PT", - "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS", "SY", - "TE", "TH", "TP", "TQ", "TS", "T&", "UC", "UE", "UR", "YS", + "AT", "B", "BI", "BR", "DT", + "EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR", + "LP", "OP", "P", "PD", "PP", + "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS", + "TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR", NULL }; @@ -397,7 +398,7 @@ roffnode_push(struct roff *r, enum rofft tok, const ch p->parent = r->last; p->line = line; p->col = col; - p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY; + p->rule = p->parent ? p->parent->rule : 0; r->last = p; } @@ -447,14 +448,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); } @@ -468,21 +464,17 @@ roff_free(struct roff *r) struct roff * -roff_alloc(enum mparset type, struct mparse *parse) +roff_alloc(struct mparse *parse, int options) { struct roff *r; - int i; r = mandoc_calloc(1, sizeof(struct roff)); - r->parsetype = type; r->parse = parse; + r->options = options; r->rstackpos = -1; roffhash_init(); - for (i = 0; i < PREDEFS_MAX; i++) - roff_setstr(r, predefs[i].name, predefs[i].str, 0); - return(r); } @@ -646,8 +638,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; @@ -793,14 +784,10 @@ roff_parse(struct roff *r, const char *buf, int *pos) '\t' == buf[*pos] || ' ' == buf[*pos]) return(ROFF_MAX); - /* - * We stop the macro parse at an escape, tab, space, or nil. - * However, `\}' is also a valid macro, so make sure we don't - * clobber it by seeing the `\' as the end of token. - */ + /* We stop the macro parse at an escape, tab, space, or nil. */ mac = buf + *pos; - maclen = strcspn(mac + 1, " \\\t\0") + 1; + maclen = strcspn(mac, " \\\t\0"); t = (r->current_string = roff_getstrn(r, mac, maclen)) ? ROFF_USERDEF : roffhash_find(mac, maclen); @@ -869,14 +856,13 @@ roffnode_cleanscope(struct roff *r) } -/* ARGSUSED */ -static enum rofferr -roff_ccond(ROFF_ARGS) +static void +roff_ccond(struct roff *r, int ln, int ppos) { if (NULL == r->last) { mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL); - return(ROFF_IGN); + return; } switch (r->last->tok) { @@ -888,20 +874,17 @@ roff_ccond(ROFF_ARGS) break; default: mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL); - return(ROFF_IGN); + return; } if (r->last->endspan > -1) { mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL); - return(ROFF_IGN); + return; } - if ((*bufp)[pos]) - mandoc_msg(MANDOCERR_ARGSLOST, r->parse, ln, pos, NULL); - roffnode_pop(r); roffnode_cleanscope(r); - return(ROFF_IGN); + return; } @@ -946,7 +929,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) @@ -1036,7 +1019,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); } @@ -1052,7 +1035,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); } @@ -1063,8 +1046,8 @@ static enum rofferr roff_cond_sub(ROFF_ARGS) { enum rofft t; - enum roffrule rr; char *ep; + int rr; rr = r->last->rule; roffnode_cleanscope(r); @@ -1076,37 +1059,31 @@ roff_cond_sub(ROFF_ARGS) */ if ((ROFF_MAX != t) && - (ROFF_ccond == t || ROFFRULE_ALLOW == rr || - ROFFMAC_STRUCT & roffs[t].flags)) { + (rr || ROFFMAC_STRUCT & roffs[t].flags)) { assert(roffs[t].proc); return((*roffs[t].proc)(r, t, bufp, szp, ln, ppos, pos, offs)); } - /* Always check for the closing delimiter `\}'. */ + /* + * If `\}' occurs on a macro line without a preceding macro, + * drop the line completely. + */ - ep = &(*bufp)[pos]; - while (NULL != (ep = strchr(ep, '\\'))) { - if ('}' != *(++ep)) - continue; + ep = *bufp + pos; + if ('\\' == ep[0] && '}' == ep[1]) + rr = 0; - /* - * If we're at the end of line, then just chop - * off the \} and resize the buffer. - * If we aren't, then convert it to spaces. - */ + /* Always check for the closing delimiter `\}'. */ - if ('\0' == *(ep + 1)) { - *--ep = '\0'; - *szp -= 2; - } else - *(ep - 1) = *ep = ' '; - - roff_ccond(r, ROFF_ccond, bufp, szp, - ln, pos, pos + 2, offs); - break; + while (NULL != (ep = strchr(ep, '\\'))) { + if ('}' == *(++ep)) { + *ep = '&'; + roff_ccond(r, ln, ep - *bufp - 1); + } + ++ep; } - return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); + return(rr ? ROFF_CONT : ROFF_IGN); } /* ARGSUSED */ @@ -1114,21 +1091,20 @@ static enum rofferr roff_cond_text(ROFF_ARGS) { char *ep; - enum roffrule rr; + int rr; rr = r->last->rule; roffnode_cleanscope(r); - ep = &(*bufp)[pos]; - for ( ; NULL != (ep = strchr(ep, '\\')); ep++) { - ep++; - if ('}' != *ep) - continue; - *ep = '&'; - roff_ccond(r, ROFF_ccond, bufp, szp, - ln, pos, pos + 2, offs); + ep = *bufp + pos; + while (NULL != (ep = strchr(ep, '\\'))) { + if ('}' == *(++ep)) { + *ep = '&'; + roff_ccond(r, ln, ep - *bufp - 1); + } + ++ep; } - return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); + return(rr ? ROFF_CONT : ROFF_IGN); } static int @@ -1181,64 +1157,103 @@ roff_getop(const char *v, int *pos, char *res) return(*res); } -static enum roffrule +/* + * Evaluate a string comparison condition. + * The first character is the delimiter. + * Succeed if the string up to its second occurrence + * matches the string up to its third occurence. + * Advance the cursor after the third occurrence + * or lacking that, to the end of the line. + */ +static int +roff_evalstrcond(const char *v, int *pos) +{ + const char *s1, *s2, *s3; + int match; + + match = 0; + s1 = v + *pos; /* initial delimiter */ + s2 = s1 + 1; /* for scanning the first string */ + s3 = strchr(s2, *s1); /* for scanning the second string */ + + if (NULL == s3) /* found no middle delimiter */ + goto out; + + while ('\0' != *++s3) { + if (*s2 != *s3) { /* mismatch */ + s3 = strchr(s3, *s1); + break; + } + if (*s3 == *s1) { /* found the final delimiter */ + match = 1; + break; + } + s2++; + } + +out: + if (NULL == s3) + s3 = strchr(s2, '\0'); + else + s3++; + *pos = s3 - v; + return(match); +} + +static int roff_evalcond(const char *v, int *pos) { - int not, lh, rh; + int wanttrue, lh, rh; char op; + if ('!' == v[*pos]) { + wanttrue = 0; + (*pos)++; + } else + wanttrue = 1; + switch (v[*pos]) { case ('n'): + /* FALLTHROUGH */ + case ('o'): (*pos)++; - return(ROFFRULE_ALLOW); + return(wanttrue); + case ('c'): + /* FALLTHROUGH */ + case ('d'): + /* FALLTHROUGH */ case ('e'): /* FALLTHROUGH */ - case ('o'): + case ('r'): /* FALLTHROUGH */ case ('t'): (*pos)++; - return(ROFFRULE_DENY); - case ('!'): - (*pos)++; - not = 1; - break; + return(!wanttrue); default: - not = 0; break; } if (!roff_getnum(v, pos, &lh)) - return ROFFRULE_DENY; - if (!roff_getop(v, pos, &op)) { - if (lh < 0) - lh = 0; - goto out; - } + return(roff_evalstrcond(v, pos) == wanttrue); + if (!roff_getop(v, pos, &op)) + return((lh > 0) == wanttrue); if (!roff_getnum(v, pos, &rh)) - return ROFFRULE_DENY; + return(0); + switch (op) { case 'g': - lh = lh >= rh; - break; + return((lh >= rh) == wanttrue); case 'l': - lh = lh <= rh; - break; + return((lh <= rh) == wanttrue); case '=': - lh = lh == rh; - break; + return((lh == rh) == wanttrue); case '>': - lh = lh > rh; - break; + return((lh > rh) == wanttrue); case '<': - lh = lh < rh; - break; + return((lh < rh) == wanttrue); default: - return ROFFRULE_DENY; + return(0); } -out: - if (not) - lh = !lh; - return lh ? ROFFRULE_ALLOW : ROFFRULE_DENY; } /* ARGSUSED */ @@ -1265,8 +1280,7 @@ roff_cond(ROFF_ARGS) */ r->last->rule = ROFF_el == tok ? - (r->rstackpos < 0 ? - ROFFRULE_DENY : r->rstack[r->rstackpos--]) : + (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) : roff_evalcond(*bufp, &pos); /* @@ -1280,15 +1294,13 @@ roff_cond(ROFF_ARGS) r->parse, ln, ppos, NULL); return(ROFF_ERR); } - r->rstack[++r->rstackpos] = - ROFFRULE_DENY == r->last->rule ? - ROFFRULE_ALLOW : ROFFRULE_DENY; + r->rstack[++r->rstackpos] = !r->last->rule; } /* If the parent has false as its rule, then so do we. */ - if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule) - r->last->rule = ROFFRULE_DENY; + if (r->last->parent && !r->last->parent->rule) + r->last->rule = 0; /* * Determine scope. @@ -1355,7 +1367,7 @@ 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); } @@ -1388,11 +1400,46 @@ roff_setreg(struct roff *r, const char *name, int val, reg->val = val; } +/* + * 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->val); @@ -1404,7 +1451,14 @@ 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)) @@ -1498,7 +1552,7 @@ roff_Dd(ROFF_ARGS) { const char *const *cp; - if (MPARSE_MDOC != r->parsetype) + if (0 == ((MPARSE_MDOC | MPARSE_QUICK) & r->options)) for (cp = __mdoc_reserved; *cp; cp++) roff_setstr(r, *cp, NULL, 0); @@ -1511,7 +1565,7 @@ roff_TH(ROFF_ARGS) { const char *const *cp; - if (MPARSE_MDOC != r->parsetype) + if (0 == (MPARSE_QUICK & r->options)) for (cp = __man_reserved; *cp; cp++) roff_setstr(r, *cp, NULL, 0); @@ -1733,7 +1787,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++) @@ -1813,22 +1867,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; @@ -1850,8 +1905,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; @@ -1864,7 +1918,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); @@ -1891,7 +1945,7 @@ roff_setstrn(struct roffkv **r, const char *name, size } /* Append terminating bytes. */ - if (multiline) + if (1 < append) *c++ = '\n'; *c = '\0'; @@ -1902,11 +1956,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); }