summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@cvs.openbsd.org>2014-03-08 04:43:40 +0000
committerIngo Schwarze <schwarze@cvs.openbsd.org>2014-03-08 04:43:40 +0000
commitc9173817bded99096dbc594042cf24715884e180 (patch)
treec4a0aeb5a32b77b22bf9693f2fb97fdfc1fb0220
parent9a80470cd3802448eae1127e6d22b15319fc4e95 (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/Makefile4
-rw-r--r--regress/usr.bin/mandoc/roff/cond/strcmp.in37
-rw-r--r--regress/usr.bin/mandoc/roff/cond/strcmp.out_ascii18
-rw-r--r--share/man/man7/roff.7113
-rw-r--r--usr.bin/mandoc/roff.c143
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.