]> git.cameronkatri.com Git - mandoc.git/blobdiff - roff.c
Minimal implementation of the read-only number register \n(.$
[mandoc.git] / roff.c
diff --git a/roff.c b/roff.c
index d671e5cd9ba72ed4aac20bcf6e30e5c31330ed2c..d28cea9c414b3238a37bd23daa9a9bfda67b795a 100644 (file)
--- a/roff.c
+++ b/roff.c
@@ -1,6 +1,6 @@
-/*     $Id: roff.c,v 1.266 2015/04/19 13:50:26 schwarze Exp $ */
+/*     $Id: roff.c,v 1.273 2015/08/29 20:26:04 schwarze Exp $ */
 /*
- * Copyright (c) 2009-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -32,7 +32,6 @@
 #include "libmandoc.h"
 #include "roff_int.h"
 #include "libroff.h"
-#include "libmdoc.h"
 
 /* Maximum number of nested if-else conditionals. */
 #define        RSTACK_MAX      128
@@ -336,6 +335,7 @@ struct      roff {
        int              rstacksz; /* current size limit of rstack */
        int              rstackpos; /* position in rstack */
        int              format; /* current file in mdoc or man format */
+       int              argc; /* number of args of the last macro */
        char             control; /* control character */
 };
 
@@ -398,8 +398,7 @@ 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 rofferr     roff_eqndelim(struct roff *, struct buf *, int);
-static int              roff_evalcond(struct roff *r, int,
-                               const char *, int *);
+static int              roff_evalcond(struct roff *r, int, char *, int *);
 static int              roff_evalnum(struct roff *, int,
                                const char *, int *, int *, int);
 static int              roff_evalpar(struct roff *, int,
@@ -413,9 +412,12 @@ static     int              roff_getnum(const char *, int *, 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 int              roff_getregro(const struct roff *,
+                               const char *name);
 static const char      *roff_getstrn(const struct roff *,
                                const char *, size_t);
+static int              roff_hasregn(const struct roff *,
+                               const char *, size_t);
 static enum rofferr     roff_insec(ROFF_ARGS);
 static enum rofferr     roff_it(ROFF_ARGS);
 static enum rofferr     roff_line_ignore(ROFF_ARGS);
@@ -1023,6 +1025,7 @@ roff_node_append(struct roff_man *man, struct roff_node *n)
                /* NOTREACHED */
        }
        n->parent->nchild++;
+       n->parent->last = n;
 
        /*
         * Copy over the normalised-data pointer of our parent.  Not
@@ -1067,6 +1070,57 @@ roff_node_append(struct roff_man *man, struct roff_node *n)
        man->last = n;
 }
 
+void
+roff_word_alloc(struct roff_man *man, int line, int pos, const char *word)
+{
+       struct roff_node        *n;
+
+       n = roff_node_alloc(man, line, pos, ROFFT_TEXT, TOKEN_NONE);
+       n->string = roff_strdup(man->roff, word);
+       roff_node_append(man, n);
+       if (man->macroset == MACROSET_MDOC)
+               mdoc_valid_post(man);
+       else
+               man_valid_post(man);
+       man->next = ROFF_NEXT_SIBLING;
+}
+
+void
+roff_word_append(struct roff_man *man, const char *word)
+{
+       struct roff_node        *n;
+       char                    *addstr, *newstr;
+
+       n = man->last;
+       addstr = roff_strdup(man->roff, word);
+       mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
+       free(addstr);
+       free(n->string);
+       n->string = newstr;
+       man->next = ROFF_NEXT_SIBLING;
+}
+
+void
+roff_elem_alloc(struct roff_man *man, int line, int pos, int tok)
+{
+       struct roff_node        *n;
+
+       n = roff_node_alloc(man, line, pos, ROFFT_ELEM, tok);
+       roff_node_append(man, n);
+       man->next = ROFF_NEXT_CHILD;
+}
+
+struct roff_node *
+roff_block_alloc(struct roff_man *man, int line, int pos, int tok)
+{
+       struct roff_node        *n;
+
+       n = roff_node_alloc(man, line, pos, ROFFT_BLOCK, tok);
+       roff_node_append(man, n);
+       man->next = ROFF_NEXT_CHILD;
+       return(n);
+}
+
 struct roff_node *
 roff_head_alloc(struct roff_man *man, int line, int pos, int tok)
 {
@@ -1089,6 +1143,36 @@ roff_body_alloc(struct roff_man *man, int line, int pos, int tok)
        return(n);
 }
 
+void
+roff_addeqn(struct roff_man *man, const struct eqn *eqn)
+{
+       struct roff_node        *n;
+
+       n = roff_node_alloc(man, eqn->ln, eqn->pos, ROFFT_EQN, TOKEN_NONE);
+       n->eqn = eqn;
+       if (eqn->ln > man->last->line)
+               n->flags |= MDOC_LINE;
+       roff_node_append(man, n);
+       man->next = ROFF_NEXT_SIBLING;
+}
+
+void
+roff_addtbl(struct roff_man *man, const struct tbl_span *tbl)
+{
+       struct roff_node        *n;
+
+       if (man->macroset == MACROSET_MAN)
+               man_breakscope(man, TOKEN_NONE);
+       n = roff_node_alloc(man, tbl->line, 0, ROFFT_TBL, TOKEN_NONE);
+       n->span = tbl;
+       roff_node_append(man, n);
+       if (man->macroset == MACROSET_MDOC)
+               mdoc_valid_post(man);
+       else
+               man_valid_post(man);
+       man->next = ROFF_NEXT_SIBLING;
+}
+
 void
 roff_node_unlink(struct roff_man *man, struct roff_node *n)
 {
@@ -1150,6 +1234,52 @@ roff_node_delete(struct roff_man *man, struct roff_node *n)
        roff_node_free(n);
 }
 
+void
+deroff(char **dest, const struct roff_node *n)
+{
+       char    *cp;
+       size_t   sz;
+
+       if (n->type != ROFFT_TEXT) {
+               for (n = n->child; n != NULL; n = n->next)
+                       deroff(dest, n);
+               return;
+       }
+
+       /* Skip leading whitespace and escape sequences. */
+
+       cp = n->string;
+       while (*cp != '\0') {
+               if ('\\' == *cp) {
+                       cp++;
+                       mandoc_escape((const char **)&cp, NULL, NULL);
+               } else if (isspace((unsigned char)*cp))
+                       cp++;
+               else
+                       break;
+       }
+
+       /* Skip trailing whitespace. */
+
+       for (sz = strlen(cp); sz; sz--)
+               if ( ! isspace((unsigned char)cp[sz-1]))
+                       break;
+
+       /* Skip empty strings. */
+
+       if (sz == 0)
+               return;
+
+       if (*dest == NULL) {
+               *dest = mandoc_strndup(cp, sz);
+               return;
+       }
+
+       mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
+       free(*dest);
+       *dest = cp;
+}
+
 /* --- main functions of the roff parser ---------------------------------- */
 
 /*
@@ -2007,8 +2137,10 @@ out:
  * or string condition.
  */
 static int
-roff_evalcond(struct roff *r, int ln, const char *v, int *pos)
+roff_evalcond(struct roff *r, int ln, char *v, int *pos)
 {
+       char    *cp, *name;
+       size_t   sz;
        int      number, savepos, wanttrue;
 
        if ('!' == v[*pos]) {
@@ -2031,13 +2163,16 @@ roff_evalcond(struct roff *r, int ln, const char *v, int *pos)
                /* FALLTHROUGH */
        case 'e':
                /* FALLTHROUGH */
-       case 'r':
-               /* FALLTHROUGH */
        case 't':
                /* FALLTHROUGH */
        case 'v':
                (*pos)++;
                return(!wanttrue);
+       case 'r':
+               cp = name = v + ++*pos;
+               sz = roff_getname(r, &cp, ln, *pos);
+               *pos = cp - v;
+               return((sz && roff_hasregn(r, name, sz)) == wanttrue);
        default:
                break;
        }
@@ -2132,6 +2267,8 @@ roff_cond(ROFF_ARGS)
        if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
                r->last->endspan = -1;
                pos += 2;
+               while (buf->buf[pos] == ' ')
+                       pos++;
                goto out;
        }
 
@@ -2440,10 +2577,12 @@ roff_setreg(struct roff *r, const char *name, int val, char sign)
  * were to turn up, another special value would have to be chosen.
  */
 static int
-roff_getregro(const char *name)
+roff_getregro(const struct roff *r, const char *name)
 {
 
        switch (*name) {
+       case '$':  /* Number of arguments of the last macro evaluated. */
+               return(r->argc);
        case 'A':  /* ASCII approximation mode is always off. */
                return(0);
        case 'g':  /* Groff compatibility mode is always on. */
@@ -2468,7 +2607,7 @@ roff_getreg(const struct roff *r, const char *name)
        int              val;
 
        if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) {
-               val = roff_getregro(name + 1);
+               val = roff_getregro(r, name + 1);
                if (-1 != val)
                        return (val);
        }
@@ -2487,7 +2626,7 @@ roff_getregn(const struct roff *r, const char *name, size_t len)
        int              val;
 
        if ('.' == name[0] && 2 == len) {
-               val = roff_getregro(name + 1);
+               val = roff_getregro(r, name + 1);
                if (-1 != val)
                        return (val);
        }
@@ -2500,6 +2639,26 @@ roff_getregn(const struct roff *r, const char *name, size_t len)
        return(0);
 }
 
+static int
+roff_hasregn(const struct roff *r, const char *name, size_t len)
+{
+       struct roffreg  *reg;
+       int              val;
+
+       if ('.' == name[0] && 2 == len) {
+               val = roff_getregro(r, name + 1);
+               if (-1 != val)
+                       return(1);
+       }
+
+       for (reg = r->regtab; reg; reg = reg->next)
+               if (len == reg->key.sz &&
+                   0 == strncmp(name, reg->key.p, len))
+                       return(1);
+
+       return(0);
+}
+
 static void
 roff_freereg(struct roffreg *reg)
 {
@@ -2929,10 +3088,16 @@ roff_userdef(ROFF_ARGS)
         * and NUL-terminate them.
         */
 
+       r->argc = 0;
        cp = buf->buf + pos;
-       for (i = 0; i < 9; i++)
-               arg[i] = *cp == '\0' ? "" :
-                   mandoc_getarg(r->parse, &cp, ln, &pos);
+       for (i = 0; i < 9; i++) {
+               if (*cp == '\0')
+                       arg[i] = "";
+               else {
+                       arg[i] = mandoc_getarg(r->parse, &cp, ln, &pos);
+                       r->argc = i + 1;
+               }
+       }
 
        /*
         * Expand macro arguments.