commonmarker 0.23.10 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +1192 -0
  3. data/Cargo.toml +7 -0
  4. data/README.md +145 -203
  5. data/ext/commonmarker/Cargo.toml +15 -0
  6. data/ext/commonmarker/extconf.rb +3 -6
  7. data/ext/commonmarker/src/lib.rs +153 -0
  8. data/ext/commonmarker/src/options.rs +144 -0
  9. data/ext/commonmarker/src/plugins/syntax_highlighting.rs +74 -0
  10. data/ext/commonmarker/src/plugins.rs +3 -0
  11. data/ext/commonmarker/src/utils.rs +8 -0
  12. data/lib/commonmarker/config.rb +85 -40
  13. data/lib/commonmarker/constants.rb +7 -0
  14. data/lib/commonmarker/extension.rb +14 -0
  15. data/lib/commonmarker/renderer.rb +1 -127
  16. data/lib/commonmarker/utils.rb +22 -0
  17. data/lib/commonmarker/version.rb +2 -2
  18. data/lib/commonmarker.rb +14 -29
  19. metadata +35 -187
  20. data/Rakefile +0 -109
  21. data/bin/commonmarker +0 -118
  22. data/commonmarker.gemspec +0 -38
  23. data/ext/commonmarker/arena.c +0 -104
  24. data/ext/commonmarker/autolink.c +0 -508
  25. data/ext/commonmarker/autolink.h +0 -8
  26. data/ext/commonmarker/blocks.c +0 -1622
  27. data/ext/commonmarker/buffer.c +0 -278
  28. data/ext/commonmarker/buffer.h +0 -116
  29. data/ext/commonmarker/case_fold_switch.inc +0 -4327
  30. data/ext/commonmarker/chunk.h +0 -135
  31. data/ext/commonmarker/cmark-gfm-core-extensions.h +0 -54
  32. data/ext/commonmarker/cmark-gfm-extension_api.h +0 -737
  33. data/ext/commonmarker/cmark-gfm-extensions_export.h +0 -42
  34. data/ext/commonmarker/cmark-gfm.h +0 -833
  35. data/ext/commonmarker/cmark-gfm_export.h +0 -42
  36. data/ext/commonmarker/cmark-gfm_version.h +0 -7
  37. data/ext/commonmarker/cmark.c +0 -55
  38. data/ext/commonmarker/cmark_ctype.c +0 -44
  39. data/ext/commonmarker/cmark_ctype.h +0 -33
  40. data/ext/commonmarker/commonmark.c +0 -514
  41. data/ext/commonmarker/commonmarker.c +0 -1308
  42. data/ext/commonmarker/commonmarker.h +0 -16
  43. data/ext/commonmarker/config.h +0 -76
  44. data/ext/commonmarker/core-extensions.c +0 -27
  45. data/ext/commonmarker/entities.inc +0 -2138
  46. data/ext/commonmarker/ext_scanners.c +0 -879
  47. data/ext/commonmarker/ext_scanners.h +0 -24
  48. data/ext/commonmarker/footnotes.c +0 -63
  49. data/ext/commonmarker/footnotes.h +0 -27
  50. data/ext/commonmarker/houdini.h +0 -57
  51. data/ext/commonmarker/houdini_href_e.c +0 -100
  52. data/ext/commonmarker/houdini_html_e.c +0 -66
  53. data/ext/commonmarker/houdini_html_u.c +0 -149
  54. data/ext/commonmarker/html.c +0 -502
  55. data/ext/commonmarker/html.h +0 -27
  56. data/ext/commonmarker/inlines.c +0 -1788
  57. data/ext/commonmarker/inlines.h +0 -29
  58. data/ext/commonmarker/iterator.c +0 -159
  59. data/ext/commonmarker/iterator.h +0 -26
  60. data/ext/commonmarker/latex.c +0 -468
  61. data/ext/commonmarker/linked_list.c +0 -37
  62. data/ext/commonmarker/man.c +0 -274
  63. data/ext/commonmarker/map.c +0 -129
  64. data/ext/commonmarker/map.h +0 -44
  65. data/ext/commonmarker/node.c +0 -1045
  66. data/ext/commonmarker/node.h +0 -167
  67. data/ext/commonmarker/parser.h +0 -59
  68. data/ext/commonmarker/plaintext.c +0 -218
  69. data/ext/commonmarker/plugin.c +0 -36
  70. data/ext/commonmarker/plugin.h +0 -34
  71. data/ext/commonmarker/references.c +0 -43
  72. data/ext/commonmarker/references.h +0 -26
  73. data/ext/commonmarker/registry.c +0 -63
  74. data/ext/commonmarker/registry.h +0 -24
  75. data/ext/commonmarker/render.c +0 -213
  76. data/ext/commonmarker/render.h +0 -62
  77. data/ext/commonmarker/scanners.c +0 -14056
  78. data/ext/commonmarker/scanners.h +0 -70
  79. data/ext/commonmarker/scanners.re +0 -341
  80. data/ext/commonmarker/strikethrough.c +0 -167
  81. data/ext/commonmarker/strikethrough.h +0 -9
  82. data/ext/commonmarker/syntax_extension.c +0 -149
  83. data/ext/commonmarker/syntax_extension.h +0 -34
  84. data/ext/commonmarker/table.c +0 -917
  85. data/ext/commonmarker/table.h +0 -12
  86. data/ext/commonmarker/tagfilter.c +0 -60
  87. data/ext/commonmarker/tagfilter.h +0 -8
  88. data/ext/commonmarker/tasklist.c +0 -156
  89. data/ext/commonmarker/tasklist.h +0 -8
  90. data/ext/commonmarker/utf8.c +0 -317
  91. data/ext/commonmarker/utf8.h +0 -35
  92. data/ext/commonmarker/xml.c +0 -182
  93. data/lib/commonmarker/node/inspect.rb +0 -47
  94. data/lib/commonmarker/node.rb +0 -83
  95. data/lib/commonmarker/renderer/html_renderer.rb +0 -256
@@ -1,917 +0,0 @@
1
- #include <cmark-gfm-extension_api.h>
2
- #include <html.h>
3
- #include <inlines.h>
4
- #include <parser.h>
5
- #include <references.h>
6
- #include <string.h>
7
- #include <render.h>
8
-
9
- #include "ext_scanners.h"
10
- #include "strikethrough.h"
11
- #include "table.h"
12
- #include "cmark-gfm-core-extensions.h"
13
-
14
- // Limit to prevent a malicious input from causing a denial of service.
15
- #define MAX_AUTOCOMPLETED_CELLS 0x80000
16
-
17
- // Custom node flag, initialized in `create_table_extension`.
18
- static cmark_node_internal_flags CMARK_NODE__TABLE_VISITED;
19
-
20
- cmark_node_type CMARK_NODE_TABLE, CMARK_NODE_TABLE_ROW,
21
- CMARK_NODE_TABLE_CELL;
22
-
23
- typedef struct {
24
- cmark_strbuf *buf;
25
- int start_offset, end_offset, internal_offset;
26
- } node_cell;
27
-
28
- typedef struct {
29
- uint16_t n_columns;
30
- int paragraph_offset;
31
- node_cell *cells;
32
- } table_row;
33
-
34
- typedef struct {
35
- uint16_t n_columns;
36
- uint8_t *alignments;
37
- int n_rows;
38
- int n_nonempty_cells;
39
- } node_table;
40
-
41
- typedef struct {
42
- bool is_header;
43
- } node_table_row;
44
-
45
- static void free_table_cell(cmark_mem *mem, node_cell *cell) {
46
- cmark_strbuf_free((cmark_strbuf *)cell->buf);
47
- mem->free(cell->buf);
48
- }
49
-
50
- static void free_row_cells(cmark_mem *mem, table_row *row) {
51
- while (row->n_columns > 0) {
52
- free_table_cell(mem, &row->cells[--row->n_columns]);
53
- }
54
- mem->free(row->cells);
55
- row->cells = NULL;
56
- }
57
-
58
- static void free_table_row(cmark_mem *mem, table_row *row) {
59
- if (!row)
60
- return;
61
-
62
- free_row_cells(mem, row);
63
- mem->free(row);
64
- }
65
-
66
- static void free_node_table(cmark_mem *mem, void *ptr) {
67
- node_table *t = (node_table *)ptr;
68
- mem->free(t->alignments);
69
- mem->free(t);
70
- }
71
-
72
- static void free_node_table_row(cmark_mem *mem, void *ptr) {
73
- mem->free(ptr);
74
- }
75
-
76
- static int get_n_table_columns(cmark_node *node) {
77
- if (!node || node->type != CMARK_NODE_TABLE)
78
- return -1;
79
-
80
- return (int)((node_table *)node->as.opaque)->n_columns;
81
- }
82
-
83
- static int set_n_table_columns(cmark_node *node, uint16_t n_columns) {
84
- if (!node || node->type != CMARK_NODE_TABLE)
85
- return 0;
86
-
87
- ((node_table *)node->as.opaque)->n_columns = n_columns;
88
- return 1;
89
- }
90
-
91
- // Increment the number of rows in the table. Also update n_nonempty_cells,
92
- // which keeps track of the number of cells which were parsed from the
93
- // input file. (If one of the rows is too short, then the trailing cells
94
- // are autocompleted. Autocompleted cells are not counted in n_nonempty_cells.)
95
- // The purpose of this is to prevent a malicious input from generating a very
96
- // large number of autocompleted cells, which could cause a denial of service
97
- // vulnerability.
98
- static int incr_table_row_count(cmark_node *node, int i) {
99
- if (!node || node->type != CMARK_NODE_TABLE) {
100
- return 0;
101
- }
102
-
103
- ((node_table *)node->as.opaque)->n_rows++;
104
- ((node_table *)node->as.opaque)->n_nonempty_cells += i;
105
- return 1;
106
- }
107
-
108
- // Calculate the number of autocompleted cells.
109
- static int get_n_autocompleted_cells(cmark_node *node) {
110
- if (!node || node->type != CMARK_NODE_TABLE) {
111
- return 0;
112
- }
113
-
114
- const node_table *nt = (node_table *)node->as.opaque;
115
- return (nt->n_columns * nt->n_rows) - nt->n_nonempty_cells;
116
- }
117
-
118
- static uint8_t *get_table_alignments(cmark_node *node) {
119
- if (!node || node->type != CMARK_NODE_TABLE)
120
- return 0;
121
-
122
- return ((node_table *)node->as.opaque)->alignments;
123
- }
124
-
125
- static int set_table_alignments(cmark_node *node, uint8_t *alignments) {
126
- if (!node || node->type != CMARK_NODE_TABLE)
127
- return 0;
128
-
129
- ((node_table *)node->as.opaque)->alignments = alignments;
130
- return 1;
131
- }
132
-
133
- static uint8_t get_cell_alignment(cmark_node *node) {
134
- if (!node || node->type != CMARK_NODE_TABLE_CELL)
135
- return 0;
136
-
137
- const uint8_t *alignments = get_table_alignments(node->parent->parent);
138
- int i = node->as.cell_index;
139
- return alignments[i];
140
- }
141
-
142
- static int set_cell_index(cmark_node *node, int i) {
143
- if (!node || node->type != CMARK_NODE_TABLE_CELL)
144
- return 0;
145
-
146
- node->as.cell_index = i;
147
- return 1;
148
- }
149
-
150
- static cmark_strbuf *unescape_pipes(cmark_mem *mem, unsigned char *string, bufsize_t len)
151
- {
152
- cmark_strbuf *res = (cmark_strbuf *)mem->calloc(1, sizeof(cmark_strbuf));
153
- bufsize_t r, w;
154
-
155
- cmark_strbuf_init(mem, res, len + 1);
156
- cmark_strbuf_put(res, string, len);
157
- cmark_strbuf_putc(res, '\0');
158
-
159
- for (r = 0, w = 0; r < len; ++r) {
160
- if (res->ptr[r] == '\\' && res->ptr[r + 1] == '|')
161
- r++;
162
-
163
- res->ptr[w++] = res->ptr[r];
164
- }
165
-
166
- cmark_strbuf_truncate(res, w);
167
-
168
- return res;
169
- }
170
-
171
- // Adds a new cell to the end of the row. A pointer to the new cell is returned
172
- // for the caller to initialize.
173
- static node_cell* append_row_cell(cmark_mem *mem, table_row *row) {
174
- const uint32_t n_columns = row->n_columns + 1;
175
- // realloc when n_columns is a power of 2
176
- if ((n_columns & (n_columns-1)) == 0) {
177
- // make sure we never wrap row->n_columns
178
- // offset will != len and our exit will clean up as intended
179
- if (n_columns > UINT16_MAX) {
180
- return NULL;
181
- }
182
- // Use realloc to double the size of the buffer.
183
- row->cells = (node_cell *)mem->realloc(row->cells, (2 * n_columns - 1) * sizeof(node_cell));
184
- }
185
- row->n_columns = (uint16_t)n_columns;
186
- return &row->cells[n_columns-1];
187
- }
188
-
189
- static table_row *row_from_string(cmark_syntax_extension *self,
190
- cmark_parser *parser, unsigned char *string,
191
- int len) {
192
- // Parses a single table row. It has the following form:
193
- // `delim? table_cell (delim table_cell)* delim? newline`
194
- // Note that cells are allowed to be empty.
195
- //
196
- // From the GitHub-flavored Markdown specification:
197
- //
198
- // > Each row consists of cells containing arbitrary text, in which inlines
199
- // > are parsed, separated by pipes (|). A leading and trailing pipe is also
200
- // > recommended for clarity of reading, and if there’s otherwise parsing
201
- // > ambiguity.
202
-
203
- table_row *row = NULL;
204
- bufsize_t cell_matched = 1, pipe_matched = 1, offset;
205
- int expect_more_cells = 1;
206
- int row_end_offset = 0;
207
- int int_overflow_abort = 0;
208
-
209
- row = (table_row *)parser->mem->calloc(1, sizeof(table_row));
210
- row->n_columns = 0;
211
- row->cells = NULL;
212
-
213
- // Scan past the (optional) leading pipe.
214
- offset = scan_table_cell_end(string, len, 0);
215
-
216
- // Parse the cells of the row. Stop if we reach the end of the input, or if we
217
- // cannot detect any more cells.
218
- while (offset < len && expect_more_cells) {
219
- cell_matched = scan_table_cell(string, len, offset);
220
- pipe_matched = scan_table_cell_end(string, len, offset + cell_matched);
221
-
222
- if (cell_matched || pipe_matched) {
223
- // We are guaranteed to have a cell, since (1) either we found some
224
- // content and cell_matched, or (2) we found an empty cell followed by a
225
- // pipe.
226
- cmark_strbuf *cell_buf = unescape_pipes(parser->mem, string + offset,
227
- cell_matched);
228
- cmark_strbuf_trim(cell_buf);
229
-
230
- node_cell *cell = append_row_cell(parser->mem, row);
231
- if (!cell) {
232
- int_overflow_abort = 1;
233
- cmark_strbuf_free(cell_buf);
234
- parser->mem->free(cell_buf);
235
- break;
236
- }
237
- cell->buf = cell_buf;
238
- cell->start_offset = offset;
239
- cell->end_offset = offset + cell_matched - 1;
240
- cell->internal_offset = 0;
241
-
242
- while (cell->start_offset > row->paragraph_offset && string[cell->start_offset - 1] != '|') {
243
- --cell->start_offset;
244
- ++cell->internal_offset;
245
- }
246
- }
247
-
248
- offset += cell_matched + pipe_matched;
249
-
250
- if (pipe_matched) {
251
- expect_more_cells = 1;
252
- } else {
253
- // We've scanned the last cell. Check if we have reached the end of the row
254
- row_end_offset = scan_table_row_end(string, len, offset);
255
- offset += row_end_offset;
256
-
257
- // If the end of the row is not the end of the input,
258
- // the row is not a real row but potentially part of the paragraph
259
- // preceding the table.
260
- if (row_end_offset && offset != len) {
261
- row->paragraph_offset = offset;
262
-
263
- free_row_cells(parser->mem, row);
264
-
265
- // Scan past the (optional) leading pipe.
266
- offset += scan_table_cell_end(string, len, offset);
267
-
268
- expect_more_cells = 1;
269
- } else {
270
- expect_more_cells = 0;
271
- }
272
- }
273
- }
274
-
275
- if (offset != len || row->n_columns == 0 || int_overflow_abort) {
276
- free_table_row(parser->mem, row);
277
- row = NULL;
278
- }
279
-
280
- return row;
281
- }
282
-
283
- static void try_inserting_table_header_paragraph(cmark_parser *parser,
284
- cmark_node *parent_container,
285
- unsigned char *parent_string,
286
- int paragraph_offset) {
287
- cmark_node *paragraph;
288
- cmark_strbuf *paragraph_content;
289
-
290
- paragraph = cmark_node_new_with_mem(CMARK_NODE_PARAGRAPH, parser->mem);
291
-
292
- paragraph_content = unescape_pipes(parser->mem, parent_string, paragraph_offset);
293
- cmark_strbuf_trim(paragraph_content);
294
- cmark_node_set_string_content(paragraph, (char *) paragraph_content->ptr);
295
- cmark_strbuf_free(paragraph_content);
296
- parser->mem->free(paragraph_content);
297
-
298
- if (!cmark_node_insert_before(parent_container, paragraph)) {
299
- parser->mem->free(paragraph);
300
- }
301
- }
302
-
303
- static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
304
- cmark_parser *parser,
305
- cmark_node *parent_container,
306
- unsigned char *input, int len) {
307
- cmark_node *table_header;
308
- table_row *header_row = NULL;
309
- table_row *delimiter_row = NULL;
310
- node_table_row *ntr;
311
- const char *parent_string;
312
- uint16_t i;
313
-
314
- if (parent_container->flags & CMARK_NODE__TABLE_VISITED) {
315
- return parent_container;
316
- }
317
-
318
- if (!scan_table_start(input, len, cmark_parser_get_first_nonspace(parser))) {
319
- return parent_container;
320
- }
321
-
322
- // Since scan_table_start was successful, we must have a delimiter row.
323
- delimiter_row = row_from_string(
324
- self, parser, input + cmark_parser_get_first_nonspace(parser),
325
- len - cmark_parser_get_first_nonspace(parser));
326
- // assert may be optimized out, don't rely on it for security boundaries
327
- if (!delimiter_row) {
328
- return parent_container;
329
- }
330
-
331
- assert(delimiter_row);
332
-
333
- cmark_arena_push();
334
-
335
- // Check for a matching header row. We call `row_from_string` with the entire
336
- // (potentially long) parent container as input, but this should be safe since
337
- // `row_from_string` bails out early if it does not find a row.
338
- parent_string = cmark_node_get_string_content(parent_container);
339
- header_row = row_from_string(self, parser, (unsigned char *)parent_string,
340
- (int)strlen(parent_string));
341
- if (!header_row || header_row->n_columns != delimiter_row->n_columns) {
342
- free_table_row(parser->mem, delimiter_row);
343
- free_table_row(parser->mem, header_row);
344
- cmark_arena_pop();
345
- parent_container->flags |= CMARK_NODE__TABLE_VISITED;
346
- return parent_container;
347
- }
348
-
349
- if (cmark_arena_pop()) {
350
- delimiter_row = row_from_string(
351
- self, parser, input + cmark_parser_get_first_nonspace(parser),
352
- len - cmark_parser_get_first_nonspace(parser));
353
- header_row = row_from_string(self, parser, (unsigned char *)parent_string,
354
- (int)strlen(parent_string));
355
- // row_from_string can return NULL, add additional check to ensure n_columns match
356
- if (!delimiter_row || !header_row || header_row->n_columns != delimiter_row->n_columns) {
357
- free_table_row(parser->mem, delimiter_row);
358
- free_table_row(parser->mem, header_row);
359
- return parent_container;
360
- }
361
- }
362
-
363
- if (!cmark_node_set_type(parent_container, CMARK_NODE_TABLE)) {
364
- free_table_row(parser->mem, header_row);
365
- free_table_row(parser->mem, delimiter_row);
366
- return parent_container;
367
- }
368
-
369
- if (header_row->paragraph_offset) {
370
- try_inserting_table_header_paragraph(parser, parent_container, (unsigned char *)parent_string,
371
- header_row->paragraph_offset);
372
- }
373
-
374
- cmark_node_set_syntax_extension(parent_container, self);
375
- parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table));
376
- set_n_table_columns(parent_container, header_row->n_columns);
377
-
378
- // allocate alignments based on delimiter_row->n_columns
379
- // since we populate the alignments array based on delimiter_row->cells
380
- uint8_t *alignments =
381
- (uint8_t *)parser->mem->calloc(delimiter_row->n_columns, sizeof(uint8_t));
382
- for (i = 0; i < delimiter_row->n_columns; ++i) {
383
- node_cell *node = &delimiter_row->cells[i];
384
- bool left = node->buf->ptr[0] == ':', right = node->buf->ptr[node->buf->size - 1] == ':';
385
-
386
- if (left && right)
387
- alignments[i] = 'c';
388
- else if (left)
389
- alignments[i] = 'l';
390
- else if (right)
391
- alignments[i] = 'r';
392
- }
393
- set_table_alignments(parent_container, alignments);
394
-
395
- table_header =
396
- cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
397
- parent_container->start_column);
398
- cmark_node_set_syntax_extension(table_header, self);
399
- table_header->end_column = parent_container->start_column + (int)strlen(parent_string) - 2;
400
- table_header->start_line = table_header->end_line = parent_container->start_line;
401
-
402
- table_header->as.opaque = ntr = (node_table_row *)parser->mem->calloc(1, sizeof(node_table_row));
403
- ntr->is_header = true;
404
-
405
- for (i = 0; i < header_row->n_columns; ++i) {
406
- node_cell *cell = &header_row->cells[i];
407
- cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
408
- CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
409
- header_cell->start_line = header_cell->end_line = parent_container->start_line;
410
- header_cell->internal_offset = cell->internal_offset;
411
- header_cell->end_column = parent_container->start_column + cell->end_offset;
412
- cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
413
- cmark_node_set_syntax_extension(header_cell, self);
414
- set_cell_index(header_cell, i);
415
- }
416
-
417
- incr_table_row_count(parent_container, i);
418
-
419
- cmark_parser_advance_offset(
420
- parser, (char *)input,
421
- (int)strlen((char *)input) - 1 - cmark_parser_get_offset(parser), false);
422
-
423
- free_table_row(parser->mem, header_row);
424
- free_table_row(parser->mem, delimiter_row);
425
- return parent_container;
426
- }
427
-
428
- static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
429
- cmark_parser *parser,
430
- cmark_node *parent_container,
431
- unsigned char *input, int len) {
432
- cmark_node *table_row_block;
433
- table_row *row;
434
-
435
- if (cmark_parser_is_blank(parser))
436
- return NULL;
437
-
438
- if (get_n_autocompleted_cells(parent_container) > MAX_AUTOCOMPLETED_CELLS) {
439
- return NULL;
440
- }
441
-
442
- table_row_block =
443
- cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
444
- parent_container->start_column);
445
- cmark_node_set_syntax_extension(table_row_block, self);
446
- table_row_block->end_column = parent_container->end_column;
447
- table_row_block->as.opaque = parser->mem->calloc(1, sizeof(node_table_row));
448
-
449
- row = row_from_string(self, parser, input + cmark_parser_get_first_nonspace(parser),
450
- len - cmark_parser_get_first_nonspace(parser));
451
-
452
- if (!row) {
453
- // clean up the dangling node
454
- cmark_node_free(table_row_block);
455
- return NULL;
456
- }
457
-
458
- {
459
- int i, table_columns = get_n_table_columns(parent_container);
460
-
461
- for (i = 0; i < row->n_columns && i < table_columns; ++i) {
462
- node_cell *cell = &row->cells[i];
463
- cmark_node *node = cmark_parser_add_child(parser, table_row_block,
464
- CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
465
- node->internal_offset = cell->internal_offset;
466
- node->end_column = parent_container->start_column + cell->end_offset;
467
- cmark_node_set_string_content(node, (char *) cell->buf->ptr);
468
- cmark_node_set_syntax_extension(node, self);
469
- set_cell_index(node, i);
470
- }
471
-
472
- incr_table_row_count(parent_container, i);
473
-
474
- for (; i < table_columns; ++i) {
475
- cmark_node *node = cmark_parser_add_child(
476
- parser, table_row_block, CMARK_NODE_TABLE_CELL, 0);
477
- cmark_node_set_syntax_extension(node, self);
478
- set_cell_index(node, i);
479
- }
480
- }
481
-
482
- free_table_row(parser->mem, row);
483
-
484
- cmark_parser_advance_offset(parser, (char *)input,
485
- len - 1 - cmark_parser_get_offset(parser), false);
486
-
487
- return table_row_block;
488
- }
489
-
490
- static cmark_node *try_opening_table_block(cmark_syntax_extension *self,
491
- int indented, cmark_parser *parser,
492
- cmark_node *parent_container,
493
- unsigned char *input, int len) {
494
- cmark_node_type parent_type = cmark_node_get_type(parent_container);
495
-
496
- if (!indented && parent_type == CMARK_NODE_PARAGRAPH) {
497
- return try_opening_table_header(self, parser, parent_container, input, len);
498
- } else if (!indented && parent_type == CMARK_NODE_TABLE) {
499
- return try_opening_table_row(self, parser, parent_container, input, len);
500
- }
501
-
502
- return NULL;
503
- }
504
-
505
- static int matches(cmark_syntax_extension *self, cmark_parser *parser,
506
- unsigned char *input, int len,
507
- cmark_node *parent_container) {
508
- int res = 0;
509
-
510
- if (cmark_node_get_type(parent_container) == CMARK_NODE_TABLE) {
511
- cmark_arena_push();
512
- table_row *new_row = row_from_string(
513
- self, parser, input + cmark_parser_get_first_nonspace(parser),
514
- len - cmark_parser_get_first_nonspace(parser));
515
- if (new_row && new_row->n_columns)
516
- res = 1;
517
- free_table_row(parser->mem, new_row);
518
- cmark_arena_pop();
519
- }
520
-
521
- return res;
522
- }
523
-
524
- static const char *get_type_string(cmark_syntax_extension *self,
525
- cmark_node *node) {
526
- if (node->type == CMARK_NODE_TABLE) {
527
- return "table";
528
- } else if (node->type == CMARK_NODE_TABLE_ROW) {
529
- if (((node_table_row *)node->as.opaque)->is_header)
530
- return "table_header";
531
- else
532
- return "table_row";
533
- } else if (node->type == CMARK_NODE_TABLE_CELL) {
534
- return "table_cell";
535
- }
536
-
537
- return "<unknown>";
538
- }
539
-
540
- static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
541
- cmark_node_type child_type) {
542
- if (node->type == CMARK_NODE_TABLE) {
543
- return child_type == CMARK_NODE_TABLE_ROW;
544
- } else if (node->type == CMARK_NODE_TABLE_ROW) {
545
- return child_type == CMARK_NODE_TABLE_CELL;
546
- } else if (node->type == CMARK_NODE_TABLE_CELL) {
547
- return child_type == CMARK_NODE_TEXT || child_type == CMARK_NODE_CODE ||
548
- child_type == CMARK_NODE_EMPH || child_type == CMARK_NODE_STRONG ||
549
- child_type == CMARK_NODE_LINK || child_type == CMARK_NODE_IMAGE ||
550
- child_type == CMARK_NODE_STRIKETHROUGH ||
551
- child_type == CMARK_NODE_HTML_INLINE ||
552
- child_type == CMARK_NODE_FOOTNOTE_REFERENCE;
553
- }
554
- return false;
555
- }
556
-
557
- static int contains_inlines(cmark_syntax_extension *extension,
558
- cmark_node *node) {
559
- return node->type == CMARK_NODE_TABLE_CELL;
560
- }
561
-
562
- static void commonmark_render(cmark_syntax_extension *extension,
563
- cmark_renderer *renderer, cmark_node *node,
564
- cmark_event_type ev_type, int options) {
565
- bool entering = (ev_type == CMARK_EVENT_ENTER);
566
-
567
- if (node->type == CMARK_NODE_TABLE) {
568
- renderer->blankline(renderer);
569
- } else if (node->type == CMARK_NODE_TABLE_ROW) {
570
- if (entering) {
571
- renderer->cr(renderer);
572
- renderer->out(renderer, node, "|", false, LITERAL);
573
- }
574
- } else if (node->type == CMARK_NODE_TABLE_CELL) {
575
- if (entering) {
576
- renderer->out(renderer, node, " ", false, LITERAL);
577
- } else {
578
- renderer->out(renderer, node, " |", false, LITERAL);
579
- if (((node_table_row *)node->parent->as.opaque)->is_header &&
580
- !node->next) {
581
- int i;
582
- uint8_t *alignments = get_table_alignments(node->parent->parent);
583
- uint16_t n_cols =
584
- ((node_table *)node->parent->parent->as.opaque)->n_columns;
585
- renderer->cr(renderer);
586
- renderer->out(renderer, node, "|", false, LITERAL);
587
- for (i = 0; i < n_cols; i++) {
588
- switch (alignments[i]) {
589
- case 0: renderer->out(renderer, node, " --- |", false, LITERAL); break;
590
- case 'l': renderer->out(renderer, node, " :-- |", false, LITERAL); break;
591
- case 'c': renderer->out(renderer, node, " :-: |", false, LITERAL); break;
592
- case 'r': renderer->out(renderer, node, " --: |", false, LITERAL); break;
593
- }
594
- }
595
- renderer->cr(renderer);
596
- }
597
- }
598
- } else {
599
- assert(false);
600
- }
601
- }
602
-
603
- static void latex_render(cmark_syntax_extension *extension,
604
- cmark_renderer *renderer, cmark_node *node,
605
- cmark_event_type ev_type, int options) {
606
- bool entering = (ev_type == CMARK_EVENT_ENTER);
607
-
608
- if (node->type == CMARK_NODE_TABLE) {
609
- if (entering) {
610
- int i;
611
- uint16_t n_cols;
612
- uint8_t *alignments = get_table_alignments(node);
613
-
614
- renderer->cr(renderer);
615
- renderer->out(renderer, node, "\\begin{table}", false, LITERAL);
616
- renderer->cr(renderer);
617
- renderer->out(renderer, node, "\\begin{tabular}{", false, LITERAL);
618
-
619
- n_cols = ((node_table *)node->as.opaque)->n_columns;
620
- for (i = 0; i < n_cols; i++) {
621
- switch(alignments[i]) {
622
- case 0:
623
- case 'l':
624
- renderer->out(renderer, node, "l", false, LITERAL);
625
- break;
626
- case 'c':
627
- renderer->out(renderer, node, "c", false, LITERAL);
628
- break;
629
- case 'r':
630
- renderer->out(renderer, node, "r", false, LITERAL);
631
- break;
632
- }
633
- }
634
- renderer->out(renderer, node, "}", false, LITERAL);
635
- renderer->cr(renderer);
636
- } else {
637
- renderer->out(renderer, node, "\\end{tabular}", false, LITERAL);
638
- renderer->cr(renderer);
639
- renderer->out(renderer, node, "\\end{table}", false, LITERAL);
640
- renderer->cr(renderer);
641
- }
642
- } else if (node->type == CMARK_NODE_TABLE_ROW) {
643
- if (!entering) {
644
- renderer->cr(renderer);
645
- }
646
- } else if (node->type == CMARK_NODE_TABLE_CELL) {
647
- if (!entering) {
648
- if (node->next) {
649
- renderer->out(renderer, node, " & ", false, LITERAL);
650
- } else {
651
- renderer->out(renderer, node, " \\\\", false, LITERAL);
652
- }
653
- }
654
- } else {
655
- assert(false);
656
- }
657
- }
658
-
659
- static const char *xml_attr(cmark_syntax_extension *extension,
660
- cmark_node *node) {
661
- if (node->type == CMARK_NODE_TABLE_CELL) {
662
- if (cmark_gfm_extensions_get_table_row_is_header(node->parent)) {
663
- switch (get_cell_alignment(node)) {
664
- case 'l': return " align=\"left\"";
665
- case 'c': return " align=\"center\"";
666
- case 'r': return " align=\"right\"";
667
- }
668
- }
669
- }
670
-
671
- return NULL;
672
- }
673
-
674
- static void man_render(cmark_syntax_extension *extension,
675
- cmark_renderer *renderer, cmark_node *node,
676
- cmark_event_type ev_type, int options) {
677
- bool entering = (ev_type == CMARK_EVENT_ENTER);
678
-
679
- if (node->type == CMARK_NODE_TABLE) {
680
- if (entering) {
681
- int i;
682
- uint16_t n_cols;
683
- uint8_t *alignments = get_table_alignments(node);
684
-
685
- renderer->cr(renderer);
686
- renderer->out(renderer, node, ".TS", false, LITERAL);
687
- renderer->cr(renderer);
688
- renderer->out(renderer, node, "tab(@);", false, LITERAL);
689
- renderer->cr(renderer);
690
-
691
- n_cols = ((node_table *)node->as.opaque)->n_columns;
692
-
693
- for (i = 0; i < n_cols; i++) {
694
- switch (alignments[i]) {
695
- case 'l':
696
- renderer->out(renderer, node, "l", false, LITERAL);
697
- break;
698
- case 0:
699
- case 'c':
700
- renderer->out(renderer, node, "c", false, LITERAL);
701
- break;
702
- case 'r':
703
- renderer->out(renderer, node, "r", false, LITERAL);
704
- break;
705
- }
706
- }
707
-
708
- if (n_cols) {
709
- renderer->out(renderer, node, ".", false, LITERAL);
710
- renderer->cr(renderer);
711
- }
712
- } else {
713
- renderer->out(renderer, node, ".TE", false, LITERAL);
714
- renderer->cr(renderer);
715
- }
716
- } else if (node->type == CMARK_NODE_TABLE_ROW) {
717
- if (!entering) {
718
- renderer->cr(renderer);
719
- }
720
- } else if (node->type == CMARK_NODE_TABLE_CELL) {
721
- if (!entering && node->next) {
722
- renderer->out(renderer, node, "@", false, LITERAL);
723
- }
724
- } else {
725
- assert(false);
726
- }
727
- }
728
-
729
- static void html_table_add_align(cmark_strbuf* html, const char* align, int options) {
730
- if (options & CMARK_OPT_TABLE_PREFER_STYLE_ATTRIBUTES) {
731
- cmark_strbuf_puts(html, " style=\"text-align: ");
732
- cmark_strbuf_puts(html, align);
733
- cmark_strbuf_puts(html, "\"");
734
- } else {
735
- cmark_strbuf_puts(html, " align=\"");
736
- cmark_strbuf_puts(html, align);
737
- cmark_strbuf_puts(html, "\"");
738
- }
739
- }
740
-
741
- struct html_table_state {
742
- unsigned need_closing_table_body : 1;
743
- unsigned in_table_header : 1;
744
- };
745
-
746
- static void html_render(cmark_syntax_extension *extension,
747
- cmark_html_renderer *renderer, cmark_node *node,
748
- cmark_event_type ev_type, int options) {
749
- bool entering = (ev_type == CMARK_EVENT_ENTER);
750
- cmark_strbuf *html = renderer->html;
751
-
752
- // XXX: we just monopolise renderer->opaque.
753
- struct html_table_state *table_state =
754
- (struct html_table_state *)&renderer->opaque;
755
-
756
- if (node->type == CMARK_NODE_TABLE) {
757
- if (entering) {
758
- cmark_html_render_cr(html);
759
- cmark_strbuf_puts(html, "<table");
760
- cmark_html_render_sourcepos(node, html, options);
761
- cmark_strbuf_putc(html, '>');
762
- table_state->need_closing_table_body = false;
763
- } else {
764
- if (table_state->need_closing_table_body) {
765
- cmark_html_render_cr(html);
766
- cmark_strbuf_puts(html, "</tbody>");
767
- cmark_html_render_cr(html);
768
- }
769
- table_state->need_closing_table_body = false;
770
- cmark_html_render_cr(html);
771
- cmark_strbuf_puts(html, "</table>");
772
- cmark_html_render_cr(html);
773
- }
774
- } else if (node->type == CMARK_NODE_TABLE_ROW) {
775
- if (entering) {
776
- cmark_html_render_cr(html);
777
- if (((node_table_row *)node->as.opaque)->is_header) {
778
- table_state->in_table_header = 1;
779
- cmark_strbuf_puts(html, "<thead>");
780
- cmark_html_render_cr(html);
781
- } else if (!table_state->need_closing_table_body) {
782
- cmark_strbuf_puts(html, "<tbody>");
783
- cmark_html_render_cr(html);
784
- table_state->need_closing_table_body = 1;
785
- }
786
- cmark_strbuf_puts(html, "<tr");
787
- cmark_html_render_sourcepos(node, html, options);
788
- cmark_strbuf_putc(html, '>');
789
- } else {
790
- cmark_html_render_cr(html);
791
- cmark_strbuf_puts(html, "</tr>");
792
- if (((node_table_row *)node->as.opaque)->is_header) {
793
- cmark_html_render_cr(html);
794
- cmark_strbuf_puts(html, "</thead>");
795
- table_state->in_table_header = false;
796
- }
797
- }
798
- } else if (node->type == CMARK_NODE_TABLE_CELL) {
799
- if (entering) {
800
- cmark_html_render_cr(html);
801
- if (table_state->in_table_header) {
802
- cmark_strbuf_puts(html, "<th");
803
- } else {
804
- cmark_strbuf_puts(html, "<td");
805
- }
806
-
807
- switch (get_cell_alignment(node)) {
808
- case 'l': html_table_add_align(html, "left", options); break;
809
- case 'c': html_table_add_align(html, "center", options); break;
810
- case 'r': html_table_add_align(html, "right", options); break;
811
- }
812
-
813
- cmark_html_render_sourcepos(node, html, options);
814
- cmark_strbuf_putc(html, '>');
815
- } else {
816
- if (table_state->in_table_header) {
817
- cmark_strbuf_puts(html, "</th>");
818
- } else {
819
- cmark_strbuf_puts(html, "</td>");
820
- }
821
- }
822
- } else {
823
- assert(false);
824
- }
825
- }
826
-
827
- static void opaque_alloc(cmark_syntax_extension *self, cmark_mem *mem, cmark_node *node) {
828
- if (node->type == CMARK_NODE_TABLE) {
829
- node->as.opaque = mem->calloc(1, sizeof(node_table));
830
- } else if (node->type == CMARK_NODE_TABLE_ROW) {
831
- node->as.opaque = mem->calloc(1, sizeof(node_table_row));
832
- } else if (node->type == CMARK_NODE_TABLE_CELL) {
833
- node->as.opaque = mem->calloc(1, sizeof(node_cell));
834
- }
835
- }
836
-
837
- static void opaque_free(cmark_syntax_extension *self, cmark_mem *mem, cmark_node *node) {
838
- if (node->type == CMARK_NODE_TABLE) {
839
- free_node_table(mem, node->as.opaque);
840
- } else if (node->type == CMARK_NODE_TABLE_ROW) {
841
- free_node_table_row(mem, node->as.opaque);
842
- }
843
- }
844
-
845
- static int escape(cmark_syntax_extension *self, cmark_node *node, int c) {
846
- return
847
- node->type != CMARK_NODE_TABLE &&
848
- node->type != CMARK_NODE_TABLE_ROW &&
849
- node->type != CMARK_NODE_TABLE_CELL &&
850
- c == '|';
851
- }
852
-
853
- cmark_syntax_extension *create_table_extension(void) {
854
- cmark_syntax_extension *self = cmark_syntax_extension_new("table");
855
-
856
- cmark_register_node_flag(&CMARK_NODE__TABLE_VISITED);
857
- cmark_syntax_extension_set_match_block_func(self, matches);
858
- cmark_syntax_extension_set_open_block_func(self, try_opening_table_block);
859
- cmark_syntax_extension_set_get_type_string_func(self, get_type_string);
860
- cmark_syntax_extension_set_can_contain_func(self, can_contain);
861
- cmark_syntax_extension_set_contains_inlines_func(self, contains_inlines);
862
- cmark_syntax_extension_set_commonmark_render_func(self, commonmark_render);
863
- cmark_syntax_extension_set_plaintext_render_func(self, commonmark_render);
864
- cmark_syntax_extension_set_latex_render_func(self, latex_render);
865
- cmark_syntax_extension_set_xml_attr_func(self, xml_attr);
866
- cmark_syntax_extension_set_man_render_func(self, man_render);
867
- cmark_syntax_extension_set_html_render_func(self, html_render);
868
- cmark_syntax_extension_set_opaque_alloc_func(self, opaque_alloc);
869
- cmark_syntax_extension_set_opaque_free_func(self, opaque_free);
870
- cmark_syntax_extension_set_commonmark_escape_func(self, escape);
871
- CMARK_NODE_TABLE = cmark_syntax_extension_add_node(0);
872
- CMARK_NODE_TABLE_ROW = cmark_syntax_extension_add_node(0);
873
- CMARK_NODE_TABLE_CELL = cmark_syntax_extension_add_node(0);
874
-
875
- return self;
876
- }
877
-
878
- uint16_t cmark_gfm_extensions_get_table_columns(cmark_node *node) {
879
- if (node->type != CMARK_NODE_TABLE)
880
- return 0;
881
-
882
- return ((node_table *)node->as.opaque)->n_columns;
883
- }
884
-
885
- uint8_t *cmark_gfm_extensions_get_table_alignments(cmark_node *node) {
886
- if (node->type != CMARK_NODE_TABLE)
887
- return 0;
888
-
889
- return ((node_table *)node->as.opaque)->alignments;
890
- }
891
-
892
- int cmark_gfm_extensions_set_table_columns(cmark_node *node, uint16_t n_columns) {
893
- return set_n_table_columns(node, n_columns);
894
- }
895
-
896
- int cmark_gfm_extensions_set_table_alignments(cmark_node *node, uint16_t ncols, uint8_t *alignments) {
897
- uint8_t *a = (uint8_t *)cmark_node_mem(node)->calloc(1, ncols);
898
- memcpy(a, alignments, ncols);
899
- return set_table_alignments(node, a);
900
- }
901
-
902
- int cmark_gfm_extensions_get_table_row_is_header(cmark_node *node)
903
- {
904
- if (!node || node->type != CMARK_NODE_TABLE_ROW)
905
- return 0;
906
-
907
- return ((node_table_row *)node->as.opaque)->is_header;
908
- }
909
-
910
- int cmark_gfm_extensions_set_table_row_is_header(cmark_node *node, int is_header)
911
- {
912
- if (!node || node->type != CMARK_NODE_TABLE_ROW)
913
- return 0;
914
-
915
- ((node_table_row *)node->as.opaque)->is_header = (is_header != 0);
916
- return 1;
917
- }