| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| #include "config.h" |
| #include "cmark.h" |
| #include "node.h" |
| #include "buffer.h" |
| #include "utf8.h" |
| #include "render.h" |
| |
| #define OUT(s, wrap, escaping) renderer->out(renderer, s, wrap, escaping) |
| #define LIT(s) renderer->out(renderer, s, false, LITERAL) |
| #define CR() renderer->cr(renderer) |
| #define BLANKLINE() renderer->blankline(renderer) |
| |
| // Functions to convert cmark_nodes to groff man strings. |
| static void S_outc(cmark_renderer *renderer, cmark_escaping escape, int32_t c, |
| unsigned char nextc) { |
| (void)(nextc); |
| |
| if (escape == LITERAL) { |
| cmark_render_code_point(renderer, c); |
| return; |
| } |
| |
| switch (c) { |
| case 46: |
| if (renderer->begin_line) { |
| cmark_render_ascii(renderer, "\\&."); |
| } else { |
| cmark_render_code_point(renderer, c); |
| } |
| break; |
| case 39: |
| if (renderer->begin_line) { |
| cmark_render_ascii(renderer, "\\&'"); |
| } else { |
| cmark_render_code_point(renderer, c); |
| } |
| break; |
| case 45: |
| cmark_render_ascii(renderer, "\\-"); |
| break; |
| case 92: |
| cmark_render_ascii(renderer, "\\e"); |
| break; |
| case 8216: // left single quote |
| cmark_render_ascii(renderer, "\\[oq]"); |
| break; |
| case 8217: // right single quote |
| cmark_render_ascii(renderer, "\\[cq]"); |
| break; |
| case 8220: // left double quote |
| cmark_render_ascii(renderer, "\\[lq]"); |
| break; |
| case 8221: // right double quote |
| cmark_render_ascii(renderer, "\\[rq]"); |
| break; |
| case 8212: // em dash |
| cmark_render_ascii(renderer, "\\[em]"); |
| break; |
| case 8211: // en dash |
| cmark_render_ascii(renderer, "\\[en]"); |
| break; |
| default: |
| cmark_render_code_point(renderer, c); |
| } |
| } |
| |
| static int S_render_node(cmark_renderer *renderer, cmark_node *node, |
| cmark_event_type ev_type, int options) { |
| cmark_node *tmp; |
| int list_number; |
| bool entering = (ev_type == CMARK_EVENT_ENTER); |
| |
| // avoid unused parameter error: |
| (void)(options); |
| |
| switch (node->type) { |
| case CMARK_NODE_DOCUMENT: |
| break; |
| |
| case CMARK_NODE_BLOCK_QUOTE: |
| if (entering) { |
| CR(); |
| LIT(".RS"); |
| CR(); |
| } else { |
| CR(); |
| LIT(".RE"); |
| CR(); |
| } |
| break; |
| |
| case CMARK_NODE_LIST: |
| break; |
| |
| case CMARK_NODE_ITEM: |
| if (entering) { |
| CR(); |
| LIT(".IP "); |
| if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) { |
| LIT("\\[bu] 2"); |
| } else { |
| list_number = cmark_node_get_list_start(node->parent); |
| tmp = node; |
| while (tmp->prev) { |
| tmp = tmp->prev; |
| list_number += 1; |
| } |
| char list_number_s[20]; |
| sprintf(list_number_s, "\"%d.\" 4", list_number); |
| LIT(list_number_s); |
| } |
| CR(); |
| } else { |
| CR(); |
| } |
| break; |
| |
| case CMARK_NODE_HEADER: |
| if (entering) { |
| CR(); |
| LIT(cmark_node_get_header_level(node) == 1 ? ".SH" : ".SS"); |
| CR(); |
| } else { |
| CR(); |
| } |
| break; |
| |
| case CMARK_NODE_CODE_BLOCK: |
| CR(); |
| LIT(".IP\n.nf\n\\f[C]\n"); |
| OUT(cmark_node_get_literal(node), false, NORMAL); |
| CR(); |
| LIT("\\f[]\n.fi"); |
| CR(); |
| break; |
| |
| case CMARK_NODE_HTML: |
| break; |
| |
| case CMARK_NODE_HRULE: |
| CR(); |
| LIT(".PP\n * * * * *"); |
| CR(); |
| break; |
| |
| case CMARK_NODE_PARAGRAPH: |
| if (entering) { |
| // no blank line if first paragraph in list: |
| if (node->parent && node->parent->type == CMARK_NODE_ITEM && |
| node->prev == NULL) { |
| // no blank line or .PP |
| } else { |
| CR(); |
| LIT(".PP"); |
| CR(); |
| } |
| } else { |
| CR(); |
| } |
| break; |
| |
| case CMARK_NODE_TEXT: |
| OUT(cmark_node_get_literal(node), true, NORMAL); |
| break; |
| |
| case CMARK_NODE_LINEBREAK: |
| LIT(".PD 0\n.P\n.PD"); |
| CR(); |
| break; |
| |
| case CMARK_NODE_SOFTBREAK: |
| if (renderer->width == 0) { |
| CR(); |
| } else { |
| OUT(" ", true, LITERAL); |
| } |
| break; |
| |
| case CMARK_NODE_CODE: |
| LIT("\\f[C]"); |
| OUT(cmark_node_get_literal(node), true, NORMAL); |
| LIT("\\f[]"); |
| break; |
| |
| case CMARK_NODE_INLINE_HTML: |
| break; |
| |
| case CMARK_NODE_STRONG: |
| if (entering) { |
| LIT("\\f[B]"); |
| } else { |
| LIT("\\f[]"); |
| } |
| break; |
| |
| case CMARK_NODE_EMPH: |
| if (entering) { |
| LIT("\\f[I]"); |
| } else { |
| LIT("\\f[]"); |
| } |
| break; |
| |
| case CMARK_NODE_LINK: |
| if (!entering) { |
| LIT(" ("); |
| OUT(cmark_node_get_url(node), true, URL); |
| LIT(")"); |
| } |
| break; |
| |
| case CMARK_NODE_IMAGE: |
| if (entering) { |
| LIT("[IMAGE: "); |
| } else { |
| LIT("]"); |
| } |
| break; |
| |
| default: |
| assert(false); |
| break; |
| } |
| |
| return 1; |
| } |
| |
| char *cmark_render_man(cmark_node *root, int options, int width) { |
| return cmark_render(root, options, width, S_outc, S_render_node); |
| } |