rdiscount 2.2.0.2 → 2.2.7

Sign up to get free protection for your applications and to get access to all the features.
data/ext/markdown.c CHANGED
@@ -147,12 +147,12 @@ typedef struct _flo {
147
147
  #define floindex(x) (x.i)
148
148
 
149
149
 
150
- static int
150
+ static unsigned int
151
151
  flogetc(FLO *f)
152
152
  {
153
153
  if ( f && f->t ) {
154
154
  if ( f->i < S(f->t->text) )
155
- return T(f->t->text)[f->i++];
155
+ return (unsigned char)T(f->t->text)[f->i++];
156
156
  f->t = f->t->next;
157
157
  f->i = 0;
158
158
  return flogetc(f);
@@ -170,8 +170,10 @@ splitline(Line *t, int cutpoint)
170
170
  tmp->next = t->next;
171
171
  t->next = tmp;
172
172
 
173
- tmp->dle = t->dle;
174
173
  SUFFIX(tmp->text, T(t->text)+cutpoint, S(t->text)-cutpoint);
174
+ EXPAND(tmp->text) = 0;
175
+ S(tmp->text)--;
176
+
175
177
  S(t->text) = cutpoint;
176
178
  }
177
179
  }
@@ -187,7 +189,7 @@ splitline(Line *t, int cutpoint)
187
189
  * types.
188
190
  */
189
191
  static void
190
- checkline(Line *l, DWORD flags)
192
+ checkline(Line *l, mkd_flag_t flags)
191
193
  {
192
194
  int eol, i;
193
195
  int dashes = 0, spaces = 0,
@@ -213,11 +215,11 @@ checkline(Line *l, DWORD flags)
213
215
  switch (c) {
214
216
  case '-': UNLESS_FENCED(dashes = 1); break;
215
217
  case ' ': UNLESS_FENCED(spaces = 1); break;
216
- case '=': equals = 1; break;
218
+ case '=': UNLESS_FENCED(equals = 1); break;
217
219
  case '_': UNLESS_FENCED(underscores = 1); break;
218
220
  case '*': stars = 1; break;
219
221
  default:
220
- if (flags & MKD_FENCEDCODE) {
222
+ if ( is_flag_set(flags, MKD_FENCEDCODE) ) {
221
223
  switch (c) {
222
224
  case '~': if (other) return; is_fence_char = 1; tildes = 1; break;
223
225
  case '`': if (other) return; is_fence_char = 1; backticks = 1; break;
@@ -379,7 +381,7 @@ iscode(Line *t)
379
381
 
380
382
 
381
383
  static inline int
382
- ishr(Line *t, DWORD flags)
384
+ ishr(Line *t, mkd_flag_t flags)
383
385
  {
384
386
  if ( ! (t->flags & CHECKED) )
385
387
  checkline(t, flags);
@@ -391,7 +393,7 @@ ishr(Line *t, DWORD flags)
391
393
 
392
394
 
393
395
  static int
394
- issetext(Line *t, int *htyp, DWORD flags)
396
+ issetext(Line *t, int *htyp, mkd_flag_t flags)
395
397
  {
396
398
  Line *n;
397
399
 
@@ -413,7 +415,7 @@ issetext(Line *t, int *htyp, DWORD flags)
413
415
 
414
416
 
415
417
  static int
416
- ishdr(Line *t, int *htyp, DWORD flags)
418
+ ishdr(Line *t, int *htyp, mkd_flag_t flags)
417
419
  {
418
420
  /* ANY leading `#`'s make this into an ETX header
419
421
  */
@@ -429,7 +431,7 @@ ishdr(Line *t, int *htyp, DWORD flags)
429
431
 
430
432
 
431
433
  static inline int
432
- end_of_block(Line *t, DWORD flags)
434
+ end_of_block(Line *t, mkd_flag_t flags)
433
435
  {
434
436
  int dummy;
435
437
 
@@ -441,9 +443,9 @@ end_of_block(Line *t, DWORD flags)
441
443
 
442
444
 
443
445
  static Line*
444
- is_discount_dt(Line *t, int *clip, DWORD flags)
446
+ is_discount_dt(Line *t, int *clip, mkd_flag_t flags)
445
447
  {
446
- if ( !(flags & MKD_NODLDISCOUNT)
448
+ if ( !is_flag_set(flags, MKD_NODLDISCOUNT)
447
449
  && t
448
450
  && t->next
449
451
  && (S(t->text) > 2)
@@ -470,9 +472,9 @@ is_extra_dd(Line *t)
470
472
 
471
473
 
472
474
  static Line*
473
- is_extra_dt(Line *t, int *clip, DWORD flags)
475
+ is_extra_dt(Line *t, int *clip, mkd_flag_t flags)
474
476
  {
475
- if ( flags & MKD_DLEXTRA
477
+ if ( is_flag_set(flags, MKD_DLEXTRA)
476
478
  && t
477
479
  && t->next && S(t->text) && T(t->text)[0] != '='
478
480
  && T(t->text)[S(t->text)-1] != '=') {
@@ -494,7 +496,7 @@ is_extra_dt(Line *t, int *clip, DWORD flags)
494
496
 
495
497
 
496
498
  static Line*
497
- isdefinition(Line *t, int *clip, int *kind, DWORD flags)
499
+ isdefinition(Line *t, int *clip, int *kind, mkd_flag_t flags)
498
500
  {
499
501
  Line *ret;
500
502
 
@@ -508,7 +510,7 @@ isdefinition(Line *t, int *clip, int *kind, DWORD flags)
508
510
 
509
511
 
510
512
  static int
511
- islist(Line *t, int *clip, DWORD flags, int *list_type)
513
+ islist(Line *t, int *clip, mkd_flag_t flags, int *list_type)
512
514
  {
513
515
  int i, j;
514
516
  char *q;
@@ -516,21 +518,22 @@ islist(Line *t, int *clip, DWORD flags, int *list_type)
516
518
  if ( end_of_block(t, flags) )
517
519
  return 0;
518
520
 
519
- if ( !(flags & (MKD_NODLIST|MKD_STRICT)) && isdefinition(t,clip,list_type,flags) )
521
+ if ( !(is_flag_set(flags, MKD_NODLIST) || is_flag_set(flags, MKD_STRICT))
522
+ && isdefinition(t,clip,list_type,flags) )
520
523
  return DL;
521
524
 
522
525
  if ( strchr("*-+", T(t->text)[t->dle]) && isspace(T(t->text)[t->dle+1]) ) {
523
526
  i = nextnonblank(t, t->dle+1);
524
527
  *clip = (i > 4) ? 4 : i;
525
528
  *list_type = UL;
526
- return AL;
529
+ return is_flag_set(flags, MKD_EXPLICITLIST) ? UL : AL;
527
530
  }
528
531
 
529
532
  if ( (j = nextblank(t,t->dle)) > t->dle ) {
530
533
  if ( T(t->text)[j-1] == '.' ) {
531
534
 
532
- if ( !(flags & (MKD_NOALPHALIST|MKD_STRICT))
533
- && (j == t->dle + 2)
535
+ if ( !(is_flag_set(flags, MKD_NOALPHALIST) || is_flag_set(flags, MKD_STRICT))
536
+ && (j == t->dle + 2)
534
537
  && isalpha(T(t->text)[t->dle]) ) {
535
538
  j = nextnonblank(t,j);
536
539
  *clip = (j > 4) ? 4 : j;
@@ -608,8 +611,7 @@ codeblock(Paragraph *p)
608
611
  Line *t = p->text, *r;
609
612
 
610
613
  for ( ; t; t = r ) {
611
- CLIP(t->text,0,4);
612
- t->dle = mkd_firstnonblank(t);
614
+ __mkd_trim_line(t,4);
613
615
 
614
616
  if ( !( (r = skipempty(t->next)) && iscode(r)) ) {
615
617
  ___mkd_freeLineRange(t,r);
@@ -622,9 +624,9 @@ codeblock(Paragraph *p)
622
624
 
623
625
 
624
626
  static int
625
- iscodefence(Line *r, int size, line_type kind, DWORD flags)
627
+ iscodefence(Line *r, int size, line_type kind, mkd_flag_t flags)
626
628
  {
627
- if ( !(flags & MKD_FENCEDCODE) )
629
+ if ( !is_flag_set(flags, MKD_FENCEDCODE) )
628
630
  return 0;
629
631
 
630
632
  if ( !(r->flags & CHECKED) )
@@ -636,39 +638,40 @@ iscodefence(Line *r, int size, line_type kind, DWORD flags)
636
638
  return (r->kind == chk_tilde || r->kind == chk_backtick) && (r->count >= size);
637
639
  }
638
640
 
641
+
639
642
  static Paragraph *
640
- fencedcodeblock(ParagraphRoot *d, Line **ptr, DWORD flags)
643
+ fencedcodeblock(ParagraphRoot *d, Line **ptr, mkd_flag_t flags)
641
644
  {
642
645
  Line *first, *r;
643
646
  Paragraph *ret;
644
647
 
645
648
  first = (*ptr);
646
-
649
+
647
650
  /* don't allow zero-length code fences
648
- */
651
+ */
649
652
  if ( (first->next == 0) || iscodefence(first->next, first->count, 0, flags) )
650
653
  return 0;
651
654
 
652
655
  /* find the closing fence, discard the fences,
653
- * return a Paragraph with the contents
654
- */
656
+ * return a Paragraph with the contents
657
+ */
655
658
  for ( r = first; r && r->next; r = r->next )
656
659
  if ( iscodefence(r->next, first->count, first->kind, flags) ) {
657
660
  (*ptr) = r->next->next;
658
661
  ret = Pp(d, first->next, CODE);
659
- if (S(first->text) - first->count > 0) {
660
- char *lang_attr = T(first->text) + first->count;
661
- while ( *lang_attr != 0 && *lang_attr == ' ' ) lang_attr++;
662
- ret->lang = strdup(lang_attr);
663
- }
664
- else {
665
- ret->lang = 0;
666
- }
667
- ___mkd_freeLine(first);
668
- ___mkd_freeLine(r->next);
669
- r->next = 0;
670
- return ret;
662
+ if (S(first->text) - first->count > 0) {
663
+ char *lang_attr = T(first->text) + first->count;
664
+ while ( *lang_attr != 0 && *lang_attr == ' ' ) lang_attr++;
665
+ ret->lang = strdup(lang_attr);
666
+ }
667
+ else {
668
+ ret->lang = 0;
671
669
  }
670
+ ___mkd_freeLine(first);
671
+ ___mkd_freeLine(r->next);
672
+ r->next = 0;
673
+ return ret;
674
+ }
672
675
  return 0;
673
676
  }
674
677
 
@@ -692,7 +695,7 @@ centered(Line *first, Line *last)
692
695
 
693
696
 
694
697
  static int
695
- endoftextblock(Line *t, int toplevelblock, DWORD flags)
698
+ endoftextblock(Line *t, int toplevelblock, mkd_flag_t flags)
696
699
  {
697
700
  int z;
698
701
 
@@ -712,7 +715,7 @@ endoftextblock(Line *t, int toplevelblock, DWORD flags)
712
715
 
713
716
 
714
717
  static Line *
715
- textblock(Paragraph *p, int toplevel, DWORD flags)
718
+ textblock(Paragraph *p, int toplevel, mkd_flag_t flags)
716
719
  {
717
720
  Line *t, *next;
718
721
 
@@ -747,12 +750,12 @@ szmarkerclass(char *p)
747
750
  #define iscsschar(c) (isalpha(c) || (c == '-') || (c == '_') )
748
751
 
749
752
  static int
750
- isdivmarker(Line *p, int start, DWORD flags)
753
+ isdivmarker(Line *p, int start, mkd_flag_t flags)
751
754
  {
752
755
  char *s;
753
756
  int last, i;
754
757
 
755
- if ( flags & (MKD_NODIVQUOTE|MKD_STRICT) )
758
+ if ( is_flag_set(flags, MKD_NODIVQUOTE) || is_flag_set(flags, MKD_STRICT) )
756
759
  return 0;
757
760
 
758
761
  start = nextnonblank(p, start);
@@ -786,7 +789,7 @@ isdivmarker(Line *p, int start, DWORD flags)
786
789
  * way the markdown sample web form at Daring Fireball works.
787
790
  */
788
791
  static Line *
789
- quoteblock(Paragraph *p, DWORD flags)
792
+ quoteblock(Paragraph *p, mkd_flag_t flags)
790
793
  {
791
794
  Line *t, *q;
792
795
  int qp;
@@ -803,9 +806,8 @@ quoteblock(Paragraph *p, DWORD flags)
803
806
  /* clip next space, if any */
804
807
  if ( T(t->text)[qp] == ' ' )
805
808
  qp++;
806
- CLIP(t->text, 0, qp);
809
+ __mkd_trim_line(t,qp);
807
810
  UNCHECK(t);
808
- t->dle = mkd_firstnonblank(t);
809
811
  }
810
812
 
811
813
  q = skipempty(t->next);
@@ -847,16 +849,40 @@ typedef int (*linefn)(Line *);
847
849
  * marker, but multiple paragraphs need to start with a 4-space indent.
848
850
  */
849
851
  static Line *
850
- listitem(Paragraph *p, int indent, DWORD flags, linefn check)
852
+ listitem(Paragraph *p, int indent, mkd_flag_t flags, linefn check)
851
853
  {
852
854
  Line *t, *q;
853
855
  int clip = indent;
854
856
  int z;
857
+ #ifdef GITHUB_CHECKBOX
858
+ int firstpara = 1;
859
+ int ischeck;
860
+ #define CHECK_NOT 0
861
+ #define CHECK_NO 1
862
+ #define CHECK_YES 2
863
+ #endif
855
864
 
856
865
  for ( t = p->text; t ; t = q) {
857
- CLIP(t->text, 0, clip);
858
866
  UNCHECK(t);
859
- t->dle = mkd_firstnonblank(t);
867
+ __mkd_trim_line(t, clip);
868
+
869
+ #ifdef GITHUB_CHECKBOX
870
+ if ( firstpara ) {
871
+ ischeck = CHECK_NOT;
872
+ if ( strncmp(T(t->text)+t->dle, "[ ]", 3) == 0 )
873
+ ischeck = CHECK_NO;
874
+ else if ( strncasecmp(T(t->text)+t->dle, "[x]", 3) == 0 )
875
+ ischeck = CHECK_YES;
876
+
877
+ if ( ischeck != CHECK_NOT ) {
878
+ __mkd_trim_line(t, 3);
879
+ p->flags |= GITHUB_CHECK;
880
+ if ( ischeck == CHECK_YES )
881
+ p->flags |= IS_CHECKED;
882
+ }
883
+ firstpara = 0;
884
+ }
885
+ #endif
860
886
 
861
887
  /* even though we had to trim a long leader off this item,
862
888
  * the indent for trailing paragraphs is still 4...
@@ -1005,6 +1031,28 @@ tgood(char c)
1005
1031
  }
1006
1032
 
1007
1033
 
1034
+ /*
1035
+ * eat lines for a markdown extra footnote
1036
+ */
1037
+ static Line *
1038
+ extrablock(Line *p)
1039
+ {
1040
+ Line *np;
1041
+
1042
+ while ( p && p->next ) {
1043
+ np = p->next;
1044
+
1045
+ if ( np->dle < 4 && np->dle < S(np->text) ) {
1046
+ p->next = 0;
1047
+ return np;
1048
+ }
1049
+ __mkd_trim_line(np,4);
1050
+ p = np;
1051
+ }
1052
+ return 0;
1053
+ }
1054
+
1055
+
1008
1056
  /*
1009
1057
  * add a new (image or link) footnote to the footnote table
1010
1058
  */
@@ -1020,20 +1068,31 @@ addfootnote(Line *p, MMIOT* f)
1020
1068
  CREATE(foot->tag);
1021
1069
  CREATE(foot->link);
1022
1070
  CREATE(foot->title);
1071
+ foot->text = 0;
1023
1072
  foot->flags = foot->height = foot->width = 0;
1024
1073
 
1074
+ /* keep the footnote label */
1025
1075
  for (j=i=p->dle+1; T(p->text)[j] != ']'; j++)
1026
1076
  EXPAND(foot->tag) = T(p->text)[j];
1027
-
1028
1077
  EXPAND(foot->tag) = 0;
1029
1078
  S(foot->tag)--;
1079
+
1080
+ /* consume the closing ]: */
1030
1081
  j = nextnonblank(p, j+2);
1031
1082
 
1032
- if ( (f->flags & MKD_EXTRA_FOOTNOTE) && (T(foot->tag)[0] == '^') ) {
1033
- /* need to consume all lines until non-indented block? */
1034
- while ( j < S(p->text) )
1035
- EXPAND(foot->title) = T(p->text)[j++];
1036
- goto skip_to_end;
1083
+ if ( is_flag_set(f->flags, MKD_EXTRA_FOOTNOTE) && (T(foot->tag)[0] == '^') ) {
1084
+ /* markdown extra footnote: All indented lines past this point;
1085
+ * the first line includes the footnote reference, so we need to
1086
+ * snip that out as we go.
1087
+ */
1088
+ foot->flags |= EXTRA_FOOTNOTE;
1089
+ __mkd_trim_line(p,j);
1090
+
1091
+ np = extrablock(p);
1092
+
1093
+ foot->text = compile(p, 0, f);
1094
+
1095
+ return np;
1037
1096
  }
1038
1097
 
1039
1098
  while ( (j < S(p->text)) && !isspace(T(p->text)[j]) )
@@ -1044,8 +1103,7 @@ addfootnote(Line *p, MMIOT* f)
1044
1103
 
1045
1104
  if ( T(p->text)[j] == '=' ) {
1046
1105
  sscanf(T(p->text)+j, "=%dx%d", &foot->width, &foot->height);
1047
- while ( (j < S(p->text)) && !isspace(T(p->text)[j]) )
1048
- ++j;
1106
+ j = nextblank(p, j);
1049
1107
  j = nextnonblank(p,j);
1050
1108
  }
1051
1109
 
@@ -1075,7 +1133,6 @@ addfootnote(Line *p, MMIOT* f)
1075
1133
  --S(foot->title);
1076
1134
  }
1077
1135
 
1078
- skip_to_end:
1079
1136
  ___mkd_freeLine(p);
1080
1137
  return np;
1081
1138
  }
@@ -1113,6 +1170,22 @@ consume(Line *ptr, int *eaten)
1113
1170
  }
1114
1171
 
1115
1172
 
1173
+ typedef ANCHOR(Line) Cache;
1174
+
1175
+ static void
1176
+ uncache(Cache *cache, ParagraphRoot *d, MMIOT *f)
1177
+ {
1178
+ Paragraph *p;
1179
+
1180
+ if ( T(*cache) ) {
1181
+ E(*cache)->next = 0;
1182
+ p = Pp(d, 0, SOURCE);
1183
+ p->down = compile(T(*cache), 1, f);
1184
+ T(*cache) = E(*cache) = 0;
1185
+ }
1186
+ }
1187
+
1188
+
1116
1189
  /*
1117
1190
  * top-level compilation; break the document into
1118
1191
  * style, html, and source blocks with footnote links
@@ -1122,25 +1195,21 @@ static Paragraph *
1122
1195
  compile_document(Line *ptr, MMIOT *f)
1123
1196
  {
1124
1197
  ParagraphRoot d = { 0, 0 };
1125
- ANCHOR(Line) source = { 0, 0 };
1198
+ Cache source = { 0, 0 };
1126
1199
  Paragraph *p = 0;
1127
1200
  struct kw *tag;
1128
1201
  int eaten, unclosed;
1202
+ int previous_was_break = 1;
1129
1203
 
1130
1204
  while ( ptr ) {
1131
- if ( !(f->flags & MKD_NOHTML) && (tag = isopentag(ptr)) ) {
1205
+ if ( !is_flag_set(f->flags, MKD_NOHTML) && (tag = isopentag(ptr)) ) {
1132
1206
  int blocktype;
1133
1207
  /* If we encounter a html/style block, compile and save all
1134
1208
  * of the cached source BEFORE processing the html/style.
1135
1209
  */
1136
- if ( T(source) ) {
1137
- E(source)->next = 0;
1138
- p = Pp(&d, 0, SOURCE);
1139
- p->down = compile(T(source), 1, f);
1140
- T(source) = E(source) = 0;
1141
- }
1210
+ uncache(&source, &d, f);
1142
1211
 
1143
- if ( f->flags & MKD_NOSTYLE )
1212
+ if (is_flag_set(f->flags, MKD_NOSTYLE) )
1144
1213
  blocktype = HTML;
1145
1214
  else
1146
1215
  blocktype = strcmp(tag->id, "STYLE") == 0 ? STYLE : HTML;
@@ -1151,6 +1220,7 @@ compile_document(Line *ptr, MMIOT *f)
1151
1220
  p->down = compile(p->text, 1, f);
1152
1221
  p->text = 0;
1153
1222
  }
1223
+ previous_was_break = 1;
1154
1224
  }
1155
1225
  else if ( isfootnote(ptr) ) {
1156
1226
  /* footnotes, like cats, sleep anywhere; pull them
@@ -1158,23 +1228,27 @@ compile_document(Line *ptr, MMIOT *f)
1158
1228
  * later processing
1159
1229
  */
1160
1230
  ptr = consume(addfootnote(ptr, f), &eaten);
1231
+ previous_was_break = 1;
1232
+ }
1233
+ else if ( previous_was_break && iscodefence(ptr,3,0,f->flags)) {
1234
+ uncache(&source, &d, f);
1235
+ if ( !fencedcodeblock(&d, &ptr, f->flags) ) /* just source */
1236
+ goto attach;
1161
1237
  }
1162
1238
  else {
1239
+ attach:
1163
1240
  /* source; cache it up to wait for eof or the
1164
1241
  * next html/style block
1165
1242
  */
1166
1243
  ATTACH(source,ptr);
1244
+ previous_was_break = blankline(ptr);
1167
1245
  ptr = ptr->next;
1168
1246
  }
1169
1247
  }
1170
- if ( T(source) ) {
1171
- /* if there's any cached source at EOF, compile
1172
- * it now.
1173
- */
1174
- E(source)->next = 0;
1175
- p = Pp(&d, 0, SOURCE);
1176
- p->down = compile(T(source), 1, f);
1177
- }
1248
+ /* if there's any cached source at EOF, compile
1249
+ * it now.
1250
+ */
1251
+ uncache(&source, &d, f);
1178
1252
  return T(d);
1179
1253
  }
1180
1254
 
@@ -1194,7 +1268,7 @@ actually_a_table(MMIOT *f, Line *pp)
1194
1268
  int c;
1195
1269
 
1196
1270
  /* tables need to be turned on */
1197
- if ( f->flags & (MKD_STRICT|MKD_NOTABLES) )
1271
+ if ( is_flag_set(f->flags, MKD_STRICT) || is_flag_set(f->flags, MKD_NOTABLES) )
1198
1272
  return 0;
1199
1273
 
1200
1274
  /* tables need three lines */
@@ -1249,10 +1323,11 @@ compile(Line *ptr, int toplevel, MMIOT *f)
1249
1323
  ptr = consume(ptr, &para);
1250
1324
 
1251
1325
  while ( ptr ) {
1326
+
1252
1327
  if ( iscode(ptr) ) {
1253
1328
  p = Pp(&d, ptr, CODE);
1254
1329
 
1255
- if ( f->flags & MKD_1_COMPAT) {
1330
+ if ( is_flag_set(f->flags, MKD_1_COMPAT) ) {
1256
1331
  /* HORRIBLE STANDARDS KLUDGE: the first line of every block
1257
1332
  * has trailing whitespace trimmed off.
1258
1333
  */
@@ -1290,13 +1365,39 @@ compile(Line *ptr, int toplevel, MMIOT *f)
1290
1365
  ptr = headerblock(p, hdr_type);
1291
1366
  }
1292
1367
  else {
1293
- p = Pp(&d, ptr, MARKUP);
1294
- ptr = textblock(p, toplevel, f->flags);
1295
- /* tables are a special kind of paragraph */
1296
- if ( actually_a_table(f, p->text) )
1297
- p->typ = TABLE;
1368
+ /* either markup or an html block element
1369
+ */
1370
+ struct kw *tag;
1371
+ int unclosed = 1;
1372
+
1373
+ p = Pp(&d, ptr, MARKUP); /* default to regular markup,
1374
+ * then check if it's an html
1375
+ * block. If it IS an html
1376
+ * block, htmlblock() will
1377
+ * populate this paragraph &
1378
+ * all we need to do is reset
1379
+ * the paragraph type to HTML,
1380
+ * otherwise the paragraph
1381
+ * remains empty and ready for
1382
+ * processing with textblock()
1383
+ */
1384
+
1385
+ if ( !is_flag_set(f->flags, MKD_NOHTML) && (tag = isopentag(ptr)) ) {
1386
+ /* possibly an html block
1387
+ */
1388
+
1389
+ ptr = htmlblock(p, tag, &unclosed);
1390
+ if ( ! unclosed ) {
1391
+ p->typ = HTML;
1392
+ }
1393
+ }
1394
+ if ( unclosed ) {
1395
+ ptr = textblock(p, toplevel, f->flags);
1396
+ /* tables are a special kind of paragraph */
1397
+ if ( actually_a_table(f, p->text) )
1398
+ p->typ = TABLE;
1399
+ }
1298
1400
  }
1299
-
1300
1401
  if ( (para||toplevel) && !p->align )
1301
1402
  p->align = PARA;
1302
1403
 
@@ -1321,7 +1422,7 @@ compile(Line *ptr, int toplevel, MMIOT *f)
1321
1422
  * prepare and compile `text`, returning a Paragraph tree.
1322
1423
  */
1323
1424
  int
1324
- mkd_compile(Document *doc, DWORD flags)
1425
+ mkd_compile(Document *doc, mkd_flag_t flags)
1325
1426
  {
1326
1427
  if ( !doc )
1327
1428
  return 0;
@@ -1329,9 +1430,10 @@ mkd_compile(Document *doc, DWORD flags)
1329
1430
  flags &= USER_FLAGS;
1330
1431
 
1331
1432
  if ( doc->compiled ) {
1332
- if ( doc->ctx->flags == flags )
1433
+ if ( doc->ctx->flags == flags && !doc->dirty)
1333
1434
  return 1;
1334
1435
  else {
1436
+ doc->compiled = doc->dirty = 0;
1335
1437
  if ( doc->code)
1336
1438
  ___mkd_freeParagraph(doc->code);
1337
1439
  if ( doc->ctx->footnotes )