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.
S_outc(cmark_renderer * renderer,cmark_escaping escape,int32_t c,unsigned char nextc)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 
S_render_node(cmark_renderer * renderer,cmark_node * node,cmark_event_type ev_type,int options)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 
cmark_render_man(cmark_node * root,int options,int width)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 }
253