]> git.cameronkatri.com Git - mandoc.git/blobdiff - mdoc_term.c
suboptarg is not portable to Linux (fixed).
[mandoc.git] / mdoc_term.c
index 9b40a4ca02fb593488fceabca28b340d9e83c972..e7329d2a8dd5cdad0ba8d1195e1e56ad391e665e 100644 (file)
@@ -1,20 +1,18 @@
-/* $Id: mdoc_term.c,v 1.3 2009/03/26 16:23:22 kristaps Exp $ */
+/*     $Id: mdoc_term.c,v 1.20 2009/07/04 11:07:34 kristaps Exp $ */
 /*
- * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
+ * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
  *
  * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the
- * above copyright notice and this permission notice appear in all
- * copies.
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
  *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
- * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 #include <sys/types.h>
 
@@ -88,10 +86,10 @@ struct      termpair {
        struct termpair  *ppair;
        int               type;
 #define        TERMPAIR_FLAG    (1 << 0)
-       int               flag;
-       size_t            offset;
-       size_t            rmargin;
-       int               count;
+       int               flag;         /* Whether being used. */
+       size_t            offset;       /* Left margin. */
+       size_t            rmargin;      /* Right margin. */
+       int               count;        /* Enum count. */
 };
 
 #define        TERMPAIR_SETFLAG(termp, p, fl) \
@@ -185,7 +183,7 @@ struct      termact {
 };
 
 static const struct termact termacts[MDOC_MAX] = {
-       { NULL, NULL }, /* \" */
+       { termp_ap_pre, NULL }, /* Ap */
        { NULL, NULL }, /* Dd */
        { NULL, NULL }, /* Dt */
        { NULL, NULL }, /* Os */
@@ -292,8 +290,7 @@ static const struct termact termacts[MDOC_MAX] = {
        { NULL, NULL }, /* Fr */
        { termp_ud_pre, NULL }, /* Ud */
        { termp_lb_pre, termp_lb_post }, /* Lb */
-       { termp_ap_pre, NULL }, /* Lb */
-       { termp_pp_pre, NULL }, /* Pp */ 
+       { termp_pp_pre, NULL }, /* Lp */ 
        { termp_lk_pre, NULL }, /* Lk */ 
        { termp_mt_pre, NULL }, /* Mt */ 
        { termp_brq_pre, termp_brq_post }, /* Brq */ 
@@ -307,8 +304,8 @@ static const struct termact termacts[MDOC_MAX] = {
 };
 
 #ifdef __linux__
-extern size_t            strlcpy(char *, const char *, size_t);
-extern size_t            strlcat(char *, const char *, size_t);
+extern size_t    strlcpy(char *, const char *, size_t);
+extern size_t    strlcat(char *, const char *, size_t);
 #endif
 
 static int       arg_hasattr(int, const struct mdoc_node *);
@@ -327,15 +324,21 @@ static    void      print_head(struct termp *,
 static void      print_body(DECL_ARGS);
 static void      print_foot(struct termp *, 
                        const struct mdoc_meta *);
-static void      sanity(const struct mdoc_node *);
 
 
 int
 mdoc_run(struct termp *p, const struct mdoc *m)
 {
+       /*
+        * Main output function.  When this is called, assume that the
+        * tree is properly formed.
+        */
 
        print_head(p, mdoc_meta(m));
-       print_body(p, NULL, mdoc_meta(m), mdoc_node(m));
+       assert(mdoc_node(m));
+       assert(MDOC_ROOT == mdoc_node(m)->type);
+       if (mdoc_node(m)->child)
+               print_body(p, NULL, mdoc_meta(m), mdoc_node(m)->child);
        print_foot(p, mdoc_meta(m));
        return(1);
 }
@@ -358,10 +361,6 @@ print_node(DECL_ARGS)
        int              dochild;
        struct termpair  npair;
 
-       /* Some quick sanity-checking. */
-
-       sanity(node);
-
        /* Pre-processing. */
 
        dochild = 1;
@@ -403,6 +402,14 @@ print_foot(struct termp *p, const struct mdoc_meta *meta)
        struct tm       *tm;
        char            *buf, *os;
 
+       /* 
+        * Output the footer in new-groff style, that is, three columns
+        * with the middle being the manual date and flanking columns
+        * being the operating system:
+        *
+        * SYSTEM                  DATE                    SYSTEM
+        */
+
        if (NULL == (buf = malloc(p->rmargin)))
                err(1, "malloc");
        if (NULL == (os = malloc(p->rmargin)))
@@ -410,39 +417,39 @@ print_foot(struct termp *p, const struct mdoc_meta *meta)
 
        tm = localtime(&meta->date);
 
-#ifdef __OpenBSD__
-       if (NULL == strftime(buf, p->rmargin, "%B %d, %Y", tm))
-#else
        if (0 == strftime(buf, p->rmargin, "%B %d, %Y", tm))
-#endif
                err(1, "strftime");
 
        (void)strlcpy(os, meta->os, p->rmargin);
 
-       /*
-        * This is /slightly/ different from regular groff output
-        * because we don't have page numbers.  Print the following:
-        *
-        * OS                                            MDOCDATE
-        */
-
        term_vspace(p);
 
-       p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
-       p->rmargin = p->maxrmargin - strlen(buf);
        p->offset = 0;
+       p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
+       p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
 
        term_word(p, os);
        term_flushln(p);
 
+       p->offset = p->rmargin;
+       p->rmargin = p->maxrmargin - strlen(os);
        p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
+
+       term_word(p, buf);
+       term_flushln(p);
+
        p->offset = p->rmargin;
        p->rmargin = p->maxrmargin;
        p->flags &= ~TERMP_NOBREAK;
+       p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
 
-       term_word(p, buf);
+       term_word(p, os);
        term_flushln(p);
 
+       p->offset = 0;
+       p->rmargin = p->maxrmargin;
+       p->flags = 0;
+
        free(buf);
        free(os);
 }
@@ -487,15 +494,15 @@ print_head(struct termp *p, const struct mdoc_meta *meta)
                        meta->title, meta->msec);
 
        p->offset = 0;
-       p->rmargin = (p->maxrmargin - strlen(buf)) / 2;
+       p->rmargin = (p->maxrmargin - strlen(buf) + 1) / 2;
        p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
 
        term_word(p, title);
        term_flushln(p);
 
-       p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
        p->offset = p->rmargin;
        p->rmargin = p->maxrmargin - strlen(title);
+       p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
 
        term_word(p, buf);
        term_flushln(p);
@@ -508,8 +515,8 @@ print_head(struct termp *p, const struct mdoc_meta *meta)
        term_word(p, title);
        term_flushln(p);
 
-       p->rmargin = p->maxrmargin;
        p->offset = 0;
+       p->rmargin = p->maxrmargin;
        p->flags &= ~TERMP_NOSPACE;
 
        free(title);
@@ -517,119 +524,6 @@ print_head(struct termp *p, const struct mdoc_meta *meta)
 }
 
 
-static void
-sanity(const struct mdoc_node *n)
-{
-       char            *p;
-
-       p = "regular form violated";
-
-       switch (n->type) {
-       case (MDOC_TEXT):
-               if (n->child) 
-                       errx(1, p);
-               if (NULL == n->parent) 
-                       errx(1, p);
-               if (NULL == n->string)
-                       errx(1, p);
-               switch (n->parent->type) {
-               case (MDOC_TEXT):
-                       /* FALLTHROUGH */
-               case (MDOC_ROOT):
-                       errx(1, p);
-                       /* NOTREACHED */
-               default:
-                       break;
-               }
-               break;
-       case (MDOC_ELEM):
-               if (NULL == n->parent)
-                       errx(1, p);
-               switch (n->parent->type) {
-               case (MDOC_TAIL):
-                       /* FALLTHROUGH */
-               case (MDOC_BODY):
-                       /* FALLTHROUGH */
-               case (MDOC_HEAD):
-                       break;
-               default:
-                       errx(1, p);
-                       /* NOTREACHED */
-               }
-               if (n->child) switch (n->child->type) {
-               case (MDOC_TEXT):
-                       break;
-               default:
-                       errx(1, p);
-                       /* NOTREACHED */
-               }
-               break;
-       case (MDOC_HEAD):
-               /* FALLTHROUGH */
-       case (MDOC_BODY):
-               /* FALLTHROUGH */
-       case (MDOC_TAIL):
-               if (NULL == n->parent)
-                       errx(1, p);
-               if (MDOC_BLOCK != n->parent->type)
-                       errx(1, p);
-               if (n->child) switch (n->child->type) {
-               case (MDOC_BLOCK):
-                       /* FALLTHROUGH */
-               case (MDOC_ELEM):
-                       /* FALLTHROUGH */
-               case (MDOC_TEXT):
-                       break;
-               default:
-                       errx(1, p);
-                       /* NOTREACHED */
-               }
-               break;
-       case (MDOC_BLOCK):
-               if (NULL == n->parent)
-                       errx(1, p);
-               if (NULL == n->child)
-                       errx(1, p);
-               switch (n->parent->type) {
-               case (MDOC_ROOT):
-                       /* FALLTHROUGH */
-               case (MDOC_HEAD):
-                       /* FALLTHROUGH */
-               case (MDOC_BODY):
-                       /* FALLTHROUGH */
-               case (MDOC_TAIL):
-                       break;
-               default:
-                       errx(1, p);
-                       /* NOTREACHED */
-               }
-               switch (n->child->type) {
-               case (MDOC_ROOT):
-                       /* FALLTHROUGH */
-               case (MDOC_ELEM):
-                       errx(1, p);
-                       /* NOTREACHED */
-               default:
-                       break;
-               }
-               break;
-       case (MDOC_ROOT):
-               if (n->parent)
-                       errx(1, p);
-               if (NULL == n->child)
-                       errx(1, p);
-               switch (n->child->type) {
-               case (MDOC_BLOCK):
-                       break;
-               default:
-                       errx(1, p);
-                       /* NOTREACHED */
-               }
-               break;
-       }
-}
-
-
 static size_t
 arg_width(const struct mdoc_argv *arg, int pos)
 {
@@ -639,9 +533,9 @@ arg_width(const struct mdoc_argv *arg, int pos)
        assert(pos < (int)arg->sz && pos >= 0);
        assert(arg->value[pos]);
        if (0 == strcmp(arg->value[pos], "indent"))
-               return(INDENT);
+               return(INDENT + 3);
        if (0 == strcmp(arg->value[pos], "indent-two"))
-               return(INDENT * 2);
+               return(INDENT * 2 + 2);
 
        if (0 == (len = (int)strlen(arg->value[pos])))
                return(0);
@@ -651,13 +545,14 @@ arg_width(const struct mdoc_argv *arg, int pos)
                        break;
 
        if (i == len - 1) {
-               if ('n' == arg->value[pos][len - 1]) {
+               if ('n' == arg->value[pos][len - 1] ||
+                               'm' == arg->value[pos][len - 1]) {
                        v = (size_t)atoi(arg->value[pos]);
-                       return(v);
+                       return(v + 2);
                }
 
        }
-       return(strlen(arg->value[pos]) + 1);
+       return(strlen(arg->value[pos]) + 2);
 }
 
 
@@ -696,6 +591,8 @@ arg_listtype(const struct mdoc_node *n)
                        break;
                }
 
+       /* FIXME: mandated by parser. */
+
        errx(1, "list type not supported");
        /* NOTREACHED */
 }
@@ -706,10 +603,15 @@ arg_offset(const struct mdoc_argv *arg)
 {
 
        assert(*arg->value);
+       if (0 == strcmp(*arg->value, "left"))
+               return(0);
        if (0 == strcmp(*arg->value, "indent"))
-               return(INDENT);
+               return(INDENT + 1);
        if (0 == strcmp(*arg->value, "indent-two"))
-               return(INDENT * 2);
+               return((INDENT + 1) * 2);
+
+       /* FIXME: needs to support field-widths (10n, etc.). */
+
        return(strlen(*arg->value));
 }
 
@@ -813,7 +715,7 @@ termp_it_pre(DECL_ARGS)
 {
        const struct mdoc_node *bl, *n;
        char                    buf[7];
-       int                     i, type, keys[3], vals[3];
+       int                     i, type, keys[3], vals[3], sv;
        size_t                  width, offset;
 
        if (MDOC_BLOCK == node->type)
@@ -859,7 +761,7 @@ termp_it_pre(DECL_ARGS)
                if (vals[0] >= 0) 
                        width = arg_width(&bl->args->argv[vals[0]], 0);
                if (vals[1] >= 0) 
-                       offset = arg_offset(&bl->args->argv[vals[1]]);
+                       offset += arg_offset(&bl->args->argv[vals[1]]);
                break;
        }
 
@@ -874,12 +776,14 @@ termp_it_pre(DECL_ARGS)
                /* FALLTHROUGH */
        case (MDOC_Dash):
                /* FALLTHROUGH */
-       case (MDOC_Enum):
-               /* FALLTHROUGH */
        case (MDOC_Hyphen):
                if (width < 4)
                        width = 4;
                break;
+       case (MDOC_Enum):
+               if (width < 5)
+                       width = 5;
+               break;
        case (MDOC_Tag):
                if (0 == width)
                        width = 10;
@@ -889,11 +793,13 @@ termp_it_pre(DECL_ARGS)
        }
 
        /* 
-        * Whitespace control.  Inset bodies need an initial space.
+        * Whitespace control.  Inset bodies need an initial space,
+        * while diagonal bodies need two.
         */
 
        switch (type) {
        case (MDOC_Diag):
+               term_word(p, "\\ ");
                /* FALLTHROUGH */
        case (MDOC_Inset):
                if (MDOC_BODY == node->type) 
@@ -997,17 +903,20 @@ termp_it_pre(DECL_ARGS)
 
        /* 
         * The dash, hyphen, bullet and enum lists all have a special
-        * HEAD character.  Print it now.
+        * HEAD character (temporarily bold, in some cases).  
         */
 
+       sv = p->flags;
        if (MDOC_HEAD == node->type)
                switch (type) {
                case (MDOC_Bullet):
+                       p->flags |= TERMP_BOLD;
                        term_word(p, "\\[bu]");
                        break;
                case (MDOC_Dash):
                        /* FALLTHROUGH */
                case (MDOC_Hyphen):
+                       p->flags |= TERMP_BOLD;
                        term_word(p, "\\-");
                        break;
                case (MDOC_Enum):
@@ -1020,6 +929,8 @@ termp_it_pre(DECL_ARGS)
                        break;
                }
 
+       p->flags = sv; /* Restore saved flags. */
+
        /* 
         * If we're not going to process our children, indicate so here.
         */
@@ -1171,6 +1082,8 @@ termp_rv_pre(DECL_ARGS)
 {
        int              i;
 
+       /* FIXME: mandated by parser. */
+
        if (-1 == (i = arg_getattr(MDOC_Std, node)))
                errx(1, "expected -std argument");
        if (1 != node->args->argv[i].sz)
@@ -1204,6 +1117,8 @@ termp_ex_pre(DECL_ARGS)
 {
        int              i;
 
+       /* FIXME: mandated by parser? */
+
        if (-1 == (i = arg_getattr(MDOC_Std, node)))
                errx(1, "expected -std argument");
        if (1 != node->args->argv[i].sz)
@@ -1257,8 +1172,9 @@ termp_xr_pre(DECL_ARGS)
 {
        const struct mdoc_node *n;
 
-       if (NULL == (n = node->child))
-               errx(1, "expected text line argument");
+       assert(node->child && MDOC_TEXT == node->child->type);
+       n = node->child;
+
        term_word(p, n->string);
        if (NULL == (n = n->next)) 
                return(0);
@@ -1392,9 +1308,9 @@ termp_lb_pre(DECL_ARGS)
 {
        const char      *lb;
 
-       if (NULL == node->child)
-               errx(1, "expected text line argument");
-       if ((lb = mdoc_a2lib(node->child->string))) {
+       assert(node->child && MDOC_TEXT == node->child->type);
+       lb = mdoc_a2lib(node->child->string);
+       if (lb) {
                term_word(p, lb);
                return(0);
        }
@@ -1427,10 +1343,11 @@ static int
 termp_d1_pre(DECL_ARGS)
 {
 
-       if (MDOC_BODY != node->type)
+       if (MDOC_BLOCK != node->type)
                return(1);
        term_newln(p);
-       p->offset += (pair->offset = INDENT);
+       pair->offset = INDENT + 1;
+       p->offset += pair->offset;
        return(1);
 }
 
@@ -1440,7 +1357,7 @@ static void
 termp_d1_post(DECL_ARGS)
 {
 
-       if (MDOC_BODY != node->type) 
+       if (MDOC_BLOCK != node->type) 
                return;
        term_newln(p);
        p->offset -= pair->offset;
@@ -1501,8 +1418,7 @@ termp_fn_pre(DECL_ARGS)
 {
        const struct mdoc_node *n;
 
-       if (NULL == node->child)
-               errx(1, "expected text line arguments");
+       assert(node->child && MDOC_TEXT == node->child->type);
 
        /* FIXME: can be "type funcname" "type varname"... */
 
@@ -1609,6 +1525,8 @@ termp_bd_pre(DECL_ARGS)
        else if (MDOC_BODY != node->type)
                return(1);
 
+       /* FIXME: display type should be mandated by parser. */
+
        if (NULL == node->parent->args)
                errx(1, "missing display type");
 
@@ -1842,7 +1760,7 @@ termp_ss_pre(DECL_ARGS)
                break;
        case (MDOC_HEAD):
                TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_SSECTION]);
-               p->offset = INDENT / 2;
+               p->offset = HALFINDENT;
                break;
        default:
                break;
@@ -2057,8 +1975,7 @@ termp_fo_pre(DECL_ARGS)
 
        p->flags |= ttypes[TTYPE_FUNC_NAME];
        for (n = node->child; n; n = n->next) {
-               if (MDOC_TEXT != n->type)
-                       errx(1, "expected text line argument");
+               assert(MDOC_TEXT == n->type);
                term_word(p, n->string);
        }
        p->flags &= ~ttypes[TTYPE_FUNC_NAME];
@@ -2102,9 +2019,7 @@ termp_bf_pre(DECL_ARGS)
                return(1);
        } 
 
-       if (MDOC_TEXT != n->type)
-               errx(1, "expected text line arguments");
-
+       assert(MDOC_TEXT == n->type);
        if (0 == strcmp("Em", n->string))
                TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_EMPH]);
        else if (0 == strcmp("Sy", n->string))
@@ -2140,9 +2055,7 @@ static int
 termp_sm_pre(DECL_ARGS)
 {
 
-       if (NULL == node->child || MDOC_TEXT != node->child->type)
-               errx(1, "expected boolean line argument");
-
+       assert(node->child && MDOC_TEXT == node->child->type);
        if (0 == strcmp("on", node->child->string)) {
                p->flags &= ~TERMP_NONOSPACE;
                p->flags &= ~TERMP_NOSPACE;
@@ -2213,21 +2126,25 @@ termp_lk_pre(DECL_ARGS)
 {
        const struct mdoc_node *n;
 
-       if (NULL == (n = node->child))
-               errx(1, "expected line argument");
+       assert(node->child);
+       n = node->child;
+
+       if (NULL == n->next) {
+               TERMPAIR_SETFLAG(p, pair, ttypes[TTYPE_LINK_ANCHOR]);
+               return(1);
+       }
 
        p->flags |= ttypes[TTYPE_LINK_ANCHOR];
        term_word(p, n->string);
-       p->flags &= ~ttypes[TTYPE_LINK_ANCHOR];
        p->flags |= TERMP_NOSPACE;
        term_word(p, ":");
+       p->flags &= ~ttypes[TTYPE_LINK_ANCHOR];
 
        p->flags |= ttypes[TTYPE_LINK_TEXT];
-       for ( ; n; n = n->next) {
+       for (n = n->next; n; n = n->next) 
                term_word(p, n->string);
-       }
-       p->flags &= ~ttypes[TTYPE_LINK_TEXT];
 
+       p->flags &= ~ttypes[TTYPE_LINK_TEXT];
        return(0);
 }