]> git.cameronkatri.com Git - mandoc.git/blobdiff - mdoc_markdown.c
In HTML output, allow switching the desired font for subsequent
[mandoc.git] / mdoc_markdown.c
index 1bf1c13cd3d0dfe4b2a50342329e8c2d87a1cbf9..e9a931218cc4f8ce303705d8435a6e7aec013806 100644 (file)
@@ -1,6 +1,6 @@
-/*     $Id: mdoc_markdown.c,v 1.14 2017/03/08 19:23:43 schwarze Exp $ */
+/*     $Id: mdoc_markdown.c,v 1.30 2018/12/30 00:49:55 schwarze Exp $ */
 /*
 /*
- * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -19,6 +19,7 @@
 #include <assert.h>
 #include <ctype.h>
 #include <stdio.h>
 #include <assert.h>
 #include <ctype.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include "mandoc_aux.h"
 #include <string.h>
 
 #include "mandoc_aux.h"
@@ -43,10 +44,12 @@ 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_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 *);
 
 
 static int      md_cond_head(struct roff_node *);
 static int      md_cond_body(struct roff_node *);
 
+static int      md_pre_abort(struct roff_node *);
 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 int      md_pre_raw(struct roff_node *);
 static int      md_pre_word(struct roff_node *);
 static int      md_pre_skip(struct roff_node *);
@@ -67,6 +70,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_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 *);
 static int      md_pre_Nd(struct roff_node *);
 static int      md_pre_Nm(struct roff_node *);
 static int      md_pre_No(struct roff_node *);
@@ -101,8 +105,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 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 */
        { NULL, NULL, NULL, NULL, NULL }, /* Dd */
        { NULL, NULL, NULL, NULL, NULL }, /* Dt */
        { NULL, NULL, NULL, NULL, NULL }, /* Os */
@@ -118,6 +121,7 @@ static      const struct md_act md_acts[MDOC_MAX + 1] = {
        { NULL, md_pre_It, md_post_It, NULL, NULL }, /* It */
        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ad */
        { NULL, md_pre_An, NULL, NULL, NULL }, /* An */
        { NULL, md_pre_It, md_post_It, NULL, NULL }, /* It */
        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ad */
        { 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 */
        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ar */
        { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cd */
        { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cm */
@@ -136,7 +140,7 @@ static      const struct md_act md_acts[MDOC_MAX + 1] = {
        { md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */
        { NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */
        { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Op */
        { md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */
        { NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */
        { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Op */
-       { NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ot */
+       { NULL, md_pre_abort, NULL, NULL, NULL }, /* Ot */
        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Pa */
        { NULL, NULL, NULL, NULL, NULL }, /* Rv */
        { NULL, NULL, NULL, NULL, NULL }, /* St */
        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Pa */
        { NULL, NULL, NULL, NULL, NULL }, /* Rv */
        { NULL, NULL, NULL, NULL, NULL }, /* St */
@@ -209,9 +213,9 @@ static      const struct md_act md_acts[MDOC_MAX + 1] = {
        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Fr */
        { NULL, NULL, NULL, NULL, NULL }, /* Ud */
        { NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */
        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Fr */
        { NULL, NULL, NULL, NULL, NULL }, /* Ud */
        { NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */
-       { NULL, md_pre_Pp, NULL, NULL, NULL }, /* Lp */
+       { NULL, md_pre_abort, NULL, NULL, NULL }, /* Lp */
        { NULL, md_pre_Lk, NULL, NULL, NULL }, /* Lk */
        { 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 */
        { 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 */
@@ -220,13 +224,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 */
        { 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, 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 *md_act(enum roff_tok);
 
 static int      outflags;
 #define        MD_spc           (1 << 0)  /* Blank character before next word. */
 
 static int      outflags;
 #define        MD_spc           (1 << 0)  /* Blank character before next word. */
@@ -251,22 +252,30 @@ static    int      escflags; /* Escape in generated markdown code: */
 static int      code_blocks, quote_blocks, list_blocks;
 static int      outcount;
 
 static int      code_blocks, quote_blocks, list_blocks;
 static int      outcount;
 
+
+static const struct md_act *
+md_act(enum roff_tok tok)
+{
+       assert(tok >= MDOC_Dd && tok <= MDOC_MAX);
+       return md_acts + (tok - MDOC_Dd);
+}
+
 void
 void
-markdown_mdoc(void *arg, const struct roff_man *mdoc)
+markdown_mdoc(void *arg, const struct roff_meta *mdoc)
 {
        outflags = MD_Sm;
 {
        outflags = MD_Sm;
-       md_word(mdoc->meta.title);
-       if (mdoc->meta.msec != NULL) {
+       md_word(mdoc->title);
+       if (mdoc->msec != NULL) {
                outflags &= ~MD_spc;
                md_word("(");
                outflags &= ~MD_spc;
                md_word("(");
-               md_word(mdoc->meta.msec);
+               md_word(mdoc->msec);
                md_word(")");
        }
        md_word("-");
                md_word(")");
        }
        md_word("-");
-       md_word(mdoc->meta.vol);
-       if (mdoc->meta.arch != NULL) {
+       md_word(mdoc->vol);
+       if (mdoc->arch != NULL) {
                md_word("(");
                md_word("(");
-               md_word(mdoc->meta.arch);
+               md_word(mdoc->arch);
                md_word(")");
        }
        outflags |= MD_sp;
                md_word(")");
        }
        outflags |= MD_sp;
@@ -274,9 +283,9 @@ markdown_mdoc(void *arg, const struct roff_man *mdoc)
        md_nodelist(mdoc->first->child);
 
        outflags |= MD_sp;
        md_nodelist(mdoc->first->child);
 
        outflags |= MD_sp;
-       md_word(mdoc->meta.os);
+       md_word(mdoc->os);
        md_word("-");
        md_word("-");
-       md_word(mdoc->meta.date);
+       md_word(mdoc->date);
        putchar('\n');
 }
 
        putchar('\n');
 }
 
@@ -295,7 +304,7 @@ md_node(struct roff_node *n)
        const struct md_act     *act;
        int                      cond, process_children;
 
        const struct md_act     *act;
        int                      cond, process_children;
 
-       if (n->flags & NODE_NOPRT)
+       if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
                return;
 
        if (outflags & MD_nonl)
                return;
 
        if (outflags & MD_nonl)
@@ -308,8 +317,7 @@ md_node(struct roff_node *n)
        process_children = 1;
        n->flags &= ~NODE_ENDED;
 
        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)
                if (n->flags & NODE_DELIMC)
                        outflags &= ~(MD_spc | MD_spc_force);
                else if (outflags & MD_Sm)
@@ -319,14 +327,24 @@ md_node(struct roff_node *n)
                        outflags &= ~(MD_spc | MD_spc_force);
                else if (outflags & MD_Sm)
                        outflags |= MD_spc;
                        outflags &= ~(MD_spc | MD_spc_force);
                else if (outflags & MD_Sm)
                        outflags |= MD_spc;
-               break;
-       default:
-               act = md_acts + n->tok;
+       } 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 {
+               act = md_act(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);
                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)
        }
 
        if (process_children && n->child != NULL)
@@ -484,7 +502,7 @@ md_word(const char *s)
 {
        const char      *seq, *prevfont, *currfont, *nextfont;
        char             c;
 {
        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' &&
 
        /* No spacing before closing delimiters. */
        if (s[0] != '\0' && s[1] == '\0' &&
@@ -501,6 +519,7 @@ md_word(const char *s)
        if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
                outflags &= ~MD_spc;
 
        if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
                outflags &= ~MD_spc;
 
+       breakline = 0;
        prevfont = currfont = "";
        while ((c = *s++) != '\0') {
                bs = 0;
        prevfont = currfont = "";
        while ((c = *s++) != '\0') {
                bs = 0;
@@ -570,6 +589,12 @@ md_word(const char *s)
                        case ESCAPE_SPECIAL:
                                uc = mchars_spec2cp(seq, sz);
                                break;
                        case ESCAPE_SPECIAL:
                                uc = mchars_spec2cp(seq, sz);
                                break;
+                       case ESCAPE_UNDEF:
+                               uc = *seq;
+                               break;
+                       case ESCAPE_DEVICE:
+                               md_rawword("markdown");
+                               continue;
                        case ESCAPE_FONTBOLD:
                                nextfont = "**";
                                break;
                        case ESCAPE_FONTBOLD:
                                nextfont = "**";
                                break;
@@ -580,12 +605,16 @@ md_word(const char *s)
                                nextfont = "***";
                                break;
                        case ESCAPE_FONT:
                                nextfont = "***";
                                break;
                        case ESCAPE_FONT:
+                       case ESCAPE_FONTCW:
                        case ESCAPE_FONTROMAN:
                                nextfont = "";
                                break;
                        case ESCAPE_FONTPREV:
                                nextfont = prevfont;
                                break;
                        case ESCAPE_FONTROMAN:
                                nextfont = "";
                                break;
                        case ESCAPE_FONTPREV:
                                nextfont = prevfont;
                                break;
+                       case ESCAPE_BREAK:
+                               breakline = 1;
+                               break;
                        case ESCAPE_NOSPACE:
                        case ESCAPE_SKIPCHAR:
                        case ESCAPE_OVERSTRIKE:
                        case ESCAPE_NOSPACE:
                        case ESCAPE_SKIPCHAR:
                        case ESCAPE_OVERSTRIKE:
@@ -633,6 +662,13 @@ md_word(const char *s)
                if (bs)
                        putchar('\\');
                md_char(c);
                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;
        }
        if (*currfont != '\0') {
                outflags &= ~MD_spc;
@@ -691,12 +727,18 @@ md_cond_body(struct roff_node *n)
        return n->type == ROFFT_BODY;
 }
 
        return n->type == ROFFT_BODY;
 }
 
+static int
+md_pre_abort(struct roff_node *n)
+{
+       abort();
+}
+
 static int
 md_pre_raw(struct roff_node *n)
 {
        const char      *prefix;
 
 static int
 md_pre_raw(struct roff_node *n)
 {
        const char      *prefix;
 
-       if ((prefix = md_acts[n->tok].prefix) != NULL) {
+       if ((prefix = md_act(n->tok)->prefix) != NULL) {
                md_rawword(prefix);
                outflags &= ~MD_spc;
                if (*prefix == '`')
                md_rawword(prefix);
                outflags &= ~MD_spc;
                if (*prefix == '`')
@@ -710,7 +752,7 @@ md_post_raw(struct roff_node *n)
 {
        const char      *suffix;
 
 {
        const char      *suffix;
 
-       if ((suffix = md_acts[n->tok].suffix) != NULL) {
+       if ((suffix = md_act(n->tok)->suffix) != NULL) {
                outflags &= ~(MD_spc | MD_nl);
                md_rawword(suffix);
                if (*suffix == '`')
                outflags &= ~(MD_spc | MD_nl);
                md_rawword(suffix);
                if (*suffix == '`')
@@ -723,7 +765,7 @@ md_pre_word(struct roff_node *n)
 {
        const char      *prefix;
 
 {
        const char      *prefix;
 
-       if ((prefix = md_acts[n->tok].prefix) != NULL) {
+       if ((prefix = md_act(n->tok)->prefix) != NULL) {
                md_word(prefix);
                outflags &= ~MD_spc;
        }
                md_word(prefix);
                outflags &= ~MD_spc;
        }
@@ -735,7 +777,7 @@ md_post_word(struct roff_node *n)
 {
        const char      *suffix;
 
 {
        const char      *suffix;
 
-       if ((suffix = md_acts[n->tok].suffix) != NULL) {
+       if ((suffix = md_act(n->tok)->suffix) != NULL) {
                outflags &= ~(MD_spc | MD_nl);
                md_word(suffix);
        }
                outflags &= ~(MD_spc | MD_nl);
                md_word(suffix);
        }
@@ -1279,39 +1321,81 @@ md_post_Lb(struct roff_node *n)
                outflags |= MD_br;
 }
 
                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)
 {
 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 ((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++;
                }
        }
                        outcount++;
                }
        }
-
        outflags &= ~MD_spc;
        outflags &= ~MD_spc;
-       md_rawword(link->next == NULL ? ">" : ")");
+       md_rawword(")");
        return 0;
 }
 
        return 0;
 }