diff options
author | Ingo Schwarze <schwarze@cvs.openbsd.org> | 2014-03-08 04:43:40 +0000 |
---|---|---|
committer | Ingo Schwarze <schwarze@cvs.openbsd.org> | 2014-03-08 04:43:40 +0000 |
commit | c9173817bded99096dbc594042cf24715884e180 (patch) | |
tree | c4a0aeb5a32b77b22bf9693f2fb97fdfc1fb0220 | |
parent | 9a80470cd3802448eae1127e6d22b15319fc4e95 (diff) |
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-- | regress/usr.bin/mandoc/roff/cond/Makefile | 4 | ||||
-rw-r--r-- | regress/usr.bin/mandoc/roff/cond/strcmp.in | 37 | ||||
-rw-r--r-- | regress/usr.bin/mandoc/roff/cond/strcmp.out_ascii | 18 | ||||
-rw-r--r-- | share/man/man7/roff.7 | 113 | ||||
-rw-r--r-- | usr.bin/mandoc/roff.c | 143 |
5 files changed, 216 insertions, 99 deletions
diff --git a/regress/usr.bin/mandoc/roff/cond/Makefile b/regress/usr.bin/mandoc/roff/cond/Makefile index 6f4f653b392..11511142584 100644 --- a/regress/usr.bin/mandoc/roff/cond/Makefile +++ b/regress/usr.bin/mandoc/roff/cond/Makefile @@ -1,5 +1,5 @@ -# $OpenBSD: Makefile,v 1.4 2013/10/04 02:19:47 schwarze Exp $ +# $OpenBSD: Makefile,v 1.5 2014/03/08 04:43:39 schwarze Exp $ -REGRESS_TARGETS = if ie close numeric before-Dd +REGRESS_TARGETS = if ie close numeric strcmp before-Dd .include <bsd.regress.mk> diff --git a/regress/usr.bin/mandoc/roff/cond/strcmp.in b/regress/usr.bin/mandoc/roff/cond/strcmp.in new file mode 100644 index 00000000000..eabe80ffe63 --- /dev/null +++ b/regress/usr.bin/mandoc/roff/cond/strcmp.in @@ -0,0 +1,37 @@ +.TH COND-STRCMP 1 "March 8, 2014" OpenBSD +.SH NAME +cond-strcmp \- roff conditions involving string comparison +.SH DESCRIPTION +empty: +.ie """ (t) +.el (f) +one char: +.ie xaxax (t) +.el (f) +three chars: +.ie xabcxabcx (t) +.el (f) +.br +mismatch: +.ie xabcxabdx (t) +.el (f) +longer: +.ie xabcxabcdx (t) +.el (f) +shorter: +.ie xabcdxabcx (t) +.el (f) +.br +no middle: +.ie xabc (t) +.el (f) +no end: +.ie xabcxabc +.el (f) +.SS Negation +match: +.ie !xabcxabcx (t) +.el (f) +mismatch: +.ie !xaxbx (t) +.el (f) diff --git a/regress/usr.bin/mandoc/roff/cond/strcmp.out_ascii b/regress/usr.bin/mandoc/roff/cond/strcmp.out_ascii new file mode 100644 index 00000000000..d96b51b527a --- /dev/null +++ b/regress/usr.bin/mandoc/roff/cond/strcmp.out_ascii @@ -0,0 +1,18 @@ +COND-STRCMP(1) OpenBSD Reference Manual COND-STRCMP(1) + + + +NNAAMMEE + cond-strcmp - roff conditions involving string comparison + +DDEESSCCRRIIPPTTIIOONN + empty: (t) one char: (t) three chars: (t) + mismatch: (f) longer: (f) shorter: (f) + no middle: (f) no end: (f) + + NNeeggaattiioonn + match: (f) mismatch: (t) + + + +OpenBSD March 8, 2014 COND-STRCMP(1) diff --git a/share/man/man7/roff.7 b/share/man/man7/roff.7 index 7d76370b966..e4f8424afe9 100644 --- a/share/man/man7/roff.7 +++ b/share/man/man7/roff.7 @@ -1,4 +1,4 @@ -.\" $OpenBSD: roff.7,v 1.27 2014/02/14 23:50:49 schwarze Exp $ +.\" $OpenBSD: roff.7,v 1.28 2014/03/08 04:43:38 schwarze Exp $ .\" .\" Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> .\" Copyright (c) 2010, 2011, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: February 14 2014 $ +.Dd $Mdocdate: March 8 2014 $ .Dt ROFF 7 .Os .Sh NAME @@ -703,10 +703,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. @@ -724,44 +787,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 diff --git a/usr.bin/mandoc/roff.c b/usr.bin/mandoc/roff.c index b335ea866ad..d9994f642a8 100644 --- a/usr.bin/mandoc/roff.c +++ b/usr.bin/mandoc/roff.c @@ -1,4 +1,4 @@ -/* $Id: roff.c,v 1.70 2014/03/07 18:37:32 schwarze Exp $ */ +/* $Id: roff.c,v 1.71 2014/03/08 04:43:39 schwarze Exp $ */ /* * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org> @@ -72,11 +72,6 @@ enum rofft { ROFF_MAX }; -enum roffrule { - ROFFRULE_DENY, - ROFFRULE_ALLOW -}; - /* * An incredibly-simple string buffer. */ @@ -108,7 +103,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 */ @@ -132,7 +127,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 */ \ @@ -180,7 +175,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 *); @@ -397,7 +393,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; } @@ -1046,8 +1042,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); @@ -1059,8 +1055,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)); @@ -1073,7 +1068,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 `\}'. */ @@ -1084,7 +1079,7 @@ roff_cond_sub(ROFF_ARGS) } ++ep; } - return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); + return(rr ? ROFF_CONT : ROFF_IGN); } /* ARGSUSED */ @@ -1092,7 +1087,7 @@ static enum rofferr roff_cond_text(ROFF_ARGS) { char *ep; - enum roffrule rr; + int rr; rr = r->last->rule; roffnode_cleanscope(r); @@ -1105,7 +1100,7 @@ roff_cond_text(ROFF_ARGS) } ++ep; } - return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT); + return(rr ? ROFF_CONT : ROFF_IGN); } static int @@ -1158,64 +1153,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 */ @@ -1242,8 +1276,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); /* @@ -1257,15 +1290,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. |