redcarpet 1.17.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,48 +18,58 @@
18
18
  */
19
19
 
20
20
  #include "markdown.h"
21
- #include "array.h"
21
+ #include "stack.h"
22
22
 
23
23
  #include <assert.h>
24
24
  #include <string.h>
25
- //#include <strings.h> /* for strncasecmp */
26
25
  #include <ctype.h>
27
26
  #include <stdio.h>
28
27
 
28
+ #define REF_TABLE_SIZE 8
29
+
29
30
  #define BUFFER_BLOCK 0
30
31
  #define BUFFER_SPAN 1
31
32
 
32
33
  #define MKD_LI_END 8 /* internal list flag */
33
34
 
35
+ #define gperf_case_strncmp(s1, s2, n) strncasecmp(s1, s2, n)
36
+ #define GPERF_DOWNCASE 1
37
+ #define GPERF_CASE_STRNCMP 1
38
+ #include "html_blocks.h"
39
+
34
40
  /***************
35
41
  * LOCAL TYPES *
36
42
  ***************/
37
43
 
38
- /* link_ref reference to a link */
44
+ /* link_ref: reference to a link */
39
45
  struct link_ref {
40
- struct buf *id;
46
+ unsigned int id;
47
+
41
48
  struct buf *link;
42
49
  struct buf *title;
50
+
51
+ struct link_ref *next;
43
52
  };
44
53
 
45
- /* char_trigger function pointer to render active chars */
54
+ /* char_trigger: function pointer to render active chars */
46
55
  /* returns the number of chars taken care of */
47
56
  /* data is the pointer of the beginning of the span */
48
57
  /* offset is the number of valid chars before data */
49
- struct render;
58
+ struct sd_markdown;
50
59
  typedef size_t
51
- (*char_trigger)(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
52
-
53
- static size_t char_emphasis(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
54
- static size_t char_linebreak(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
55
- static size_t char_codespan(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
56
- static size_t char_escape(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
57
- static size_t char_entity(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
58
- static size_t char_langle_tag(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
59
- static size_t char_autolink_url(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
60
- static size_t char_autolink_email(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
61
- static size_t char_autolink_www(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
62
- static size_t char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
60
+ (*char_trigger)(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
61
+
62
+ static size_t char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
63
+ static size_t char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
64
+ static size_t char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
65
+ static size_t char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
66
+ static size_t char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
67
+ static size_t char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
68
+ static size_t char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
69
+ static size_t char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
70
+ static size_t char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
71
+ static size_t char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
72
+ static size_t char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
63
73
 
64
74
  enum markdown_char_t {
65
75
  MD_CHAR_NONE = 0,
@@ -72,7 +82,8 @@ enum markdown_char_t {
72
82
  MD_CHAR_ENTITITY,
73
83
  MD_CHAR_AUTOLINK_URL,
74
84
  MD_CHAR_AUTOLINK_EMAIL,
75
- MD_CHAR_AUTOLINK_WWW
85
+ MD_CHAR_AUTOLINK_WWW,
86
+ MD_CHAR_SUPERSCRIPT,
76
87
  };
77
88
 
78
89
  static char_trigger markdown_char_ptrs[] = {
@@ -87,84 +98,51 @@ static char_trigger markdown_char_ptrs[] = {
87
98
  &char_autolink_url,
88
99
  &char_autolink_email,
89
100
  &char_autolink_www,
101
+ &char_superscript,
90
102
  };
91
103
 
92
104
  /* render • structure containing one particular render */
93
- struct render {
94
- struct mkd_renderer make;
95
- struct array refs;
96
- char active_char[256];
97
- struct parray work_bufs[2];
105
+ struct sd_markdown {
106
+ struct sd_callbacks cb;
107
+ void *opaque;
108
+
109
+ struct link_ref *refs[REF_TABLE_SIZE];
110
+ uint8_t active_char[256];
111
+ struct stack work_bufs[2];
98
112
  unsigned int ext_flags;
99
113
  size_t max_nesting;
114
+ int in_link_body;
100
115
  };
101
116
 
102
- /* html_tag • structure for quick HTML tag search (inspired from discount) */
103
- struct html_tag {
104
- const char *text;
105
- size_t size;
106
- };
117
+ /***************************
118
+ * HELPER FUNCTIONS *
119
+ ***************************/
107
120
 
108
121
  static inline struct buf *
109
- rndr_newbuf(struct render *rndr, int type)
122
+ rndr_newbuf(struct sd_markdown *rndr, int type)
110
123
  {
111
124
  static const size_t buf_size[2] = {256, 64};
112
125
  struct buf *work = NULL;
113
- struct parray *queue = &rndr->work_bufs[type];
126
+ struct stack *pool = &rndr->work_bufs[type];
114
127
 
115
- if (queue->size < queue->asize) {
116
- work = queue->item[queue->size++];
128
+ if (pool->size < pool->asize &&
129
+ pool->item[pool->size] != NULL) {
130
+ work = pool->item[pool->size++];
117
131
  work->size = 0;
118
132
  } else {
119
133
  work = bufnew(buf_size[type]);
120
- parr_push(queue, work);
134
+ stack_push(pool, work);
121
135
  }
122
136
 
123
137
  return work;
124
138
  }
125
139
 
126
140
  static inline void
127
- rndr_popbuf(struct render *rndr, int type)
141
+ rndr_popbuf(struct sd_markdown *rndr, int type)
128
142
  {
129
143
  rndr->work_bufs[type].size--;
130
144
  }
131
145
 
132
- /********************
133
- * GLOBAL VARIABLES *
134
- ********************/
135
-
136
- /* block_tags • recognised block tags, sorted by cmp_html_tag */
137
- static struct html_tag block_tags[] = {
138
- /*0*/ { "p", 1 },
139
- { "dl", 2 },
140
- { "h1", 2 },
141
- { "h2", 2 },
142
- { "h3", 2 },
143
- { "h4", 2 },
144
- { "h5", 2 },
145
- { "h6", 2 },
146
- { "ol", 2 },
147
- { "ul", 2 },
148
- /*10*/ { "del", 3 },
149
- { "div", 3 },
150
- /*12*/ { "ins", 3 },
151
- { "pre", 3 },
152
- { "form", 4 },
153
- { "math", 4 },
154
- { "table", 5 },
155
- { "iframe", 6 },
156
- { "script", 6 },
157
- { "fieldset", 8 },
158
- { "noscript", 8 },
159
- { "blockquote", 10 }
160
- };
161
-
162
- #define INS_TAG (block_tags + 12)
163
- #define DEL_TAG (block_tags + 10)
164
-
165
- /***************************
166
- * HELPER FUNCTIONS *
167
- ***************************/
168
146
  static void
169
147
  unscape_text(struct buf *ob, struct buf *src)
170
148
  {
@@ -185,54 +163,87 @@ unscape_text(struct buf *ob, struct buf *src)
185
163
  }
186
164
  }
187
165
 
188
- /* cmp_link_ref • comparison function for link_ref sorted arrays */
189
- static int
190
- cmp_link_ref(void *key, void *array_entry)
166
+ static unsigned int
167
+ hash_link_ref(const uint8_t *link_ref, size_t length)
191
168
  {
192
- struct link_ref *lr = array_entry;
193
- return bufcasecmp(key, lr->id);
169
+ size_t i;
170
+ unsigned int hash = 0;
171
+
172
+ for (i = 0; i < length; ++i)
173
+ hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash;
174
+
175
+ return hash;
194
176
  }
195
177
 
196
- /* cmp_link_ref_sort • comparison function for link_ref qsort */
197
- static int
198
- cmp_link_ref_sort(const void *a, const void *b)
178
+ static struct link_ref *
179
+ add_link_ref(
180
+ struct link_ref **references,
181
+ const uint8_t *name, size_t name_size)
199
182
  {
200
- const struct link_ref *lra = a;
201
- const struct link_ref *lrb = b;
202
- return bufcasecmp(lra->id, lrb->id);
183
+ struct link_ref *ref = calloc(1, sizeof(struct link_ref));
184
+
185
+ if (!ref)
186
+ return NULL;
187
+
188
+ ref->id = hash_link_ref(name, name_size);
189
+ ref->next = references[ref->id % REF_TABLE_SIZE];
190
+
191
+ references[ref->id % REF_TABLE_SIZE] = ref;
192
+ return ref;
203
193
  }
204
194
 
205
- /* cmp_html_tag comparison function for bsearch() (stolen from discount) */
206
- static int
207
- cmp_html_tag(const void *a, const void *b)
195
+ static struct link_ref *
196
+ find_link_ref(struct link_ref **references, uint8_t *name, size_t length)
208
197
  {
209
- const struct html_tag *hta = a;
210
- const struct html_tag *htb = b;
211
- if (hta->size != htb->size) return (int)(hta->size - htb->size);
212
- return strncasecmp(hta->text, htb->text, hta->size);
213
- }
198
+ unsigned int hash = hash_link_ref(name, length);
199
+ struct link_ref *ref = NULL;
200
+
201
+ ref = references[hash % REF_TABLE_SIZE];
214
202
 
203
+ while (ref != NULL) {
204
+ if (ref->id == hash)
205
+ return ref;
215
206
 
216
- /* find_block_tag • returns the current block tag */
217
- static struct html_tag *
218
- find_block_tag(char *data, size_t size)
207
+ ref = ref->next;
208
+ }
209
+
210
+ return NULL;
211
+ }
212
+
213
+ static void
214
+ free_link_refs(struct link_ref **references)
219
215
  {
220
- size_t i = 0;
221
- struct html_tag key;
216
+ size_t i;
222
217
 
223
- /* looking for the word end */
224
- while (i < size && ((data[i] >= '0' && data[i] <= '9')
225
- || (data[i] >= 'A' && data[i] <= 'Z')
226
- || (data[i] >= 'a' && data[i] <= 'z')))
227
- i += 1;
228
- if (i >= size) return 0;
218
+ for (i = 0; i < REF_TABLE_SIZE; ++i) {
219
+ struct link_ref *r = references[i];
220
+ struct link_ref *next;
221
+
222
+ while (r) {
223
+ next = r->next;
224
+ bufrelease(r->link);
225
+ bufrelease(r->title);
226
+ free(r);
227
+ r = next;
228
+ }
229
+ }
230
+ }
231
+
232
+ /*
233
+ * Check whether a char is a Markdown space.
229
234
 
230
- /* binary search of the tag */
231
- key.text = data;
232
- key.size = i;
233
- return bsearch(&key, block_tags,
234
- sizeof block_tags / sizeof block_tags[0],
235
- sizeof block_tags[0], cmp_html_tag);
235
+ * Right now we only consider spaces the actual
236
+ * space and a newline: tabs and carriage returns
237
+ * are filtered out during the preprocessing phase.
238
+ *
239
+ * If we wanted to actually be UTF-8 compliant, we
240
+ * should instead extract an Unicode codepoint from
241
+ * this character and check for space properties.
242
+ */
243
+ static inline int
244
+ _isspace(int c)
245
+ {
246
+ return c == ' ' || c == '\n';
236
247
  }
237
248
 
238
249
  /****************************
@@ -242,7 +253,7 @@ find_block_tag(char *data, size_t size)
242
253
  /* is_mail_autolink • looks for the address part of a mail autolink and '>' */
243
254
  /* this is less strict than the original markdown e-mail address matching */
244
255
  static size_t
245
- is_mail_autolink(char *data, size_t size)
256
+ is_mail_autolink(uint8_t *data, size_t size)
246
257
  {
247
258
  size_t i = 0, nb = 0;
248
259
 
@@ -273,7 +284,7 @@ is_mail_autolink(char *data, size_t size)
273
284
 
274
285
  /* tag_length • returns the length of the given tag, or 0 is it's not valid */
275
286
  static size_t
276
- tag_length(char *data, size_t size, enum mkd_autolink *autolink)
287
+ tag_length(uint8_t *data, size_t size, enum mkd_autolink *autolink)
277
288
  {
278
289
  size_t i, j;
279
290
 
@@ -316,8 +327,9 @@ tag_length(char *data, size_t size, enum mkd_autolink *autolink)
316
327
  while (i < size) {
317
328
  if (data[i] == '\\') i += 2;
318
329
  else if (data[i] == '>' || data[i] == '\'' ||
319
- data[i] == '"' || isspace(data[i])) break;
320
- else i += 1;
330
+ data[i] == '"' || data[i] == ' ' || data[i] == '\n')
331
+ break;
332
+ else i++;
321
333
  }
322
334
 
323
335
  if (i >= size) return 0;
@@ -327,33 +339,33 @@ tag_length(char *data, size_t size, enum mkd_autolink *autolink)
327
339
  }
328
340
 
329
341
  /* looking for sometinhg looking like a tag end */
330
- while (i < size && data[i] != '>') i += 1;
342
+ while (i < size && data[i] != '>') i++;
331
343
  if (i >= size) return 0;
332
344
  return i + 1;
333
345
  }
334
346
 
335
347
  /* parse_inline • parses inline markdown elements */
336
348
  static void
337
- parse_inline(struct buf *ob, struct render *rndr, char *data, size_t size)
349
+ parse_inline(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
338
350
  {
339
351
  size_t i = 0, end = 0;
340
- char action = 0;
341
- struct buf work = { 0, 0, 0, 0, 0 };
352
+ uint8_t action = 0;
353
+ struct buf work = { 0, 0, 0, 0 };
342
354
 
343
355
  if (rndr->work_bufs[BUFFER_SPAN].size +
344
- rndr->work_bufs[BUFFER_BLOCK].size > (int)rndr->max_nesting)
356
+ rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting)
345
357
  return;
346
358
 
347
359
  while (i < size) {
348
360
  /* copying inactive chars into the output */
349
- while (end < size && (action = rndr->active_char[(unsigned char)data[end]]) == 0) {
361
+ while (end < size && (action = rndr->active_char[data[end]]) == 0) {
350
362
  end++;
351
363
  }
352
364
 
353
- if (rndr->make.normal_text) {
365
+ if (rndr->cb.normal_text) {
354
366
  work.data = data + i;
355
367
  work.size = end - i;
356
- rndr->make.normal_text(ob, &work, rndr->make.opaque);
368
+ rndr->cb.normal_text(ob, &work, rndr->opaque);
357
369
  }
358
370
  else
359
371
  bufput(ob, data + i, end - i);
@@ -361,78 +373,117 @@ parse_inline(struct buf *ob, struct render *rndr, char *data, size_t size)
361
373
  if (end >= size) break;
362
374
  i = end;
363
375
 
364
- /* calling the trigger */
365
376
  end = markdown_char_ptrs[(int)action](ob, rndr, data + i, i, size - i);
366
377
  if (!end) /* no action from the callback */
367
378
  end = i + 1;
368
- else {
379
+ else {
369
380
  i += end;
370
381
  end = i;
371
- }
382
+ }
372
383
  }
373
384
  }
374
385
 
375
- /* find_emph_char • looks for the next emph char, skipping other constructs */
386
+ /* find_emph_char • looks for the next emph uint8_t, skipping other constructs */
376
387
  static size_t
377
- find_emph_char(char *data, size_t size, char c)
388
+ find_emph_char(uint8_t *data, size_t size, uint8_t c)
378
389
  {
379
390
  size_t i = 1;
380
391
 
381
392
  while (i < size) {
382
- while (i < size && data[i] != c
383
- && data[i] != '`' && data[i] != '[')
384
- i += 1;
385
- if (data[i] == c) return i;
393
+ while (i < size && data[i] != c && data[i] != '`' && data[i] != '[')
394
+ i++;
395
+
396
+ if (i == size)
397
+ return 0;
398
+
399
+ if (data[i] == c)
400
+ return i;
386
401
 
387
402
  /* not counting escaped chars */
388
- if (i && data[i - 1] == '\\') { i += 1; continue; }
403
+ if (i && data[i - 1] == '\\') {
404
+ i++; continue;
405
+ }
389
406
 
390
- /* skipping a code span */
391
407
  if (data[i] == '`') {
408
+ size_t span_nb = 0, bt;
392
409
  size_t tmp_i = 0;
393
- i += 1;
394
- while (i < size && data[i] != '`') {
410
+
411
+ /* counting the number of opening backticks */
412
+ while (i < size && data[i] == '`') {
413
+ i++; span_nb++;
414
+ }
415
+
416
+ if (i >= size) return 0;
417
+
418
+ /* finding the matching closing sequence */
419
+ bt = 0;
420
+ while (i < size && bt < span_nb) {
395
421
  if (!tmp_i && data[i] == c) tmp_i = i;
396
- i += 1; }
397
- if (i >= size) return tmp_i;
398
- i += 1; }
422
+ if (data[i] == '`') bt++;
423
+ else bt = 0;
424
+ i++;
425
+ }
399
426
 
427
+ if (i >= size) return tmp_i;
428
+ }
400
429
  /* skipping a link */
401
430
  else if (data[i] == '[') {
402
431
  size_t tmp_i = 0;
403
- char cc;
404
- i += 1;
432
+ uint8_t cc;
433
+
434
+ i++;
405
435
  while (i < size && data[i] != ']') {
406
436
  if (!tmp_i && data[i] == c) tmp_i = i;
407
- i += 1; }
408
- i += 1;
409
- while (i < size && (data[i] == ' '
410
- || data[i] == '\t' || data[i] == '\n'))
411
- i += 1;
412
- if (i >= size) return tmp_i;
413
- if (data[i] != '[' && data[i] != '(') { /* not a link*/
414
- if (tmp_i) return tmp_i;
415
- else continue; }
416
- cc = data[i];
417
- i += 1;
437
+ i++;
438
+ }
439
+
440
+ i++;
441
+ while (i < size && (data[i] == ' ' || data[i] == '\n'))
442
+ i++;
443
+
444
+ if (i >= size)
445
+ return tmp_i;
446
+
447
+ switch (data[i]) {
448
+ case '[':
449
+ cc = ']'; break;
450
+
451
+ case '(':
452
+ cc = ')'; break;
453
+
454
+ default:
455
+ if (tmp_i)
456
+ return tmp_i;
457
+ else
458
+ continue;
459
+ }
460
+
461
+ i++;
418
462
  while (i < size && data[i] != cc) {
419
463
  if (!tmp_i && data[i] == c) tmp_i = i;
420
- i += 1; }
421
- if (i >= size) return tmp_i;
422
- i += 1; } }
464
+ i++;
465
+ }
466
+
467
+ if (i >= size)
468
+ return tmp_i;
469
+
470
+ i++;
471
+ }
472
+ }
473
+
423
474
  return 0;
424
475
  }
425
476
 
426
477
  /* parse_emph1 • parsing single emphase */
427
478
  /* closed by a symbol not preceded by whitespace and not followed by symbol */
428
479
  static size_t
429
- parse_emph1(struct buf *ob, struct render *rndr, char *data, size_t size, char c)
480
+ parse_emph1(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)
430
481
  {
431
482
  size_t i = 0, len;
432
483
  struct buf *work = 0;
433
484
  int r;
434
485
 
435
- if (!rndr->make.emphasis) return 0;
486
+ if (!rndr->cb.emphasis) return 0;
436
487
 
437
488
  /* skipping one symbol if coming from emph3 */
438
489
  if (size > 1 && data[0] == c && data[1] == c) i = 1;
@@ -443,21 +494,16 @@ parse_emph1(struct buf *ob, struct render *rndr, char *data, size_t size, char c
443
494
  i += len;
444
495
  if (i >= size) return 0;
445
496
 
446
- if (i + 1 < size && data[i + 1] == c) {
447
- i += 1;
448
- continue;
449
- }
450
-
451
- if (data[i] == c && !isspace(data[i - 1])) {
497
+ if (data[i] == c && !_isspace(data[i - 1])) {
452
498
 
453
499
  if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) {
454
- if (!(i + 1 == size || isspace(data[i + 1]) || ispunct(data[i + 1])))
500
+ if (!(i + 1 == size || _isspace(data[i + 1]) || ispunct(data[i + 1])))
455
501
  continue;
456
502
  }
457
503
 
458
504
  work = rndr_newbuf(rndr, BUFFER_SPAN);
459
505
  parse_inline(work, rndr, data, i);
460
- r = rndr->make.emphasis(ob, work, rndr->make.opaque);
506
+ r = rndr->cb.emphasis(ob, work, rndr->opaque);
461
507
  rndr_popbuf(rndr, BUFFER_SPAN);
462
508
  return r ? i + 1 : 0;
463
509
  }
@@ -468,27 +514,27 @@ parse_emph1(struct buf *ob, struct render *rndr, char *data, size_t size, char c
468
514
 
469
515
  /* parse_emph2 • parsing single emphase */
470
516
  static size_t
471
- parse_emph2(struct buf *ob, struct render *rndr, char *data, size_t size, char c)
517
+ parse_emph2(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)
472
518
  {
473
- int (*render_method)(struct buf *ob, struct buf *text, void *opaque);
519
+ int (*render_method)(struct buf *ob, const struct buf *text, void *opaque);
474
520
  size_t i = 0, len;
475
521
  struct buf *work = 0;
476
522
  int r;
477
523
 
478
- render_method = (c == '~') ? rndr->make.strikethrough : rndr->make.double_emphasis;
524
+ render_method = (c == '~') ? rndr->cb.strikethrough : rndr->cb.double_emphasis;
479
525
 
480
526
  if (!render_method)
481
527
  return 0;
482
-
528
+
483
529
  while (i < size) {
484
530
  len = find_emph_char(data + i, size - i, c);
485
531
  if (!len) return 0;
486
532
  i += len;
487
533
 
488
- if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !isspace(data[i - 1])) {
534
+ if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) {
489
535
  work = rndr_newbuf(rndr, BUFFER_SPAN);
490
536
  parse_inline(work, rndr, data, i);
491
- r = render_method(ob, work, rndr->make.opaque);
537
+ r = render_method(ob, work, rndr->opaque);
492
538
  rndr_popbuf(rndr, BUFFER_SPAN);
493
539
  return r ? i + 2 : 0;
494
540
  }
@@ -500,7 +546,7 @@ parse_emph2(struct buf *ob, struct render *rndr, char *data, size_t size, char c
500
546
  /* parse_emph3 • parsing single emphase */
501
547
  /* finds the first closing tag, and delegates to the other emph */
502
548
  static size_t
503
- parse_emph3(struct buf *ob, struct render *rndr, char *data, size_t size, char c)
549
+ parse_emph3(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)
504
550
  {
505
551
  size_t i = 0, len;
506
552
  int r;
@@ -511,15 +557,15 @@ parse_emph3(struct buf *ob, struct render *rndr, char *data, size_t size, char c
511
557
  i += len;
512
558
 
513
559
  /* skip whitespace preceded symbols */
514
- if (data[i] != c || isspace(data[i - 1]))
560
+ if (data[i] != c || _isspace(data[i - 1]))
515
561
  continue;
516
562
 
517
- if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->make.triple_emphasis) {
563
+ if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->cb.triple_emphasis) {
518
564
  /* triple symbol found */
519
565
  struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN);
520
566
 
521
567
  parse_inline(work, rndr, data, i);
522
- r = rndr->make.triple_emphasis(ob, work, rndr->make.opaque);
568
+ r = rndr->cb.triple_emphasis(ob, work, rndr->opaque);
523
569
  rndr_popbuf(rndr, BUFFER_SPAN);
524
570
  return r ? i + 3 : 0;
525
571
 
@@ -536,46 +582,46 @@ parse_emph3(struct buf *ob, struct render *rndr, char *data, size_t size, char c
536
582
  else return len - 1;
537
583
  }
538
584
  }
539
- return 0;
585
+ return 0;
540
586
  }
541
587
 
542
588
  /* char_emphasis • single and double emphasis parsing */
543
589
  static size_t
544
- char_emphasis(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
590
+ char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
545
591
  {
546
- char c = data[0];
592
+ uint8_t c = data[0];
547
593
  size_t ret;
548
594
 
549
595
  if (size > 2 && data[1] != c) {
550
596
  /* whitespace cannot follow an opening emphasis;
551
597
  * strikethrough only takes two characters '~~' */
552
- if (c == '~' || isspace(data[1]) || (ret = parse_emph1(ob, rndr, data + 1, size - 1, c)) == 0)
598
+ if (c == '~' || _isspace(data[1]) || (ret = parse_emph1(ob, rndr, data + 1, size - 1, c)) == 0)
553
599
  return 0;
554
600
 
555
601
  return ret + 1;
556
602
  }
557
603
 
558
604
  if (size > 3 && data[1] == c && data[2] != c) {
559
- if (isspace(data[2]) || (ret = parse_emph2(ob, rndr, data + 2, size - 2, c)) == 0)
605
+ if (_isspace(data[2]) || (ret = parse_emph2(ob, rndr, data + 2, size - 2, c)) == 0)
560
606
  return 0;
561
607
 
562
608
  return ret + 2;
563
609
  }
564
610
 
565
611
  if (size > 4 && data[1] == c && data[2] == c && data[3] != c) {
566
- if (c == '~' || isspace(data[3]) || (ret = parse_emph3(ob, rndr, data + 3, size - 3, c)) == 0)
612
+ if (c == '~' || _isspace(data[3]) || (ret = parse_emph3(ob, rndr, data + 3, size - 3, c)) == 0)
567
613
  return 0;
568
614
 
569
615
  return ret + 3;
570
616
  }
571
617
 
572
- return 0;
618
+ return 0;
573
619
  }
574
620
 
575
621
 
576
622
  /* char_linebreak • '\n' preceded by two spaces (assuming linebreak != 0) */
577
623
  static size_t
578
- char_linebreak(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
624
+ char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
579
625
  {
580
626
  if (offset < 2 || data[-1] != ' ' || data[-2] != ' ')
581
627
  return 0;
@@ -584,13 +630,13 @@ char_linebreak(struct buf *ob, struct render *rndr, char *data, size_t offset, s
584
630
  while (ob->size && ob->data[ob->size - 1] == ' ')
585
631
  ob->size--;
586
632
 
587
- return rndr->make.linebreak(ob, rndr->make.opaque) ? 1 : 0;
633
+ return rndr->cb.linebreak(ob, rndr->opaque) ? 1 : 0;
588
634
  }
589
635
 
590
636
 
591
637
  /* char_codespan • '`' parsing a code span (assuming codespan != 0) */
592
638
  static size_t
593
- char_codespan(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
639
+ char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
594
640
  {
595
641
  size_t end, nb = 0, i, f_begin, f_end;
596
642
 
@@ -610,20 +656,20 @@ char_codespan(struct buf *ob, struct render *rndr, char *data, size_t offset, si
610
656
 
611
657
  /* trimming outside whitespaces */
612
658
  f_begin = nb;
613
- while (f_begin < end && (data[f_begin] == ' ' || data[f_begin] == '\t'))
659
+ while (f_begin < end && data[f_begin] == ' ')
614
660
  f_begin++;
615
661
 
616
662
  f_end = end - nb;
617
- while (f_end > nb && (data[f_end-1] == ' ' || data[f_end-1] == '\t'))
663
+ while (f_end > nb && data[f_end-1] == ' ')
618
664
  f_end--;
619
665
 
620
666
  /* real code span */
621
667
  if (f_begin < f_end) {
622
- struct buf work = { data + f_begin, f_end - f_begin, 0, 0, 0 };
623
- if (!rndr->make.codespan(ob, &work, rndr->make.opaque))
668
+ struct buf work = { data + f_begin, f_end - f_begin, 0, 0 };
669
+ if (!rndr->cb.codespan(ob, &work, rndr->opaque))
624
670
  end = 0;
625
671
  } else {
626
- if (!rndr->make.codespan(ob, 0, rndr->make.opaque))
672
+ if (!rndr->cb.codespan(ob, 0, rndr->opaque))
627
673
  end = 0;
628
674
  }
629
675
 
@@ -633,19 +679,19 @@ char_codespan(struct buf *ob, struct render *rndr, char *data, size_t offset, si
633
679
 
634
680
  /* char_escape • '\\' backslash escape */
635
681
  static size_t
636
- char_escape(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
682
+ char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
637
683
  {
638
- static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>";
639
- struct buf work = { 0, 0, 0, 0, 0 };
684
+ static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~";
685
+ struct buf work = { 0, 0, 0, 0 };
640
686
 
641
687
  if (size > 1) {
642
688
  if (strchr(escape_chars, data[1]) == NULL)
643
689
  return 0;
644
690
 
645
- if (rndr->make.normal_text) {
691
+ if (rndr->cb.normal_text) {
646
692
  work.data = data + 1;
647
693
  work.size = 1;
648
- rndr->make.normal_text(ob, &work, rndr->make.opaque);
694
+ rndr->cb.normal_text(ob, &work, rndr->opaque);
649
695
  }
650
696
  else bufputc(ob, data[1]);
651
697
  }
@@ -656,10 +702,10 @@ char_escape(struct buf *ob, struct render *rndr, char *data, size_t offset, size
656
702
  /* char_entity • '&' escaped when it doesn't belong to an entity */
657
703
  /* valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */
658
704
  static size_t
659
- char_entity(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
705
+ char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
660
706
  {
661
707
  size_t end = 1;
662
- struct buf work;
708
+ struct buf work = { 0, 0, 0, 0 };
663
709
 
664
710
  if (end < size && data[end] == '#')
665
711
  end++;
@@ -668,14 +714,14 @@ char_entity(struct buf *ob, struct render *rndr, char *data, size_t offset, size
668
714
  end++;
669
715
 
670
716
  if (end < size && data[end] == ';')
671
- end += 1; /* real entity */
717
+ end++; /* real entity */
672
718
  else
673
719
  return 0; /* lone '&' */
674
720
 
675
- if (rndr->make.entity) {
721
+ if (rndr->cb.entity) {
676
722
  work.data = data;
677
723
  work.size = end;
678
- rndr->make.entity(ob, &work, rndr->make.opaque);
724
+ rndr->cb.entity(ob, &work, rndr->opaque);
679
725
  }
680
726
  else bufput(ob, data, end);
681
727
 
@@ -684,24 +730,24 @@ char_entity(struct buf *ob, struct render *rndr, char *data, size_t offset, size
684
730
 
685
731
  /* char_langle_tag • '<' when tags or autolinks are allowed */
686
732
  static size_t
687
- char_langle_tag(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
733
+ char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
688
734
  {
689
735
  enum mkd_autolink altype = MKDA_NOT_AUTOLINK;
690
736
  size_t end = tag_length(data, size, &altype);
691
- struct buf work = { data, end, 0, 0, 0 };
737
+ struct buf work = { data, end, 0, 0 };
692
738
  int ret = 0;
693
739
 
694
740
  if (end > 2) {
695
- if (rndr->make.autolink && altype != MKDA_NOT_AUTOLINK) {
741
+ if (rndr->cb.autolink && altype != MKDA_NOT_AUTOLINK) {
696
742
  struct buf *u_link = rndr_newbuf(rndr, BUFFER_SPAN);
697
743
  work.data = data + 1;
698
744
  work.size = end - 2;
699
745
  unscape_text(u_link, &work);
700
- ret = rndr->make.autolink(ob, u_link, altype, rndr->make.opaque);
746
+ ret = rndr->cb.autolink(ob, u_link, altype, rndr->opaque);
701
747
  rndr_popbuf(rndr, BUFFER_SPAN);
702
748
  }
703
- else if (rndr->make.raw_html_tag)
704
- ret = rndr->make.raw_html_tag(ob, &work, rndr->make.opaque);
749
+ else if (rndr->cb.raw_html_tag)
750
+ ret = rndr->cb.raw_html_tag(ob, &work, rndr->opaque);
705
751
  }
706
752
 
707
753
  if (!ret) return 0;
@@ -709,23 +755,30 @@ char_langle_tag(struct buf *ob, struct render *rndr, char *data, size_t offset,
709
755
  }
710
756
 
711
757
  static size_t
712
- char_autolink_www(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
758
+ char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
713
759
  {
714
- struct buf *link, *link_url;
760
+ struct buf *link, *link_url, *link_text;
715
761
  size_t link_len, rewind;
716
762
 
717
- if (!rndr->make.link)
763
+ if (!rndr->cb.link || rndr->in_link_body)
718
764
  return 0;
719
765
 
720
766
  link = rndr_newbuf(rndr, BUFFER_SPAN);
721
767
 
722
- if ((link_len = ups_autolink__www(&rewind, link, data, offset, size)) > 0) {
768
+ if ((link_len = sd_autolink__www(&rewind, link, data, offset, size)) > 0) {
723
769
  link_url = rndr_newbuf(rndr, BUFFER_SPAN);
724
770
  BUFPUTSL(link_url, "http://");
725
771
  bufput(link_url, link->data, link->size);
726
772
 
727
773
  ob->size -= rewind;
728
- rndr->make.link(ob, link_url, NULL, link, rndr->make.opaque);
774
+ if (rndr->cb.normal_text) {
775
+ link_text = rndr_newbuf(rndr, BUFFER_SPAN);
776
+ rndr->cb.normal_text(link_text, link, rndr->opaque);
777
+ rndr->cb.link(ob, link_url, NULL, link_text, rndr->opaque);
778
+ rndr_popbuf(rndr, BUFFER_SPAN);
779
+ } else {
780
+ rndr->cb.link(ob, link_url, NULL, link, rndr->opaque);
781
+ }
729
782
  rndr_popbuf(rndr, BUFFER_SPAN);
730
783
  }
731
784
 
@@ -734,19 +787,19 @@ char_autolink_www(struct buf *ob, struct render *rndr, char *data, size_t offset
734
787
  }
735
788
 
736
789
  static size_t
737
- char_autolink_email(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
790
+ char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
738
791
  {
739
792
  struct buf *link;
740
793
  size_t link_len, rewind;
741
794
 
742
- if (!rndr->make.autolink)
795
+ if (!rndr->cb.autolink || rndr->in_link_body)
743
796
  return 0;
744
797
 
745
798
  link = rndr_newbuf(rndr, BUFFER_SPAN);
746
799
 
747
- if ((link_len = ups_autolink__email(&rewind, link, data, offset, size)) > 0) {
800
+ if ((link_len = sd_autolink__email(&rewind, link, data, offset, size)) > 0) {
748
801
  ob->size -= rewind;
749
- rndr->make.autolink(ob, link, MKDA_EMAIL, rndr->make.opaque);
802
+ rndr->cb.autolink(ob, link, MKDA_EMAIL, rndr->opaque);
750
803
  }
751
804
 
752
805
  rndr_popbuf(rndr, BUFFER_SPAN);
@@ -754,19 +807,19 @@ char_autolink_email(struct buf *ob, struct render *rndr, char *data, size_t offs
754
807
  }
755
808
 
756
809
  static size_t
757
- char_autolink_url(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
810
+ char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
758
811
  {
759
812
  struct buf *link;
760
813
  size_t link_len, rewind;
761
814
 
762
- if (!rndr->make.autolink)
815
+ if (!rndr->cb.autolink || rndr->in_link_body)
763
816
  return 0;
764
817
 
765
818
  link = rndr_newbuf(rndr, BUFFER_SPAN);
766
819
 
767
- if ((link_len = ups_autolink__url(&rewind, link, data, offset, size)) > 0) {
820
+ if ((link_len = sd_autolink__url(&rewind, link, data, offset, size)) > 0) {
768
821
  ob->size -= rewind;
769
- rndr->make.autolink(ob, link, MKDA_NORMAL, rndr->make.opaque);
822
+ rndr->cb.autolink(ob, link, MKDA_NORMAL, rndr->opaque);
770
823
  }
771
824
 
772
825
  rndr_popbuf(rndr, BUFFER_SPAN);
@@ -775,7 +828,7 @@ char_autolink_url(struct buf *ob, struct render *rndr, char *data, size_t offset
775
828
 
776
829
  /* char_link • '[': parsing a link or an image */
777
830
  static size_t
778
- char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
831
+ char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
779
832
  {
780
833
  int is_img = (offset && data[-1] == '!'), level;
781
834
  size_t i = 1, txt_e, link_b = 0, link_e = 0, title_b = 0, title_e = 0;
@@ -785,13 +838,14 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
785
838
  struct buf *u_link = 0;
786
839
  size_t org_work_size = rndr->work_bufs[BUFFER_SPAN].size;
787
840
  int text_has_nl = 0, ret = 0;
841
+ int in_title = 0, qtype = 0;
788
842
 
789
843
  /* checking whether the correct renderer exists */
790
- if ((is_img && !rndr->make.image) || (!is_img && !rndr->make.link))
844
+ if ((is_img && !rndr->cb.image) || (!is_img && !rndr->cb.link))
791
845
  goto cleanup;
792
846
 
793
847
  /* looking for the matching closing bracket */
794
- for (level = 1; i < size; i += 1) {
848
+ for (level = 1; i < size; i++) {
795
849
  if (data[i] == '\n')
796
850
  text_has_nl = 1;
797
851
 
@@ -812,19 +866,19 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
812
866
  goto cleanup;
813
867
 
814
868
  txt_e = i;
815
- i += 1;
869
+ i++;
816
870
 
817
871
  /* skip any amount of whitespace or newline */
818
872
  /* (this is much more laxist than original markdown syntax) */
819
- while (i < size && isspace(data[i]))
873
+ while (i < size && _isspace(data[i]))
820
874
  i++;
821
875
 
822
876
  /* inline style link */
823
877
  if (i < size && data[i] == '(') {
824
878
  /* skipping initial whitespace */
825
- i += 1;
879
+ i++;
826
880
 
827
- while (i < size && isspace(data[i]))
881
+ while (i < size && _isspace(data[i]))
828
882
  i++;
829
883
 
830
884
  link_b = i;
@@ -833,7 +887,7 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
833
887
  while (i < size) {
834
888
  if (data[i] == '\\') i += 2;
835
889
  else if (data[i] == ')' || data[i] == '\'' || data[i] == '"') break;
836
- else i += 1;
890
+ else i++;
837
891
  }
838
892
 
839
893
  if (i >= size) goto cleanup;
@@ -841,20 +895,23 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
841
895
 
842
896
  /* looking for title end if present */
843
897
  if (data[i] == '\'' || data[i] == '"') {
898
+ qtype = data[i];
899
+ in_title = 1;
844
900
  i++;
845
901
  title_b = i;
846
902
 
847
903
  while (i < size) {
848
904
  if (data[i] == '\\') i += 2;
849
- else if (data[i] == ')') break;
850
- else i += 1;
905
+ else if (data[i] == qtype) {in_title = 0; i++;}
906
+ else if ((data[i] == ')') && !in_title) break;
907
+ else i++;
851
908
  }
852
909
 
853
910
  if (i >= size) goto cleanup;
854
911
 
855
912
  /* skipping whitespaces after title */
856
913
  title_e = i - 1;
857
- while (title_e > title_b && isspace(data[title_e]))
914
+ while (title_e > title_b && _isspace(data[title_e]))
858
915
  title_e--;
859
916
 
860
917
  /* checking for closing quote presence */
@@ -865,7 +922,7 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
865
922
  }
866
923
 
867
924
  /* remove whitespace at the end of the link */
868
- while (link_e > link_b && isspace(data[link_e - 1]))
925
+ while (link_e > link_b && _isspace(data[link_e - 1]))
869
926
  link_e--;
870
927
 
871
928
  /* remove optional angle brackets around the link */
@@ -888,11 +945,11 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
888
945
 
889
946
  /* reference style link */
890
947
  else if (i < size && data[i] == '[') {
891
- struct buf id = { 0, 0, 0, 0, 0 };
948
+ struct buf id = { 0, 0, 0, 0 };
892
949
  struct link_ref *lr;
893
950
 
894
951
  /* looking for the id */
895
- i += 1;
952
+ i++;
896
953
  link_b = i;
897
954
  while (i < size && data[i] != ']') i++;
898
955
  if (i >= size) goto cleanup;
@@ -922,18 +979,19 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
922
979
  id.size = link_e - link_b;
923
980
  }
924
981
 
925
- lr = arr_sorted_find(&rndr->refs, &id, cmp_link_ref);
926
- if (!lr) goto cleanup;
982
+ lr = find_link_ref(rndr->refs, id.data, id.size);
983
+ if (!lr)
984
+ goto cleanup;
927
985
 
928
986
  /* keeping link and title from link_ref */
929
987
  link = lr->link;
930
988
  title = lr->title;
931
- i += 1;
989
+ i++;
932
990
  }
933
991
 
934
992
  /* shortcut reference style link */
935
993
  else {
936
- struct buf id = { 0, 0, 0, 0, 0 };
994
+ struct buf id = { 0, 0, 0, 0 };
937
995
  struct link_ref *lr;
938
996
 
939
997
  /* crafting the id */
@@ -956,8 +1014,9 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
956
1014
  }
957
1015
 
958
1016
  /* finding the link_ref */
959
- lr = arr_sorted_find(&rndr->refs, &id, cmp_link_ref);
960
- if (!lr) goto cleanup;
1017
+ lr = find_link_ref(rndr->refs, id.data, id.size);
1018
+ if (!lr)
1019
+ goto cleanup;
961
1020
 
962
1021
  /* keeping link and title from link_ref */
963
1022
  link = lr->link;
@@ -970,8 +1029,15 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
970
1029
  /* building content: img alt is escaped, link content is parsed */
971
1030
  if (txt_e > 1) {
972
1031
  content = rndr_newbuf(rndr, BUFFER_SPAN);
973
- if (is_img) bufput(content, data + 1, txt_e - 1);
974
- else parse_inline(content, rndr, data + 1, txt_e - 1);
1032
+ if (is_img) {
1033
+ bufput(content, data + 1, txt_e - 1);
1034
+ } else {
1035
+ /* disable autolinking when parsing inline the
1036
+ * content of a link */
1037
+ rndr->in_link_body = 1;
1038
+ parse_inline(content, rndr, data + 1, txt_e - 1);
1039
+ rndr->in_link_body = 0;
1040
+ }
975
1041
  }
976
1042
 
977
1043
  if (link) {
@@ -984,9 +1050,9 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
984
1050
  if (ob->size && ob->data[ob->size - 1] == '!')
985
1051
  ob->size -= 1;
986
1052
 
987
- ret = rndr->make.image(ob, u_link, title, content, rndr->make.opaque);
1053
+ ret = rndr->cb.image(ob, u_link, title, content, rndr->opaque);
988
1054
  } else {
989
- ret = rndr->make.link(ob, u_link, title, content, rndr->make.opaque);
1055
+ ret = rndr->cb.link(ob, u_link, title, content, rndr->opaque);
990
1056
  }
991
1057
 
992
1058
  /* cleanup */
@@ -995,7 +1061,43 @@ cleanup:
995
1061
  return ret ? i : 0;
996
1062
  }
997
1063
 
1064
+ static size_t
1065
+ char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
1066
+ {
1067
+ size_t sup_start, sup_len;
1068
+ struct buf *sup;
1069
+
1070
+ if (!rndr->cb.superscript)
1071
+ return 0;
1072
+
1073
+ if (size < 2)
1074
+ return 0;
1075
+
1076
+ if (data[1] == '(') {
1077
+ sup_start = sup_len = 2;
998
1078
 
1079
+ while (sup_len < size && data[sup_len] != ')' && data[sup_len - 1] != '\\')
1080
+ sup_len++;
1081
+
1082
+ if (sup_len == size)
1083
+ return 0;
1084
+ } else {
1085
+ sup_start = sup_len = 1;
1086
+
1087
+ while (sup_len < size && !_isspace(data[sup_len]))
1088
+ sup_len++;
1089
+ }
1090
+
1091
+ if (sup_len - sup_start == 0)
1092
+ return (sup_start == 2) ? 3 : 0;
1093
+
1094
+ sup = rndr_newbuf(rndr, BUFFER_SPAN);
1095
+ parse_inline(sup, rndr, data + sup_start, sup_len - sup_start);
1096
+ rndr->cb.superscript(ob, sup, rndr->opaque);
1097
+ rndr_popbuf(rndr, BUFFER_SPAN);
1098
+
1099
+ return (sup_start == 2) ? sup_len + 1 : sup_len;
1100
+ }
999
1101
 
1000
1102
  /*********************************
1001
1103
  * BLOCK-LEVEL PARSING FUNCTIONS *
@@ -1003,28 +1105,31 @@ cleanup:
1003
1105
 
1004
1106
  /* is_empty • returns the line length when it is empty, 0 otherwise */
1005
1107
  static size_t
1006
- is_empty(char *data, size_t size)
1108
+ is_empty(uint8_t *data, size_t size)
1007
1109
  {
1008
1110
  size_t i;
1009
- for (i = 0; i < size && data[i] != '\n'; i += 1)
1010
- if (data[i] != ' ' && data[i] != '\t') return 0;
1111
+
1112
+ for (i = 0; i < size && data[i] != '\n'; i++)
1113
+ if (data[i] != ' ')
1114
+ return 0;
1115
+
1011
1116
  return i + 1;
1012
1117
  }
1013
1118
 
1014
1119
  /* is_hrule • returns whether a line is a horizontal rule */
1015
1120
  static int
1016
- is_hrule(char *data, size_t size)
1121
+ is_hrule(uint8_t *data, size_t size)
1017
1122
  {
1018
1123
  size_t i = 0, n = 0;
1019
- char c;
1124
+ uint8_t c;
1020
1125
 
1021
1126
  /* skipping initial spaces */
1022
1127
  if (size < 3) return 0;
1023
- if (data[0] == ' ') { i += 1;
1024
- if (data[1] == ' ') { i += 1;
1025
- if (data[2] == ' ') { i += 1; } } }
1128
+ if (data[0] == ' ') { i++;
1129
+ if (data[1] == ' ') { i++;
1130
+ if (data[2] == ' ') { i++; } } }
1026
1131
 
1027
- /* looking at the hrule char */
1132
+ /* looking at the hrule uint8_t */
1028
1133
  if (i + 2 >= size
1029
1134
  || (data[i] != '*' && data[i] != '-' && data[i] != '_'))
1030
1135
  return 0;
@@ -1032,34 +1137,36 @@ is_hrule(char *data, size_t size)
1032
1137
 
1033
1138
  /* the whole line must be the char or whitespace */
1034
1139
  while (i < size && data[i] != '\n') {
1035
- if (data[i] == c) n += 1;
1036
- else if (data[i] != ' ' && data[i] != '\t')
1140
+ if (data[i] == c) n++;
1141
+ else if (data[i] != ' ')
1037
1142
  return 0;
1038
- i += 1; }
1143
+
1144
+ i++;
1145
+ }
1039
1146
 
1040
1147
  return n >= 3;
1041
1148
  }
1042
1149
 
1043
1150
  /* check if a line is a code fence; return its size if it is */
1044
1151
  static size_t
1045
- is_codefence(char *data, size_t size, struct buf *syntax)
1152
+ is_codefence(uint8_t *data, size_t size, struct buf *syntax)
1046
1153
  {
1047
1154
  size_t i = 0, n = 0;
1048
- char c;
1155
+ uint8_t c;
1049
1156
 
1050
1157
  /* skipping initial spaces */
1051
1158
  if (size < 3) return 0;
1052
- if (data[0] == ' ') { i += 1;
1053
- if (data[1] == ' ') { i += 1;
1054
- if (data[2] == ' ') { i += 1; } } }
1159
+ if (data[0] == ' ') { i++;
1160
+ if (data[1] == ' ') { i++;
1161
+ if (data[2] == ' ') { i++; } } }
1055
1162
 
1056
- /* looking at the hrule char */
1163
+ /* looking at the hrule uint8_t */
1057
1164
  if (i + 2 >= size || !(data[i] == '~' || data[i] == '`'))
1058
1165
  return 0;
1059
1166
 
1060
1167
  c = data[i];
1061
1168
 
1062
- /* the whole line must be the char or whitespace */
1169
+ /* the whole line must be the uint8_t or whitespace */
1063
1170
  while (i < size && data[i] == c) {
1064
1171
  n++; i++;
1065
1172
  }
@@ -1070,7 +1177,7 @@ is_codefence(char *data, size_t size, struct buf *syntax)
1070
1177
  if (syntax != NULL) {
1071
1178
  size_t syn = 0;
1072
1179
 
1073
- while (i < size && (data[i] == ' ' || data[i] == '\t'))
1180
+ while (i < size && data[i] == ' ')
1074
1181
  i++;
1075
1182
 
1076
1183
  syntax->data = data + i;
@@ -1087,16 +1194,16 @@ is_codefence(char *data, size_t size, struct buf *syntax)
1087
1194
 
1088
1195
  /* strip all whitespace at the beginning and the end
1089
1196
  * of the {} block */
1090
- while (syn > 0 && isspace(syntax->data[0])) {
1197
+ while (syn > 0 && _isspace(syntax->data[0])) {
1091
1198
  syntax->data++; syn--;
1092
1199
  }
1093
1200
 
1094
- while (syn > 0 && isspace(syntax->data[syn - 1]))
1201
+ while (syn > 0 && _isspace(syntax->data[syn - 1]))
1095
1202
  syn--;
1096
1203
 
1097
1204
  i++;
1098
1205
  } else {
1099
- while (i < size && !isspace(data[i])) {
1206
+ while (i < size && !_isspace(data[i])) {
1100
1207
  syn++; i++;
1101
1208
  }
1102
1209
  }
@@ -1105,7 +1212,7 @@ is_codefence(char *data, size_t size, struct buf *syntax)
1105
1212
  }
1106
1213
 
1107
1214
  while (i < size && data[i] != '\n') {
1108
- if (!isspace(data[i]))
1215
+ if (!_isspace(data[i]))
1109
1216
  return 0;
1110
1217
 
1111
1218
  i++;
@@ -1116,7 +1223,7 @@ is_codefence(char *data, size_t size, struct buf *syntax)
1116
1223
 
1117
1224
  /* is_atxheader • returns whether the line is a hash-prefixed header */
1118
1225
  static int
1119
- is_atxheader(struct render *rndr, char *data, size_t size)
1226
+ is_atxheader(struct sd_markdown *rndr, uint8_t *data, size_t size)
1120
1227
  {
1121
1228
  if (data[0] != '#')
1122
1229
  return 0;
@@ -1127,7 +1234,7 @@ is_atxheader(struct render *rndr, char *data, size_t size)
1127
1234
  while (level < size && level < 6 && data[level] == '#')
1128
1235
  level++;
1129
1236
 
1130
- if (level < size && data[level] != ' ' && data[level] != '\t')
1237
+ if (level < size && data[level] != ' ')
1131
1238
  return 0;
1132
1239
  }
1133
1240
 
@@ -1136,92 +1243,126 @@ is_atxheader(struct render *rndr, char *data, size_t size)
1136
1243
 
1137
1244
  /* is_headerline • returns whether the line is a setext-style hdr underline */
1138
1245
  static int
1139
- is_headerline(char *data, size_t size)
1246
+ is_headerline(uint8_t *data, size_t size)
1140
1247
  {
1141
1248
  size_t i = 0;
1142
1249
 
1143
1250
  /* test of level 1 header */
1144
1251
  if (data[i] == '=') {
1145
- for (i = 1; i < size && data[i] == '='; i += 1);
1146
- while (i < size && (data[i] == ' ' || data[i] == '\t')) i += 1;
1252
+ for (i = 1; i < size && data[i] == '='; i++);
1253
+ while (i < size && data[i] == ' ') i++;
1147
1254
  return (i >= size || data[i] == '\n') ? 1 : 0; }
1148
1255
 
1149
1256
  /* test of level 2 header */
1150
1257
  if (data[i] == '-') {
1151
- for (i = 1; i < size && data[i] == '-'; i += 1);
1152
- while (i < size && (data[i] == ' ' || data[i] == '\t')) i += 1;
1258
+ for (i = 1; i < size && data[i] == '-'; i++);
1259
+ while (i < size && data[i] == ' ') i++;
1153
1260
  return (i >= size || data[i] == '\n') ? 2 : 0; }
1154
1261
 
1155
1262
  return 0;
1156
1263
  }
1157
1264
 
1265
+ static int
1266
+ is_next_headerline(uint8_t *data, size_t size)
1267
+ {
1268
+ size_t i = 0;
1269
+
1270
+ while (i < size && data[i] != '\n')
1271
+ i++;
1272
+
1273
+ if (++i >= size)
1274
+ return 0;
1275
+
1276
+ return is_headerline(data + i, size - i);
1277
+ }
1278
+
1158
1279
  /* prefix_quote • returns blockquote prefix length */
1159
1280
  static size_t
1160
- prefix_quote(char *data, size_t size)
1281
+ prefix_quote(uint8_t *data, size_t size)
1161
1282
  {
1162
1283
  size_t i = 0;
1163
- if (i < size && data[i] == ' ') i += 1;
1164
- if (i < size && data[i] == ' ') i += 1;
1165
- if (i < size && data[i] == ' ') i += 1;
1284
+ if (i < size && data[i] == ' ') i++;
1285
+ if (i < size && data[i] == ' ') i++;
1286
+ if (i < size && data[i] == ' ') i++;
1287
+
1166
1288
  if (i < size && data[i] == '>') {
1167
- if (i + 1 < size && (data[i + 1] == ' ' || data[i+1] == '\t'))
1289
+ if (i + 1 < size && data[i + 1] == ' ')
1168
1290
  return i + 2;
1169
- else return i + 1; }
1170
- else return 0;
1291
+
1292
+ return i + 1;
1293
+ }
1294
+
1295
+ return 0;
1171
1296
  }
1172
1297
 
1173
1298
  /* prefix_code • returns prefix length for block code*/
1174
1299
  static size_t
1175
- prefix_code(char *data, size_t size)
1300
+ prefix_code(uint8_t *data, size_t size)
1176
1301
  {
1177
- if (size > 0 && data[0] == '\t') return 1;
1178
1302
  if (size > 3 && data[0] == ' ' && data[1] == ' '
1179
- && data[2] == ' ' && data[3] == ' ') return 4;
1303
+ && data[2] == ' ' && data[3] == ' ') return 4;
1304
+
1180
1305
  return 0;
1181
1306
  }
1182
1307
 
1183
1308
  /* prefix_oli • returns ordered list item prefix */
1184
1309
  static size_t
1185
- prefix_oli(char *data, size_t size)
1310
+ prefix_oli(uint8_t *data, size_t size)
1186
1311
  {
1187
1312
  size_t i = 0;
1188
- if (i < size && data[i] == ' ') i += 1;
1189
- if (i < size && data[i] == ' ') i += 1;
1190
- if (i < size && data[i] == ' ') i += 1;
1191
- if (i >= size || data[i] < '0' || data[i] > '9') return 0;
1192
- while (i < size && data[i] >= '0' && data[i] <= '9') i += 1;
1193
- if (i + 1 >= size || data[i] != '.'
1194
- || (data[i + 1] != ' ' && data[i + 1] != '\t')) return 0;
1313
+
1314
+ if (i < size && data[i] == ' ') i++;
1315
+ if (i < size && data[i] == ' ') i++;
1316
+ if (i < size && data[i] == ' ') i++;
1317
+
1318
+ if (i >= size || data[i] < '0' || data[i] > '9')
1319
+ return 0;
1320
+
1321
+ while (i < size && data[i] >= '0' && data[i] <= '9')
1322
+ i++;
1323
+
1324
+ if (i + 1 >= size || data[i] != '.' || data[i + 1] != ' ')
1325
+ return 0;
1326
+
1327
+ if (is_next_headerline(data + i, size - i))
1328
+ return 0;
1329
+
1195
1330
  return i + 2;
1196
1331
  }
1197
1332
 
1198
1333
  /* prefix_uli • returns ordered list item prefix */
1199
1334
  static size_t
1200
- prefix_uli(char *data, size_t size)
1335
+ prefix_uli(uint8_t *data, size_t size)
1201
1336
  {
1202
1337
  size_t i = 0;
1203
- if (i < size && data[i] == ' ') i += 1;
1204
- if (i < size && data[i] == ' ') i += 1;
1205
- if (i < size && data[i] == ' ') i += 1;
1206
- if (i + 1 >= size
1207
- || (data[i] != '*' && data[i] != '+' && data[i] != '-')
1208
- || (data[i + 1] != ' ' && data[i + 1] != '\t'))
1338
+
1339
+ if (i < size && data[i] == ' ') i++;
1340
+ if (i < size && data[i] == ' ') i++;
1341
+ if (i < size && data[i] == ' ') i++;
1342
+
1343
+ if (i + 1 >= size ||
1344
+ (data[i] != '*' && data[i] != '+' && data[i] != '-') ||
1345
+ data[i + 1] != ' ')
1209
1346
  return 0;
1347
+
1348
+ if (is_next_headerline(data + i, size - i))
1349
+ return 0;
1350
+
1210
1351
  return i + 2;
1211
1352
  }
1212
1353
 
1213
1354
 
1214
- /* parse_block • parsing of one block, returning next char to parse */
1215
- static void parse_block(struct buf *ob, struct render *rndr,
1216
- char *data, size_t size);
1355
+ /* parse_block • parsing of one block, returning next uint8_t to parse */
1356
+ static void parse_block(struct buf *ob, struct sd_markdown *rndr,
1357
+ uint8_t *data, size_t size);
1217
1358
 
1218
1359
 
1219
1360
  /* parse_blockquote • handles parsing of a blockquote fragment */
1220
1361
  static size_t
1221
- parse_blockquote(struct buf *ob, struct render *rndr, char *data, size_t size)
1362
+ parse_blockquote(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
1222
1363
  {
1223
1364
  size_t beg, end = 0, pre, work_size = 0;
1224
- char *work_data = 0;
1365
+ uint8_t *work_data = 0;
1225
1366
  struct buf *out = 0;
1226
1367
 
1227
1368
  out = rndr_newbuf(rndr, BUFFER_BLOCK);
@@ -1252,22 +1393,22 @@ parse_blockquote(struct buf *ob, struct render *rndr, char *data, size_t size)
1252
1393
  }
1253
1394
 
1254
1395
  parse_block(out, rndr, work_data, work_size);
1255
- if (rndr->make.blockquote)
1256
- rndr->make.blockquote(ob, out, rndr->make.opaque);
1396
+ if (rndr->cb.blockquote)
1397
+ rndr->cb.blockquote(ob, out, rndr->opaque);
1257
1398
  rndr_popbuf(rndr, BUFFER_BLOCK);
1258
1399
  return end;
1259
1400
  }
1260
1401
 
1261
1402
  static size_t
1262
- parse_htmlblock(struct buf *ob, struct render *rndr, char *data, size_t size, int do_render);
1403
+ parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render);
1263
1404
 
1264
1405
  /* parse_blockquote • handles parsing of a regular paragraph */
1265
1406
  static size_t
1266
- parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size)
1407
+ parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
1267
1408
  {
1268
1409
  size_t i = 0, end = 0;
1269
1410
  int level = 0;
1270
- struct buf work = { data, 0, 0, 0, 0 }; /* volatile working buffer */
1411
+ struct buf work = { data, 0, 0, 0 };
1271
1412
 
1272
1413
  while (i < size) {
1273
1414
  for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */;
@@ -1276,7 +1417,7 @@ parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size)
1276
1417
  break;
1277
1418
 
1278
1419
  if (rndr->ext_flags & MKDEXT_LAX_HTML_BLOCKS) {
1279
- if (data[i] == '<' && rndr->make.blockhtml && parse_htmlblock(ob, rndr, data + i, size - i, 0)) {
1420
+ if (data[i] == '<' && rndr->cb.blockhtml && parse_htmlblock(ob, rndr, data + i, size - i, 0)) {
1280
1421
  end = i;
1281
1422
  break;
1282
1423
  }
@@ -1297,8 +1438,8 @@ parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size)
1297
1438
  if (!level) {
1298
1439
  struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK);
1299
1440
  parse_inline(tmp, rndr, work.data, work.size);
1300
- if (rndr->make.paragraph)
1301
- rndr->make.paragraph(ob, tmp, rndr->make.opaque);
1441
+ if (rndr->cb.paragraph)
1442
+ rndr->cb.paragraph(ob, tmp, rndr->opaque);
1302
1443
  rndr_popbuf(rndr, BUFFER_BLOCK);
1303
1444
  } else {
1304
1445
  struct buf *header_work;
@@ -1319,8 +1460,8 @@ parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size)
1319
1460
  struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK);
1320
1461
  parse_inline(tmp, rndr, work.data, work.size);
1321
1462
 
1322
- if (rndr->make.paragraph)
1323
- rndr->make.paragraph(ob, tmp, rndr->make.opaque);
1463
+ if (rndr->cb.paragraph)
1464
+ rndr->cb.paragraph(ob, tmp, rndr->opaque);
1324
1465
 
1325
1466
  rndr_popbuf(rndr, BUFFER_BLOCK);
1326
1467
  work.data += beg;
@@ -1332,8 +1473,8 @@ parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size)
1332
1473
  header_work = rndr_newbuf(rndr, BUFFER_SPAN);
1333
1474
  parse_inline(header_work, rndr, work.data, work.size);
1334
1475
 
1335
- if (rndr->make.header)
1336
- rndr->make.header(ob, header_work, (int)level, rndr->make.opaque);
1476
+ if (rndr->cb.header)
1477
+ rndr->cb.header(ob, header_work, (int)level, rndr->opaque);
1337
1478
 
1338
1479
  rndr_popbuf(rndr, BUFFER_SPAN);
1339
1480
  }
@@ -1343,11 +1484,11 @@ parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size)
1343
1484
 
1344
1485
  /* parse_fencedcode • handles parsing of a block-level code fragment */
1345
1486
  static size_t
1346
- parse_fencedcode(struct buf *ob, struct render *rndr, char *data, size_t size)
1487
+ parse_fencedcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
1347
1488
  {
1348
1489
  size_t beg, end;
1349
1490
  struct buf *work = 0;
1350
- struct buf lang = { 0, 0, 0, 0, 0 };
1491
+ struct buf lang = { 0, 0, 0, 0 };
1351
1492
 
1352
1493
  beg = is_codefence(data, size, &lang);
1353
1494
  if (beg == 0) return 0;
@@ -1363,7 +1504,7 @@ parse_fencedcode(struct buf *ob, struct render *rndr, char *data, size_t size)
1363
1504
  break;
1364
1505
  }
1365
1506
 
1366
- for (end = beg + 1; end < size && data[end - 1] != '\n'; end += 1);
1507
+ for (end = beg + 1; end < size && data[end - 1] != '\n'; end++);
1367
1508
 
1368
1509
  if (beg < end) {
1369
1510
  /* verbatim copy to the working buffer,
@@ -1378,15 +1519,15 @@ parse_fencedcode(struct buf *ob, struct render *rndr, char *data, size_t size)
1378
1519
  if (work->size && work->data[work->size - 1] != '\n')
1379
1520
  bufputc(work, '\n');
1380
1521
 
1381
- if (rndr->make.blockcode)
1382
- rndr->make.blockcode(ob, work, lang.size ? &lang : NULL, rndr->make.opaque);
1522
+ if (rndr->cb.blockcode)
1523
+ rndr->cb.blockcode(ob, work, lang.size ? &lang : NULL, rndr->opaque);
1383
1524
 
1384
1525
  rndr_popbuf(rndr, BUFFER_BLOCK);
1385
1526
  return beg;
1386
1527
  }
1387
1528
 
1388
1529
  static size_t
1389
- parse_blockcode(struct buf *ob, struct render *rndr, char *data, size_t size)
1530
+ parse_blockcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
1390
1531
  {
1391
1532
  size_t beg, end, pre;
1392
1533
  struct buf *work = 0;
@@ -1419,8 +1560,8 @@ parse_blockcode(struct buf *ob, struct render *rndr, char *data, size_t size)
1419
1560
 
1420
1561
  bufputc(work, '\n');
1421
1562
 
1422
- if (rndr->make.blockcode)
1423
- rndr->make.blockcode(ob, work, NULL, rndr->make.opaque);
1563
+ if (rndr->cb.blockcode)
1564
+ rndr->cb.blockcode(ob, work, NULL, rndr->opaque);
1424
1565
 
1425
1566
  rndr_popbuf(rndr, BUFFER_BLOCK);
1426
1567
  return beg;
@@ -1429,11 +1570,12 @@ parse_blockcode(struct buf *ob, struct render *rndr, char *data, size_t size)
1429
1570
  /* parse_listitem • parsing of a single list item */
1430
1571
  /* assuming initial prefix is already removed */
1431
1572
  static size_t
1432
- parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int *flags)
1573
+ parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int *flags)
1433
1574
  {
1434
1575
  struct buf *work = 0, *inter = 0;
1435
1576
  size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i;
1436
1577
  int in_empty = 0, has_inside_empty = 0;
1578
+ int has_next_uli, has_next_oli;
1437
1579
 
1438
1580
  /* keeping track of the first indentation prefix */
1439
1581
  while (orgpre < 3 && orgpre < size && data[orgpre] == ' ')
@@ -1479,12 +1621,21 @@ parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int
1479
1621
  i++;
1480
1622
 
1481
1623
  pre = i;
1482
- if (data[beg] == '\t') { i = 1; pre = 8; }
1624
+
1625
+ has_next_uli = prefix_uli(data + beg + i, end - beg - i);
1626
+ has_next_oli = prefix_oli(data + beg + i, end - beg - i);
1627
+
1628
+ /* checking for ul/ol switch */
1629
+ if (in_empty && (
1630
+ ((*flags & MKD_LIST_ORDERED) && has_next_uli) ||
1631
+ (!(*flags & MKD_LIST_ORDERED) && has_next_oli)
1632
+ )){
1633
+ *flags |= MKD_LI_END;
1634
+ break; /* the following item must have same list type */
1635
+ }
1483
1636
 
1484
1637
  /* checking for a new item */
1485
- if ((prefix_uli(data + beg + i, end - beg - i) &&
1486
- !is_hrule(data + beg + i, end - beg - i)) ||
1487
- prefix_oli(data + beg + i, end - beg - i)) {
1638
+ if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) {
1488
1639
  if (in_empty)
1489
1640
  has_inside_empty = 1;
1490
1641
 
@@ -1495,7 +1646,7 @@ parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int
1495
1646
  sublist = work->size;
1496
1647
  }
1497
1648
  /* joining only indented stuff after empty lines */
1498
- else if (in_empty && i < 4 && data[beg] != '\t') {
1649
+ else if (in_empty && i < 4) {
1499
1650
  *flags |= MKD_LI_END;
1500
1651
  break;
1501
1652
  }
@@ -1519,7 +1670,7 @@ parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int
1519
1670
  /* intermediate render of block li */
1520
1671
  if (sublist && sublist < work->size) {
1521
1672
  parse_block(inter, rndr, work->data, sublist);
1522
- parse_block(inter, rndr, work->data + sublist, work->size - sublist);
1673
+ parse_block(inter, rndr, work->data + sublist, work->size - sublist);
1523
1674
  }
1524
1675
  else
1525
1676
  parse_block(inter, rndr, work->data, work->size);
@@ -1534,8 +1685,8 @@ parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int
1534
1685
  }
1535
1686
 
1536
1687
  /* render of li itself */
1537
- if (rndr->make.listitem)
1538
- rndr->make.listitem(ob, inter, *flags, rndr->make.opaque);
1688
+ if (rndr->cb.listitem)
1689
+ rndr->cb.listitem(ob, inter, *flags, rndr->opaque);
1539
1690
 
1540
1691
  rndr_popbuf(rndr, BUFFER_SPAN);
1541
1692
  rndr_popbuf(rndr, BUFFER_SPAN);
@@ -1545,7 +1696,7 @@ parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int
1545
1696
 
1546
1697
  /* parse_list • parsing ordered or unordered list block */
1547
1698
  static size_t
1548
- parse_list(struct buf *ob, struct render *rndr, char *data, size_t size, int flags)
1699
+ parse_list(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int flags)
1549
1700
  {
1550
1701
  struct buf *work = 0;
1551
1702
  size_t i = 0, j;
@@ -1560,15 +1711,15 @@ parse_list(struct buf *ob, struct render *rndr, char *data, size_t size, int fla
1560
1711
  break;
1561
1712
  }
1562
1713
 
1563
- if (rndr->make.list)
1564
- rndr->make.list(ob, work, flags, rndr->make.opaque);
1714
+ if (rndr->cb.list)
1715
+ rndr->cb.list(ob, work, flags, rndr->opaque);
1565
1716
  rndr_popbuf(rndr, BUFFER_BLOCK);
1566
1717
  return i;
1567
1718
  }
1568
1719
 
1569
1720
  /* parse_atxheader • parsing of atx-style headers */
1570
1721
  static size_t
1571
- parse_atxheader(struct buf *ob, struct render *rndr, char *data, size_t size)
1722
+ parse_atxheader(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
1572
1723
  {
1573
1724
  size_t level = 0;
1574
1725
  size_t i, end, skip;
@@ -1576,7 +1727,7 @@ parse_atxheader(struct buf *ob, struct render *rndr, char *data, size_t size)
1576
1727
  while (level < size && level < 6 && data[level] == '#')
1577
1728
  level++;
1578
1729
 
1579
- for (i = level; i < size && (data[i] == ' ' || data[i] == '\t'); i++);
1730
+ for (i = level; i < size && data[i] == ' '; i++);
1580
1731
 
1581
1732
  for (end = i; end < size && data[end] != '\n'; end++);
1582
1733
  skip = end;
@@ -1584,7 +1735,7 @@ parse_atxheader(struct buf *ob, struct render *rndr, char *data, size_t size)
1584
1735
  while (end && data[end - 1] == '#')
1585
1736
  end--;
1586
1737
 
1587
- while (end && (data[end - 1] == ' ' || data[end - 1] == '\t'))
1738
+ while (end && data[end - 1] == ' ')
1588
1739
  end--;
1589
1740
 
1590
1741
  if (end > i) {
@@ -1592,8 +1743,8 @@ parse_atxheader(struct buf *ob, struct render *rndr, char *data, size_t size)
1592
1743
 
1593
1744
  parse_inline(work, rndr, data + i, end - i);
1594
1745
 
1595
- if (rndr->make.header)
1596
- rndr->make.header(ob, work, (int)level, rndr->make.opaque);
1746
+ if (rndr->cb.header)
1747
+ rndr->cb.header(ob, work, (int)level, rndr->opaque);
1597
1748
 
1598
1749
  rndr_popbuf(rndr, BUFFER_SPAN);
1599
1750
  }
@@ -1605,20 +1756,18 @@ parse_atxheader(struct buf *ob, struct render *rndr, char *data, size_t size)
1605
1756
  /* htmlblock_end • checking end of HTML block : </tag>[ \t]*\n[ \t*]\n */
1606
1757
  /* returns the length on match, 0 otherwise */
1607
1758
  static size_t
1608
- htmlblock_end(struct html_tag *tag, struct render *rndr, char *data, size_t size)
1759
+ htmlblock_end(const char *tag, size_t tag_len, struct sd_markdown *rndr, uint8_t *data, size_t size)
1609
1760
  {
1610
1761
  size_t i, w;
1611
1762
 
1612
- /* assuming data[0] == '<' && data[1] == '/' already tested */
1613
-
1614
1763
  /* checking if tag is a match */
1615
- if (tag->size + 3 >= size
1616
- || strncasecmp(data + 2, tag->text, tag->size)
1617
- || data[tag->size + 2] != '>')
1764
+ if (tag_len + 3 >= size ||
1765
+ strncasecmp((char *)data + 2, tag, tag_len) != 0 ||
1766
+ data[tag_len + 2] != '>')
1618
1767
  return 0;
1619
1768
 
1620
1769
  /* checking white lines */
1621
- i = tag->size + 3;
1770
+ i = tag_len + 3;
1622
1771
  w = 0;
1623
1772
  if (i < size && (w = is_empty(data + i, size - i)) == 0)
1624
1773
  return 0; /* non-blank after tag */
@@ -1639,16 +1788,23 @@ htmlblock_end(struct html_tag *tag, struct render *rndr, char *data, size_t size
1639
1788
 
1640
1789
  /* parse_htmlblock • parsing of inline HTML block */
1641
1790
  static size_t
1642
- parse_htmlblock(struct buf *ob, struct render *rndr, char *data, size_t size, int do_render)
1791
+ parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render)
1643
1792
  {
1644
1793
  size_t i, j = 0;
1645
- struct html_tag *curtag;
1794
+ const char *curtag = NULL;
1646
1795
  int found;
1647
- struct buf work = { data, 0, 0, 0, 0 };
1796
+ struct buf work = { data, 0, 0, 0 };
1648
1797
 
1649
1798
  /* identification of the opening tag */
1650
- if (size < 2 || data[0] != '<') return 0;
1651
- curtag = find_block_tag(data + 1, size - 1);
1799
+ if (size < 2 || data[0] != '<')
1800
+ return 0;
1801
+
1802
+ i = 1;
1803
+ while (i < size && data[i] != '>' && data[i] != ' ')
1804
+ i++;
1805
+
1806
+ if (i < size)
1807
+ curtag = find_block_tag((char *)data + 1, i - 1);
1652
1808
 
1653
1809
  /* handling of special cases */
1654
1810
  if (!curtag) {
@@ -1667,28 +1823,28 @@ parse_htmlblock(struct buf *ob, struct render *rndr, char *data, size_t size, in
1667
1823
 
1668
1824
  if (j) {
1669
1825
  work.size = i + j;
1670
- if (do_render && rndr->make.blockhtml)
1671
- rndr->make.blockhtml(ob, &work, rndr->make.opaque);
1826
+ if (do_render && rndr->cb.blockhtml)
1827
+ rndr->cb.blockhtml(ob, &work, rndr->opaque);
1672
1828
  return work.size;
1673
- }
1829
+ }
1674
1830
  }
1675
1831
 
1676
1832
  /* HR, which is the only self-closing block tag considered */
1677
1833
  if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) {
1678
1834
  i = 3;
1679
1835
  while (i < size && data[i] != '>')
1680
- i += 1;
1836
+ i++;
1681
1837
 
1682
1838
  if (i + 1 < size) {
1683
- i += 1;
1839
+ i++;
1684
1840
  j = is_empty(data + i, size - i);
1685
1841
  if (j) {
1686
1842
  work.size = i + j;
1687
- if (do_render && rndr->make.blockhtml)
1688
- rndr->make.blockhtml(ob, &work, rndr->make.opaque);
1843
+ if (do_render && rndr->cb.blockhtml)
1844
+ rndr->cb.blockhtml(ob, &work, rndr->opaque);
1689
1845
  return work.size;
1690
1846
  }
1691
- }
1847
+ }
1692
1848
  }
1693
1849
 
1694
1850
  /* no special case recognised */
@@ -1702,42 +1858,53 @@ parse_htmlblock(struct buf *ob, struct render *rndr, char *data, size_t size, in
1702
1858
 
1703
1859
  /* if not found, trying a second pass looking for indented match */
1704
1860
  /* but not if tag is "ins" or "del" (following original Markdown.pl) */
1705
- if (curtag != INS_TAG && curtag != DEL_TAG) {
1861
+ if (strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0) {
1862
+ size_t tag_size = strlen(curtag);
1706
1863
  i = 1;
1707
1864
  while (i < size) {
1708
1865
  i++;
1709
1866
  while (i < size && !(data[i - 1] == '<' && data[i] == '/'))
1710
1867
  i++;
1711
1868
 
1712
- if (i + 2 + curtag->size >= size)
1869
+ if (i + 2 + tag_size >= size)
1713
1870
  break;
1714
1871
 
1715
- j = htmlblock_end(curtag, rndr, data + i - 1, size - i + 1);
1872
+ j = htmlblock_end(curtag, tag_size, rndr, data + i - 1, size - i + 1);
1716
1873
 
1717
1874
  if (j) {
1718
1875
  i += j - 1;
1719
1876
  found = 1;
1720
1877
  break;
1721
1878
  }
1722
- }
1879
+ }
1723
1880
  }
1724
1881
 
1725
1882
  if (!found) return 0;
1726
1883
 
1727
1884
  /* the end of the block has been found */
1728
1885
  work.size = i;
1729
- if (do_render && rndr->make.blockhtml)
1730
- rndr->make.blockhtml(ob, &work, rndr->make.opaque);
1886
+ if (do_render && rndr->cb.blockhtml)
1887
+ rndr->cb.blockhtml(ob, &work, rndr->opaque);
1731
1888
 
1732
1889
  return i;
1733
1890
  }
1734
1891
 
1735
1892
  static void
1736
- parse_table_row(struct buf *ob, struct render *rndr, char *data, size_t size, size_t columns, int *col_data)
1893
+ parse_table_row(
1894
+ struct buf *ob,
1895
+ struct sd_markdown *rndr,
1896
+ uint8_t *data,
1897
+ size_t size,
1898
+ size_t columns,
1899
+ int *col_data,
1900
+ int header_flag)
1737
1901
  {
1738
1902
  size_t i = 0, col;
1739
1903
  struct buf *row_work = 0;
1740
1904
 
1905
+ if (!rndr->cb.table_cell || !rndr->cb.table_row)
1906
+ return;
1907
+
1741
1908
  row_work = rndr_newbuf(rndr, BUFFER_SPAN);
1742
1909
 
1743
1910
  if (i < size && data[i] == '|')
@@ -1749,7 +1916,7 @@ parse_table_row(struct buf *ob, struct render *rndr, char *data, size_t size, si
1749
1916
 
1750
1917
  cell_work = rndr_newbuf(rndr, BUFFER_SPAN);
1751
1918
 
1752
- while (i < size && isspace(data[i]))
1919
+ while (i < size && _isspace(data[i]))
1753
1920
  i++;
1754
1921
 
1755
1922
  cell_start = i;
@@ -1759,31 +1926,34 @@ parse_table_row(struct buf *ob, struct render *rndr, char *data, size_t size, si
1759
1926
 
1760
1927
  cell_end = i - 1;
1761
1928
 
1762
- while (cell_end > cell_start && isspace(data[cell_end]))
1929
+ while (cell_end > cell_start && _isspace(data[cell_end]))
1763
1930
  cell_end--;
1764
1931
 
1765
1932
  parse_inline(cell_work, rndr, data + cell_start, 1 + cell_end - cell_start);
1766
- if (rndr->make.table_cell)
1767
- rndr->make.table_cell(row_work, cell_work, col_data ? col_data[col] : 0, rndr->make.opaque);
1933
+ rndr->cb.table_cell(row_work, cell_work, col_data[col] | header_flag, rndr->opaque);
1768
1934
 
1769
1935
  rndr_popbuf(rndr, BUFFER_SPAN);
1770
1936
  i++;
1771
1937
  }
1772
1938
 
1773
1939
  for (; col < columns; ++col) {
1774
- struct buf empty_cell = {0, 0, 0, 0, 0};
1775
- if (rndr->make.table_cell)
1776
- rndr->make.table_cell(row_work, &empty_cell, col_data ? col_data[col] : 0, rndr->make.opaque);
1940
+ struct buf empty_cell = { 0, 0, 0, 0 };
1941
+ rndr->cb.table_cell(row_work, &empty_cell, col_data[col] | header_flag, rndr->opaque);
1777
1942
  }
1778
1943
 
1779
- if (rndr->make.table_row)
1780
- rndr->make.table_row(ob, row_work, rndr->make.opaque);
1944
+ rndr->cb.table_row(ob, row_work, rndr->opaque);
1781
1945
 
1782
1946
  rndr_popbuf(rndr, BUFFER_SPAN);
1783
1947
  }
1784
1948
 
1785
1949
  static size_t
1786
- parse_table_header(struct buf *ob, struct render *rndr, char *data, size_t size, size_t *columns, int **column_data)
1950
+ parse_table_header(
1951
+ struct buf *ob,
1952
+ struct sd_markdown *rndr,
1953
+ uint8_t *data,
1954
+ size_t size,
1955
+ size_t *columns,
1956
+ int **column_data)
1787
1957
  {
1788
1958
  int pipes;
1789
1959
  size_t i = 0, col, header_end, under_end;
@@ -1819,7 +1989,7 @@ parse_table_header(struct buf *ob, struct render *rndr, char *data, size_t size,
1819
1989
  for (col = 0; col < *columns && i < under_end; ++col) {
1820
1990
  size_t dashes = 0;
1821
1991
 
1822
- while (i < under_end && (data[i] == ' ' || data[i] == '\t'))
1992
+ while (i < under_end && data[i] == ' ')
1823
1993
  i++;
1824
1994
 
1825
1995
  if (data[i] == ':') {
@@ -1836,7 +2006,7 @@ parse_table_header(struct buf *ob, struct render *rndr, char *data, size_t size,
1836
2006
  dashes++;
1837
2007
  }
1838
2008
 
1839
- while (i < under_end && (data[i] == ' ' || data[i] == '\t'))
2009
+ while (i < under_end && data[i] == ' ')
1840
2010
  i++;
1841
2011
 
1842
2012
  if (i < under_end && data[i] != '|')
@@ -1851,12 +2021,23 @@ parse_table_header(struct buf *ob, struct render *rndr, char *data, size_t size,
1851
2021
  if (col < *columns)
1852
2022
  return 0;
1853
2023
 
1854
- parse_table_row(ob, rndr, data, header_end, *columns, *column_data);
2024
+ parse_table_row(
2025
+ ob, rndr, data,
2026
+ header_end,
2027
+ *columns,
2028
+ *column_data,
2029
+ MKD_TABLE_HEADER
2030
+ );
2031
+
1855
2032
  return under_end + 1;
1856
2033
  }
1857
2034
 
1858
2035
  static size_t
1859
- parse_table(struct buf *ob, struct render *rndr, char *data, size_t size)
2036
+ parse_table(
2037
+ struct buf *ob,
2038
+ struct sd_markdown *rndr,
2039
+ uint8_t *data,
2040
+ size_t size)
1860
2041
  {
1861
2042
  size_t i;
1862
2043
 
@@ -1887,12 +2068,20 @@ parse_table(struct buf *ob, struct render *rndr, char *data, size_t size)
1887
2068
  break;
1888
2069
  }
1889
2070
 
1890
- parse_table_row(body_work, rndr, data + row_start, i - row_start, columns, col_data);
2071
+ parse_table_row(
2072
+ body_work,
2073
+ rndr,
2074
+ data + row_start,
2075
+ i - row_start,
2076
+ columns,
2077
+ col_data, 0
2078
+ );
2079
+
1891
2080
  i++;
1892
2081
  }
1893
2082
 
1894
- if (rndr->make.table)
1895
- rndr->make.table(ob, header_work, body_work, rndr->make.opaque);
2083
+ if (rndr->cb.table)
2084
+ rndr->cb.table(ob, header_work, body_work, rndr->opaque);
1896
2085
  }
1897
2086
 
1898
2087
  free(col_data);
@@ -1901,16 +2090,16 @@ parse_table(struct buf *ob, struct render *rndr, char *data, size_t size)
1901
2090
  return i;
1902
2091
  }
1903
2092
 
1904
- /* parse_block • parsing of one block, returning next char to parse */
2093
+ /* parse_block • parsing of one block, returning next uint8_t to parse */
1905
2094
  static void
1906
- parse_block(struct buf *ob, struct render *rndr, char *data, size_t size)
2095
+ parse_block(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
1907
2096
  {
1908
2097
  size_t beg, end, i;
1909
- char *txt_data;
2098
+ uint8_t *txt_data;
1910
2099
  beg = 0;
1911
2100
 
1912
2101
  if (rndr->work_bufs[BUFFER_SPAN].size +
1913
- rndr->work_bufs[BUFFER_BLOCK].size > (int)rndr->max_nesting)
2102
+ rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting)
1914
2103
  return;
1915
2104
 
1916
2105
  while (beg < size) {
@@ -1920,7 +2109,7 @@ parse_block(struct buf *ob, struct render *rndr, char *data, size_t size)
1920
2109
  if (is_atxheader(rndr, txt_data, end))
1921
2110
  beg += parse_atxheader(ob, rndr, txt_data, end);
1922
2111
 
1923
- else if (data[beg] == '<' && rndr->make.blockhtml &&
2112
+ else if (data[beg] == '<' && rndr->cb.blockhtml &&
1924
2113
  (i = parse_htmlblock(ob, rndr, txt_data, end, 1)) != 0)
1925
2114
  beg += i;
1926
2115
 
@@ -1928,8 +2117,8 @@ parse_block(struct buf *ob, struct render *rndr, char *data, size_t size)
1928
2117
  beg += i;
1929
2118
 
1930
2119
  else if (is_hrule(txt_data, end)) {
1931
- if (rndr->make.hrule)
1932
- rndr->make.hrule(ob, rndr->make.opaque);
2120
+ if (rndr->cb.hrule)
2121
+ rndr->cb.hrule(ob, rndr->opaque);
1933
2122
 
1934
2123
  while (beg < size && data[beg] != '\n')
1935
2124
  beg++;
@@ -1970,7 +2159,7 @@ parse_block(struct buf *ob, struct render *rndr, char *data, size_t size)
1970
2159
 
1971
2160
  /* is_ref • returns whether a line is a reference or not */
1972
2161
  static int
1973
- is_ref(char *data, size_t beg, size_t end, size_t *last, struct array *refs)
2162
+ is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs)
1974
2163
  {
1975
2164
  /* int n; */
1976
2165
  size_t i = 0;
@@ -1978,8 +2167,6 @@ is_ref(char *data, size_t beg, size_t end, size_t *last, struct array *refs)
1978
2167
  size_t link_offset, link_end;
1979
2168
  size_t title_offset, title_end;
1980
2169
  size_t line_end;
1981
- struct link_ref *lr;
1982
- /* struct buf id = { 0, 0, 0, 0, 0 }; / * volatile buf for id search */
1983
2170
 
1984
2171
  /* up to 3 optional leading spaces */
1985
2172
  if (beg + 3 >= end) return 0;
@@ -1991,34 +2178,38 @@ is_ref(char *data, size_t beg, size_t end, size_t *last, struct array *refs)
1991
2178
 
1992
2179
  /* id part: anything but a newline between brackets */
1993
2180
  if (data[i] != '[') return 0;
1994
- i += 1;
2181
+ i++;
1995
2182
  id_offset = i;
1996
2183
  while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']')
1997
- i += 1;
2184
+ i++;
1998
2185
  if (i >= end || data[i] != ']') return 0;
1999
2186
  id_end = i;
2000
2187
 
2001
2188
  /* spacer: colon (space | tab)* newline? (space | tab)* */
2002
- i += 1;
2189
+ i++;
2003
2190
  if (i >= end || data[i] != ':') return 0;
2004
- i += 1;
2005
- while (i < end && (data[i] == ' ' || data[i] == '\t')) i += 1;
2191
+ i++;
2192
+ while (i < end && data[i] == ' ') i++;
2006
2193
  if (i < end && (data[i] == '\n' || data[i] == '\r')) {
2007
- i += 1;
2008
- if (i < end && data[i] == '\r' && data[i - 1] == '\n') i += 1; }
2009
- while (i < end && (data[i] == ' ' || data[i] == '\t')) i += 1;
2194
+ i++;
2195
+ if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; }
2196
+ while (i < end && data[i] == ' ') i++;
2010
2197
  if (i >= end) return 0;
2011
2198
 
2012
2199
  /* link: whitespace-free sequence, optionally between angle brackets */
2013
- if (data[i] == '<') i += 1;
2200
+ if (data[i] == '<')
2201
+ i++;
2202
+
2014
2203
  link_offset = i;
2015
- while (i < end && data[i] != ' ' && data[i] != '\t'
2016
- && data[i] != '\n' && data[i] != '\r') i += 1;
2204
+
2205
+ while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r')
2206
+ i++;
2207
+
2017
2208
  if (data[i - 1] == '>') link_end = i - 1;
2018
2209
  else link_end = i;
2019
2210
 
2020
2211
  /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
2021
- while (i < end && (data[i] == ' ' || data[i] == '\t')) i += 1;
2212
+ while (i < end && data[i] == ' ') i++;
2022
2213
  if (i < end && data[i] != '\n' && data[i] != '\r'
2023
2214
  && data[i] != '\'' && data[i] != '"' && data[i] != '(')
2024
2215
  return 0;
@@ -2031,47 +2222,54 @@ is_ref(char *data, size_t beg, size_t end, size_t *last, struct array *refs)
2031
2222
  /* optional (space|tab)* spacer after a newline */
2032
2223
  if (line_end) {
2033
2224
  i = line_end + 1;
2034
- while (i < end && (data[i] == ' ' || data[i] == '\t')) i += 1; }
2225
+ while (i < end && data[i] == ' ') i++; }
2035
2226
 
2036
2227
  /* optional title: any non-newline sequence enclosed in '"()
2037
2228
  alone on its line */
2038
2229
  title_offset = title_end = 0;
2039
2230
  if (i + 1 < end
2040
2231
  && (data[i] == '\'' || data[i] == '"' || data[i] == '(')) {
2041
- i += 1;
2232
+ i++;
2042
2233
  title_offset = i;
2043
2234
  /* looking for EOL */
2044
- while (i < end && data[i] != '\n' && data[i] != '\r') i += 1;
2235
+ while (i < end && data[i] != '\n' && data[i] != '\r') i++;
2045
2236
  if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')
2046
2237
  title_end = i + 1;
2047
2238
  else title_end = i;
2048
2239
  /* stepping back */
2049
2240
  i -= 1;
2050
- while (i > title_offset && (data[i] == ' ' || data[i] == '\t'))
2241
+ while (i > title_offset && data[i] == ' ')
2051
2242
  i -= 1;
2052
2243
  if (i > title_offset
2053
2244
  && (data[i] == '\'' || data[i] == '"' || data[i] == ')')) {
2054
2245
  line_end = title_end;
2055
2246
  title_end = i; } }
2056
- if (!line_end) return 0; /* garbage after the link */
2247
+
2248
+ if (!line_end)
2249
+ return 0; /* garbage after the link */
2057
2250
 
2058
2251
  /* a valid ref has been found, filling-in return structures */
2059
- if (last) *last = line_end;
2060
- if (!refs) return 1;
2061
- lr = arr_item(refs, arr_newitem(refs));
2062
- lr->id = bufnew(id_end - id_offset);
2063
- bufput(lr->id, data + id_offset, id_end - id_offset);
2064
- lr->link = bufnew(link_end - link_offset);
2065
- bufput(lr->link, data + link_offset, link_end - link_offset);
2066
- if (title_end > title_offset) {
2067
- lr->title = bufnew(title_end - title_offset);
2068
- bufput(lr->title, data + title_offset,
2069
- title_end - title_offset); }
2070
- else lr->title = 0;
2071
- return 1;
2252
+ if (last)
2253
+ *last = line_end;
2254
+
2255
+ if (refs) {
2256
+ struct link_ref *ref;
2257
+
2258
+ ref = add_link_ref(refs, data + id_offset, id_end - id_offset);
2259
+
2260
+ ref->link = bufnew(link_end - link_offset);
2261
+ bufput(ref->link, data + link_offset, link_end - link_offset);
2262
+
2263
+ if (title_end > title_offset) {
2264
+ ref->title = bufnew(title_end - title_offset);
2265
+ bufput(ref->title, data + title_offset, title_end - title_offset);
2266
+ }
2267
+ }
2268
+
2269
+ return 1;
2072
2270
  }
2073
2271
 
2074
- static void expand_tabs(struct buf *ob, const char *line, size_t size)
2272
+ static void expand_tabs(struct buf *ob, const uint8_t *line, size_t size)
2075
2273
  {
2076
2274
  size_t i = 0, tab = 0;
2077
2275
 
@@ -2100,132 +2298,153 @@ static void expand_tabs(struct buf *ob, const char *line, size_t size)
2100
2298
  * EXPORTED FUNCTIONS *
2101
2299
  **********************/
2102
2300
 
2103
- /* markdown • parses the input buffer and renders it into the output buffer */
2104
- void
2105
- ups_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer, unsigned int extensions) {
2106
- struct link_ref *lr;
2107
- struct buf *text;
2108
- size_t i, beg, end;
2109
- struct render rndr;
2301
+ struct sd_markdown *
2302
+ sd_markdown_new(
2303
+ unsigned int extensions,
2304
+ size_t max_nesting,
2305
+ const struct sd_callbacks *callbacks,
2306
+ void *opaque)
2307
+ {
2308
+ struct sd_markdown *md = NULL;
2110
2309
 
2111
- /* filling the render structure */
2112
- if (!rndrer)
2113
- return;
2310
+ assert(max_nesting > 0 && callbacks);
2114
2311
 
2115
- text = bufnew(64);
2116
- if (!text)
2117
- return;
2312
+ md = malloc(sizeof(struct sd_markdown));
2313
+ if (!md)
2314
+ return NULL;
2118
2315
 
2119
- /* Preallocate enough space for our buffer to avoid expanding while copying */
2120
- bufgrow(text, ib->size);
2316
+ memcpy(&md->cb, callbacks, sizeof(struct sd_callbacks));
2121
2317
 
2122
- memcpy(&rndr.make, rndrer, sizeof(struct mkd_renderer));
2123
- arr_init(&rndr.refs, sizeof (struct link_ref));
2124
- parr_init(&rndr.work_bufs[BUFFER_BLOCK]);
2125
- parr_init(&rndr.work_bufs[BUFFER_SPAN]);
2318
+ stack_init(&md->work_bufs[BUFFER_BLOCK], 4);
2319
+ stack_init(&md->work_bufs[BUFFER_SPAN], 8);
2126
2320
 
2127
- for (i = 0; i < 256; i += 1)
2128
- rndr.active_char[i] = 0;
2321
+ memset(md->active_char, 0x0, 256);
2129
2322
 
2130
- if (rndr.make.emphasis || rndr.make.double_emphasis || rndr.make.triple_emphasis) {
2131
- rndr.active_char['*'] = MD_CHAR_EMPHASIS;
2132
- rndr.active_char['_'] = MD_CHAR_EMPHASIS;
2323
+ if (md->cb.emphasis || md->cb.double_emphasis || md->cb.triple_emphasis) {
2324
+ md->active_char['*'] = MD_CHAR_EMPHASIS;
2325
+ md->active_char['_'] = MD_CHAR_EMPHASIS;
2133
2326
  if (extensions & MKDEXT_STRIKETHROUGH)
2134
- rndr.active_char['~'] = MD_CHAR_EMPHASIS;
2327
+ md->active_char['~'] = MD_CHAR_EMPHASIS;
2135
2328
  }
2136
2329
 
2137
- if (rndr.make.codespan)
2138
- rndr.active_char['`'] = MD_CHAR_CODESPAN;
2330
+ if (md->cb.codespan)
2331
+ md->active_char['`'] = MD_CHAR_CODESPAN;
2139
2332
 
2140
- if (rndr.make.linebreak)
2141
- rndr.active_char['\n'] = MD_CHAR_LINEBREAK;
2333
+ if (md->cb.linebreak)
2334
+ md->active_char['\n'] = MD_CHAR_LINEBREAK;
2142
2335
 
2143
- if (rndr.make.image || rndr.make.link)
2144
- rndr.active_char['['] = MD_CHAR_LINK;
2336
+ if (md->cb.image || md->cb.link)
2337
+ md->active_char['['] = MD_CHAR_LINK;
2145
2338
 
2146
- rndr.active_char['<'] = MD_CHAR_LANGLE;
2147
- rndr.active_char['\\'] = MD_CHAR_ESCAPE;
2148
- rndr.active_char['&'] = MD_CHAR_ENTITITY;
2339
+ md->active_char['<'] = MD_CHAR_LANGLE;
2340
+ md->active_char['\\'] = MD_CHAR_ESCAPE;
2341
+ md->active_char['&'] = MD_CHAR_ENTITITY;
2149
2342
 
2150
2343
  if (extensions & MKDEXT_AUTOLINK) {
2151
- rndr.active_char[':'] = MD_CHAR_AUTOLINK_URL;
2152
- rndr.active_char['@'] = MD_CHAR_AUTOLINK_EMAIL;
2153
- rndr.active_char['w'] = MD_CHAR_AUTOLINK_WWW;
2344
+ md->active_char[':'] = MD_CHAR_AUTOLINK_URL;
2345
+ md->active_char['@'] = MD_CHAR_AUTOLINK_EMAIL;
2346
+ md->active_char['w'] = MD_CHAR_AUTOLINK_WWW;
2154
2347
  }
2155
2348
 
2349
+ if (extensions & MKDEXT_SUPERSCRIPT)
2350
+ md->active_char['^'] = MD_CHAR_SUPERSCRIPT;
2351
+
2156
2352
  /* Extension data */
2157
- rndr.ext_flags = extensions;
2158
- rndr.max_nesting = 16;
2353
+ md->ext_flags = extensions;
2354
+ md->opaque = opaque;
2355
+ md->max_nesting = max_nesting;
2356
+ md->in_link_body = 0;
2357
+
2358
+ return md;
2359
+ }
2360
+
2361
+ void
2362
+ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md)
2363
+ {
2364
+ static const float MARKDOWN_GROW_FACTOR = 1.4f;
2365
+
2366
+ struct buf *text;
2367
+ size_t beg, end;
2368
+
2369
+ text = bufnew(64);
2370
+ if (!text)
2371
+ return;
2372
+
2373
+ /* Preallocate enough space for our buffer to avoid expanding while copying */
2374
+ bufgrow(text, doc_size);
2375
+
2376
+ /* reset the references table */
2377
+ memset(&md->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));
2159
2378
 
2160
2379
  /* first pass: looking for references, copying everything else */
2161
2380
  beg = 0;
2162
- while (beg < ib->size) /* iterating over lines */
2163
- if (is_ref(ib->data, beg, ib->size, &end, &rndr.refs))
2381
+ while (beg < doc_size) /* iterating over lines */
2382
+ if (is_ref(document, beg, doc_size, &end, md->refs))
2164
2383
  beg = end;
2165
2384
  else { /* skipping to the next line */
2166
2385
  end = beg;
2167
- while (end < ib->size && ib->data[end] != '\n' && ib->data[end] != '\r')
2168
- end += 1;
2386
+ while (end < doc_size && document[end] != '\n' && document[end] != '\r')
2387
+ end++;
2169
2388
 
2170
2389
  /* adding the line body if present */
2171
2390
  if (end > beg)
2172
- expand_tabs(text, ib->data + beg, end - beg);
2391
+ expand_tabs(text, document + beg, end - beg);
2173
2392
 
2174
- while (end < ib->size && (ib->data[end] == '\n' || ib->data[end] == '\r')) {
2393
+ while (end < doc_size && (document[end] == '\n' || document[end] == '\r')) {
2175
2394
  /* add one \n per newline */
2176
- if (ib->data[end] == '\n' || (end + 1 < ib->size && ib->data[end + 1] != '\n'))
2395
+ if (document[end] == '\n' || (end + 1 < doc_size && document[end + 1] != '\n'))
2177
2396
  bufputc(text, '\n');
2178
- end += 1;
2397
+ end++;
2179
2398
  }
2180
2399
 
2181
2400
  beg = end;
2182
2401
  }
2183
2402
 
2184
- /* sorting the reference array */
2185
- if (rndr.refs.size)
2186
- qsort(rndr.refs.base, rndr.refs.size, rndr.refs.unit, cmp_link_ref_sort);
2403
+ /* pre-grow the output buffer to minimize allocations */
2404
+ bufgrow(ob, text->size * MARKDOWN_GROW_FACTOR);
2187
2405
 
2188
2406
  /* second pass: actual rendering */
2189
- if (rndr.make.doc_header)
2190
- rndr.make.doc_header(ob, rndr.make.opaque);
2407
+ if (md->cb.doc_header)
2408
+ md->cb.doc_header(ob, md->opaque);
2191
2409
 
2192
2410
  if (text->size) {
2193
2411
  /* adding a final newline if not already present */
2194
2412
  if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r')
2195
2413
  bufputc(text, '\n');
2196
2414
 
2197
- parse_block(ob, &rndr, text->data, text->size);
2415
+ parse_block(ob, md, text->data, text->size);
2198
2416
  }
2199
2417
 
2200
- if (rndr.make.doc_footer)
2201
- rndr.make.doc_footer(ob, rndr.make.opaque);
2418
+ if (md->cb.doc_footer)
2419
+ md->cb.doc_footer(ob, md->opaque);
2202
2420
 
2203
2421
  /* clean-up */
2204
2422
  bufrelease(text);
2205
- lr = rndr.refs.base;
2206
- for (i = 0; i < (size_t)rndr.refs.size; i += 1) {
2207
- bufrelease(lr[i].id);
2208
- bufrelease(lr[i].link);
2209
- bufrelease(lr[i].title);
2210
- }
2423
+ free_link_refs(md->refs);
2424
+
2425
+ assert(md->work_bufs[BUFFER_SPAN].size == 0);
2426
+ assert(md->work_bufs[BUFFER_BLOCK].size == 0);
2427
+ }
2211
2428
 
2212
- arr_free(&rndr.refs);
2429
+ void
2430
+ sd_markdown_free(struct sd_markdown *md)
2431
+ {
2432
+ size_t i;
2213
2433
 
2214
- assert(rndr.work_bufs[BUFFER_SPAN].size == 0);
2215
- assert(rndr.work_bufs[BUFFER_BLOCK].size == 0);
2434
+ for (i = 0; i < (size_t)md->work_bufs[BUFFER_SPAN].asize; ++i)
2435
+ bufrelease(md->work_bufs[BUFFER_SPAN].item[i]);
2216
2436
 
2217
- for (i = 0; i < (size_t)rndr.work_bufs[BUFFER_SPAN].asize; ++i)
2218
- bufrelease(rndr.work_bufs[BUFFER_SPAN].item[i]);
2437
+ for (i = 0; i < (size_t)md->work_bufs[BUFFER_BLOCK].asize; ++i)
2438
+ bufrelease(md->work_bufs[BUFFER_BLOCK].item[i]);
2219
2439
 
2220
- for (i = 0; i < (size_t)rndr.work_bufs[BUFFER_BLOCK].asize; ++i)
2221
- bufrelease(rndr.work_bufs[BUFFER_BLOCK].item[i]);
2440
+ stack_free(&md->work_bufs[BUFFER_SPAN]);
2441
+ stack_free(&md->work_bufs[BUFFER_BLOCK]);
2222
2442
 
2223
- parr_free(&rndr.work_bufs[BUFFER_SPAN]);
2224
- parr_free(&rndr.work_bufs[BUFFER_BLOCK]);
2443
+ free(md);
2225
2444
  }
2226
2445
 
2227
2446
  void
2228
- ups_version(int *ver_major, int *ver_minor, int *ver_revision)
2447
+ sd_version(int *ver_major, int *ver_minor, int *ver_revision)
2229
2448
  {
2230
2449
  *ver_major = UPSKIRT_VER_MAJOR;
2231
2450
  *ver_minor = UPSKIRT_VER_MINOR;