Fossil SCM

Fix another use-after-realloc bug in handling of inline footnotes which was discovered during fuzzing. Also fix a few other issues revealed via fuzzer.

george 2022-04-21 21:13 markdown-footnotes
Commit c5456211f4652561b6166ee55d7839197b94438d5bd083159cb723056c1986e3
2 files changed +67 -65 +3 -3
+67 -65
--- src/markdown.c
+++ src/markdown.c
@@ -1049,11 +1049,11 @@
10491049
link_e--;
10501050
}
10511051
10521052
/* remove optional angle brackets around the link */
10531053
if( data[link_b]=='<' ) link_b += 1;
1054
- if( data[link_e-1]=='>' ) link_e -= 1;
1054
+ if( data[link_e-1]=='>' ) link_e -= 1; /* TODO: handle link_e == 0 */
10551055
10561056
/* escape backslashed character from link */
10571057
blob_reset(link);
10581058
i = link_b;
10591059
while( i<link_e ){
@@ -1081,17 +1081,18 @@
10811081
struct Blob *title,
10821082
char *data,
10831083
size_t size
10841084
){
10851085
struct link_ref *lr;
1086
+ const size_t sz = blob_size(&rndr->refs);
10861087
10871088
/* find the link from its id (stored temporarily in link) */
10881089
blob_reset(link);
1089
- if( build_ref_id(link, data, size)<0 ) return -1;
1090
+ if( !sz || build_ref_id(link, data, size)<0 ) return -1;
10901091
lr = bsearch(link,
10911092
blob_buffer(&rndr->refs),
1092
- blob_size(&rndr->refs)/sizeof(struct link_ref),
1093
+ sz/sizeof(struct link_ref),
10931094
sizeof (struct link_ref),
10941095
cmp_link_ref);
10951096
if( !lr ) return -1;
10961097
10971098
/* fill the output buffers */
@@ -1102,20 +1103,23 @@
11021103
return 0;
11031104
}
11041105
11051106
/*
11061107
** get_footnote() -- find a footnote by label, invoked during the 2nd pass.
1107
-** On success returns a footnote (after incrementing its nUsed field),
1108
-** otherwise returns NULL.
1108
+** If found then return a shallow copy of the corresponding footnote;
1109
+** otherwise return a shallow copy of rndr->notes.misref.
1110
+** In both cases corresponding `nUsed` field is incremented before return.
11091111
*/
1110
-static const struct footnote* get_footnote(
1112
+static struct footnote get_footnote(
11111113
struct render *rndr,
11121114
const char *data,
11131115
size_t size
11141116
){
1115
- struct footnote *fn = NULL;
1116
- struct Blob *id = new_work_buffer(rndr);
1117
+ struct footnote *fn = 0;
1118
+ struct Blob *id;
1119
+ if( !rndr->notes.nLbled ) goto fallback;
1120
+ id = new_work_buffer(rndr);
11171121
if( build_ref_id(id, data, size)<0 ) goto cleanup;
11181122
fn = bsearch(id, blob_buffer(&rndr->notes.all),
11191123
rndr->notes.nLbled,
11201124
sizeof (struct footnote),
11211125
cmp_link_ref);
@@ -1123,16 +1127,18 @@
11231127
11241128
if( fn->nUsed == 0 ){ /* the first reference to the footnote */
11251129
assert( fn->iMark == 0 );
11261130
fn->iMark = ++(rndr->notes.nMarks);
11271131
}
1128
- fn->nUsed++;
11291132
assert( fn->iMark > 0 );
1130
- assert( fn->nUsed > 0 );
11311133
cleanup:
11321134
release_work_buffer( rndr, id );
1133
- return fn;
1135
+fallback:
1136
+ if( !fn ) fn = &rndr->notes.misref;
1137
+ fn->nUsed++;
1138
+ assert( fn->nUsed > 0 );
1139
+ return *fn;
11341140
}
11351141
11361142
/*
11371143
** Counts characters in the blank prefix within at most nHalfLines.
11381144
** A sequence of spaces and tabs counts as odd halfline,
@@ -1301,11 +1307,11 @@
13011307
const int is_img = (offset && data[-1] == '!');
13021308
size_t i = 1, txt_e;
13031309
struct Blob *content = 0;
13041310
struct Blob *link = 0;
13051311
struct Blob *title = 0;
1306
- const struct footnote *fn = 0;
1312
+ struct footnote fn;
13071313
int ret;
13081314
13091315
/* checking whether the correct renderer exists */
13101316
if( (is_img && !rndr->make.image) || (!is_img && !rndr->make.link) ){
13111317
return 0;
@@ -1314,18 +1320,15 @@
13141320
/* looking for the matching closing bracket */
13151321
txt_e = matching_bracket_offset(data, data+size);
13161322
if( !txt_e ) return 0;
13171323
i = txt_e + 1;
13181324
ret = 0; /* error if we don't get to the callback */
1325
+ fn.nUsed = 0;
13191326
13201327
/* free-standing footnote refernece */
13211328
if(!is_img && size>3 && data[1]=='^'){
13221329
fn = get_footnote(rndr, data+2, txt_e-2);
1323
- if( !fn ) {
1324
- rndr->notes.misref.nUsed++;
1325
- fn = &rndr->notes.misref;
1326
- }
13271330
}else{
13281331
13291332
/* skip "inter-bracket-whitespace" - any amount of whitespace or newline */
13301333
/* (this is much more lax than original markdown syntax) */
13311334
while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; }
@@ -1338,12 +1341,14 @@
13381341
if( i<size && data[i]=='(' ){
13391342
13401343
if( i+2<size && data[i+1]=='^' ){ /* span-bounded inline footnote */
13411344
13421345
const size_t k = matching_bracket_offset(data+i, data+size);
1346
+ const struct footnote *x;
13431347
if( !k ) goto char_link_cleanup;
1344
- fn = add_inline_footnote(rndr, data+(i+2), k-2);
1348
+ x = add_inline_footnote(rndr, data+(i+2), k-2);
1349
+ if( x ) fn = *x;
13451350
i += k+1;
13461351
}else{ /* inline style link */
13471352
size_t span_end = i;
13481353
while( span_end<size
13491354
&& !(data[span_end]==')'
@@ -1378,14 +1383,10 @@
13781383
id_size--;
13791384
}
13801385
}
13811386
if( bFootnote ){
13821387
fn = get_footnote(rndr, id_data, id_size);
1383
- if( !fn ) {
1384
- rndr->notes.misref.nUsed++;
1385
- fn = &rndr->notes.misref;
1386
- }
13871388
}else if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
13881389
goto char_link_cleanup;
13891390
}
13901391
i = id_end+1;
13911392
/* shortcut reference style link */
@@ -1407,14 +1408,14 @@
14071408
if( is_img ){
14081409
if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ){
14091410
ob->nUsed--;
14101411
}
14111412
ret = rndr->make.image(ob, link, title, content, rndr->make.opaque);
1412
- }else if( fn ){
1413
+ }else if( fn.nUsed ){
14131414
if( rndr->make.footnote_ref ){
1414
- ret = rndr->make.footnote_ref(ob, content, &fn->upc, fn->iMark,
1415
- fn->nUsed, rndr->make.opaque);
1415
+ ret = rndr->make.footnote_ref(ob, content, &fn.upc, fn.iMark,
1416
+ fn.nUsed, rndr->make.opaque);
14161417
}
14171418
}else{
14181419
ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
14191420
}
14201421
@@ -2317,11 +2318,11 @@
23172318
size_t beg, end, i;
23182319
char *txt_data;
23192320
int has_table = (rndr->make.table
23202321
&& rndr->make.table_row
23212322
&& rndr->make.table_cell
2322
- && memchr(data, '|', size)!=0);
2323
+ && memchr(data, '|', size)!=0); /* TODO: handle data == 0 */
23232324
23242325
beg = 0;
23252326
while( beg<size ){
23262327
txt_data = data+beg;
23272328
end = size-beg;
@@ -2765,50 +2766,52 @@
27652766
27662767
/* second pass: actual rendering */
27672768
if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque);
27682769
parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text));
27692770
2770
- if( (blob_size(allNotes) || rndr.notes.misref.nUsed) ){
2771
+ if( blob_size(allNotes) || rndr.notes.misref.nUsed ){
27712772
27722773
/* Footnotes must be parsed for the correct discovery of (back)links */
27732774
Blob *notes = new_work_buffer( &rndr );
2774
- Blob *tmp = new_work_buffer( &rndr );
2775
- int nMarks = -1, maxDepth = 5;
2776
-
2777
- /* inline notes may get appended to rndr.notes.all while rendering */
2778
- while(1){
2779
- struct footnote *aNotes;
2780
- const int N = COUNT_FOOTNOTES( allNotes );
2781
-
2782
- /* make a shallow copy of `allNotes` */
2783
- blob_truncate(notes,0);
2784
- blob_appendb(notes, allNotes);
2785
- aNotes = CAST_AS_FOOTNOTES(notes);
2786
- qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort);
2787
-
2788
- if( --maxDepth < 0 || nMarks == rndr.notes.nMarks ) break;
2789
- nMarks = rndr.notes.nMarks;
2790
-
2791
- for(i=0; i<N; i++){
2792
- const int j = aNotes[i].index;
2793
- struct footnote *x = CAST_AS_FOOTNOTES(allNotes) + j;
2794
- assert( 0<=j && j<N );
2795
- if( x->bRndred || !x->nUsed ) continue;
2796
- assert( x->iMark > 0 );
2797
- assert( blob_size(&x->text) );
2798
- blob_truncate(tmp,0);
2799
-
2800
- /* `allNotes` may be altered and extended through this call */
2801
- parse_inline(tmp, &rndr, blob_buffer(&x->text), blob_size(&x->text));
2802
-
2803
- x = CAST_AS_FOOTNOTES(allNotes) + j;
2804
- blob_truncate(&x->text,0);
2805
- blob_appendb(&x->text, tmp);
2806
- x->bRndred = 1;
2807
- }
2808
- }
2809
- release_work_buffer(&rndr,tmp);
2775
+ if( blob_size(allNotes) ){
2776
+ Blob *tmp = new_work_buffer( &rndr );
2777
+ int nMarks = -1, maxDepth = 5;
2778
+
2779
+ /* inline notes may get appended to rndr.notes.all while rendering */
2780
+ while(1){
2781
+ struct footnote *aNotes;
2782
+ const int N = COUNT_FOOTNOTES( allNotes );
2783
+
2784
+ /* make a shallow copy of `allNotes` */
2785
+ blob_truncate(notes,0);
2786
+ blob_appendb(notes, allNotes);
2787
+ aNotes = CAST_AS_FOOTNOTES(notes);
2788
+ qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort);
2789
+
2790
+ if( --maxDepth < 0 || nMarks == rndr.notes.nMarks ) break;
2791
+ nMarks = rndr.notes.nMarks;
2792
+
2793
+ for(i=0; i<N; i++){
2794
+ const int j = aNotes[i].index;
2795
+ struct footnote *x = CAST_AS_FOOTNOTES(allNotes) + j;
2796
+ assert( 0<=j && j<N );
2797
+ if( x->bRndred || !x->nUsed ) continue;
2798
+ assert( x->iMark > 0 );
2799
+ assert( blob_size(&x->text) );
2800
+ blob_truncate(tmp,0);
2801
+
2802
+ /* `allNotes` may be altered and extended through this call */
2803
+ parse_inline(tmp, &rndr, blob_buffer(&x->text), blob_size(&x->text));
2804
+
2805
+ x = CAST_AS_FOOTNOTES(allNotes) + j;
2806
+ blob_truncate(&x->text,0);
2807
+ blob_appendb(&x->text, tmp);
2808
+ x->bRndred = 1;
2809
+ }
2810
+ }
2811
+ release_work_buffer(&rndr,tmp);
2812
+ }
28102813
28112814
/* footnotes rendering */
28122815
if( rndr.make.footnote_item && rndr.make.footnotes ){
28132816
Blob *all_items = new_work_buffer(&rndr);
28142817
int j = -1;
@@ -2815,13 +2818,12 @@
28152818
28162819
/* Assert that the in-memory layout of id, text and upc within
28172820
** footnote struct matches the expectations of html_footnote_item()
28182821
** If it doesn't then a compiler has done something very weird.
28192822
*/
2820
- const struct footnote *dummy = 0;
2821
- assert( &(dummy->id) == &(dummy->text) - 1 );
2822
- assert( &(dummy->upc) == &(dummy->text) + 1 );
2823
+ assert( &(rndr.notes.misref.id) == &(rndr.notes.misref.text) - 1 );
2824
+ assert( &(rndr.notes.misref.upc) == &(rndr.notes.misref.text) + 1 );
28232825
28242826
for(i=0; i<COUNT_FOOTNOTES(notes); i++){
28252827
const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i;
28262828
const int xUsed = x->bRndred ? x->nUsed : 0;
28272829
if( !x->iMark ) break;
28282830
--- src/markdown.c
+++ src/markdown.c
@@ -1049,11 +1049,11 @@
1049 link_e--;
1050 }
1051
1052 /* remove optional angle brackets around the link */
1053 if( data[link_b]=='<' ) link_b += 1;
1054 if( data[link_e-1]=='>' ) link_e -= 1;
1055
1056 /* escape backslashed character from link */
1057 blob_reset(link);
1058 i = link_b;
1059 while( i<link_e ){
@@ -1081,17 +1081,18 @@
1081 struct Blob *title,
1082 char *data,
1083 size_t size
1084 ){
1085 struct link_ref *lr;
 
1086
1087 /* find the link from its id (stored temporarily in link) */
1088 blob_reset(link);
1089 if( build_ref_id(link, data, size)<0 ) return -1;
1090 lr = bsearch(link,
1091 blob_buffer(&rndr->refs),
1092 blob_size(&rndr->refs)/sizeof(struct link_ref),
1093 sizeof (struct link_ref),
1094 cmp_link_ref);
1095 if( !lr ) return -1;
1096
1097 /* fill the output buffers */
@@ -1102,20 +1103,23 @@
1102 return 0;
1103 }
1104
1105 /*
1106 ** get_footnote() -- find a footnote by label, invoked during the 2nd pass.
1107 ** On success returns a footnote (after incrementing its nUsed field),
1108 ** otherwise returns NULL.
 
1109 */
1110 static const struct footnote* get_footnote(
1111 struct render *rndr,
1112 const char *data,
1113 size_t size
1114 ){
1115 struct footnote *fn = NULL;
1116 struct Blob *id = new_work_buffer(rndr);
 
 
1117 if( build_ref_id(id, data, size)<0 ) goto cleanup;
1118 fn = bsearch(id, blob_buffer(&rndr->notes.all),
1119 rndr->notes.nLbled,
1120 sizeof (struct footnote),
1121 cmp_link_ref);
@@ -1123,16 +1127,18 @@
1123
1124 if( fn->nUsed == 0 ){ /* the first reference to the footnote */
1125 assert( fn->iMark == 0 );
1126 fn->iMark = ++(rndr->notes.nMarks);
1127 }
1128 fn->nUsed++;
1129 assert( fn->iMark > 0 );
1130 assert( fn->nUsed > 0 );
1131 cleanup:
1132 release_work_buffer( rndr, id );
1133 return fn;
 
 
 
 
1134 }
1135
1136 /*
1137 ** Counts characters in the blank prefix within at most nHalfLines.
1138 ** A sequence of spaces and tabs counts as odd halfline,
@@ -1301,11 +1307,11 @@
1301 const int is_img = (offset && data[-1] == '!');
1302 size_t i = 1, txt_e;
1303 struct Blob *content = 0;
1304 struct Blob *link = 0;
1305 struct Blob *title = 0;
1306 const struct footnote *fn = 0;
1307 int ret;
1308
1309 /* checking whether the correct renderer exists */
1310 if( (is_img && !rndr->make.image) || (!is_img && !rndr->make.link) ){
1311 return 0;
@@ -1314,18 +1320,15 @@
1314 /* looking for the matching closing bracket */
1315 txt_e = matching_bracket_offset(data, data+size);
1316 if( !txt_e ) return 0;
1317 i = txt_e + 1;
1318 ret = 0; /* error if we don't get to the callback */
 
1319
1320 /* free-standing footnote refernece */
1321 if(!is_img && size>3 && data[1]=='^'){
1322 fn = get_footnote(rndr, data+2, txt_e-2);
1323 if( !fn ) {
1324 rndr->notes.misref.nUsed++;
1325 fn = &rndr->notes.misref;
1326 }
1327 }else{
1328
1329 /* skip "inter-bracket-whitespace" - any amount of whitespace or newline */
1330 /* (this is much more lax than original markdown syntax) */
1331 while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; }
@@ -1338,12 +1341,14 @@
1338 if( i<size && data[i]=='(' ){
1339
1340 if( i+2<size && data[i+1]=='^' ){ /* span-bounded inline footnote */
1341
1342 const size_t k = matching_bracket_offset(data+i, data+size);
 
1343 if( !k ) goto char_link_cleanup;
1344 fn = add_inline_footnote(rndr, data+(i+2), k-2);
 
1345 i += k+1;
1346 }else{ /* inline style link */
1347 size_t span_end = i;
1348 while( span_end<size
1349 && !(data[span_end]==')'
@@ -1378,14 +1383,10 @@
1378 id_size--;
1379 }
1380 }
1381 if( bFootnote ){
1382 fn = get_footnote(rndr, id_data, id_size);
1383 if( !fn ) {
1384 rndr->notes.misref.nUsed++;
1385 fn = &rndr->notes.misref;
1386 }
1387 }else if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
1388 goto char_link_cleanup;
1389 }
1390 i = id_end+1;
1391 /* shortcut reference style link */
@@ -1407,14 +1408,14 @@
1407 if( is_img ){
1408 if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ){
1409 ob->nUsed--;
1410 }
1411 ret = rndr->make.image(ob, link, title, content, rndr->make.opaque);
1412 }else if( fn ){
1413 if( rndr->make.footnote_ref ){
1414 ret = rndr->make.footnote_ref(ob, content, &fn->upc, fn->iMark,
1415 fn->nUsed, rndr->make.opaque);
1416 }
1417 }else{
1418 ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
1419 }
1420
@@ -2317,11 +2318,11 @@
2317 size_t beg, end, i;
2318 char *txt_data;
2319 int has_table = (rndr->make.table
2320 && rndr->make.table_row
2321 && rndr->make.table_cell
2322 && memchr(data, '|', size)!=0);
2323
2324 beg = 0;
2325 while( beg<size ){
2326 txt_data = data+beg;
2327 end = size-beg;
@@ -2765,50 +2766,52 @@
2765
2766 /* second pass: actual rendering */
2767 if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque);
2768 parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text));
2769
2770 if( (blob_size(allNotes) || rndr.notes.misref.nUsed) ){
2771
2772 /* Footnotes must be parsed for the correct discovery of (back)links */
2773 Blob *notes = new_work_buffer( &rndr );
2774 Blob *tmp = new_work_buffer( &rndr );
2775 int nMarks = -1, maxDepth = 5;
2776
2777 /* inline notes may get appended to rndr.notes.all while rendering */
2778 while(1){
2779 struct footnote *aNotes;
2780 const int N = COUNT_FOOTNOTES( allNotes );
2781
2782 /* make a shallow copy of `allNotes` */
2783 blob_truncate(notes,0);
2784 blob_appendb(notes, allNotes);
2785 aNotes = CAST_AS_FOOTNOTES(notes);
2786 qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort);
2787
2788 if( --maxDepth < 0 || nMarks == rndr.notes.nMarks ) break;
2789 nMarks = rndr.notes.nMarks;
2790
2791 for(i=0; i<N; i++){
2792 const int j = aNotes[i].index;
2793 struct footnote *x = CAST_AS_FOOTNOTES(allNotes) + j;
2794 assert( 0<=j && j<N );
2795 if( x->bRndred || !x->nUsed ) continue;
2796 assert( x->iMark > 0 );
2797 assert( blob_size(&x->text) );
2798 blob_truncate(tmp,0);
2799
2800 /* `allNotes` may be altered and extended through this call */
2801 parse_inline(tmp, &rndr, blob_buffer(&x->text), blob_size(&x->text));
2802
2803 x = CAST_AS_FOOTNOTES(allNotes) + j;
2804 blob_truncate(&x->text,0);
2805 blob_appendb(&x->text, tmp);
2806 x->bRndred = 1;
2807 }
2808 }
2809 release_work_buffer(&rndr,tmp);
 
 
2810
2811 /* footnotes rendering */
2812 if( rndr.make.footnote_item && rndr.make.footnotes ){
2813 Blob *all_items = new_work_buffer(&rndr);
2814 int j = -1;
@@ -2815,13 +2818,12 @@
2815
2816 /* Assert that the in-memory layout of id, text and upc within
2817 ** footnote struct matches the expectations of html_footnote_item()
2818 ** If it doesn't then a compiler has done something very weird.
2819 */
2820 const struct footnote *dummy = 0;
2821 assert( &(dummy->id) == &(dummy->text) - 1 );
2822 assert( &(dummy->upc) == &(dummy->text) + 1 );
2823
2824 for(i=0; i<COUNT_FOOTNOTES(notes); i++){
2825 const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i;
2826 const int xUsed = x->bRndred ? x->nUsed : 0;
2827 if( !x->iMark ) break;
2828
--- src/markdown.c
+++ src/markdown.c
@@ -1049,11 +1049,11 @@
1049 link_e--;
1050 }
1051
1052 /* remove optional angle brackets around the link */
1053 if( data[link_b]=='<' ) link_b += 1;
1054 if( data[link_e-1]=='>' ) link_e -= 1; /* TODO: handle link_e == 0 */
1055
1056 /* escape backslashed character from link */
1057 blob_reset(link);
1058 i = link_b;
1059 while( i<link_e ){
@@ -1081,17 +1081,18 @@
1081 struct Blob *title,
1082 char *data,
1083 size_t size
1084 ){
1085 struct link_ref *lr;
1086 const size_t sz = blob_size(&rndr->refs);
1087
1088 /* find the link from its id (stored temporarily in link) */
1089 blob_reset(link);
1090 if( !sz || build_ref_id(link, data, size)<0 ) return -1;
1091 lr = bsearch(link,
1092 blob_buffer(&rndr->refs),
1093 sz/sizeof(struct link_ref),
1094 sizeof (struct link_ref),
1095 cmp_link_ref);
1096 if( !lr ) return -1;
1097
1098 /* fill the output buffers */
@@ -1102,20 +1103,23 @@
1103 return 0;
1104 }
1105
1106 /*
1107 ** get_footnote() -- find a footnote by label, invoked during the 2nd pass.
1108 ** If found then return a shallow copy of the corresponding footnote;
1109 ** otherwise return a shallow copy of rndr->notes.misref.
1110 ** In both cases corresponding `nUsed` field is incremented before return.
1111 */
1112 static struct footnote get_footnote(
1113 struct render *rndr,
1114 const char *data,
1115 size_t size
1116 ){
1117 struct footnote *fn = 0;
1118 struct Blob *id;
1119 if( !rndr->notes.nLbled ) goto fallback;
1120 id = new_work_buffer(rndr);
1121 if( build_ref_id(id, data, size)<0 ) goto cleanup;
1122 fn = bsearch(id, blob_buffer(&rndr->notes.all),
1123 rndr->notes.nLbled,
1124 sizeof (struct footnote),
1125 cmp_link_ref);
@@ -1123,16 +1127,18 @@
1127
1128 if( fn->nUsed == 0 ){ /* the first reference to the footnote */
1129 assert( fn->iMark == 0 );
1130 fn->iMark = ++(rndr->notes.nMarks);
1131 }
 
1132 assert( fn->iMark > 0 );
 
1133 cleanup:
1134 release_work_buffer( rndr, id );
1135 fallback:
1136 if( !fn ) fn = &rndr->notes.misref;
1137 fn->nUsed++;
1138 assert( fn->nUsed > 0 );
1139 return *fn;
1140 }
1141
1142 /*
1143 ** Counts characters in the blank prefix within at most nHalfLines.
1144 ** A sequence of spaces and tabs counts as odd halfline,
@@ -1301,11 +1307,11 @@
1307 const int is_img = (offset && data[-1] == '!');
1308 size_t i = 1, txt_e;
1309 struct Blob *content = 0;
1310 struct Blob *link = 0;
1311 struct Blob *title = 0;
1312 struct footnote fn;
1313 int ret;
1314
1315 /* checking whether the correct renderer exists */
1316 if( (is_img && !rndr->make.image) || (!is_img && !rndr->make.link) ){
1317 return 0;
@@ -1314,18 +1320,15 @@
1320 /* looking for the matching closing bracket */
1321 txt_e = matching_bracket_offset(data, data+size);
1322 if( !txt_e ) return 0;
1323 i = txt_e + 1;
1324 ret = 0; /* error if we don't get to the callback */
1325 fn.nUsed = 0;
1326
1327 /* free-standing footnote refernece */
1328 if(!is_img && size>3 && data[1]=='^'){
1329 fn = get_footnote(rndr, data+2, txt_e-2);
 
 
 
 
1330 }else{
1331
1332 /* skip "inter-bracket-whitespace" - any amount of whitespace or newline */
1333 /* (this is much more lax than original markdown syntax) */
1334 while( i<size && (data[i]==' ' || data[i]=='\t' || data[i]=='\n') ){ i++; }
@@ -1338,12 +1341,14 @@
1341 if( i<size && data[i]=='(' ){
1342
1343 if( i+2<size && data[i+1]=='^' ){ /* span-bounded inline footnote */
1344
1345 const size_t k = matching_bracket_offset(data+i, data+size);
1346 const struct footnote *x;
1347 if( !k ) goto char_link_cleanup;
1348 x = add_inline_footnote(rndr, data+(i+2), k-2);
1349 if( x ) fn = *x;
1350 i += k+1;
1351 }else{ /* inline style link */
1352 size_t span_end = i;
1353 while( span_end<size
1354 && !(data[span_end]==')'
@@ -1378,14 +1383,10 @@
1383 id_size--;
1384 }
1385 }
1386 if( bFootnote ){
1387 fn = get_footnote(rndr, id_data, id_size);
 
 
 
 
1388 }else if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
1389 goto char_link_cleanup;
1390 }
1391 i = id_end+1;
1392 /* shortcut reference style link */
@@ -1407,14 +1408,14 @@
1408 if( is_img ){
1409 if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ){
1410 ob->nUsed--;
1411 }
1412 ret = rndr->make.image(ob, link, title, content, rndr->make.opaque);
1413 }else if( fn.nUsed ){
1414 if( rndr->make.footnote_ref ){
1415 ret = rndr->make.footnote_ref(ob, content, &fn.upc, fn.iMark,
1416 fn.nUsed, rndr->make.opaque);
1417 }
1418 }else{
1419 ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
1420 }
1421
@@ -2317,11 +2318,11 @@
2318 size_t beg, end, i;
2319 char *txt_data;
2320 int has_table = (rndr->make.table
2321 && rndr->make.table_row
2322 && rndr->make.table_cell
2323 && memchr(data, '|', size)!=0); /* TODO: handle data == 0 */
2324
2325 beg = 0;
2326 while( beg<size ){
2327 txt_data = data+beg;
2328 end = size-beg;
@@ -2765,50 +2766,52 @@
2766
2767 /* second pass: actual rendering */
2768 if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque);
2769 parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text));
2770
2771 if( blob_size(allNotes) || rndr.notes.misref.nUsed ){
2772
2773 /* Footnotes must be parsed for the correct discovery of (back)links */
2774 Blob *notes = new_work_buffer( &rndr );
2775 if( blob_size(allNotes) ){
2776 Blob *tmp = new_work_buffer( &rndr );
2777 int nMarks = -1, maxDepth = 5;
2778
2779 /* inline notes may get appended to rndr.notes.all while rendering */
2780 while(1){
2781 struct footnote *aNotes;
2782 const int N = COUNT_FOOTNOTES( allNotes );
2783
2784 /* make a shallow copy of `allNotes` */
2785 blob_truncate(notes,0);
2786 blob_appendb(notes, allNotes);
2787 aNotes = CAST_AS_FOOTNOTES(notes);
2788 qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort);
2789
2790 if( --maxDepth < 0 || nMarks == rndr.notes.nMarks ) break;
2791 nMarks = rndr.notes.nMarks;
2792
2793 for(i=0; i<N; i++){
2794 const int j = aNotes[i].index;
2795 struct footnote *x = CAST_AS_FOOTNOTES(allNotes) + j;
2796 assert( 0<=j && j<N );
2797 if( x->bRndred || !x->nUsed ) continue;
2798 assert( x->iMark > 0 );
2799 assert( blob_size(&x->text) );
2800 blob_truncate(tmp,0);
2801
2802 /* `allNotes` may be altered and extended through this call */
2803 parse_inline(tmp, &rndr, blob_buffer(&x->text), blob_size(&x->text));
2804
2805 x = CAST_AS_FOOTNOTES(allNotes) + j;
2806 blob_truncate(&x->text,0);
2807 blob_appendb(&x->text, tmp);
2808 x->bRndred = 1;
2809 }
2810 }
2811 release_work_buffer(&rndr,tmp);
2812 }
2813
2814 /* footnotes rendering */
2815 if( rndr.make.footnote_item && rndr.make.footnotes ){
2816 Blob *all_items = new_work_buffer(&rndr);
2817 int j = -1;
@@ -2815,13 +2818,12 @@
2818
2819 /* Assert that the in-memory layout of id, text and upc within
2820 ** footnote struct matches the expectations of html_footnote_item()
2821 ** If it doesn't then a compiler has done something very weird.
2822 */
2823 assert( &(rndr.notes.misref.id) == &(rndr.notes.misref.text) - 1 );
2824 assert( &(rndr.notes.misref.upc) == &(rndr.notes.misref.text) + 1 );
 
2825
2826 for(i=0; i<COUNT_FOOTNOTES(notes); i++){
2827 const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i;
2828 const int xUsed = x->bRndred ? x->nUsed : 0;
2829 if( !x->iMark ) break;
2830
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -451,12 +451,10 @@
451451
static void html_footnote_item(
452452
struct Blob *ob, const struct Blob *text, int iMark, int nUsed, void *opaque
453453
){
454454
const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque;
455455
const char * const unique = ctx->unique.c;
456
- /* make.footnote_item() invocations should pass args accordingly */
457
- const struct Blob *upc = text+1;
458456
assert( nUsed >= 0 );
459457
/* expect BUGs if the following yields compiler warnings */
460458
461459
if( iMark < 0 ){ /* misreferences */
462460
assert( iMark == -1 );
@@ -482,10 +480,12 @@
482480
}else if( iMark > 0 ){ /* regular, joined and overnested footnotes */
483481
char pos[24];
484482
int bJoin = 0;
485483
#define _joined_footnote_indicator "<ul class='fn-joined'>"
486484
#define _jfi_sz (sizeof(_joined_footnote_indicator)-1)
485
+ /* make.footnote_item() invocations should pass args accordingly */
486
+ const struct Blob *upc = text+1;
487487
assert( text );
488488
assert( blob_size(text) );
489489
memset(pos,0,24);
490490
sprintf(pos, "%s-%d", unique, iMark);
491491
blob_appendf(ob, "<li id='footnote%s' class='", pos);
@@ -541,11 +541,11 @@
541541
}
542542
#undef _joined_footnote_indicator
543543
#undef _jfi_sz
544544
}else{ /* a footnote was defined but wasn't referenced */
545545
/* make.footnote_item() invocations should pass args accordingly */
546
- const struct Blob * id = text-1;
546
+ const struct Blob *id = text-1, *upc = text+1;
547547
assert( !nUsed );
548548
assert( text );
549549
assert( blob_size(text) );
550550
assert( blob_size(id) );
551551
blob_append_literal(ob,"<li class='fn-unreferenced'>\n[^&nbsp;<code>");
552552
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -451,12 +451,10 @@
451 static void html_footnote_item(
452 struct Blob *ob, const struct Blob *text, int iMark, int nUsed, void *opaque
453 ){
454 const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque;
455 const char * const unique = ctx->unique.c;
456 /* make.footnote_item() invocations should pass args accordingly */
457 const struct Blob *upc = text+1;
458 assert( nUsed >= 0 );
459 /* expect BUGs if the following yields compiler warnings */
460
461 if( iMark < 0 ){ /* misreferences */
462 assert( iMark == -1 );
@@ -482,10 +480,12 @@
482 }else if( iMark > 0 ){ /* regular, joined and overnested footnotes */
483 char pos[24];
484 int bJoin = 0;
485 #define _joined_footnote_indicator "<ul class='fn-joined'>"
486 #define _jfi_sz (sizeof(_joined_footnote_indicator)-1)
 
 
487 assert( text );
488 assert( blob_size(text) );
489 memset(pos,0,24);
490 sprintf(pos, "%s-%d", unique, iMark);
491 blob_appendf(ob, "<li id='footnote%s' class='", pos);
@@ -541,11 +541,11 @@
541 }
542 #undef _joined_footnote_indicator
543 #undef _jfi_sz
544 }else{ /* a footnote was defined but wasn't referenced */
545 /* make.footnote_item() invocations should pass args accordingly */
546 const struct Blob * id = text-1;
547 assert( !nUsed );
548 assert( text );
549 assert( blob_size(text) );
550 assert( blob_size(id) );
551 blob_append_literal(ob,"<li class='fn-unreferenced'>\n[^&nbsp;<code>");
552
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -451,12 +451,10 @@
451 static void html_footnote_item(
452 struct Blob *ob, const struct Blob *text, int iMark, int nUsed, void *opaque
453 ){
454 const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque;
455 const char * const unique = ctx->unique.c;
 
 
456 assert( nUsed >= 0 );
457 /* expect BUGs if the following yields compiler warnings */
458
459 if( iMark < 0 ){ /* misreferences */
460 assert( iMark == -1 );
@@ -482,10 +480,12 @@
480 }else if( iMark > 0 ){ /* regular, joined and overnested footnotes */
481 char pos[24];
482 int bJoin = 0;
483 #define _joined_footnote_indicator "<ul class='fn-joined'>"
484 #define _jfi_sz (sizeof(_joined_footnote_indicator)-1)
485 /* make.footnote_item() invocations should pass args accordingly */
486 const struct Blob *upc = text+1;
487 assert( text );
488 assert( blob_size(text) );
489 memset(pos,0,24);
490 sprintf(pos, "%s-%d", unique, iMark);
491 blob_appendf(ob, "<li id='footnote%s' class='", pos);
@@ -541,11 +541,11 @@
541 }
542 #undef _joined_footnote_indicator
543 #undef _jfi_sz
544 }else{ /* a footnote was defined but wasn't referenced */
545 /* make.footnote_item() invocations should pass args accordingly */
546 const struct Blob *id = text-1, *upc = text+1;
547 assert( !nUsed );
548 assert( text );
549 assert( blob_size(text) );
550 assert( blob_size(id) );
551 blob_append_literal(ob,"<li class='fn-unreferenced'>\n[^&nbsp;<code>");
552

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button