diff options
author | Ingo Schwarze <schwarze@openbsd.org> | 2014-03-08 04:43:54 +0000 |
---|---|---|
committer | Ingo Schwarze <schwarze@openbsd.org> | 2014-03-08 04:43:54 +0000 |
commit | bdbb7d0355bd86cdd2bcaf08293edf32a911ebe5 (patch) | |
tree | 783b4adb0fd04a6698a987dd2282e5f9805dc654 | |
parent | d3bc35e0fd4d9bed56d9663c0b330ceac96e4a9f (diff) | |
download | mandoc-bdbb7d0355bd86cdd2bcaf08293edf32a911ebe5.tar.gz |
Improve .if/.ie condition handling.
* Support string comparisons.
* Support negation not only for numerical, but for all conditions.
* Switch the `o' condition from false to true.
* Handle the `c', `d', and `r' conditions as false for now.
* Use int for boolean data instead of rolling our own "enum roffrule";
needed such that we can use the standard ! and == operators.
Havard Eidnes reported via the NetBSD bug tracking system that some
Tcl*(3) manuals need this, and Thomas Klausner <wiz at NetBSD>
forwarded the report to me. This doesn't make the crazy Tcl*(3)
macrology maze happy yet, but brings us a bit closer.
-rw-r--r-- | roff.7 | 109 | ||||
-rw-r--r-- | roff.c | 141 |
2 files changed, 156 insertions, 94 deletions
@@ -677,10 +677,73 @@ Its syntax is equivalent to .Sx \&if . .Ss \&if Begins a conditional. -Right now, the conditional evaluates to true -if and only if it starts with the letter -.Sy n , -indicating processing in nroff style as opposed to troff style. +This request has the following syntax: +.Bd -literal -offset indent +\&.if COND BODY +.Ed +.Bd -literal -offset indent +\&.if COND \e{BODY +BODY...\e} +.Ed +.Bd -literal -offset indent +\&.if COND \e{\e +BODY... +\&.\e} +.Ed +.Pp +COND is a conditional statement. +Currently, +.Xr mandoc 1 +supports the following subset of roff conditionals: +.Bl -bullet +.It +If +.Sq \&! +is prefixed to COND, the condition is logically inverted. +.It +If the first character of COND is +.Sq n +.Pq nroff mode +or +.Sq o +.Pq odd page , +COND evaluates to true. +.It +If the first character of COND is +.Sq c +.Pq character available , +.Sq d +.Pq string defined , +.Sq e +.Pq even page , +.Sq r +.Pq register accessed , +or +.Sq t +.Pq troff mode , +COND evaluates to false. +.It +If COND starts with a digit, optionally prefixed by a minus sign, +it is evaluated as a numerical expression of the form +.Ar number operator number , +where +.Ar operator +is one of +.Sq < , +.Sq <= , +.Sq = , +.Sq >= , +or +.Sq > . +.It +Otherwise, the first character of COND is regarded as a delimiter +and COND evaluates to true if the string extending from its first +to its second occurrence is equal to the string extending from its +second to its third occurrence. +.It +If COND cannot be parsed, it evaluates to false. +.El +.Pp If a conditional is false, its children are not processed, but are syntactically interpreted to preserve the integrity of the input document. @@ -698,44 +761,12 @@ will continue to syntactically interpret to the block close of the final conditional. Sub-conditionals, in this case, obviously inherit the truth value of the parent. -This request has the following syntax: -.Bd -literal -offset indent -\&.if COND \e{\e -BODY... -\&.\e} -.Ed -.Bd -literal -offset indent -\&.if COND \e{ BODY -BODY... \e} -.Ed -.Bd -literal -offset indent -\&.if COND \e{ BODY -BODY... -\&.\e} -.Ed -.Bd -literal -offset indent -\&.if COND \e -BODY -.Ed -.Pp -COND is a conditional statement. -roff allows for complicated conditionals; mandoc is much simpler. -At this time, mandoc supports only -.Sq n , -evaluating to true; -and -.Sq t , -.Sq e , -and -.Sq o , -evaluating to false. -All other invocations are read up to the next end of line or space and -evaluate as false. .Pp If the BODY section is begun by an escaped brace .Sq \e{ , -scope continues until a closing-brace escape sequence -.Sq \.\e} . +scope continues until the end of the input line containing the +matching closing-brace escape sequence +.Sq \e} . If the BODY is not enclosed in braces, scope continues until the end of the line. If the COND is followed by a BODY on the same line, whether after a @@ -76,11 +76,6 @@ enum rofft { ROFF_MAX }; -enum roffrule { - ROFFRULE_DENY, - ROFFRULE_ALLOW -}; - /* * An incredibly-simple string buffer. */ @@ -112,7 +107,7 @@ struct roff { 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 */ + int rstack[RSTACK_MAX]; /* stack of !`ie' rules */ char control; /* control character */ int rstackpos; /* position in rstack */ struct roffreg *regtab; /* number registers */ @@ -136,7 +131,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 */ \ @@ -184,7 +179,8 @@ 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 *); @@ -401,7 +397,7 @@ roffnode_push(struct roff *r, enum rofft tok, const char *name, 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; } @@ -1050,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); @@ -1063,8 +1059,7 @@ roff_cond_sub(ROFF_ARGS) */ if ((ROFF_MAX != 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)); @@ -1077,7 +1072,7 @@ roff_cond_sub(ROFF_ARGS) ep = *bufp + pos; if ('\\' == ep[0] && '}' == ep[1]) - rr = ROFFRULE_DENY; + rr = 0; /* Always check for the closing delimiter `\}'. */ @@ -1088,7 +1083,7 @@ roff_cond_sub(ROFF_ARGS) } ++ep; } - return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); + return(rr ? ROFF_CONT : ROFF_IGN); } /* ARGSUSED */ @@ -1096,7 +1091,7 @@ static enum rofferr roff_cond_text(ROFF_ARGS) { char *ep; - enum roffrule rr; + int rr; rr = r->last->rule; roffnode_cleanscope(r); @@ -1109,7 +1104,7 @@ roff_cond_text(ROFF_ARGS) } ++ep; } - return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); + return(rr ? ROFF_CONT : ROFF_IGN); } static int @@ -1162,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 */ @@ -1246,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); /* @@ -1261,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. |