summaryrefslogtreecommitdiffstats
path: root/roff.c
diff options
context:
space:
mode:
authorKristaps Dzonsons <kristaps@bsd.lv>2010-06-10 21:42:02 +0000
committerKristaps Dzonsons <kristaps@bsd.lv>2010-06-10 21:42:02 +0000
commit92fc4a31da7eb79879434d883684209266ac3ff2 (patch)
treee95551d6c9052f1c92423aee19c07cd2adb86de7 /roff.c
parentb33e04fa7bbe5f17c54a8a302bf93d8a56784800 (diff)
downloadmandoc-92fc4a31da7eb79879434d883684209266ac3ff2.tar.gz
Fix adding some minimal intelligence to conditional parser. See roff.7
for what's supported. This simplified the roff_cond() function quite nicely. From a bug report by uqs@. Added regression test based on bug-report example by uqs@. Also added ROFF_DEBUG to see what the hell the parser is actually doing. Obviously turned off by default.
Diffstat (limited to 'roff.c')
-rw-r--r--roff.c121
1 files changed, 88 insertions, 33 deletions
diff --git a/roff.c b/roff.c
index a3a5b550..f0c23664 100644
--- a/roff.c
+++ b/roff.c
@@ -32,6 +32,14 @@
#define ROFF_CTL(c) \
('.' == (c) || '\'' == (c))
+#if 1
+#define ROFF_DEBUG(fmt, args...) \
+ do { /* Nothing. */ } while (/*CONSTCOND*/ 0)
+#else
+#define ROFF_DEBUG(fmt, args...) \
+ do { fprintf(stderr, fmt , ##args); } while (/*CONSTCOND*/ 0)
+#endif
+
enum rofft {
ROFF_am,
ROFF_ami,
@@ -103,6 +111,7 @@ static enum rofferr roff_ccond(ROFF_ARGS);
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 roffrule roff_evalcond(const char *, int *);
static enum rofferr roff_line(ROFF_ARGS);
/* See roff_hash_find() */
@@ -306,10 +315,15 @@ roff_parseln(struct roff *r, int ln,
if (r->last && ! ROFF_CTL((*bufp)[pos])) {
t = r->last->tok;
assert(roffs[t].text);
+ ROFF_DEBUG("roff: intercept scoped text: %s, [%s]\n",
+ roffs[t].name, &(*bufp)[pos]);
return((*roffs[t].text)
(r, t, bufp, szp, ln, pos, pos, offs));
- } else if ( ! ROFF_CTL((*bufp)[pos]))
+ } else if ( ! ROFF_CTL((*bufp)[pos])) {
+ ROFF_DEBUG("roff: pass non-scoped text: [%s]\n",
+ &(*bufp)[pos]);
return(ROFF_CONT);
+ }
/*
* If a scope is open, go to the child handler for that macro,
@@ -319,6 +333,8 @@ roff_parseln(struct roff *r, int ln,
if (r->last) {
t = r->last->tok;
assert(roffs[t].sub);
+ ROFF_DEBUG("roff: intercept scoped context: %s\n",
+ roffs[t].name);
return((*roffs[t].sub)
(r, t, bufp, szp, ln, pos, pos, offs));
}
@@ -330,9 +346,14 @@ roff_parseln(struct roff *r, int ln,
*/
ppos = pos;
- if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
+ if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) {
+ ROFF_DEBUG("roff: pass non-scoped non-macro: [%s]\n",
+ &(*bufp)[pos]);
return(ROFF_CONT);
+ }
+ ROFF_DEBUG("roff: intercept new-scope: %s, [%s]\n",
+ roffs[t].name, &(*bufp)[pos]);
assert(roffs[t].proc);
return((*roffs[t].proc)
(r, t, bufp, szp, ln, ppos, pos, offs));
@@ -684,12 +705,37 @@ roff_cond_text(ROFF_ARGS)
}
+static enum roffrule
+roff_evalcond(const char *v, int *pos)
+{
+
+ switch (v[*pos]) {
+ case ('n'):
+ (*pos)++;
+ return(ROFFRULE_ALLOW);
+ case ('e'):
+ /* FALLTHROUGH */
+ case ('o'):
+ /* FALLTHROUGH */
+ case ('t'):
+ (*pos)++;
+ return(ROFFRULE_DENY);
+ default:
+ break;
+ }
+
+ while (v[*pos] && ' ' != v[*pos])
+ (*pos)++;
+ return(ROFFRULE_DENY);
+}
+
+
/* ARGSUSED */
static enum rofferr
roff_cond(ROFF_ARGS)
{
- int cpos; /* position of the condition */
int sv;
+ enum roffrule rule;
/* Stack overflow! */
@@ -698,20 +744,22 @@ roff_cond(ROFF_ARGS)
return(ROFF_ERR);
}
- cpos = pos;
+ /* First, evaluate the conditional. */
- if (ROFF_if == tok || ROFF_ie == tok) {
- /*
- * Read ahead past the conditional. FIXME: this does
- * not work, as conditionals don't end on whitespace,
- * but are parsed according to a formal grammar. It's
- * good enough for now, however.
- */
- while ((*bufp)[pos] && ' ' != (*bufp)[pos])
- pos++;
- }
+ if (ROFF_el == tok) {
+ /*
+ * An `.el' will get the value of the current rstack
+ * entry set in prior `ie' calls or defaults to DENY.
+ */
+ if (r->rstackpos < 0)
+ rule = ROFFRULE_DENY;
+ else
+ rule = r->rstack[r->rstackpos];
+ } else
+ rule = roff_evalcond(*bufp, &pos);
sv = pos;
+
while (' ' == (*bufp)[pos])
pos++;
@@ -721,30 +769,21 @@ roff_cond(ROFF_ARGS)
* really doing anything. Warn about this. It's probably
* wrong.
*/
+
if ('\0' == (*bufp)[pos] && sv != pos) {
- if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
- return(ROFF_ERR);
- return(ROFF_IGN);
+ if ((*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
+ return(ROFF_IGN);
+ return(ROFF_ERR);
}
if ( ! roffnode_push(r, tok, ln, ppos))
return(ROFF_ERR);
- /* XXX: Implement more conditionals. */
+ r->last->rule = rule;
+
+ ROFF_DEBUG("roff: cond: %s -> %s\n", roffs[tok].name,
+ ROFFRULE_ALLOW == rule ? "allow" : "deny");
- if (ROFF_if == tok || ROFF_ie == tok)
- r->last->rule = 'n' == (*bufp)[cpos] ?
- ROFFRULE_ALLOW : ROFFRULE_DENY;
- else if (ROFF_el == tok) {
- /*
- * An `.el' will get the value of the current rstack
- * entry set in prior `ie' calls or defaults to DENY.
- */
- if (r->rstackpos < 0)
- r->last->rule = ROFFRULE_DENY;
- else
- r->last->rule = r->rstack[r->rstackpos];
- }
if (ROFF_ie == tok) {
/*
* An if-else will put the NEGATION of the current
@@ -756,15 +795,31 @@ roff_cond(ROFF_ARGS)
else
r->rstack[r->rstackpos] = ROFFRULE_DENY;
}
- if (r->last->parent && ROFFRULE_DENY == r->last->parent->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;
+ ROFF_DEBUG("roff: cond override: %s -> deny\n",
+ roffs[tok].name);
+ }
+
+ /*
+ * Determine scope. If we're invoked with "\{" trailing the
+ * conditional, then we're in a multiline scope. Else our scope
+ * expires on the next line.
+ */
r->last->endspan = 1;
if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
r->last->endspan = -1;
pos += 2;
- }
+ ROFF_DEBUG("roff: cond-scope: %s, multi-line\n",
+ roffs[tok].name);
+ } else
+ ROFF_DEBUG("roff: cond-scope: %s, one-line\n",
+ roffs[tok].name);
/*
* If there are no arguments on the line, the next-line scope is