commonmarker 0.23.6 → 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +70 -212
  3. data/commonmarker.gemspec +34 -31
  4. data/ext/commonmarker/Cargo.toml +12 -0
  5. data/ext/commonmarker/_util.rb +102 -0
  6. data/ext/commonmarker/extconf.rb +4 -5
  7. data/ext/commonmarker/src/comrak_options.rs +107 -0
  8. data/ext/commonmarker/src/lib.rs +27 -0
  9. data/lib/commonmarker/config.rb +58 -37
  10. data/lib/commonmarker/extension.rb +14 -0
  11. data/lib/commonmarker/renderer.rb +1 -127
  12. data/lib/commonmarker/version.rb +2 -2
  13. data/lib/commonmarker.rb +19 -32
  14. metadata +33 -177
  15. data/Rakefile +0 -109
  16. data/bin/commonmarker +0 -118
  17. data/ext/commonmarker/arena.c +0 -103
  18. data/ext/commonmarker/autolink.c +0 -456
  19. data/ext/commonmarker/autolink.h +0 -8
  20. data/ext/commonmarker/blocks.c +0 -1596
  21. data/ext/commonmarker/buffer.c +0 -278
  22. data/ext/commonmarker/buffer.h +0 -116
  23. data/ext/commonmarker/case_fold_switch.inc +0 -4327
  24. data/ext/commonmarker/chunk.h +0 -135
  25. data/ext/commonmarker/cmark-gfm-core-extensions.h +0 -54
  26. data/ext/commonmarker/cmark-gfm-extension_api.h +0 -736
  27. data/ext/commonmarker/cmark-gfm-extensions_export.h +0 -42
  28. data/ext/commonmarker/cmark-gfm.h +0 -817
  29. data/ext/commonmarker/cmark-gfm_export.h +0 -42
  30. data/ext/commonmarker/cmark-gfm_version.h +0 -7
  31. data/ext/commonmarker/cmark.c +0 -55
  32. data/ext/commonmarker/cmark_ctype.c +0 -44
  33. data/ext/commonmarker/cmark_ctype.h +0 -33
  34. data/ext/commonmarker/commonmark.c +0 -529
  35. data/ext/commonmarker/commonmarker.c +0 -1307
  36. data/ext/commonmarker/commonmarker.h +0 -16
  37. data/ext/commonmarker/config.h +0 -76
  38. data/ext/commonmarker/core-extensions.c +0 -27
  39. data/ext/commonmarker/entities.inc +0 -2138
  40. data/ext/commonmarker/ext_scanners.c +0 -879
  41. data/ext/commonmarker/ext_scanners.h +0 -24
  42. data/ext/commonmarker/footnotes.c +0 -63
  43. data/ext/commonmarker/footnotes.h +0 -27
  44. data/ext/commonmarker/houdini.h +0 -57
  45. data/ext/commonmarker/houdini_href_e.c +0 -100
  46. data/ext/commonmarker/houdini_html_e.c +0 -66
  47. data/ext/commonmarker/houdini_html_u.c +0 -149
  48. data/ext/commonmarker/html.c +0 -486
  49. data/ext/commonmarker/html.h +0 -27
  50. data/ext/commonmarker/inlines.c +0 -1716
  51. data/ext/commonmarker/inlines.h +0 -29
  52. data/ext/commonmarker/iterator.c +0 -159
  53. data/ext/commonmarker/iterator.h +0 -26
  54. data/ext/commonmarker/latex.c +0 -466
  55. data/ext/commonmarker/linked_list.c +0 -37
  56. data/ext/commonmarker/man.c +0 -278
  57. data/ext/commonmarker/map.c +0 -122
  58. data/ext/commonmarker/map.h +0 -41
  59. data/ext/commonmarker/node.c +0 -979
  60. data/ext/commonmarker/node.h +0 -125
  61. data/ext/commonmarker/parser.h +0 -58
  62. data/ext/commonmarker/plaintext.c +0 -235
  63. data/ext/commonmarker/plugin.c +0 -36
  64. data/ext/commonmarker/plugin.h +0 -34
  65. data/ext/commonmarker/references.c +0 -42
  66. data/ext/commonmarker/references.h +0 -26
  67. data/ext/commonmarker/registry.c +0 -63
  68. data/ext/commonmarker/registry.h +0 -24
  69. data/ext/commonmarker/render.c +0 -205
  70. data/ext/commonmarker/render.h +0 -62
  71. data/ext/commonmarker/scanners.c +0 -10508
  72. data/ext/commonmarker/scanners.h +0 -62
  73. data/ext/commonmarker/scanners.re +0 -341
  74. data/ext/commonmarker/strikethrough.c +0 -167
  75. data/ext/commonmarker/strikethrough.h +0 -9
  76. data/ext/commonmarker/syntax_extension.c +0 -149
  77. data/ext/commonmarker/syntax_extension.h +0 -34
  78. data/ext/commonmarker/table.c +0 -848
  79. data/ext/commonmarker/table.h +0 -12
  80. data/ext/commonmarker/tagfilter.c +0 -60
  81. data/ext/commonmarker/tagfilter.h +0 -8
  82. data/ext/commonmarker/tasklist.c +0 -156
  83. data/ext/commonmarker/tasklist.h +0 -8
  84. data/ext/commonmarker/utf8.c +0 -317
  85. data/ext/commonmarker/utf8.h +0 -35
  86. data/ext/commonmarker/xml.c +0 -181
  87. data/lib/commonmarker/node/inspect.rb +0 -47
  88. data/lib/commonmarker/node.rb +0 -83
  89. data/lib/commonmarker/renderer/html_renderer.rb +0 -252
@@ -1,1716 +0,0 @@
1
- #include <stdlib.h>
2
- #include <string.h>
3
- #include <stdio.h>
4
-
5
- #include "cmark_ctype.h"
6
- #include "config.h"
7
- #include "node.h"
8
- #include "parser.h"
9
- #include "references.h"
10
- #include "cmark-gfm.h"
11
- #include "houdini.h"
12
- #include "utf8.h"
13
- #include "scanners.h"
14
- #include "inlines.h"
15
- #include "syntax_extension.h"
16
-
17
- static const char *EMDASH = "\xE2\x80\x94";
18
- static const char *ENDASH = "\xE2\x80\x93";
19
- static const char *ELLIPSES = "\xE2\x80\xA6";
20
- static const char *LEFTDOUBLEQUOTE = "\xE2\x80\x9C";
21
- static const char *RIGHTDOUBLEQUOTE = "\xE2\x80\x9D";
22
- static const char *LEFTSINGLEQUOTE = "\xE2\x80\x98";
23
- static const char *RIGHTSINGLEQUOTE = "\xE2\x80\x99";
24
-
25
- // Macros for creating various kinds of simple.
26
- #define make_str(subj, sc, ec, s) make_literal(subj, CMARK_NODE_TEXT, sc, ec, s)
27
- #define make_code(subj, sc, ec, s) make_literal(subj, CMARK_NODE_CODE, sc, ec, s)
28
- #define make_raw_html(subj, sc, ec, s) make_literal(subj, CMARK_NODE_HTML_INLINE, sc, ec, s)
29
- #define make_linebreak(mem) make_simple(mem, CMARK_NODE_LINEBREAK)
30
- #define make_softbreak(mem) make_simple(mem, CMARK_NODE_SOFTBREAK)
31
- #define make_emph(mem) make_simple(mem, CMARK_NODE_EMPH)
32
- #define make_strong(mem) make_simple(mem, CMARK_NODE_STRONG)
33
-
34
- #define MAXBACKTICKS 80
35
-
36
- typedef struct bracket {
37
- struct bracket *previous;
38
- struct delimiter *previous_delimiter;
39
- cmark_node *inl_text;
40
- bufsize_t position;
41
- bool image;
42
- bool active;
43
- bool bracket_after;
44
- bool in_bracket_image0;
45
- bool in_bracket_image1;
46
- } bracket;
47
-
48
- typedef struct subject{
49
- cmark_mem *mem;
50
- cmark_chunk input;
51
- int line;
52
- bufsize_t pos;
53
- int block_offset;
54
- int column_offset;
55
- cmark_map *refmap;
56
- delimiter *last_delim;
57
- bracket *last_bracket;
58
- bufsize_t backticks[MAXBACKTICKS + 1];
59
- bool scanned_for_backticks;
60
- } subject;
61
-
62
- // Extensions may populate this.
63
- static int8_t SKIP_CHARS[256];
64
-
65
- static CMARK_INLINE bool S_is_line_end_char(char c) {
66
- return (c == '\n' || c == '\r');
67
- }
68
-
69
- static delimiter *S_insert_emph(subject *subj, delimiter *opener,
70
- delimiter *closer);
71
-
72
- static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent, int options);
73
-
74
- static void subject_from_buf(cmark_mem *mem, int line_number, int block_offset, subject *e,
75
- cmark_chunk *buffer, cmark_map *refmap);
76
- static bufsize_t subject_find_special_char(subject *subj, int options);
77
-
78
- // Create an inline with a literal string value.
79
- static CMARK_INLINE cmark_node *make_literal(subject *subj, cmark_node_type t,
80
- int start_column, int end_column,
81
- cmark_chunk s) {
82
- cmark_node *e = (cmark_node *)subj->mem->calloc(1, sizeof(*e));
83
- cmark_strbuf_init(subj->mem, &e->content, 0);
84
- e->type = (uint16_t)t;
85
- e->as.literal = s;
86
- e->start_line = e->end_line = subj->line;
87
- // columns are 1 based.
88
- e->start_column = start_column + 1 + subj->column_offset + subj->block_offset;
89
- e->end_column = end_column + 1 + subj->column_offset + subj->block_offset;
90
- return e;
91
- }
92
-
93
- // Create an inline with no value.
94
- static CMARK_INLINE cmark_node *make_simple(cmark_mem *mem, cmark_node_type t) {
95
- cmark_node *e = (cmark_node *)mem->calloc(1, sizeof(*e));
96
- cmark_strbuf_init(mem, &e->content, 0);
97
- e->type = (uint16_t)t;
98
- return e;
99
- }
100
-
101
- // Like make_str, but parses entities.
102
- static cmark_node *make_str_with_entities(subject *subj,
103
- int start_column, int end_column,
104
- cmark_chunk *content) {
105
- cmark_strbuf unescaped = CMARK_BUF_INIT(subj->mem);
106
-
107
- if (houdini_unescape_html(&unescaped, content->data, content->len)) {
108
- return make_str(subj, start_column, end_column, cmark_chunk_buf_detach(&unescaped));
109
- } else {
110
- return make_str(subj, start_column, end_column, *content);
111
- }
112
- }
113
-
114
- // Duplicate a chunk by creating a copy of the buffer not by reusing the
115
- // buffer like cmark_chunk_dup does.
116
- static cmark_chunk chunk_clone(cmark_mem *mem, cmark_chunk *src) {
117
- cmark_chunk c;
118
- bufsize_t len = src->len;
119
-
120
- c.len = len;
121
- c.data = (unsigned char *)mem->calloc(len + 1, 1);
122
- c.alloc = 1;
123
- if (len)
124
- memcpy(c.data, src->data, len);
125
- c.data[len] = '\0';
126
-
127
- return c;
128
- }
129
-
130
- static cmark_chunk cmark_clean_autolink(cmark_mem *mem, cmark_chunk *url,
131
- int is_email) {
132
- cmark_strbuf buf = CMARK_BUF_INIT(mem);
133
-
134
- cmark_chunk_trim(url);
135
-
136
- if (url->len == 0) {
137
- cmark_chunk result = CMARK_CHUNK_EMPTY;
138
- return result;
139
- }
140
-
141
- if (is_email)
142
- cmark_strbuf_puts(&buf, "mailto:");
143
-
144
- houdini_unescape_html_f(&buf, url->data, url->len);
145
- return cmark_chunk_buf_detach(&buf);
146
- }
147
-
148
- static CMARK_INLINE cmark_node *make_autolink(subject *subj,
149
- int start_column, int end_column,
150
- cmark_chunk url, int is_email) {
151
- cmark_node *link = make_simple(subj->mem, CMARK_NODE_LINK);
152
- link->as.link.url = cmark_clean_autolink(subj->mem, &url, is_email);
153
- link->as.link.title = cmark_chunk_literal("");
154
- link->start_line = link->end_line = subj->line;
155
- link->start_column = start_column + 1;
156
- link->end_column = end_column + 1;
157
- cmark_node_append_child(link, make_str_with_entities(subj, start_column + 1, end_column - 1, &url));
158
- return link;
159
- }
160
-
161
- static void subject_from_buf(cmark_mem *mem, int line_number, int block_offset, subject *e,
162
- cmark_chunk *chunk, cmark_map *refmap) {
163
- int i;
164
- e->mem = mem;
165
- e->input = *chunk;
166
- e->line = line_number;
167
- e->pos = 0;
168
- e->block_offset = block_offset;
169
- e->column_offset = 0;
170
- e->refmap = refmap;
171
- e->last_delim = NULL;
172
- e->last_bracket = NULL;
173
- for (i = 0; i <= MAXBACKTICKS; i++) {
174
- e->backticks[i] = 0;
175
- }
176
- e->scanned_for_backticks = false;
177
- }
178
-
179
- static CMARK_INLINE int isbacktick(int c) { return (c == '`'); }
180
-
181
- static CMARK_INLINE unsigned char peek_char_n(subject *subj, bufsize_t n) {
182
- // NULL bytes should have been stripped out by now. If they're
183
- // present, it's a programming error:
184
- assert(!(subj->pos + n < subj->input.len && subj->input.data[subj->pos + n] == 0));
185
- return (subj->pos + n < subj->input.len) ? subj->input.data[subj->pos + n] : 0;
186
- }
187
-
188
- static CMARK_INLINE unsigned char peek_char(subject *subj) {
189
- return peek_char_n(subj, 0);
190
- }
191
-
192
- static CMARK_INLINE unsigned char peek_at(subject *subj, bufsize_t pos) {
193
- return subj->input.data[pos];
194
- }
195
-
196
- // Return true if there are more characters in the subject.
197
- static CMARK_INLINE int is_eof(subject *subj) {
198
- return (subj->pos >= subj->input.len);
199
- }
200
-
201
- // Advance the subject. Doesn't check for eof.
202
- #define advance(subj) (subj)->pos += 1
203
-
204
- static CMARK_INLINE bool skip_spaces(subject *subj) {
205
- bool skipped = false;
206
- while (peek_char(subj) == ' ' || peek_char(subj) == '\t') {
207
- advance(subj);
208
- skipped = true;
209
- }
210
- return skipped;
211
- }
212
-
213
- static CMARK_INLINE bool skip_line_end(subject *subj) {
214
- bool seen_line_end_char = false;
215
- if (peek_char(subj) == '\r') {
216
- advance(subj);
217
- seen_line_end_char = true;
218
- }
219
- if (peek_char(subj) == '\n') {
220
- advance(subj);
221
- seen_line_end_char = true;
222
- }
223
- return seen_line_end_char || is_eof(subj);
224
- }
225
-
226
- // Take characters while a predicate holds, and return a string.
227
- static CMARK_INLINE cmark_chunk take_while(subject *subj, int (*f)(int)) {
228
- unsigned char c;
229
- bufsize_t startpos = subj->pos;
230
- bufsize_t len = 0;
231
-
232
- while ((c = peek_char(subj)) && (*f)(c)) {
233
- advance(subj);
234
- len++;
235
- }
236
-
237
- return cmark_chunk_dup(&subj->input, startpos, len);
238
- }
239
-
240
- // Return the number of newlines in a given span of text in a subject. If
241
- // the number is greater than zero, also return the number of characters
242
- // between the last newline and the end of the span in `since_newline`.
243
- static int count_newlines(subject *subj, bufsize_t from, bufsize_t len, int *since_newline) {
244
- int nls = 0;
245
- int since_nl = 0;
246
-
247
- while (len--) {
248
- if (subj->input.data[from++] == '\n') {
249
- ++nls;
250
- since_nl = 0;
251
- } else {
252
- ++since_nl;
253
- }
254
- }
255
-
256
- if (!nls)
257
- return 0;
258
-
259
- *since_newline = since_nl;
260
- return nls;
261
- }
262
-
263
- // Adjust `node`'s `end_line`, `end_column`, and `subj`'s `line` and
264
- // `column_offset` according to the number of newlines in a just-matched span
265
- // of text in `subj`.
266
- static void adjust_subj_node_newlines(subject *subj, cmark_node *node, int matchlen, int extra, int options) {
267
- if (!(options & CMARK_OPT_SOURCEPOS)) {
268
- return;
269
- }
270
-
271
- int since_newline;
272
- int newlines = count_newlines(subj, subj->pos - matchlen - extra, matchlen, &since_newline);
273
- if (newlines) {
274
- subj->line += newlines;
275
- node->end_line += newlines;
276
- node->end_column = since_newline;
277
- subj->column_offset = -subj->pos + since_newline + extra;
278
- }
279
- }
280
-
281
- // Try to process a backtick code span that began with a
282
- // span of ticks of length openticklength length (already
283
- // parsed). Return 0 if you don't find matching closing
284
- // backticks, otherwise return the position in the subject
285
- // after the closing backticks.
286
- static bufsize_t scan_to_closing_backticks(subject *subj,
287
- bufsize_t openticklength) {
288
-
289
- bool found = false;
290
- if (openticklength > MAXBACKTICKS) {
291
- // we limit backtick string length because of the array subj->backticks:
292
- return 0;
293
- }
294
- if (subj->scanned_for_backticks &&
295
- subj->backticks[openticklength] <= subj->pos) {
296
- // return if we already know there's no closer
297
- return 0;
298
- }
299
- while (!found) {
300
- // read non backticks
301
- unsigned char c;
302
- while ((c = peek_char(subj)) && c != '`') {
303
- advance(subj);
304
- }
305
- if (is_eof(subj)) {
306
- break;
307
- }
308
- bufsize_t numticks = 0;
309
- while (peek_char(subj) == '`') {
310
- advance(subj);
311
- numticks++;
312
- }
313
- // store position of ender
314
- if (numticks <= MAXBACKTICKS) {
315
- subj->backticks[numticks] = subj->pos - numticks;
316
- }
317
- if (numticks == openticklength) {
318
- return (subj->pos);
319
- }
320
- }
321
- // got through whole input without finding closer
322
- subj->scanned_for_backticks = true;
323
- return 0;
324
- }
325
-
326
- // Destructively modify string, converting newlines to
327
- // spaces, then removing a single leading + trailing space,
328
- // unless the code span consists entirely of space characters.
329
- static void S_normalize_code(cmark_strbuf *s) {
330
- bufsize_t r, w;
331
- bool contains_nonspace = false;
332
-
333
- for (r = 0, w = 0; r < s->size; ++r) {
334
- switch (s->ptr[r]) {
335
- case '\r':
336
- if (s->ptr[r + 1] != '\n') {
337
- s->ptr[w++] = ' ';
338
- }
339
- break;
340
- case '\n':
341
- s->ptr[w++] = ' ';
342
- break;
343
- default:
344
- s->ptr[w++] = s->ptr[r];
345
- }
346
- if (s->ptr[r] != ' ') {
347
- contains_nonspace = true;
348
- }
349
- }
350
-
351
- // begins and ends with space?
352
- if (contains_nonspace &&
353
- s->ptr[0] == ' ' && s->ptr[w - 1] == ' ') {
354
- cmark_strbuf_drop(s, 1);
355
- cmark_strbuf_truncate(s, w - 2);
356
- } else {
357
- cmark_strbuf_truncate(s, w);
358
- }
359
-
360
- }
361
-
362
-
363
- // Parse backtick code section or raw backticks, return an inline.
364
- // Assumes that the subject has a backtick at the current position.
365
- static cmark_node *handle_backticks(subject *subj, int options) {
366
- cmark_chunk openticks = take_while(subj, isbacktick);
367
- bufsize_t startpos = subj->pos;
368
- bufsize_t endpos = scan_to_closing_backticks(subj, openticks.len);
369
-
370
- if (endpos == 0) { // not found
371
- subj->pos = startpos; // rewind
372
- return make_str(subj, subj->pos, subj->pos, openticks);
373
- } else {
374
- cmark_strbuf buf = CMARK_BUF_INIT(subj->mem);
375
-
376
- cmark_strbuf_set(&buf, subj->input.data + startpos,
377
- endpos - startpos - openticks.len);
378
- S_normalize_code(&buf);
379
-
380
- cmark_node *node = make_code(subj, startpos, endpos - openticks.len - 1, cmark_chunk_buf_detach(&buf));
381
- adjust_subj_node_newlines(subj, node, endpos - startpos, openticks.len, options);
382
- return node;
383
- }
384
- }
385
-
386
-
387
- // Scan ***, **, or * and return number scanned, or 0.
388
- // Advances position.
389
- static int scan_delims(subject *subj, unsigned char c, bool *can_open,
390
- bool *can_close) {
391
- int numdelims = 0;
392
- bufsize_t before_char_pos, after_char_pos;
393
- int32_t after_char = 0;
394
- int32_t before_char = 0;
395
- int len;
396
- bool left_flanking, right_flanking;
397
-
398
- if (subj->pos == 0) {
399
- before_char = 10;
400
- } else {
401
- before_char_pos = subj->pos - 1;
402
- // walk back to the beginning of the UTF_8 sequence:
403
- while ((peek_at(subj, before_char_pos) >> 6 == 2 || SKIP_CHARS[peek_at(subj, before_char_pos)]) && before_char_pos > 0) {
404
- before_char_pos -= 1;
405
- }
406
- len = cmark_utf8proc_iterate(subj->input.data + before_char_pos,
407
- subj->pos - before_char_pos, &before_char);
408
- if (len == -1 || (before_char < 256 && SKIP_CHARS[(unsigned char) before_char])) {
409
- before_char = 10;
410
- }
411
- }
412
-
413
- if (c == '\'' || c == '"') {
414
- numdelims++;
415
- advance(subj); // limit to 1 delim for quotes
416
- } else {
417
- while (peek_char(subj) == c) {
418
- numdelims++;
419
- advance(subj);
420
- }
421
- }
422
-
423
- if (subj->pos == subj->input.len) {
424
- after_char = 10;
425
- } else {
426
- after_char_pos = subj->pos;
427
- while (SKIP_CHARS[peek_at(subj, after_char_pos)] && after_char_pos < subj->input.len) {
428
- after_char_pos += 1;
429
- }
430
- len = cmark_utf8proc_iterate(subj->input.data + after_char_pos,
431
- subj->input.len - after_char_pos, &after_char);
432
- if (len == -1 || (after_char < 256 && SKIP_CHARS[(unsigned char) after_char])) {
433
- after_char = 10;
434
- }
435
- }
436
-
437
- left_flanking = numdelims > 0 && !cmark_utf8proc_is_space(after_char) &&
438
- (!cmark_utf8proc_is_punctuation(after_char) ||
439
- cmark_utf8proc_is_space(before_char) ||
440
- cmark_utf8proc_is_punctuation(before_char));
441
- right_flanking = numdelims > 0 && !cmark_utf8proc_is_space(before_char) &&
442
- (!cmark_utf8proc_is_punctuation(before_char) ||
443
- cmark_utf8proc_is_space(after_char) ||
444
- cmark_utf8proc_is_punctuation(after_char));
445
- if (c == '_') {
446
- *can_open = left_flanking &&
447
- (!right_flanking || cmark_utf8proc_is_punctuation(before_char));
448
- *can_close = right_flanking &&
449
- (!left_flanking || cmark_utf8proc_is_punctuation(after_char));
450
- } else if (c == '\'' || c == '"') {
451
- *can_open = left_flanking && !right_flanking &&
452
- before_char != ']' && before_char != ')';
453
- *can_close = right_flanking;
454
- } else {
455
- *can_open = left_flanking;
456
- *can_close = right_flanking;
457
- }
458
- return numdelims;
459
- }
460
-
461
- /*
462
- static void print_delimiters(subject *subj)
463
- {
464
- delimiter *delim;
465
- delim = subj->last_delim;
466
- while (delim != NULL) {
467
- printf("Item at stack pos %p: %d %d %d next(%p) prev(%p)\n",
468
- (void*)delim, delim->delim_char,
469
- delim->can_open, delim->can_close,
470
- (void*)delim->next, (void*)delim->previous);
471
- delim = delim->previous;
472
- }
473
- }
474
- */
475
-
476
- static void remove_delimiter(subject *subj, delimiter *delim) {
477
- if (delim == NULL)
478
- return;
479
- if (delim->next == NULL) {
480
- // end of list:
481
- assert(delim == subj->last_delim);
482
- subj->last_delim = delim->previous;
483
- } else {
484
- delim->next->previous = delim->previous;
485
- }
486
- if (delim->previous != NULL) {
487
- delim->previous->next = delim->next;
488
- }
489
- subj->mem->free(delim);
490
- }
491
-
492
- static void pop_bracket(subject *subj) {
493
- bracket *b;
494
- if (subj->last_bracket == NULL)
495
- return;
496
- b = subj->last_bracket;
497
- subj->last_bracket = subj->last_bracket->previous;
498
- subj->mem->free(b);
499
- }
500
-
501
- static void push_delimiter(subject *subj, unsigned char c, bool can_open,
502
- bool can_close, cmark_node *inl_text) {
503
- delimiter *delim = (delimiter *)subj->mem->calloc(1, sizeof(delimiter));
504
- delim->delim_char = c;
505
- delim->can_open = can_open;
506
- delim->can_close = can_close;
507
- delim->inl_text = inl_text;
508
- delim->length = inl_text->as.literal.len;
509
- delim->previous = subj->last_delim;
510
- delim->next = NULL;
511
- if (delim->previous != NULL) {
512
- delim->previous->next = delim;
513
- }
514
- subj->last_delim = delim;
515
- }
516
-
517
- static void push_bracket(subject *subj, bool image, cmark_node *inl_text) {
518
- bracket *b = (bracket *)subj->mem->calloc(1, sizeof(bracket));
519
- if (subj->last_bracket != NULL) {
520
- subj->last_bracket->bracket_after = true;
521
- b->in_bracket_image0 = subj->last_bracket->in_bracket_image0;
522
- b->in_bracket_image1 = subj->last_bracket->in_bracket_image1;
523
- }
524
- b->image = image;
525
- b->active = true;
526
- b->inl_text = inl_text;
527
- b->previous = subj->last_bracket;
528
- b->previous_delimiter = subj->last_delim;
529
- b->position = subj->pos;
530
- b->bracket_after = false;
531
- if (image) {
532
- b->in_bracket_image1 = true;
533
- } else {
534
- b->in_bracket_image0 = true;
535
- }
536
- subj->last_bracket = b;
537
- }
538
-
539
- // Assumes the subject has a c at the current position.
540
- static cmark_node *handle_delim(subject *subj, unsigned char c, bool smart) {
541
- bufsize_t numdelims;
542
- cmark_node *inl_text;
543
- bool can_open, can_close;
544
- cmark_chunk contents;
545
-
546
- numdelims = scan_delims(subj, c, &can_open, &can_close);
547
-
548
- if (c == '\'' && smart) {
549
- contents = cmark_chunk_literal(RIGHTSINGLEQUOTE);
550
- } else if (c == '"' && smart) {
551
- contents =
552
- cmark_chunk_literal(can_close ? RIGHTDOUBLEQUOTE : LEFTDOUBLEQUOTE);
553
- } else {
554
- contents = cmark_chunk_dup(&subj->input, subj->pos - numdelims, numdelims);
555
- }
556
-
557
- inl_text = make_str(subj, subj->pos - numdelims, subj->pos - 1, contents);
558
-
559
- if ((can_open || can_close) && (!(c == '\'' || c == '"') || smart)) {
560
- push_delimiter(subj, c, can_open, can_close, inl_text);
561
- }
562
-
563
- return inl_text;
564
- }
565
-
566
- // Assumes we have a hyphen at the current position.
567
- static cmark_node *handle_hyphen(subject *subj, bool smart) {
568
- int startpos = subj->pos;
569
-
570
- advance(subj);
571
-
572
- if (!smart || peek_char(subj) != '-') {
573
- return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("-"));
574
- }
575
-
576
- while (smart && peek_char(subj) == '-') {
577
- advance(subj);
578
- }
579
-
580
- int numhyphens = subj->pos - startpos;
581
- int en_count = 0;
582
- int em_count = 0;
583
- int i;
584
- cmark_strbuf buf = CMARK_BUF_INIT(subj->mem);
585
-
586
- if (numhyphens % 3 == 0) { // if divisible by 3, use all em dashes
587
- em_count = numhyphens / 3;
588
- } else if (numhyphens % 2 == 0) { // if divisible by 2, use all en dashes
589
- en_count = numhyphens / 2;
590
- } else if (numhyphens % 3 == 2) { // use one en dash at end
591
- en_count = 1;
592
- em_count = (numhyphens - 2) / 3;
593
- } else { // use two en dashes at the end
594
- en_count = 2;
595
- em_count = (numhyphens - 4) / 3;
596
- }
597
-
598
- for (i = em_count; i > 0; i--) {
599
- cmark_strbuf_puts(&buf, EMDASH);
600
- }
601
-
602
- for (i = en_count; i > 0; i--) {
603
- cmark_strbuf_puts(&buf, ENDASH);
604
- }
605
-
606
- return make_str(subj, startpos, subj->pos - 1, cmark_chunk_buf_detach(&buf));
607
- }
608
-
609
- // Assumes we have a period at the current position.
610
- static cmark_node *handle_period(subject *subj, bool smart) {
611
- advance(subj);
612
- if (smart && peek_char(subj) == '.') {
613
- advance(subj);
614
- if (peek_char(subj) == '.') {
615
- advance(subj);
616
- return make_str(subj, subj->pos - 3, subj->pos - 1, cmark_chunk_literal(ELLIPSES));
617
- } else {
618
- return make_str(subj, subj->pos - 2, subj->pos - 1, cmark_chunk_literal(".."));
619
- }
620
- } else {
621
- return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("."));
622
- }
623
- }
624
-
625
- static cmark_syntax_extension *get_extension_for_special_char(cmark_parser *parser, unsigned char c) {
626
- cmark_llist *tmp_ext;
627
-
628
- for (tmp_ext = parser->inline_syntax_extensions; tmp_ext; tmp_ext=tmp_ext->next) {
629
- cmark_syntax_extension *ext = (cmark_syntax_extension *) tmp_ext->data;
630
- cmark_llist *tmp_char;
631
- for (tmp_char = ext->special_inline_chars; tmp_char; tmp_char=tmp_char->next) {
632
- unsigned char tmp_c = (unsigned char)(size_t)tmp_char->data;
633
-
634
- if (tmp_c == c) {
635
- return ext;
636
- }
637
- }
638
- }
639
-
640
- return NULL;
641
- }
642
-
643
- static void process_emphasis(cmark_parser *parser, subject *subj, delimiter *stack_bottom) {
644
- delimiter *closer = subj->last_delim;
645
- delimiter *opener;
646
- delimiter *old_closer;
647
- bool opener_found;
648
- delimiter *openers_bottom[3][128];
649
- int i;
650
-
651
- // initialize openers_bottom:
652
- memset(&openers_bottom, 0, sizeof(openers_bottom));
653
- for (i=0; i < 3; i++) {
654
- openers_bottom[i]['*'] = stack_bottom;
655
- openers_bottom[i]['_'] = stack_bottom;
656
- openers_bottom[i]['\''] = stack_bottom;
657
- openers_bottom[i]['"'] = stack_bottom;
658
- }
659
-
660
- // move back to first relevant delim.
661
- while (closer != NULL && closer->previous != stack_bottom) {
662
- closer = closer->previous;
663
- }
664
-
665
- // now move forward, looking for closers, and handling each
666
- while (closer != NULL) {
667
- cmark_syntax_extension *extension = get_extension_for_special_char(parser, closer->delim_char);
668
- if (closer->can_close) {
669
- // Now look backwards for first matching opener:
670
- opener = closer->previous;
671
- opener_found = false;
672
- while (opener != NULL && opener != stack_bottom &&
673
- opener != openers_bottom[closer->length % 3][closer->delim_char]) {
674
- if (opener->can_open && opener->delim_char == closer->delim_char) {
675
- // interior closer of size 2 can't match opener of size 1
676
- // or of size 1 can't match 2
677
- if (!(closer->can_open || opener->can_close) ||
678
- closer->length % 3 == 0 ||
679
- (opener->length + closer->length) % 3 != 0) {
680
- opener_found = true;
681
- break;
682
- }
683
- }
684
- opener = opener->previous;
685
- }
686
- old_closer = closer;
687
-
688
- if (extension) {
689
- if (opener_found)
690
- closer = extension->insert_inline_from_delim(extension, parser, subj, opener, closer);
691
- else
692
- closer = closer->next;
693
- } else if (closer->delim_char == '*' || closer->delim_char == '_') {
694
- if (opener_found) {
695
- closer = S_insert_emph(subj, opener, closer);
696
- } else {
697
- closer = closer->next;
698
- }
699
- } else if (closer->delim_char == '\'') {
700
- cmark_chunk_free(subj->mem, &closer->inl_text->as.literal);
701
- closer->inl_text->as.literal = cmark_chunk_literal(RIGHTSINGLEQUOTE);
702
- if (opener_found) {
703
- cmark_chunk_free(subj->mem, &opener->inl_text->as.literal);
704
- opener->inl_text->as.literal = cmark_chunk_literal(LEFTSINGLEQUOTE);
705
- }
706
- closer = closer->next;
707
- } else if (closer->delim_char == '"') {
708
- cmark_chunk_free(subj->mem, &closer->inl_text->as.literal);
709
- closer->inl_text->as.literal = cmark_chunk_literal(RIGHTDOUBLEQUOTE);
710
- if (opener_found) {
711
- cmark_chunk_free(subj->mem, &opener->inl_text->as.literal);
712
- opener->inl_text->as.literal = cmark_chunk_literal(LEFTDOUBLEQUOTE);
713
- }
714
- closer = closer->next;
715
- }
716
- if (!opener_found) {
717
- // set lower bound for future searches for openers
718
- openers_bottom[old_closer->length % 3][old_closer->delim_char] =
719
- old_closer->previous;
720
- if (!old_closer->can_open) {
721
- // we can remove a closer that can't be an
722
- // opener, once we've seen there's no
723
- // matching opener:
724
- remove_delimiter(subj, old_closer);
725
- }
726
- }
727
- } else {
728
- closer = closer->next;
729
- }
730
- }
731
- // free all delimiters in list until stack_bottom:
732
- while (subj->last_delim != NULL && subj->last_delim != stack_bottom) {
733
- remove_delimiter(subj, subj->last_delim);
734
- }
735
- }
736
-
737
- static delimiter *S_insert_emph(subject *subj, delimiter *opener,
738
- delimiter *closer) {
739
- delimiter *delim, *tmp_delim;
740
- bufsize_t use_delims;
741
- cmark_node *opener_inl = opener->inl_text;
742
- cmark_node *closer_inl = closer->inl_text;
743
- bufsize_t opener_num_chars = opener_inl->as.literal.len;
744
- bufsize_t closer_num_chars = closer_inl->as.literal.len;
745
- cmark_node *tmp, *tmpnext, *emph;
746
-
747
- // calculate the actual number of characters used from this closer
748
- use_delims = (closer_num_chars >= 2 && opener_num_chars >= 2) ? 2 : 1;
749
-
750
- // remove used characters from associated inlines.
751
- opener_num_chars -= use_delims;
752
- closer_num_chars -= use_delims;
753
- opener_inl->as.literal.len = opener_num_chars;
754
- closer_inl->as.literal.len = closer_num_chars;
755
-
756
- // free delimiters between opener and closer
757
- delim = closer->previous;
758
- while (delim != NULL && delim != opener) {
759
- tmp_delim = delim->previous;
760
- remove_delimiter(subj, delim);
761
- delim = tmp_delim;
762
- }
763
-
764
- // create new emph or strong, and splice it in to our inlines
765
- // between the opener and closer
766
- emph = use_delims == 1 ? make_emph(subj->mem) : make_strong(subj->mem);
767
-
768
- tmp = opener_inl->next;
769
- while (tmp && tmp != closer_inl) {
770
- tmpnext = tmp->next;
771
- cmark_node_append_child(emph, tmp);
772
- tmp = tmpnext;
773
- }
774
- cmark_node_insert_after(opener_inl, emph);
775
-
776
- emph->start_line = opener_inl->start_line;
777
- emph->end_line = closer_inl->end_line;
778
- emph->start_column = opener_inl->start_column;
779
- emph->end_column = closer_inl->end_column;
780
-
781
- // if opener has 0 characters, remove it and its associated inline
782
- if (opener_num_chars == 0) {
783
- cmark_node_free(opener_inl);
784
- remove_delimiter(subj, opener);
785
- }
786
-
787
- // if closer has 0 characters, remove it and its associated inline
788
- if (closer_num_chars == 0) {
789
- // remove empty closer inline
790
- cmark_node_free(closer_inl);
791
- // remove closer from list
792
- tmp_delim = closer->next;
793
- remove_delimiter(subj, closer);
794
- closer = tmp_delim;
795
- }
796
-
797
- return closer;
798
- }
799
-
800
- // Parse backslash-escape or just a backslash, returning an inline.
801
- static cmark_node *handle_backslash(cmark_parser *parser, subject *subj) {
802
- advance(subj);
803
- unsigned char nextchar = peek_char(subj);
804
- if ((parser->backslash_ispunct ? parser->backslash_ispunct : cmark_ispunct)(nextchar)) {
805
- // only ascii symbols and newline can be escaped
806
- advance(subj);
807
- return make_str(subj, subj->pos - 2, subj->pos - 1, cmark_chunk_dup(&subj->input, subj->pos - 1, 1));
808
- } else if (!is_eof(subj) && skip_line_end(subj)) {
809
- return make_linebreak(subj->mem);
810
- } else {
811
- return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("\\"));
812
- }
813
- }
814
-
815
- // Parse an entity or a regular "&" string.
816
- // Assumes the subject has an '&' character at the current position.
817
- static cmark_node *handle_entity(subject *subj) {
818
- cmark_strbuf ent = CMARK_BUF_INIT(subj->mem);
819
- bufsize_t len;
820
-
821
- advance(subj);
822
-
823
- len = houdini_unescape_ent(&ent, subj->input.data + subj->pos,
824
- subj->input.len - subj->pos);
825
-
826
- if (len == 0)
827
- return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("&"));
828
-
829
- subj->pos += len;
830
- return make_str(subj, subj->pos - 1 - len, subj->pos - 1, cmark_chunk_buf_detach(&ent));
831
- }
832
-
833
- // Clean a URL: remove surrounding whitespace, and remove \ that escape
834
- // punctuation.
835
- cmark_chunk cmark_clean_url(cmark_mem *mem, cmark_chunk *url) {
836
- cmark_strbuf buf = CMARK_BUF_INIT(mem);
837
-
838
- cmark_chunk_trim(url);
839
-
840
- if (url->len == 0) {
841
- cmark_chunk result = CMARK_CHUNK_EMPTY;
842
- return result;
843
- }
844
-
845
- houdini_unescape_html_f(&buf, url->data, url->len);
846
-
847
- cmark_strbuf_unescape(&buf);
848
- return cmark_chunk_buf_detach(&buf);
849
- }
850
-
851
- cmark_chunk cmark_clean_title(cmark_mem *mem, cmark_chunk *title) {
852
- cmark_strbuf buf = CMARK_BUF_INIT(mem);
853
- unsigned char first, last;
854
-
855
- if (title->len == 0) {
856
- cmark_chunk result = CMARK_CHUNK_EMPTY;
857
- return result;
858
- }
859
-
860
- first = title->data[0];
861
- last = title->data[title->len - 1];
862
-
863
- // remove surrounding quotes if any:
864
- if ((first == '\'' && last == '\'') || (first == '(' && last == ')') ||
865
- (first == '"' && last == '"')) {
866
- houdini_unescape_html_f(&buf, title->data + 1, title->len - 2);
867
- } else {
868
- houdini_unescape_html_f(&buf, title->data, title->len);
869
- }
870
-
871
- cmark_strbuf_unescape(&buf);
872
- return cmark_chunk_buf_detach(&buf);
873
- }
874
-
875
- // Parse an autolink or HTML tag.
876
- // Assumes the subject has a '<' character at the current position.
877
- static cmark_node *handle_pointy_brace(subject *subj, int options) {
878
- bufsize_t matchlen = 0;
879
- cmark_chunk contents;
880
-
881
- advance(subj); // advance past first <
882
-
883
- // first try to match a URL autolink
884
- matchlen = scan_autolink_uri(&subj->input, subj->pos);
885
- if (matchlen > 0) {
886
- contents = cmark_chunk_dup(&subj->input, subj->pos, matchlen - 1);
887
- subj->pos += matchlen;
888
-
889
- return make_autolink(subj, subj->pos - 1 - matchlen, subj->pos - 1, contents, 0);
890
- }
891
-
892
- // next try to match an email autolink
893
- matchlen = scan_autolink_email(&subj->input, subj->pos);
894
- if (matchlen > 0) {
895
- contents = cmark_chunk_dup(&subj->input, subj->pos, matchlen - 1);
896
- subj->pos += matchlen;
897
-
898
- return make_autolink(subj, subj->pos - 1 - matchlen, subj->pos - 1, contents, 1);
899
- }
900
-
901
- // finally, try to match an html tag
902
- matchlen = scan_html_tag(&subj->input, subj->pos);
903
- if (matchlen > 0) {
904
- contents = cmark_chunk_dup(&subj->input, subj->pos - 1, matchlen + 1);
905
- subj->pos += matchlen;
906
- cmark_node *node = make_raw_html(subj, subj->pos - matchlen - 1, subj->pos - 1, contents);
907
- adjust_subj_node_newlines(subj, node, matchlen, 1, options);
908
- return node;
909
- }
910
-
911
- if (options & CMARK_OPT_LIBERAL_HTML_TAG) {
912
- matchlen = scan_liberal_html_tag(&subj->input, subj->pos);
913
- if (matchlen > 0) {
914
- contents = cmark_chunk_dup(&subj->input, subj->pos - 1, matchlen + 1);
915
- subj->pos += matchlen;
916
- cmark_node *node = make_raw_html(subj, subj->pos - matchlen - 1, subj->pos - 1, contents);
917
- adjust_subj_node_newlines(subj, node, matchlen, 1, options);
918
- return node;
919
- }
920
- }
921
-
922
- // if nothing matches, just return the opening <:
923
- return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("<"));
924
- }
925
-
926
- // Parse a link label. Returns 1 if successful.
927
- // Note: unescaped brackets are not allowed in labels.
928
- // The label begins with `[` and ends with the first `]` character
929
- // encountered. Backticks in labels do not start code spans.
930
- static int link_label(subject *subj, cmark_chunk *raw_label) {
931
- bufsize_t startpos = subj->pos;
932
- int length = 0;
933
- unsigned char c;
934
-
935
- // advance past [
936
- if (peek_char(subj) == '[') {
937
- advance(subj);
938
- } else {
939
- return 0;
940
- }
941
-
942
- while ((c = peek_char(subj)) && c != '[' && c != ']') {
943
- if (c == '\\') {
944
- advance(subj);
945
- length++;
946
- if (cmark_ispunct(peek_char(subj))) {
947
- advance(subj);
948
- length++;
949
- }
950
- } else {
951
- advance(subj);
952
- length++;
953
- }
954
- if (length > MAX_LINK_LABEL_LENGTH) {
955
- goto noMatch;
956
- }
957
- }
958
-
959
- if (c == ']') { // match found
960
- *raw_label =
961
- cmark_chunk_dup(&subj->input, startpos + 1, subj->pos - (startpos + 1));
962
- cmark_chunk_trim(raw_label);
963
- advance(subj); // advance past ]
964
- return 1;
965
- }
966
-
967
- noMatch:
968
- subj->pos = startpos; // rewind
969
- return 0;
970
- }
971
-
972
- static bufsize_t manual_scan_link_url_2(cmark_chunk *input, bufsize_t offset,
973
- cmark_chunk *output) {
974
- bufsize_t i = offset;
975
- size_t nb_p = 0;
976
-
977
- while (i < input->len) {
978
- if (input->data[i] == '\\' &&
979
- i + 1 < input-> len &&
980
- cmark_ispunct(input->data[i+1]))
981
- i += 2;
982
- else if (input->data[i] == '(') {
983
- ++nb_p;
984
- ++i;
985
- if (nb_p > 32)
986
- return -1;
987
- } else if (input->data[i] == ')') {
988
- if (nb_p == 0)
989
- break;
990
- --nb_p;
991
- ++i;
992
- } else if (cmark_isspace(input->data[i])) {
993
- if (i == offset) {
994
- return -1;
995
- }
996
- break;
997
- } else {
998
- ++i;
999
- }
1000
- }
1001
-
1002
- if (i >= input->len)
1003
- return -1;
1004
-
1005
- {
1006
- cmark_chunk result = {input->data + offset, i - offset, 0};
1007
- *output = result;
1008
- }
1009
- return i - offset;
1010
- }
1011
-
1012
- static bufsize_t manual_scan_link_url(cmark_chunk *input, bufsize_t offset,
1013
- cmark_chunk *output) {
1014
- bufsize_t i = offset;
1015
-
1016
- if (i < input->len && input->data[i] == '<') {
1017
- ++i;
1018
- while (i < input->len) {
1019
- if (input->data[i] == '>') {
1020
- ++i;
1021
- break;
1022
- } else if (input->data[i] == '\\')
1023
- i += 2;
1024
- else if (input->data[i] == '\n' || input->data[i] == '<')
1025
- return -1;
1026
- else
1027
- ++i;
1028
- }
1029
- } else {
1030
- return manual_scan_link_url_2(input, offset, output);
1031
- }
1032
-
1033
- if (i >= input->len)
1034
- return -1;
1035
-
1036
- {
1037
- cmark_chunk result = {input->data + offset + 1, i - 2 - offset, 0};
1038
- *output = result;
1039
- }
1040
- return i - offset;
1041
- }
1042
-
1043
- // Return a link, an image, or a literal close bracket.
1044
- static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
1045
- bufsize_t initial_pos, after_link_text_pos;
1046
- bufsize_t endurl, starttitle, endtitle, endall;
1047
- bufsize_t sps, n;
1048
- cmark_reference *ref = NULL;
1049
- cmark_chunk url_chunk, title_chunk;
1050
- cmark_chunk url, title;
1051
- bracket *opener;
1052
- cmark_node *inl;
1053
- cmark_chunk raw_label;
1054
- int found_label;
1055
- cmark_node *tmp, *tmpnext;
1056
- bool is_image;
1057
-
1058
- advance(subj); // advance past ]
1059
- initial_pos = subj->pos;
1060
-
1061
- // get last [ or ![
1062
- opener = subj->last_bracket;
1063
-
1064
- if (opener == NULL) {
1065
- return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
1066
- }
1067
-
1068
- if (!opener->active) {
1069
- // take delimiter off stack
1070
- pop_bracket(subj);
1071
- return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
1072
- }
1073
-
1074
- // If we got here, we matched a potential link/image text.
1075
- // Now we check to see if it's a link/image.
1076
- is_image = opener->image;
1077
-
1078
- after_link_text_pos = subj->pos;
1079
-
1080
- // First, look for an inline link.
1081
- if (peek_char(subj) == '(' &&
1082
- ((sps = scan_spacechars(&subj->input, subj->pos + 1)) > -1) &&
1083
- ((n = manual_scan_link_url(&subj->input, subj->pos + 1 + sps,
1084
- &url_chunk)) > -1)) {
1085
-
1086
- // try to parse an explicit link:
1087
- endurl = subj->pos + 1 + sps + n;
1088
- starttitle = endurl + scan_spacechars(&subj->input, endurl);
1089
-
1090
- // ensure there are spaces btw url and title
1091
- endtitle = (starttitle == endurl)
1092
- ? starttitle
1093
- : starttitle + scan_link_title(&subj->input, starttitle);
1094
-
1095
- endall = endtitle + scan_spacechars(&subj->input, endtitle);
1096
-
1097
- if (peek_at(subj, endall) == ')') {
1098
- subj->pos = endall + 1;
1099
-
1100
- title_chunk =
1101
- cmark_chunk_dup(&subj->input, starttitle, endtitle - starttitle);
1102
- url = cmark_clean_url(subj->mem, &url_chunk);
1103
- title = cmark_clean_title(subj->mem, &title_chunk);
1104
- cmark_chunk_free(subj->mem, &url_chunk);
1105
- cmark_chunk_free(subj->mem, &title_chunk);
1106
- goto match;
1107
-
1108
- } else {
1109
- // it could still be a shortcut reference link
1110
- subj->pos = after_link_text_pos;
1111
- }
1112
- }
1113
-
1114
- // Next, look for a following [link label] that matches in refmap.
1115
- // skip spaces
1116
- raw_label = cmark_chunk_literal("");
1117
- found_label = link_label(subj, &raw_label);
1118
- if (!found_label) {
1119
- // If we have a shortcut reference link, back up
1120
- // to before the spacse we skipped.
1121
- subj->pos = initial_pos;
1122
- }
1123
-
1124
- if ((!found_label || raw_label.len == 0) && !opener->bracket_after) {
1125
- cmark_chunk_free(subj->mem, &raw_label);
1126
- raw_label = cmark_chunk_dup(&subj->input, opener->position,
1127
- initial_pos - opener->position - 1);
1128
- found_label = true;
1129
- }
1130
-
1131
- if (found_label) {
1132
- ref = (cmark_reference *)cmark_map_lookup(subj->refmap, &raw_label);
1133
- cmark_chunk_free(subj->mem, &raw_label);
1134
- }
1135
-
1136
- if (ref != NULL) { // found
1137
- url = chunk_clone(subj->mem, &ref->url);
1138
- title = chunk_clone(subj->mem, &ref->title);
1139
- goto match;
1140
- } else {
1141
- goto noMatch;
1142
- }
1143
-
1144
- noMatch:
1145
- // If we fall through to here, it means we didn't match a link.
1146
- // What if we're a footnote link?
1147
- if (parser->options & CMARK_OPT_FOOTNOTES &&
1148
- opener->inl_text->next &&
1149
- opener->inl_text->next->type == CMARK_NODE_TEXT) {
1150
-
1151
- cmark_chunk *literal = &opener->inl_text->next->as.literal;
1152
-
1153
- // look back to the opening '[', and skip ahead to the next character
1154
- // if we're looking at a '[^' sequence, and there is other text or nodes
1155
- // after the ^, let's call it a footnote reference.
1156
- if ((literal->len > 0 && literal->data[0] == '^') && (literal->len > 1 || opener->inl_text->next->next)) {
1157
-
1158
- // Before we got this far, the `handle_close_bracket` function may have
1159
- // advanced the current state beyond our footnote's actual closing
1160
- // bracket, ie if it went looking for a `link_label`.
1161
- // Let's just rewind the subject's position:
1162
- subj->pos = initial_pos;
1163
-
1164
- cmark_node *fnref = make_simple(subj->mem, CMARK_NODE_FOOTNOTE_REFERENCE);
1165
-
1166
- // the start and end of the footnote ref is the opening and closing brace
1167
- // i.e. the subject's current position, and the opener's start_column
1168
- int fnref_end_column = subj->pos + subj->column_offset + subj->block_offset;
1169
- int fnref_start_column = opener->inl_text->start_column;
1170
-
1171
- // any given node delineates a substring of the line being processed,
1172
- // with the remainder of the line being pointed to thru its 'literal'
1173
- // struct member.
1174
- // here, we copy the literal's pointer, moving it past the '^' character
1175
- // for a length equal to the size of footnote reference text.
1176
- // i.e. end_col minus start_col, minus the [ and the ^ characters
1177
- //
1178
- // this copies the footnote reference string, even if between the
1179
- // `opener` and the subject's current position there are other nodes
1180
- //
1181
- // (first, check for underflows)
1182
- if ((fnref_start_column + 2) <= fnref_end_column) {
1183
- fnref->as.literal = cmark_chunk_dup(literal, 1, (fnref_end_column - fnref_start_column) - 2);
1184
- } else {
1185
- fnref->as.literal = cmark_chunk_dup(literal, 1, 0);
1186
- }
1187
-
1188
- fnref->start_line = fnref->end_line = subj->line;
1189
- fnref->start_column = fnref_start_column;
1190
- fnref->end_column = fnref_end_column;
1191
-
1192
- // we then replace the opener with this new fnref node, the net effect
1193
- // being replacing the opening '[' text node with a `^footnote-ref]` node.
1194
- cmark_node_insert_before(opener->inl_text, fnref);
1195
-
1196
- process_emphasis(parser, subj, opener->previous_delimiter);
1197
- // sometimes, the footnote reference text gets parsed into multiple nodes
1198
- // i.e. '[^example]' parsed into '[', '^exam', 'ple]'.
1199
- // this happens for ex with the autolink extension. when the autolinker
1200
- // finds the 'w' character, it will split the text into multiple nodes
1201
- // in hopes of being able to match a 'www.' substring.
1202
- //
1203
- // because this function is called one character at a time via the
1204
- // `parse_inlines` function, and the current subj->pos is pointing at the
1205
- // closing ] brace, and because we copy all the text between the [ ]
1206
- // braces, we should be able to safely ignore and delete any nodes after
1207
- // the opener->inl_text->next.
1208
- //
1209
- // therefore, here we walk thru the list and free them all up
1210
- cmark_node *next_node;
1211
- cmark_node *current_node = opener->inl_text->next;
1212
- while(current_node) {
1213
- next_node = current_node->next;
1214
- cmark_node_free(current_node);
1215
- current_node = next_node;
1216
- }
1217
-
1218
- cmark_node_free(opener->inl_text);
1219
-
1220
- pop_bracket(subj);
1221
- return NULL;
1222
- }
1223
- }
1224
-
1225
- pop_bracket(subj); // remove this opener from delimiter list
1226
- subj->pos = initial_pos;
1227
- return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
1228
-
1229
- match:
1230
- inl = make_simple(subj->mem, is_image ? CMARK_NODE_IMAGE : CMARK_NODE_LINK);
1231
- inl->as.link.url = url;
1232
- inl->as.link.title = title;
1233
- inl->start_line = inl->end_line = subj->line;
1234
- inl->start_column = opener->inl_text->start_column;
1235
- inl->end_column = subj->pos + subj->column_offset + subj->block_offset;
1236
- cmark_node_insert_before(opener->inl_text, inl);
1237
- // Add link text:
1238
- tmp = opener->inl_text->next;
1239
- while (tmp) {
1240
- tmpnext = tmp->next;
1241
- cmark_node_append_child(inl, tmp);
1242
- tmp = tmpnext;
1243
- }
1244
-
1245
- // Free the bracket [:
1246
- cmark_node_free(opener->inl_text);
1247
-
1248
- process_emphasis(parser, subj, opener->previous_delimiter);
1249
- pop_bracket(subj);
1250
-
1251
- // Now, if we have a link, we also want to deactivate earlier link
1252
- // delimiters. (This code can be removed if we decide to allow links
1253
- // inside links.)
1254
- if (!is_image) {
1255
- opener = subj->last_bracket;
1256
- while (opener != NULL) {
1257
- if (!opener->image) {
1258
- if (!opener->active) {
1259
- break;
1260
- } else {
1261
- opener->active = false;
1262
- }
1263
- }
1264
- opener = opener->previous;
1265
- }
1266
- bool in_bracket_image1 = false;
1267
- if (opener) {
1268
- in_bracket_image1 = opener->in_bracket_image1;
1269
- }
1270
- bracket *opener2 = subj->last_bracket;
1271
- while (opener2 != opener) {
1272
- if (opener2->image) {
1273
- opener2->in_bracket_image1 = in_bracket_image1;
1274
- }
1275
- opener2 = opener2->previous;
1276
- }
1277
- }
1278
-
1279
- return NULL;
1280
- }
1281
-
1282
- // Parse a hard or soft linebreak, returning an inline.
1283
- // Assumes the subject has a cr or newline at the current position.
1284
- static cmark_node *handle_newline(subject *subj) {
1285
- bufsize_t nlpos = subj->pos;
1286
- // skip over cr, crlf, or lf:
1287
- if (peek_at(subj, subj->pos) == '\r') {
1288
- advance(subj);
1289
- }
1290
- if (peek_at(subj, subj->pos) == '\n') {
1291
- advance(subj);
1292
- }
1293
- ++subj->line;
1294
- subj->column_offset = -subj->pos;
1295
- // skip spaces at beginning of line
1296
- skip_spaces(subj);
1297
- if (nlpos > 1 && peek_at(subj, nlpos - 1) == ' ' &&
1298
- peek_at(subj, nlpos - 2) == ' ') {
1299
- return make_linebreak(subj->mem);
1300
- } else {
1301
- return make_softbreak(subj->mem);
1302
- }
1303
- }
1304
-
1305
- // "\r\n\\`&_*[]<!"
1306
- static int8_t SPECIAL_CHARS[256] = {
1307
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1308
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0,
1309
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1310
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,
1311
- 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1312
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1313
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1314
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1315
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1316
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1317
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1318
-
1319
- // " ' . -
1320
- static char SMART_PUNCT_CHARS[] = {
1321
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1322
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0,
1323
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1324
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1325
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1326
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1327
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1328
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1329
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1330
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1331
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1332
- };
1333
-
1334
- static bufsize_t subject_find_special_char(subject *subj, int options) {
1335
- bufsize_t n = subj->pos + 1;
1336
-
1337
- while (n < subj->input.len) {
1338
- if (SPECIAL_CHARS[subj->input.data[n]])
1339
- return n;
1340
- if (options & CMARK_OPT_SMART && SMART_PUNCT_CHARS[subj->input.data[n]])
1341
- return n;
1342
- n++;
1343
- }
1344
-
1345
- return subj->input.len;
1346
- }
1347
-
1348
- void cmark_inlines_add_special_character(unsigned char c, bool emphasis) {
1349
- SPECIAL_CHARS[c] = 1;
1350
- if (emphasis)
1351
- SKIP_CHARS[c] = 1;
1352
- }
1353
-
1354
- void cmark_inlines_remove_special_character(unsigned char c, bool emphasis) {
1355
- SPECIAL_CHARS[c] = 0;
1356
- if (emphasis)
1357
- SKIP_CHARS[c] = 0;
1358
- }
1359
-
1360
- static cmark_node *try_extensions(cmark_parser *parser,
1361
- cmark_node *parent,
1362
- unsigned char c,
1363
- subject *subj) {
1364
- cmark_node *res = NULL;
1365
- cmark_llist *tmp;
1366
-
1367
- for (tmp = parser->inline_syntax_extensions; tmp; tmp = tmp->next) {
1368
- cmark_syntax_extension *ext = (cmark_syntax_extension *) tmp->data;
1369
- res = ext->match_inline(ext, parser, parent, c, subj);
1370
-
1371
- if (res)
1372
- break;
1373
- }
1374
-
1375
- return res;
1376
- }
1377
-
1378
- // Parse an inline, advancing subject, and add it as a child of parent.
1379
- // Return 0 if no inline can be parsed, 1 otherwise.
1380
- static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent, int options) {
1381
- cmark_node *new_inl = NULL;
1382
- cmark_chunk contents;
1383
- unsigned char c;
1384
- bufsize_t startpos, endpos;
1385
- c = peek_char(subj);
1386
- if (c == 0) {
1387
- return 0;
1388
- }
1389
- switch (c) {
1390
- case '\r':
1391
- case '\n':
1392
- new_inl = handle_newline(subj);
1393
- break;
1394
- case '`':
1395
- new_inl = handle_backticks(subj, options);
1396
- break;
1397
- case '\\':
1398
- new_inl = handle_backslash(parser, subj);
1399
- break;
1400
- case '&':
1401
- new_inl = handle_entity(subj);
1402
- break;
1403
- case '<':
1404
- new_inl = handle_pointy_brace(subj, options);
1405
- break;
1406
- case '*':
1407
- case '_':
1408
- case '\'':
1409
- case '"':
1410
- new_inl = handle_delim(subj, c, (options & CMARK_OPT_SMART) != 0);
1411
- break;
1412
- case '-':
1413
- new_inl = handle_hyphen(subj, (options & CMARK_OPT_SMART) != 0);
1414
- break;
1415
- case '.':
1416
- new_inl = handle_period(subj, (options & CMARK_OPT_SMART) != 0);
1417
- break;
1418
- case '[':
1419
- advance(subj);
1420
- new_inl = make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("["));
1421
- push_bracket(subj, false, new_inl);
1422
- break;
1423
- case ']':
1424
- new_inl = handle_close_bracket(parser, subj);
1425
- break;
1426
- case '!':
1427
- advance(subj);
1428
- if (peek_char(subj) == '[' && peek_char_n(subj, 1) != '^') {
1429
- advance(subj);
1430
- new_inl = make_str(subj, subj->pos - 2, subj->pos - 1, cmark_chunk_literal("!["));
1431
- push_bracket(subj, true, new_inl);
1432
- } else {
1433
- new_inl = make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("!"));
1434
- }
1435
- break;
1436
- default:
1437
- new_inl = try_extensions(parser, parent, c, subj);
1438
- if (new_inl != NULL)
1439
- break;
1440
-
1441
- endpos = subject_find_special_char(subj, options);
1442
- contents = cmark_chunk_dup(&subj->input, subj->pos, endpos - subj->pos);
1443
- startpos = subj->pos;
1444
- subj->pos = endpos;
1445
-
1446
- // if we're at a newline, strip trailing spaces.
1447
- if (S_is_line_end_char(peek_char(subj))) {
1448
- cmark_chunk_rtrim(&contents);
1449
- }
1450
-
1451
- new_inl = make_str(subj, startpos, endpos - 1, contents);
1452
- }
1453
- if (new_inl != NULL) {
1454
- cmark_node_append_child(parent, new_inl);
1455
- }
1456
-
1457
- return 1;
1458
- }
1459
-
1460
- // Parse inlines from parent's string_content, adding as children of parent.
1461
- void cmark_parse_inlines(cmark_parser *parser,
1462
- cmark_node *parent,
1463
- cmark_map *refmap,
1464
- int options) {
1465
- subject subj;
1466
- cmark_chunk content = {parent->content.ptr, parent->content.size, 0};
1467
- subject_from_buf(parser->mem, parent->start_line, parent->start_column - 1 + parent->internal_offset, &subj, &content, refmap);
1468
- cmark_chunk_rtrim(&subj.input);
1469
-
1470
- while (!is_eof(&subj) && parse_inline(parser, &subj, parent, options))
1471
- ;
1472
-
1473
- process_emphasis(parser, &subj, NULL);
1474
- // free bracket and delim stack
1475
- while (subj.last_delim) {
1476
- remove_delimiter(&subj, subj.last_delim);
1477
- }
1478
- while (subj.last_bracket) {
1479
- pop_bracket(&subj);
1480
- }
1481
- }
1482
-
1483
- // Parse zero or more space characters, including at most one newline.
1484
- static void spnl(subject *subj) {
1485
- skip_spaces(subj);
1486
- if (skip_line_end(subj)) {
1487
- skip_spaces(subj);
1488
- }
1489
- }
1490
-
1491
- // Parse reference. Assumes string begins with '[' character.
1492
- // Modify refmap if a reference is encountered.
1493
- // Return 0 if no reference found, otherwise position of subject
1494
- // after reference is parsed.
1495
- bufsize_t cmark_parse_reference_inline(cmark_mem *mem, cmark_chunk *input,
1496
- cmark_map *refmap) {
1497
- subject subj;
1498
-
1499
- cmark_chunk lab;
1500
- cmark_chunk url;
1501
- cmark_chunk title;
1502
-
1503
- bufsize_t matchlen = 0;
1504
- bufsize_t beforetitle;
1505
-
1506
- subject_from_buf(mem, -1, 0, &subj, input, NULL);
1507
-
1508
- // parse label:
1509
- if (!link_label(&subj, &lab) || lab.len == 0)
1510
- return 0;
1511
-
1512
- // colon:
1513
- if (peek_char(&subj) == ':') {
1514
- advance(&subj);
1515
- } else {
1516
- return 0;
1517
- }
1518
-
1519
- // parse link url:
1520
- spnl(&subj);
1521
- if ((matchlen = manual_scan_link_url(&subj.input, subj.pos, &url)) > -1) {
1522
- subj.pos += matchlen;
1523
- } else {
1524
- return 0;
1525
- }
1526
-
1527
- // parse optional link_title
1528
- beforetitle = subj.pos;
1529
- spnl(&subj);
1530
- matchlen = subj.pos == beforetitle ? 0 : scan_link_title(&subj.input, subj.pos);
1531
- if (matchlen) {
1532
- title = cmark_chunk_dup(&subj.input, subj.pos, matchlen);
1533
- subj.pos += matchlen;
1534
- } else {
1535
- subj.pos = beforetitle;
1536
- title = cmark_chunk_literal("");
1537
- }
1538
-
1539
- // parse final spaces and newline:
1540
- skip_spaces(&subj);
1541
- if (!skip_line_end(&subj)) {
1542
- if (matchlen) { // try rewinding before title
1543
- subj.pos = beforetitle;
1544
- skip_spaces(&subj);
1545
- if (!skip_line_end(&subj)) {
1546
- return 0;
1547
- }
1548
- } else {
1549
- return 0;
1550
- }
1551
- }
1552
- // insert reference into refmap
1553
- cmark_reference_create(refmap, &lab, &url, &title);
1554
- return subj.pos;
1555
- }
1556
-
1557
- unsigned char cmark_inline_parser_peek_char(cmark_inline_parser *parser) {
1558
- return peek_char(parser);
1559
- }
1560
-
1561
- unsigned char cmark_inline_parser_peek_at(cmark_inline_parser *parser, bufsize_t pos) {
1562
- return peek_at(parser, pos);
1563
- }
1564
-
1565
- int cmark_inline_parser_is_eof(cmark_inline_parser *parser) {
1566
- return is_eof(parser);
1567
- }
1568
-
1569
- static char *
1570
- my_strndup (const char *s, size_t n)
1571
- {
1572
- char *result;
1573
- size_t len = strlen (s);
1574
-
1575
- if (n < len)
1576
- len = n;
1577
-
1578
- result = (char *) malloc (len + 1);
1579
- if (!result)
1580
- return 0;
1581
-
1582
- result[len] = '\0';
1583
- return (char *) memcpy (result, s, len);
1584
- }
1585
-
1586
- char *cmark_inline_parser_take_while(cmark_inline_parser *parser, cmark_inline_predicate pred) {
1587
- unsigned char c;
1588
- bufsize_t startpos = parser->pos;
1589
- bufsize_t len = 0;
1590
-
1591
- while ((c = peek_char(parser)) && (*pred)(c)) {
1592
- advance(parser);
1593
- len++;
1594
- }
1595
-
1596
- return my_strndup((const char *) parser->input.data + startpos, len);
1597
- }
1598
-
1599
- void cmark_inline_parser_push_delimiter(cmark_inline_parser *parser,
1600
- unsigned char c,
1601
- int can_open,
1602
- int can_close,
1603
- cmark_node *inl_text) {
1604
- push_delimiter(parser, c, can_open != 0, can_close != 0, inl_text);
1605
- }
1606
-
1607
- void cmark_inline_parser_remove_delimiter(cmark_inline_parser *parser, delimiter *delim) {
1608
- remove_delimiter(parser, delim);
1609
- }
1610
-
1611
- int cmark_inline_parser_scan_delimiters(cmark_inline_parser *parser,
1612
- int max_delims,
1613
- unsigned char c,
1614
- int *left_flanking,
1615
- int *right_flanking,
1616
- int *punct_before,
1617
- int *punct_after) {
1618
- int numdelims = 0;
1619
- bufsize_t before_char_pos;
1620
- int32_t after_char = 0;
1621
- int32_t before_char = 0;
1622
- int len;
1623
- bool space_before, space_after;
1624
-
1625
- if (parser->pos == 0) {
1626
- before_char = 10;
1627
- } else {
1628
- before_char_pos = parser->pos - 1;
1629
- // walk back to the beginning of the UTF_8 sequence:
1630
- while (peek_at(parser, before_char_pos) >> 6 == 2 && before_char_pos > 0) {
1631
- before_char_pos -= 1;
1632
- }
1633
- len = cmark_utf8proc_iterate(parser->input.data + before_char_pos,
1634
- parser->pos - before_char_pos, &before_char);
1635
- if (len == -1) {
1636
- before_char = 10;
1637
- }
1638
- }
1639
-
1640
- while (peek_char(parser) == c && numdelims < max_delims) {
1641
- numdelims++;
1642
- advance(parser);
1643
- }
1644
-
1645
- len = cmark_utf8proc_iterate(parser->input.data + parser->pos,
1646
- parser->input.len - parser->pos, &after_char);
1647
- if (len == -1) {
1648
- after_char = 10;
1649
- }
1650
-
1651
- *punct_before = cmark_utf8proc_is_punctuation(before_char);
1652
- *punct_after = cmark_utf8proc_is_punctuation(after_char);
1653
- space_before = cmark_utf8proc_is_space(before_char) != 0;
1654
- space_after = cmark_utf8proc_is_space(after_char) != 0;
1655
-
1656
- *left_flanking = numdelims > 0 && !cmark_utf8proc_is_space(after_char) &&
1657
- !(*punct_after && !space_before && !*punct_before);
1658
- *right_flanking = numdelims > 0 && !cmark_utf8proc_is_space(before_char) &&
1659
- !(*punct_before && !space_after && !*punct_after);
1660
-
1661
- return numdelims;
1662
- }
1663
-
1664
- void cmark_inline_parser_advance_offset(cmark_inline_parser *parser) {
1665
- advance(parser);
1666
- }
1667
-
1668
- int cmark_inline_parser_get_offset(cmark_inline_parser *parser) {
1669
- return parser->pos;
1670
- }
1671
-
1672
- void cmark_inline_parser_set_offset(cmark_inline_parser *parser, int offset) {
1673
- parser->pos = offset;
1674
- }
1675
-
1676
- int cmark_inline_parser_get_column(cmark_inline_parser *parser) {
1677
- return parser->pos + 1 + parser->column_offset + parser->block_offset;
1678
- }
1679
-
1680
- cmark_chunk *cmark_inline_parser_get_chunk(cmark_inline_parser *parser) {
1681
- return &parser->input;
1682
- }
1683
-
1684
- int cmark_inline_parser_in_bracket(cmark_inline_parser *parser, int image) {
1685
- bracket *b = parser->last_bracket;
1686
- if (!b) {
1687
- return 0;
1688
- }
1689
- if (image != 0) {
1690
- return b->in_bracket_image1;
1691
- } else {
1692
- return b->in_bracket_image0;
1693
- }
1694
- }
1695
-
1696
- void cmark_node_unput(cmark_node *node, int n) {
1697
- node = node->last_child;
1698
- while (n > 0 && node && node->type == CMARK_NODE_TEXT) {
1699
- if (node->as.literal.len < n) {
1700
- n -= node->as.literal.len;
1701
- node->as.literal.len = 0;
1702
- } else {
1703
- node->as.literal.len -= n;
1704
- n = 0;
1705
- }
1706
- node = node->prev;
1707
- }
1708
- }
1709
-
1710
- delimiter *cmark_inline_parser_get_last_delimiter(cmark_inline_parser *parser) {
1711
- return parser->last_delim;
1712
- }
1713
-
1714
- int cmark_inline_parser_get_line(cmark_inline_parser *parser) {
1715
- return parser->line;
1716
- }