rmultimarkdown 6.4.0.3 → 6.7.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.
Files changed (76) hide show
  1. checksums.yaml +5 -5
  2. data/Rakefile +7 -13
  3. data/ext/Makefile +67 -55
  4. data/ext/extconf.rb +8 -6
  5. data/ext/mmd/aho-corasick.c +8 -8
  6. data/ext/mmd/aho-corasick.h +3 -3
  7. data/ext/mmd/argtable3.c +6537 -0
  8. data/ext/mmd/argtable3.h +273 -0
  9. data/ext/mmd/beamer.c +12 -1
  10. data/ext/mmd/char.c +120 -27
  11. data/ext/mmd/char.h +23 -23
  12. data/ext/mmd/critic_markup.c +7 -6
  13. data/ext/mmd/d_string.c +88 -32
  14. data/ext/mmd/{include/d_string.h → d_string.h} +50 -38
  15. data/ext/mmd/epub.c +36 -12
  16. data/ext/mmd/epub.h +2 -2
  17. data/ext/mmd/file.c +50 -40
  18. data/ext/mmd/file.h +2 -2
  19. data/ext/mmd/html.c +164 -99
  20. data/ext/mmd/html.h +3 -2
  21. data/ext/mmd/i18n.h +15 -11
  22. data/ext/mmd/itmz-lexer.c +16978 -0
  23. data/ext/mmd/itmz-lexer.h +132 -0
  24. data/ext/mmd/itmz-parser.c +1189 -0
  25. data/ext/mmd/itmz-parser.h +11 -0
  26. data/ext/mmd/itmz-reader.c +388 -0
  27. data/ext/mmd/itmz-reader.h +111 -0
  28. data/ext/mmd/itmz.c +567 -0
  29. data/ext/mmd/itmz.h +117 -0
  30. data/ext/mmd/latex.c +93 -41
  31. data/ext/mmd/lexer.c +3506 -2774
  32. data/ext/mmd/{include/libMultiMarkdown.h → libMultiMarkdown.h} +49 -2
  33. data/ext/mmd/main.c +612 -0
  34. data/ext/mmd/memoir.c +4 -1
  35. data/ext/mmd/miniz.c +6905 -6680
  36. data/ext/mmd/miniz.h +456 -476
  37. data/ext/mmd/mmd.c +399 -94
  38. data/ext/mmd/mmd.h +25 -25
  39. data/ext/mmd/object_pool.h +3 -3
  40. data/ext/mmd/opendocument-content.c +137 -69
  41. data/ext/mmd/opendocument-content.h +2 -2
  42. data/ext/mmd/opendocument.c +35 -14
  43. data/ext/mmd/opendocument.h +2 -2
  44. data/ext/mmd/opml-lexer.c +259 -637
  45. data/ext/mmd/opml-lexer.h +1 -17
  46. data/ext/mmd/opml-parser.c +194 -188
  47. data/ext/mmd/opml-reader.c +72 -142
  48. data/ext/mmd/opml-reader.h +1 -1
  49. data/ext/mmd/opml.c +13 -13
  50. data/ext/mmd/opml.h +1 -1
  51. data/ext/mmd/parser.c +1623 -1244
  52. data/ext/mmd/rng.c +8 -3
  53. data/ext/mmd/scanners.c +66625 -103198
  54. data/ext/mmd/scanners.h +1 -0
  55. data/ext/mmd/stack.c +62 -20
  56. data/ext/mmd/stack.h +10 -21
  57. data/ext/mmd/textbundle.c +23 -7
  58. data/ext/mmd/textbundle.h +2 -2
  59. data/ext/mmd/token.c +42 -16
  60. data/ext/mmd/{include/token.h → token.h} +22 -8
  61. data/ext/mmd/token_pairs.c +0 -16
  62. data/ext/mmd/transclude.c +6 -2
  63. data/ext/mmd/uthash.h +745 -745
  64. data/ext/mmd/version.h +8 -8
  65. data/ext/mmd/writer.c +225 -63
  66. data/ext/mmd/writer.h +50 -36
  67. data/ext/mmd/xml.c +855 -0
  68. data/ext/mmd/xml.h +134 -0
  69. data/ext/mmd/zip.c +71 -4
  70. data/ext/mmd/zip.h +7 -1
  71. data/ext/ruby_multi_markdown.c +9 -18
  72. data/lib/multi_markdown/version.rb +1 -1
  73. data/lib/multi_markdown.bundle +0 -0
  74. data/rmultimarkdown.gemspec +0 -2
  75. metadata +22 -28
  76. data/ext/mmd/char_lookup.c +0 -212
data/ext/mmd/version.h CHANGED
@@ -1,8 +1,8 @@
1
1
  /*
2
2
 
3
- version.h -- MultiMarkdown
3
+ version.h -- libMultiMarkdown
4
4
 
5
- Copyright © 2016 - 2018 Fletcher T. Penney.
5
+ Copyright © 2016 - 2023 Fletcher T. Penney.
6
6
 
7
7
 
8
8
 
@@ -18,15 +18,15 @@
18
18
  **/
19
19
 
20
20
 
21
- #ifndef FILE_MULTIMARKDOWN_H
22
- #define FILE_MULTIMARKDOWN_H
21
+ #ifndef FILE_LIBMULTIMARKDOWN_H
22
+ #define FILE_LIBMULTIMARKDOWN_H
23
23
 
24
- #define MULTIMARKDOWN_NAME "MultiMarkdown"
24
+ #define LIBMULTIMARKDOWN_NAME "MultiMarkdown"
25
25
 
26
- #define MULTIMARKDOWN_VERSION "6.4.0"
27
- #define MULTIMARKDOWN_COPYRIGHT "Copyright © 2016 - 2018 Fletcher T. Penney."
26
+ #define LIBMULTIMARKDOWN_VERSION "6.7.0"
27
+ #define LIBMULTIMARKDOWN_COPYRIGHT "Copyright © 2016 - 2023 Fletcher T. Penney."
28
28
 
29
- #define MULTIMARKDOWN_LICENSE "\tThe `MultiMarkdown 6` project is released under the MIT License..\n"\
29
+ #define LIBMULTIMARKDOWN_LICENSE "\tThe `MultiMarkdown 6` project is released under the MIT License..\n"\
30
30
  " \n"\
31
31
  " GLibFacade.c and GLibFacade.h are from the MultiMarkdown v4 project:\n"\
32
32
  " \n"\
data/ext/mmd/writer.c CHANGED
@@ -64,6 +64,7 @@
64
64
  #include "char.h"
65
65
  #include "d_string.h"
66
66
  #include "html.h"
67
+ #include "itmz.h"
67
68
  #include "i18n.h"
68
69
  #include "latex.h"
69
70
  #include "memoir.h"
@@ -72,6 +73,7 @@
72
73
  #include "opml.h"
73
74
  #include "parser.h"
74
75
  #include "scanners.h"
76
+ #include "stack.h"
75
77
  #include "token.h"
76
78
  #include "uuid.h"
77
79
  #include "writer.h"
@@ -100,9 +102,9 @@ static char * my_strndup(const char * source, size_t n) {
100
102
  char * result;
101
103
  const char * test = source;
102
104
 
103
- // strlen is too slow is strlen(source) >> n
105
+ // strlen is too slow if strlen(source) >> n
104
106
  for (len = 0; len < n; ++len) {
105
- if (test == '\0') {
107
+ if (*test == '\0') {
106
108
  break;
107
109
  }
108
110
 
@@ -168,6 +170,14 @@ scratch_pad * scratch_pad_new(mmd_engine * e, short format) {
168
170
  p->random_seed_base = 0;
169
171
  }
170
172
 
173
+ if (e->extensions & EXT_RANDOM_LABELS) {
174
+ p->random_seed_base_labels = rand() % 32000;
175
+ } else {
176
+ p->random_seed_base_labels = 0;
177
+ }
178
+
179
+ p->label_counter = 0;
180
+
171
181
  // Store links in a hash for rapid retrieval when exporting
172
182
  p->link_hash = NULL;
173
183
  link * l;
@@ -248,6 +258,8 @@ scratch_pad * scratch_pad_new(mmd_engine * e, short format) {
248
258
  p->asset_hash = NULL;
249
259
  p->store_assets = 0;
250
260
  p->remember_assets = 0;
261
+
262
+ p->critic_stack = e->critic_stack;
251
263
  }
252
264
 
253
265
  return p;
@@ -358,10 +370,12 @@ void print_token_raw(DString * out, const char * source, token * t) {
358
370
  case STRONG_START:
359
371
  case STRONG_STOP:
360
372
  case TEXT_EMPTY:
373
+ case MARKER_BLOCKQUOTE:
361
374
  break;
362
375
 
363
376
  case PAIR_EMPH:
364
377
  case PAIR_STRONG:
378
+ case BLOCK_HTML:
365
379
  print_token_tree_raw(out, source, t->child);
366
380
  break;
367
381
 
@@ -451,14 +465,40 @@ char * label_from_token(const char * source, token * t) {
451
465
  }
452
466
 
453
467
 
454
- char * label_from_header(const char * source, token * t) {
468
+ char * label_from_header(const char * source, token * t, scratch_pad * scratch) {
455
469
  char * result;
470
+ short temp_short;
471
+
456
472
  token * temp_token = manual_label_from_header(t, source);
457
473
 
458
474
  if (temp_token) {
459
475
  result = label_from_token(source, temp_token);
460
476
  } else {
461
- result = label_from_token(source, t);
477
+ if (scratch->extensions & EXT_RANDOM_LABELS) {
478
+ srand(scratch->random_seed_base_labels + scratch->label_counter);
479
+ temp_short = rand() % 32000 + 1;
480
+ result = malloc(sizeof(char) * 6);
481
+ sprintf(result, "%d", temp_short);
482
+
483
+ scratch->label_counter++;
484
+ } else {
485
+ temp_token = token_new(t->type, t->start, t->len);
486
+
487
+ if (t->child && t->child->tail) {
488
+ switch (t->child->tail->type) {
489
+ case MARKER_SETEXT_1:
490
+ case MARKER_SETEXT_2:
491
+ temp_token->len = t->child->tail->start - t->start;
492
+ break;
493
+
494
+ default:
495
+ break;
496
+ }
497
+ }
498
+
499
+ result = label_from_token(source, temp_token);
500
+ token_free(temp_token);
501
+ }
462
502
  }
463
503
 
464
504
  return result;
@@ -466,7 +506,7 @@ char * label_from_header(const char * source, token * t) {
466
506
 
467
507
 
468
508
  /// Clean up whitespace in string for standardization
469
- char * clean_string(const char * str, bool lowercase) {
509
+ char * clean_string(const char * str, bool lowercase, bool url_clean) {
470
510
  if (str == NULL) {
471
511
  return NULL;
472
512
  }
@@ -478,17 +518,19 @@ char * clean_string(const char * str, bool lowercase) {
478
518
  while (*str != '\0') {
479
519
  switch (*str) {
480
520
  case '\\':
481
- switch (*(str + 1)) {
482
- case '\n':
483
- case '\r':
484
- d_string_append_c(out, '\n');
485
- block_whitespace = true;
486
- break;
487
-
488
- default:
489
- d_string_append_c(out, '\\');
490
- block_whitespace = false;
491
- break;
521
+ if (!url_clean) {
522
+ switch (*(str + 1)) {
523
+ case '\n':
524
+ case '\r':
525
+ d_string_append_c(out, '\n');
526
+ block_whitespace = true;
527
+ break;
528
+
529
+ default:
530
+ d_string_append_c(out, '\\');
531
+ block_whitespace = false;
532
+ break;
533
+ }
492
534
  }
493
535
 
494
536
  break;
@@ -504,6 +546,16 @@ char * clean_string(const char * str, bool lowercase) {
504
546
 
505
547
  break;
506
548
 
549
+ case '&':
550
+ if (url_clean) {
551
+ if (strncmp(str, "&amp;", 5) == 0) {
552
+ str += 4;
553
+ }
554
+ }
555
+
556
+ d_string_append_c(out, '&');
557
+ break;
558
+
507
559
  default:
508
560
  if (lowercase) {
509
561
  d_string_append_c(out, tolower(*str));
@@ -540,7 +592,7 @@ char * clean_string_from_range(const char * source, size_t start, size_t len, bo
540
592
 
541
593
  d_string_append_c_array(raw, &source[start], len);
542
594
 
543
- clean = clean_string(raw->str, lowercase);
595
+ clean = clean_string(raw->str, lowercase, false);
544
596
 
545
597
  d_string_free(raw, true);
546
598
 
@@ -556,7 +608,7 @@ char * clean_string_from_token(const char * source, token * t, bool lowercase) {
556
608
  char * clean_inside_pair(const char * source, token * t, bool lowercase) {
557
609
  char * text = text_inside_pair(source, t);
558
610
 
559
- char * clean = clean_string(text, lowercase);
611
+ char * clean = clean_string(text, lowercase, false);
560
612
 
561
613
  free(text);
562
614
 
@@ -616,10 +668,10 @@ attr * parse_attributes(char * source) {
616
668
  a->next = attr_new(key, value);
617
669
  a = a->next;
618
670
  } else {
619
- #ifndef __clang_analyzer__
671
+ #ifndef __clang_analyzer__
620
672
  a = attr_new(key, value);
621
673
  attributes = a;
622
- #endif
674
+ #endif
623
675
  }
624
676
 
625
677
  free(value); // We stored a modified copy
@@ -643,7 +695,7 @@ link * link_new(const char * source, token * label, char * url, char * title, ch
643
695
  l->label_text = NULL;
644
696
  }
645
697
 
646
- l->url = clean_string(url, false);
698
+ l->url = clean_string(url, false, true);
647
699
  l->title = (title == NULL) ? NULL : my_strdup(title);
648
700
  l->attributes = (attributes == NULL) ? NULL : parse_attributes(attributes);
649
701
 
@@ -713,7 +765,7 @@ link * retrieve_link(scratch_pad * scratch, const char * key) {
713
765
  return l;
714
766
  }
715
767
 
716
- char * clean = clean_string(key, true);
768
+ char * clean = clean_string(key, true, false);
717
769
 
718
770
  HASH_FIND_STR(scratch->link_hash, clean, l);
719
771
 
@@ -839,24 +891,44 @@ void store_abbreviation(scratch_pad * scratch, footnote * f) {
839
891
 
840
892
 
841
893
  void link_free(link * l) {
842
- free(l->label_text);
843
- free(l->clean_text);
844
- free(l->url);
845
- free(l->title);
846
- // free(l->id);
847
-
848
- attr * a = l->attributes;
849
- attr * b;
850
-
851
- while (a) {
852
- b = a->next;
853
- free(a->key);
854
- free(a->value);
855
- free(a);
856
- a = b;
894
+ if (l) {
895
+ free(l->label_text);
896
+ free(l->clean_text);
897
+ free(l->url);
898
+ free(l->title);
899
+ // free(l->id);
900
+
901
+ attr * a = l->attributes;
902
+ attr * b;
903
+
904
+ while (a) {
905
+ b = a->next;
906
+ free(a->key);
907
+ free(a->value);
908
+ free(a);
909
+ a = b;
910
+ }
911
+
912
+ free(l);
857
913
  }
914
+ }
858
915
 
859
- free(l);
916
+
917
+ /// Fix single whitespace characters
918
+ void whitespace_fix(token * t, const char * source) {
919
+ while (t) {
920
+ if ((t->type == TEXT_PLAIN) && (t->len == 1)) {
921
+ if (source[t->start] == ' ') {
922
+ t->type = NON_INDENT_SPACE;
923
+ }
924
+ }
925
+
926
+ if (t->child) {
927
+ whitespace_fix(t->child, source);
928
+ }
929
+
930
+ t = t->next;
931
+ }
860
932
  }
861
933
 
862
934
 
@@ -867,7 +939,7 @@ void whitespace_accept(token ** remainder) {
867
939
 
868
940
  /// Find link based on label
869
941
  link * extract_link_from_stack(scratch_pad * scratch, const char * target) {
870
- char * key = clean_string(target, true);
942
+ char * key = clean_string(target, true, false);
871
943
 
872
944
  link * temp = NULL;
873
945
 
@@ -954,7 +1026,7 @@ char * destination_accept(const char * source, token ** remainder, bool validate
954
1026
  }
955
1027
 
956
1028
  // Is this a valid URL?
957
- clean = clean_string(url, false);
1029
+ clean = clean_string(url, false, true);
958
1030
 
959
1031
  if (validate && !validate_url(clean)) {
960
1032
  free(clean);
@@ -992,7 +1064,7 @@ char * url_accept(const char * source, size_t start, size_t max_len, size_t * en
992
1064
 
993
1065
  url = my_strndup(&source[start], scan_len);
994
1066
 
995
- clean = clean_string(url, false);
1067
+ clean = clean_string(url, false, true);
996
1068
 
997
1069
  if (validate && !validate_url(clean)) {
998
1070
  free(clean);
@@ -1130,11 +1202,11 @@ footnote * footnote_new(const char * source, token * label, token * content, boo
1130
1202
  void footnote_free(footnote * f) {
1131
1203
  if (f) {
1132
1204
  if (f->free_para) {
1133
- #ifdef kUseObjectPool
1205
+ #ifdef kUseObjectPool
1134
1206
  // Nothing to do here
1135
- #else
1207
+ #else
1136
1208
  free(f->content);
1137
- #endif
1209
+ #endif
1138
1210
  }
1139
1211
 
1140
1212
  free(f->clean_text);
@@ -1167,22 +1239,24 @@ void meta_set_value(meta * m, const char * value) {
1167
1239
  free(m->value);
1168
1240
  }
1169
1241
 
1170
- m->value = clean_string(value, false);
1242
+ m->value = clean_string(value, false, false);
1171
1243
  }
1172
1244
  }
1173
1245
 
1174
1246
 
1175
1247
  void meta_free(meta * m) {
1176
- free(m->key);
1177
- free(m->value);
1248
+ if (m) {
1249
+ free(m->key);
1250
+ free(m->value);
1178
1251
 
1179
- free(m);
1252
+ free(m);
1253
+ }
1180
1254
  }
1181
1255
 
1182
1256
 
1183
1257
  /// Find metadata based on key
1184
1258
  meta * extract_meta_from_stack(scratch_pad * scratch, const char * target) {
1185
- char * key = clean_string(target, true);
1259
+ char * key = clean_string(target, true, false);
1186
1260
 
1187
1261
  meta * temp = NULL;
1188
1262
 
@@ -1293,6 +1367,7 @@ bool definition_extract(mmd_engine * e, token ** remainder) {
1293
1367
  }
1294
1368
 
1295
1369
  // Skip space
1370
+ whitespace_fix(*remainder, e->dstr->str);
1296
1371
  whitespace_accept(remainder);
1297
1372
 
1298
1373
  // Grab destination
@@ -1408,14 +1483,15 @@ void process_definition_block(mmd_engine * e, token * block) {
1408
1483
  if (f) {
1409
1484
  free(f->label_text);
1410
1485
  f->label_text = f->clean_text;
1411
- }
1412
1486
 
1413
- if (f->content->child &&
1414
- f->content->child->next &&
1415
- f->content->child->next->next) {
1416
- f->clean_text = clean_string_from_range(e->dstr->str, f->content->child->next->next->start, block->start + block->len - f->content->child->next->next->start, false);
1417
- } else {
1418
- f->clean_text = NULL;
1487
+ if (f->content &&
1488
+ f->content->child &&
1489
+ f->content->child->next &&
1490
+ f->content->child->next->next) {
1491
+ f->clean_text = clean_string_from_range(e->dstr->str, f->content->child->next->next->start, block->start + block->len - f->content->child->next->next->start, false);
1492
+ } else {
1493
+ f->clean_text = NULL;
1494
+ }
1419
1495
  }
1420
1496
 
1421
1497
  stack_push(e->abbreviation_stack, f);
@@ -1502,10 +1578,17 @@ token * manual_label_from_header(token * h, const char * source) {
1502
1578
  case MARKER_H4:
1503
1579
  case MARKER_H5:
1504
1580
  case MARKER_H6:
1581
+ case MARKER_SETEXT_1:
1582
+ case MARKER_SETEXT_2:
1505
1583
  walker = walker->prev;
1506
1584
  break;
1507
1585
 
1508
1586
  case TEXT_PLAIN:
1587
+ if (walker->len == 0) {
1588
+ walker = walker->prev;
1589
+ break;
1590
+ }
1591
+
1509
1592
  if (walker->len == 1) {
1510
1593
  if (source[walker->start] == ' ') {
1511
1594
  walker = walker->prev;
@@ -1666,6 +1749,9 @@ void process_metadata_stack(mmd_engine * e, scratch_pad * scratch) {
1666
1749
  } else if (strcmp(temp_char, "fr") == 0) {
1667
1750
  scratch->language = LC_FR;
1668
1751
  scratch->quotes_lang = FRENCH;
1752
+ } else if (strcmp(temp_char, "he") == 0) {
1753
+ scratch->language = LC_HE;
1754
+ scratch->quotes_lang = ENGLISH;
1669
1755
  } else if (strcmp(temp_char, "nl") == 0) {
1670
1756
  scratch->language = LC_NL;
1671
1757
  scratch->quotes_lang = DUTCH;
@@ -1899,6 +1985,10 @@ void mmd_engine_export_token_tree(DString * out, mmd_engine * e, short format) {
1899
1985
 
1900
1986
  break;
1901
1987
 
1988
+ case FORMAT_HTML_WITH_ASSETS:
1989
+ scratch->remember_assets = true;
1990
+ scratch->output_format = FORMAT_HTML;
1991
+
1902
1992
  case FORMAT_HTML:
1903
1993
  if (scratch->extensions & EXT_COMPLETE) {
1904
1994
  mmd_start_complete_html(out, e->dstr->str, scratch);
@@ -1957,11 +2047,18 @@ void mmd_engine_export_token_tree(DString * out, mmd_engine * e, short format) {
1957
2047
  case FORMAT_OPML:
1958
2048
  mmd_export_token_tree_opml(out, e->dstr->str, e->root, scratch);
1959
2049
  break;
2050
+
2051
+ case FORMAT_ITMZ:
2052
+ mmd_export_token_tree_itmz(out, e->dstr->str, e->root, scratch);
2053
+ break;
1960
2054
  }
1961
2055
 
1962
2056
  // Preserve asset_hash for possible use in export
1963
2057
  e->asset_hash = scratch->asset_hash;
1964
2058
 
2059
+ // Preserve random label seed
2060
+ e->random_seed_base_labels = scratch->random_seed_base_labels;
2061
+
1965
2062
  scratch_pad_free(scratch);
1966
2063
  }
1967
2064
 
@@ -2112,7 +2209,7 @@ void mark_abbreviation_as_used(scratch_pad * scratch, footnote * c) {
2112
2209
 
2113
2210
 
2114
2211
  size_t extract_citation_from_stack(scratch_pad * scratch, const char * target) {
2115
- char * key = clean_string(target, true);
2212
+ char * key = clean_string(target, true, false);
2116
2213
 
2117
2214
  fn_holder * h;
2118
2215
 
@@ -2142,7 +2239,7 @@ size_t extract_citation_from_stack(scratch_pad * scratch, const char * target) {
2142
2239
 
2143
2240
 
2144
2241
  size_t extract_footnote_from_stack(scratch_pad * scratch, const char * target) {
2145
- char * key = clean_string(target, true);
2242
+ char * key = clean_string(target, true, false);
2146
2243
 
2147
2244
  fn_holder * h;
2148
2245
 
@@ -2172,7 +2269,7 @@ size_t extract_footnote_from_stack(scratch_pad * scratch, const char * target) {
2172
2269
 
2173
2270
 
2174
2271
  size_t extract_abbreviation_from_stack(scratch_pad * scratch, const char * target) {
2175
- char * key = clean_string(target, false);
2272
+ char * key = clean_string(target, false, false);
2176
2273
 
2177
2274
  fn_holder * h;
2178
2275
 
@@ -2202,7 +2299,7 @@ size_t extract_abbreviation_from_stack(scratch_pad * scratch, const char * targe
2202
2299
 
2203
2300
 
2204
2301
  size_t extract_glossary_from_stack(scratch_pad * scratch, const char * target) {
2205
- char * key = clean_string(target, false);
2302
+ char * key = clean_string(target, false, false);
2206
2303
 
2207
2304
  fn_holder * h;
2208
2305
 
@@ -2494,7 +2591,6 @@ void strip_leading_whitespace(token * chain, const char * source) {
2494
2591
  chain->type = TEXT_EMPTY;
2495
2592
 
2496
2593
  case TEXT_EMPTY:
2497
- chain = chain->next;
2498
2594
  break;
2499
2595
 
2500
2596
  case TEXT_PLAIN:
@@ -2562,6 +2658,10 @@ bool table_has_caption(token * t) {
2562
2658
  /// or
2563
2659
  /// ```` perl
2564
2660
  char * get_fence_language_specifier(token * fence, const char * source) {
2661
+ if (fence == NULL) {
2662
+ return NULL;
2663
+ }
2664
+
2565
2665
  char * result = NULL;
2566
2666
  size_t start = fence->start + fence->len;
2567
2667
  size_t len = 0;
@@ -2609,6 +2709,67 @@ short raw_level_for_header(token * header) {
2609
2709
  }
2610
2710
 
2611
2711
 
2712
+ void header_clean_trailing_whitespace(token * header, const char * source) {
2713
+ token * walker = header->tail;
2714
+ bool done = false;
2715
+
2716
+ while (!done && walker) {
2717
+ switch (walker->type) {
2718
+ case TEXT_PLAIN:
2719
+ token_trim_trailing_whitespace(walker, source);
2720
+
2721
+ if (walker->len) {
2722
+ done = true;
2723
+ }
2724
+
2725
+ break;
2726
+
2727
+ case NON_INDENT_SPACE:
2728
+ case INDENT_SPACE:
2729
+ case INDENT_TAB:
2730
+ walker->type = TEXT_PLAIN;
2731
+
2732
+ case TEXT_NL:
2733
+ case TEXT_NL_SP:
2734
+ case TEXT_LINEBREAK:
2735
+ case TEXT_LINEBREAK_SP:
2736
+ token_trim_trailing_whitespace(walker, source);
2737
+ break;
2738
+
2739
+ case MARKER_H1:
2740
+ case MARKER_H2:
2741
+ case MARKER_H3:
2742
+ case MARKER_H4:
2743
+ case MARKER_H5:
2744
+ case MARKER_H6:
2745
+ case MANUAL_LABEL:
2746
+ break;
2747
+
2748
+ case MARKER_SETEXT_1:
2749
+ case MARKER_SETEXT_2:
2750
+ if (walker->prev) {
2751
+ switch (walker->prev->type) {
2752
+ case TEXT_NL:
2753
+ case TEXT_NL_SP:
2754
+ case TEXT_LINEBREAK:
2755
+ case TEXT_LINEBREAK_SP:
2756
+ walker->prev->type = NON_INDENT_SPACE;
2757
+ break;
2758
+ }
2759
+ }
2760
+
2761
+ break;
2762
+
2763
+ default:
2764
+ done = true;
2765
+ break;
2766
+ }
2767
+
2768
+ walker = walker->prev;
2769
+ }
2770
+ }
2771
+
2772
+
2612
2773
  asset * asset_new(char * url, scratch_pad * scratch) {
2613
2774
  asset * a = malloc(sizeof(asset));
2614
2775
 
@@ -2627,9 +2788,9 @@ void asset_free(asset * a) {
2627
2788
  if (a) {
2628
2789
  free(a->url);
2629
2790
  free(a->asset_path);
2630
- }
2631
2791
 
2632
- free(a);
2792
+ free(a);
2793
+ }
2633
2794
  }
2634
2795
 
2635
2796
 
@@ -2666,6 +2827,7 @@ bool raw_filter_text_matches(char * pattern, short format) {
2666
2827
  } else {
2667
2828
  switch (format) {
2668
2829
  case FORMAT_HTML:
2830
+ case FORMAT_HTML_WITH_ASSETS:
2669
2831
  if (strstr(pattern, "html")) {
2670
2832
  return true;
2671
2833
  }