cmark

My personal build of CMark ✏️

man.c (5444B)

  1 #include <stdlib.h>
  2 #include <stdio.h>
  3 #include <string.h>
  4 #include <assert.h>
  5 
  6 #include "config.h"
  7 #include "cmark.h"
  8 #include "node.h"
  9 #include "buffer.h"
 10 #include "utf8.h"
 11 #include "render.h"
 12 
 13 #define OUT(s, wrap, escaping) renderer->out(renderer, s, wrap, escaping)
 14 #define LIT(s) renderer->out(renderer, s, false, LITERAL)
 15 #define CR() renderer->cr(renderer)
 16 #define BLANKLINE() renderer->blankline(renderer)
 17 #define LIST_NUMBER_SIZE 20
 18 
 19 // Functions to convert cmark_nodes to groff man strings.
 20 static void S_outc(cmark_renderer *renderer, cmark_escaping escape, int32_t c,
 21                    unsigned char nextc) {
 22   (void)(nextc);
 23 
 24   if (escape == LITERAL) {
 25     cmark_render_code_point(renderer, c);
 26     return;
 27   }
 28 
 29   switch (c) {
 30   case 46:
 31     if (renderer->begin_line) {
 32       cmark_render_ascii(renderer, "\\&.");
 33     } else {
 34       cmark_render_code_point(renderer, c);
 35     }
 36     break;
 37   case 39:
 38     if (renderer->begin_line) {
 39       cmark_render_ascii(renderer, "\\&'");
 40     } else {
 41       cmark_render_code_point(renderer, c);
 42     }
 43     break;
 44   case 45:
 45     cmark_render_ascii(renderer, "\\-");
 46     break;
 47   case 92:
 48     cmark_render_ascii(renderer, "\\e");
 49     break;
 50   case 8216: // left single quote
 51     cmark_render_ascii(renderer, "\\[oq]");
 52     break;
 53   case 8217: // right single quote
 54     cmark_render_ascii(renderer, "\\[cq]");
 55     break;
 56   case 8220: // left double quote
 57     cmark_render_ascii(renderer, "\\[lq]");
 58     break;
 59   case 8221: // right double quote
 60     cmark_render_ascii(renderer, "\\[rq]");
 61     break;
 62   case 8212: // em dash
 63     cmark_render_ascii(renderer, "\\[em]");
 64     break;
 65   case 8211: // en dash
 66     cmark_render_ascii(renderer, "\\[en]");
 67     break;
 68   default:
 69     cmark_render_code_point(renderer, c);
 70   }
 71 }
 72 
 73 static int S_render_node(cmark_renderer *renderer, cmark_node *node,
 74                          cmark_event_type ev_type, int options) {
 75   cmark_node *tmp;
 76   int list_number;
 77   bool entering = (ev_type == CMARK_EVENT_ENTER);
 78   bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options);
 79 
 80   // avoid unused parameter error:
 81   (void)(options);
 82 
 83   switch (node->type) {
 84   case CMARK_NODE_DOCUMENT:
 85     break;
 86 
 87   case CMARK_NODE_BLOCK_QUOTE:
 88     if (entering) {
 89       CR();
 90       LIT(".RS");
 91       CR();
 92     } else {
 93       CR();
 94       LIT(".RE");
 95       CR();
 96     }
 97     break;
 98 
 99   case CMARK_NODE_LIST:
100     break;
101 
102   case CMARK_NODE_ITEM:
103     if (entering) {
104       CR();
105       LIT(".IP ");
106       if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
107         LIT("\\[bu] 2");
108       } else {
109         list_number = cmark_node_get_list_start(node->parent);
110         tmp = node;
111         while (tmp->prev) {
112           tmp = tmp->prev;
113           list_number += 1;
114         }
115         char list_number_s[LIST_NUMBER_SIZE];
116         snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number);
117         LIT(list_number_s);
118       }
119       CR();
120     } else {
121       CR();
122     }
123     break;
124 
125   case CMARK_NODE_HEADING:
126     if (entering) {
127       CR();
128       LIT(cmark_node_get_heading_level(node) == 1 ? ".SH" : ".SS");
129       CR();
130     } else {
131       CR();
132     }
133     break;
134 
135   case CMARK_NODE_CODE_BLOCK:
136     CR();
137     LIT(".IP\n.nf\n\\f[C]\n");
138     OUT(cmark_node_get_literal(node), false, NORMAL);
139     CR();
140     LIT("\\f[]\n.fi");
141     CR();
142     break;
143 
144   case CMARK_NODE_HTML_BLOCK:
145     break;
146 
147   case CMARK_NODE_CUSTOM_BLOCK:
148     CR();
149     OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
150         false, LITERAL);
151     CR();
152     break;
153 
154   case CMARK_NODE_THEMATIC_BREAK:
155     CR();
156     LIT(".PP\n  *  *  *  *  *");
157     CR();
158     break;
159 
160   case CMARK_NODE_PARAGRAPH:
161     if (entering) {
162       // no blank line if first paragraph in list:
163       if (node->parent && node->parent->type == CMARK_NODE_ITEM &&
164           node->prev == NULL) {
165         // no blank line or .PP
166       } else {
167         CR();
168         LIT(".PP");
169         CR();
170       }
171     } else {
172       CR();
173     }
174     break;
175 
176   case CMARK_NODE_TEXT:
177     OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
178     break;
179 
180   case CMARK_NODE_LINEBREAK:
181     LIT(".PD 0\n.P\n.PD");
182     CR();
183     break;
184 
185   case CMARK_NODE_SOFTBREAK:
186     if (options & CMARK_OPT_HARDBREAKS) {
187       LIT(".PD 0\n.P\n.PD");
188       CR();
189     } else if (renderer->width == 0 && !(CMARK_OPT_NOBREAKS & options)) {
190       CR();
191     } else {
192       OUT(" ", allow_wrap, LITERAL);
193     }
194     break;
195 
196   case CMARK_NODE_CODE:
197     LIT("\\f[C]");
198     OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
199     LIT("\\f[]");
200     break;
201 
202   case CMARK_NODE_HTML_INLINE:
203     break;
204 
205   case CMARK_NODE_CUSTOM_INLINE:
206     OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node),
207         false, LITERAL);
208     break;
209 
210   case CMARK_NODE_STRONG:
211     if (entering) {
212       LIT("\\f[B]");
213     } else {
214       LIT("\\f[]");
215     }
216     break;
217 
218   case CMARK_NODE_EMPH:
219     if (entering) {
220       LIT("\\f[I]");
221     } else {
222       LIT("\\f[]");
223     }
224     break;
225 
226   case CMARK_NODE_LINK:
227     if (!entering) {
228       LIT(" (");
229       OUT(cmark_node_get_url(node), allow_wrap, URL);
230       LIT(")");
231     }
232     break;
233 
234   case CMARK_NODE_IMAGE:
235     if (entering) {
236       LIT("[IMAGE: ");
237     } else {
238       LIT("]");
239     }
240     break;
241 
242   default:
243     assert(false);
244     break;
245   }
246 
247   return 1;
248 }
249 
250 char *cmark_render_man(cmark_node *root, int options, int width) {
251   return cmark_render(root, options, width, S_outc, S_render_node);
252 }