]> git.cameronkatri.com Git - mandoc.git/blobdiff - mdoc_markdown.c
Fix incorrectly swapped labels of left/right arrows; from bentley@.
[mandoc.git] / mdoc_markdown.c
index e43eb568f806f9dd39dada03e41739469c25b0ae..0b0f184821e5bf5990f1eb1a6f9d6a274f41764e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $Id: mdoc_markdown.c,v 1.3 2017/03/06 14:58:10 schwarze Exp $ */
+/*     $Id: mdoc_markdown.c,v 1.23 2017/06/14 01:31:26 schwarze Exp $ */
 /*
  * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
  *
@@ -43,6 +43,7 @@ static        void     md_rawword(const char *);
 static void     md_word(const char *);
 static void     md_named(const char *);
 static void     md_char(unsigned char);
+static void     md_uri(const char *);
 
 static int      md_cond_head(struct roff_node *);
 static int      md_cond_body(struct roff_node *);
@@ -51,6 +52,7 @@ static        int      md_pre_raw(struct roff_node *);
 static int      md_pre_word(struct roff_node *);
 static int      md_pre_skip(struct roff_node *);
 static void     md_pre_syn(struct roff_node *);
+static int      md_pre_An(struct roff_node *);
 static int      md_pre_Ap(struct roff_node *);
 static int      md_pre_Bd(struct roff_node *);
 static int      md_pre_Bk(struct roff_node *);
@@ -66,6 +68,7 @@ static        int      md_pre_Fo(struct roff_node *);
 static int      md_pre_In(struct roff_node *);
 static int      md_pre_It(struct roff_node *);
 static int      md_pre_Lk(struct roff_node *);
+static int      md_pre_Mt(struct roff_node *);
 static int      md_pre_Nd(struct roff_node *);
 static int      md_pre_Nm(struct roff_node *);
 static int      md_pre_No(struct roff_node *);
@@ -89,6 +92,7 @@ static        void     md_post_En(struct roff_node *);
 static void     md_post_Eo(struct roff_node *);
 static void     md_post_Fa(struct roff_node *);
 static void     md_post_Fd(struct roff_node *);
+static void     md_post_Fl(struct roff_node *);
 static void     md_post_Fn(struct roff_node *);
 static void     md_post_Fo(struct roff_node *);
 static void     md_post_In(struct roff_node *);
@@ -99,8 +103,7 @@ static       void     md_post_Pf(struct roff_node *);
 static void     md_post_Vt(struct roff_node *);
 static void     md_post__T(struct roff_node *);
 
-static const struct md_act md_acts[MDOC_MAX + 1] = {
-       { NULL, md_pre_Ap, NULL, NULL, NULL }, /* Ap */
+static const struct md_act __md_acts[MDOC_MAX - MDOC_Dd] = {
        { NULL, NULL, NULL, NULL, NULL }, /* Dd */
        { NULL, NULL, NULL, NULL, NULL }, /* Dt */
        { NULL, NULL, NULL, NULL, NULL }, /* Os */
@@ -115,7 +118,8 @@ static      const struct md_act md_acts[MDOC_MAX + 1] = {
        { NULL, NULL, NULL, NULL, NULL }, /* El */
        { NULL, md_pre_It, md_post_It, NULL, NULL }, /* It */
        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ad */
-       { NULL, NULL, NULL, NULL, NULL }, /* An */
+       { NULL, md_pre_An, NULL, NULL, NULL }, /* An */
+       { NULL, md_pre_Ap, NULL, NULL, NULL }, /* Ap */
        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ar */
        { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cd */
        { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cm */
@@ -125,11 +129,11 @@ static    const struct md_act md_acts[MDOC_MAX + 1] = {
        { NULL, NULL, NULL, NULL, NULL }, /* Ex */
        { NULL, md_pre_Fa, md_post_Fa, NULL, NULL }, /* Fa */
        { NULL, md_pre_Fd, md_post_Fd, "**", "**" }, /* Fd */
-       { NULL, md_pre_raw, md_post_raw, "**-", "**" }, /* Fl */
+       { NULL, md_pre_raw, md_post_Fl, "**-", "**" }, /* Fl */
        { NULL, md_pre_Fn, md_post_Fn, NULL, NULL }, /* Fn */
        { NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ft */
        { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ic */
-       { NULL, md_pre_In, md_post_In, "*", "*" }, /* In */
+       { NULL, md_pre_In, md_post_In, NULL, NULL }, /* In */
        { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Li */
        { md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */
        { NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */
@@ -209,7 +213,7 @@ static      const struct md_act md_acts[MDOC_MAX + 1] = {
        { NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */
        { NULL, md_pre_Pp, NULL, NULL, NULL }, /* Lp */
        { NULL, md_pre_Lk, NULL, NULL, NULL }, /* Lk */
-       { NULL, md_pre_raw, md_post_raw, "<", ">" }, /* Mt */
+       { NULL, md_pre_Mt, NULL, NULL, NULL }, /* Mt */
        { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Brq */
        { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Bro */
        { NULL, NULL, NULL, NULL, NULL }, /* Brc */
@@ -218,13 +222,10 @@ static    const struct md_act md_acts[MDOC_MAX + 1] = {
        { md_cond_body, md_pre_En, md_post_En, NULL, NULL }, /* En */
        { NULL, NULL, NULL, NULL, NULL }, /* Dx */
        { NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */
-       { NULL, md_pre_br, NULL, NULL, NULL }, /* br */
-       { NULL, md_pre_Pp, NULL, NULL, NULL }, /* sp */
        { NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */
        { NULL, NULL, NULL, NULL, NULL }, /* Ta */
-       { NULL, NULL, NULL, NULL, NULL }, /* ll */
-       { NULL, NULL, NULL, NULL, NULL }, /* ROOT */
 };
+static const struct md_act *const md_acts = __md_acts - MDOC_Dd;
 
 static int      outflags;
 #define        MD_spc           (1 << 0)  /* Blank character before next word. */
@@ -235,14 +236,16 @@ static    int      outflags;
 #define        MD_sp            (1 << 5)  /* Insert a paragraph break. */
 #define        MD_Sm            (1 << 6)  /* Horizontal spacing mode. */
 #define        MD_Bk            (1 << 7)  /* Word keep mode. */
+#define        MD_An_split      (1 << 8)  /* Author mode is "split". */
+#define        MD_An_nosplit    (1 << 9)  /* Author mode is "nosplit". */
 
 static int      escflags; /* Escape in generated markdown code: */
 #define        ESC_BOL  (1 << 0)  /* "#*+-" near the beginning of a line. */
 #define        ESC_NUM  (1 << 1)  /* "." after a leading number. */
 #define        ESC_HYP  (1 << 2)  /* "(" immediately after "]". */
-#define        ESC_PAR  (1 << 3)  /* ")" when "(" is open. */
 #define        ESC_SQU  (1 << 4)  /* "]" when "[" is open. */
 #define        ESC_FON  (1 << 5)  /* "*" immediately after unrelated "*". */
+#define        ESC_EOL  (1 << 6)  /* " " at the and of a line. */
 
 static int      code_blocks, quote_blocks, list_blocks;
 static int      outcount;
@@ -304,8 +307,7 @@ md_node(struct roff_node *n)
        process_children = 1;
        n->flags &= ~NODE_ENDED;
 
-       switch (n->type) {
-       case ROFFT_TEXT:
+       if (n->type == ROFFT_TEXT) {
                if (n->flags & NODE_DELIMC)
                        outflags &= ~(MD_spc | MD_spc_force);
                else if (outflags & MD_Sm)
@@ -315,14 +317,25 @@ md_node(struct roff_node *n)
                        outflags &= ~(MD_spc | MD_spc_force);
                else if (outflags & MD_Sm)
                        outflags |= MD_spc;
-               break;
-       default:
+       } else if (n->tok < ROFF_MAX) {
+               switch (n->tok) {
+               case ROFF_br:
+                       process_children = md_pre_br(n);
+                       break;
+               case ROFF_sp:
+                       process_children = md_pre_Pp(n);
+                       break;
+               default:
+                       process_children = 0;
+                       break;
+               }
+       } else {
+               assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
                act = md_acts + n->tok;
                cond = act->cond == NULL || (*act->cond)(n);
                if (cond && act->pre != NULL &&
                    (n->end == ENDBODY_NOT || n->child != NULL))
                        process_children = (*act->pre)(n);
-               break;
        }
 
        if (process_children && n->child != NULL)
@@ -370,37 +383,43 @@ md_stack(char c)
 static void
 md_preword(void)
 {
+       const char      *cp;
+
        /*
         * If a list block is nested inside a code block or a blockquote,
         * blank lines for paragraph breaks no longer work; instead,
         * they terminate the list.  Work around this markdown issue
         * by using mere line breaks instead.
         */
+
        if (list_blocks && outflags & MD_sp) {
                outflags &= ~MD_sp;
                outflags |= MD_br;
        }
 
-       /* End the old line if requested. */
+       /*
+        * End the old line if requested.
+        * Escape whitespace at the end of the markdown line
+        * such that it won't look like an output line break.
+        */
 
        if (outflags & MD_sp)
                putchar('\n');
        else if (outflags & MD_br) {
                putchar(' ');
                putchar(' ');
-#ifdef DEBUG
-               putchar(':');
-               putchar(':');
-               putchar(' ');
-               putchar(' ');
-#endif
-       }
+       } else if (outflags & MD_nl && escflags & ESC_EOL)
+               md_named("zwnj");
 
        /* Start a new line if necessary. */
 
        if (outflags & (MD_nl | MD_br | MD_sp)) {
                putchar('\n');
-               fputs(md_stack('\0'), stdout);
+               for (cp = md_stack('\0'); *cp != '\0'; cp++) {
+                       putchar(*cp);
+                       if (*cp == '>')
+                               putchar(' ');
+               }
                outflags &= ~(MD_nl | MD_br | MD_sp);
                escflags = ESC_BOL;
                outcount = 0;
@@ -433,7 +452,7 @@ md_rawword(const char *s)
 {
        md_preword();
 
-       if (*s == 0)
+       if (*s == '\0')
                return;
 
        if (escflags & ESC_FON) {
@@ -444,12 +463,6 @@ md_rawword(const char *s)
 
        while (*s != '\0') {
                switch(*s) {
-               case '(':
-                       escflags |= ESC_PAR;
-                       break;
-               case ')':
-                       escflags |= ~ESC_PAR;
-                       break;
                case '*':
                        if (s[1] == '\0')
                                escflags |= ESC_FON;
@@ -466,6 +479,10 @@ md_rawword(const char *s)
                }
                md_char(*s++);
        }
+       if (s[-1] == ' ')
+               escflags |= ESC_EOL;
+       else
+               escflags &= ~ESC_EOL;
 }
 
 /*
@@ -476,7 +493,7 @@ md_word(const char *s)
 {
        const char      *seq, *prevfont, *currfont, *nextfont;
        char             c;
-       int              bs, sz, uc;
+       int              bs, sz, uc, breakline;
 
        /* No spacing before closing delimiters. */
        if (s[0] != '\0' && s[1] == '\0' &&
@@ -486,10 +503,14 @@ md_word(const char *s)
 
        md_preword();
 
+       if (*s == '\0')
+               return;
+
        /* No spacing after opening delimiters. */
        if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
                outflags &= ~MD_spc;
 
+       breakline = 0;
        prevfont = currfont = "";
        while ((c = *s++) != '\0') {
                bs = 0;
@@ -517,7 +538,7 @@ md_word(const char *s)
                        bs = escflags & ESC_HYP && !code_blocks;
                        break;
                case ')':
-                       bs = escflags & ESC_PAR && !code_blocks;
+                       bs = escflags & ESC_NUM && !code_blocks;
                        break;
                case '*':
                case '[':
@@ -575,6 +596,9 @@ md_word(const char *s)
                        case ESCAPE_FONTPREV:
                                nextfont = prevfont;
                                break;
+                       case ESCAPE_BREAK:
+                               breakline = 1;
+                               break;
                        case ESCAPE_NOSPACE:
                        case ESCAPE_SKIPCHAR:
                        case ESCAPE_OVERSTRIKE:
@@ -622,11 +646,21 @@ md_word(const char *s)
                if (bs)
                        putchar('\\');
                md_char(c);
+               if (breakline &&
+                   (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) {
+                       printf("  \n");
+                       breakline = 0;
+                       while (*s == ' ' || *s == ASCII_NBRSP)
+                               s++;
+               }
        }
        if (*currfont != '\0') {
                outflags &= ~MD_spc;
                md_rawword(currfont);
-       }
+       } else if (s[-2] == ' ')
+               escflags |= ESC_EOL;
+       else
+               escflags &= ~ESC_EOL;
 }
 
 /*
@@ -636,7 +670,7 @@ static void
 md_named(const char *s)
 {
        printf("&%s;", s);
-       escflags &= ~ESC_FON;
+       escflags &= ~(ESC_FON | ESC_EOL);
        outcount++;
 }
 
@@ -685,6 +719,8 @@ md_pre_raw(struct roff_node *n)
        if ((prefix = md_acts[n->tok].prefix) != NULL) {
                md_rawword(prefix);
                outflags &= ~MD_spc;
+               if (*prefix == '`')
+                       code_blocks++;
        }
        return 1;
 }
@@ -697,6 +733,8 @@ md_post_raw(struct roff_node *n)
        if ((suffix = md_acts[n->tok].suffix) != NULL) {
                outflags &= ~(MD_spc | MD_nl);
                md_rawword(suffix);
+               if (*suffix == '`')
+                       code_blocks--;
        }
 }
 
@@ -781,6 +819,28 @@ md_pre_syn(struct roff_node *n)
        }
 }
 
+static int
+md_pre_An(struct roff_node *n)
+{
+       switch (n->norm->An.auth) {
+       case AUTH_split:
+               outflags &= ~MD_An_nosplit;
+               outflags |= MD_An_split;
+               return 0;
+       case AUTH_nosplit:
+               outflags &= ~MD_An_split;
+               outflags |= MD_An_nosplit;
+               return 0;
+       default:
+               if (outflags & MD_An_split)
+                       outflags |= MD_br;
+               else if (n->sec == SEC_AUTHORS &&
+                   ! (outflags & MD_An_nosplit))
+                       outflags |= MD_An_split;
+               return 1;
+       }
+}
+
 static int
 md_pre_Ap(struct roff_node *n)
 {
@@ -931,21 +991,17 @@ md_pre_Eo(struct roff_node *n)
 static void
 md_post_Eo(struct roff_node *n)
 {
-       int      body, tail;
-
        if (n->end != ENDBODY_NOT) {
                outflags |= MD_spc;
                return;
        }
 
-       body = n->child != NULL || n->parent->head->child != NULL;
-       tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
+       if (n->child == NULL && n->parent->head->child == NULL)
+               return;
 
-       if (body && tail)
+       if (n->parent->tail != NULL && n->parent->tail->child != NULL)
                outflags &= ~MD_spc;
-        else if ( ! (body || tail))
-               md_preword();
-        else if ( ! tail)
+        else
                outflags |= MD_spc;
 }
 
@@ -993,6 +1049,15 @@ md_post_Fd(struct roff_node *n)
        outflags |= MD_br;
 }
 
+static void
+md_post_Fl(struct roff_node *n)
+{
+       md_post_raw(n);
+       if (n->child == NULL && n->next != NULL &&
+           n->next->type != ROFFT_TEXT && !(n->next->flags & NODE_LINE))
+               outflags &= ~MD_spc;
+}
+
 static int
 md_pre_Fn(struct roff_node *n)
 {
@@ -1067,16 +1132,15 @@ md_pre_In(struct roff_node *n)
 {
        if (n->flags & NODE_SYNPRETTY) {
                md_pre_syn(n);
-               md_pre_raw(n);
-               md_rawword("*");
+               md_rawword("**");
                outflags &= ~MD_spc;
                md_word("#include <");
-               outflags &= ~MD_spc;
        } else {
                md_word("<");
                outflags &= ~MD_spc;
-               md_pre_raw(n);
+               md_rawword("*");
        }
+       outflags &= ~MD_spc;
        return 1;
 }
 
@@ -1085,13 +1149,11 @@ md_post_In(struct roff_node *n)
 {
        if (n->flags & NODE_SYNPRETTY) {
                outflags &= ~MD_spc;
-               md_rawword(">*");
-               md_post_raw(n);
+               md_rawword(">**");
                outflags |= MD_nl;
        } else {
-               md_post_raw(n);
                outflags &= ~MD_spc;
-               md_rawword(">");
+               md_rawword("*>");
        }
 }
 
@@ -1106,7 +1168,8 @@ md_pre_It(struct roff_node *n)
 
        case ROFFT_HEAD:
                bln = n->parent->parent;
-               if (bln->norm->Bl.comp == 0)
+               if (bln->norm->Bl.comp == 0 &&
+                   bln->norm->Bl.type != LIST_column)
                        outflags |= MD_sp;
                outflags |= MD_nl;
 
@@ -1132,9 +1195,14 @@ md_pre_It(struct roff_node *n)
                        break;
                case LIST_enum:
                        md_preword();
-                       printf("%d.\t", ++bln->norm->Bl.count);
+                       if (bln->norm->Bl.count < 99)
+                               bln->norm->Bl.count++;
+                       printf("%d.\t", bln->norm->Bl.count);
                        escflags &= ~ESC_FON;
                        break;
+               case LIST_column:
+                       outflags |= MD_br;
+                       return 0;
                default:
                        return 0;
                }
@@ -1231,39 +1299,81 @@ md_post_Lb(struct roff_node *n)
                outflags |= MD_br;
 }
 
+static void
+md_uri(const char *s)
+{
+       while (*s != '\0') {
+               if (strchr("%()<>", *s) != NULL) {
+                       printf("%%%2.2hhX", *s);
+                       outcount += 3;
+               } else {
+                       putchar(*s);
+                       outcount++;
+               }
+               s++;
+       }
+}
+
 static int
 md_pre_Lk(struct roff_node *n)
 {
-       const struct roff_node *link, *descr;
-       const unsigned char *s;
+       const struct roff_node *link, *descr, *punct;
 
        if ((link = n->child) == NULL)
                return 0;
 
-       if ((descr = link->next) != NULL) {
-               md_rawword("[");
-               outflags &= ~MD_spc;
-               while (descr != NULL) {
-                       md_word(descr->string);
-                       descr = descr->next;
-               }
-               outflags &= ~MD_spc;
-               md_rawword("](");
-       } else
-               md_rawword("<");
+       /* Find beginning of trailing punctuation. */
+       punct = n->last;
+       while (punct != link && punct->flags & NODE_DELIMC)
+               punct = punct->prev;
+       punct = punct->next;
+
+       /* Link text. */
+       descr = link->next;
+       if (descr == punct)
+               descr = link;  /* no text */
+       md_rawword("[");
+       outflags &= ~MD_spc;
+       do {
+               md_word(descr->string);
+               descr = descr->next;
+       } while (descr != punct);
+       outflags &= ~MD_spc;
 
-       for (s = link->string; *s != '\0'; s++) {
-               if (strchr("%)<>", *s) != NULL) {
-                       printf("%%%2.2hhX", *s);
-                       outcount += 3;
-               } else {
-                       putchar(*s);
+       /* Link target. */
+       md_rawword("](");
+       md_uri(link->string);
+       outflags &= ~MD_spc;
+       md_rawword(")");
+
+       /* Trailing punctuation. */
+       while (punct != NULL) {
+               md_word(punct->string);
+               punct = punct->next;
+       }
+       return 0;
+}
+
+static int
+md_pre_Mt(struct roff_node *n)
+{
+       const struct roff_node *nch;
+
+       md_rawword("[");
+       outflags &= ~MD_spc;
+       for (nch = n->child; nch != NULL; nch = nch->next)
+               md_word(nch->string);
+       outflags &= ~MD_spc;
+       md_rawword("](mailto:");
+       for (nch = n->child; nch != NULL; nch = nch->next) {
+               md_uri(nch->string);
+               if (nch->next != NULL) {
+                       putchar(' ');
                        outcount++;
                }
        }
-
        outflags &= ~MD_spc;
-       md_rawword(link->next == NULL ? ">" : ")");
+       md_rawword(")");
        return 0;
 }
 
@@ -1350,6 +1460,10 @@ static int
 md_pre_Sh(struct roff_node *n)
 {
        switch (n->type) {
+       case ROFFT_BLOCK:
+               if (n->sec == SEC_AUTHORS)
+                       outflags &= ~(MD_An_split | MD_An_nosplit);
+               break;
        case ROFFT_HEAD:
                outflags |= MD_sp;
                md_rawword(n->tok == MDOC_Sh ? "#" : "##");