Fossil SCM

Fix handling of user-provided classes for unreferenced, joined and overnested footnotes. In all these cases the tokens of user-provided classes are rendered as plain-text and no special classes are added anywhere.

george 2022-02-23 07:36 markdown-footnotes
Commit 875472a8b0992badd11882ce25e89eb38199d19ab8bde3232d4fe385506b6f88
+31 -17
--- src/markdown.c
+++ src/markdown.c
@@ -142,10 +142,13 @@
142142
struct Blob id; /* must be the first field as in footnote struct */
143143
struct Blob link;
144144
struct Blob title;
145145
};
146146
147
+/* A footnote's data.
148
+** id, text, and upc fields must be in that particular order.
149
+*/
147150
struct footnote {
148151
struct Blob id; /* must be the first field as in link_ref struct */
149152
struct Blob text; /* footnote's content that is rendered at the end */
150153
struct Blob upc; /* user-provided classes .ASCII-alnum.or-hypen: */
151154
int bRndred; /* indicates if `text` holds a rendered content */
@@ -2505,12 +2508,12 @@
25052508
size_t j;
25062509
upc_size = is_footnote_classlist(data+i, end-i, 1);
25072510
upc_offset = i; /* prevent further checks for a classlist */
25082511
i += upc_size;
25092512
j = i;
2510
- do i++; while( i<end && data[i]!='\n' && data[i]!='\r' );
2511
- blob_append(&fn.text, data+j, i-j);
2513
+ while( i<end && data[i]!='\n' && data[i]!='\r' ){ i++; };
2514
+ if( i!=j )blob_append(&fn.text, data+j, i-j);
25122515
if( i<end ){
25132516
blob_append_char(&fn.text, data[i]);
25142517
i++;
25152518
if( i<end && data[i]=='\n' && data[i-1]=='\r' ){
25162519
blob_append_char(&fn.text, data[i]);
@@ -2524,14 +2527,13 @@
25242527
if( i<end ){
25252528
25262529
/* compute the indentation from the 2nd line */
25272530
size_t indent = i;
25282531
const char *spaces = data+i;
2529
- while( i<end && data[i]==' ' ){ i++; }
2530
- if( i>=end ) goto footnote_finish;
2531
- indent = i - indent;
2532
- i -= indent;
2532
+ while( indent<end && data[indent]==' ' ){ indent++; }
2533
+ if( indent>=end ) goto footnote_finish;
2534
+ indent -= i;
25332535
if( indent<2 ) goto footnote_finish;
25342536
25352537
/* process the 2nd and the following lines */
25362538
while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){
25372539
size_t j;
@@ -2543,12 +2545,12 @@
25432545
if( upc_size ){
25442546
i = upc_offset + upc_size;
25452547
}
25462548
}
25472549
j = i;
2548
- while( i<end && data[i]!='\n' && data[i]!='\r' ) i++;
2549
- blob_append(&fn.text, data+j, i-j);
2550
+ while( i<end && data[i]!='\n' && data[i]!='\r' ){ i++; }
2551
+ if( i!=j ) blob_append(&fn.text, data+j, i-j);
25502552
if( i>=end ) break;
25512553
blob_append_char(&fn.text, data[i]);
25522554
i++;
25532555
if( i<end && data[i]=='\n' && data[i-1]=='\r' ){
25542556
blob_append_char(&fn.text, data[i]);
@@ -2675,13 +2677,13 @@
26752677
qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id);
26762678
26772679
/* concatenate footnotes with equal labels */
26782680
for(i=0; i<rndr.notes.nLbled ;){
26792681
struct footnote *x = fn + i;
2680
- size_t j = i+1, k = blob_size(&x->text) + 64;
2682
+ size_t j = i+1, k = blob_size(&x->text) + 64 + blob_size(&x->upc);
26812683
while(j<rndr.notes.nLbled && !blob_compare(&x->id, &fn[j].id)){
2682
- k += blob_size(&fn[j].text) + 10;
2684
+ k += blob_size(&fn[j].text) + 10 + blob_size(&fn[j].upc);
26832685
j++;
26842686
nDups++;
26852687
}
26862688
if( i+1<j ){
26872689
Blob list = empty_blob;
@@ -2689,10 +2691,14 @@
26892691
/* must match _joined_footnote_indicator in html_footnote_item() */
26902692
blob_append_string(&list, "<ul class='fn-joined'>\n");
26912693
for(k=i; k<j; k++){
26922694
struct footnote *y = fn + k;
26932695
blob_append_string(&list, "<li>");
2696
+ if( blob_size(&y->upc) ){
2697
+ blob_append(&list, blob_buffer(&y->upc), blob_size(&y->upc));
2698
+ blob_reset(&y->upc);
2699
+ }
26942700
blob_append(&list, blob_buffer(&y->text), blob_size(&y->text));
26952701
blob_append_string(&list, "</li>\n");
26962702
26972703
/* free memory buffer */
26982704
blob_reset(&y->text);
@@ -2771,29 +2777,37 @@
27712777
27722778
/* footnotes rendering */
27732779
if( rndr.make.footnote_item && rndr.make.footnotes ){
27742780
Blob *all_items = new_work_buffer(&rndr);
27752781
int j = -1;
2782
+
2783
+ /* Assert that the in-memory layout of id, text and upc within
2784
+ ** footnote struct matches the expectations of html_footnote_item()
2785
+ ** If it doesn't then a compiler has done something very weird.
2786
+ */
2787
+ const struct footnote *dummy = 0;
2788
+ assert( &(dummy->id) == &(dummy->text) - 1 );
2789
+ assert( &(dummy->upc) == &(dummy->text) + 1 );
2790
+
27762791
for(i=0; i<COUNT_FOOTNOTES(notes); i++){
27772792
const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i;
2778
- if( x->iMark ){
2779
- rndr.make.footnote_item(all_items, &x->text, x->iMark,
2780
- x->bRndred ? x->nUsed : 0, rndr.make.opaque);
2781
- j = i;
2782
- }
2793
+ if( !x->iMark ) break;
2794
+ assert( x->nUsed );
2795
+ rndr.make.footnote_item(all_items, &x->text, x->iMark,
2796
+ x->bRndred ? x->nUsed : 0, rndr.make.opaque);
2797
+ j = i;
27832798
}
27842799
if( rndr.notes.misref.nUsed ){
27852800
rndr.make.footnote_item(all_items, 0, -1,
27862801
rndr.notes.misref.nUsed, rndr.make.opaque);
27872802
g.ftntsIssues[0] += rndr.notes.misref.nUsed;
27882803
}
27892804
while( ++j < COUNT_FOOTNOTES(notes) ){
27902805
const struct footnote* x = CAST_AS_FOOTNOTES(notes) + j;
27912806
assert( !x->iMark );
2807
+ assert( !x->nUsed );
27922808
assert( !x->bRndred );
2793
- assert( (&x->id) + 1 == &x->text ); /* see html_footnote_item() */
2794
- assert( (&x->upc)- 1 == &x->text );
27952809
rndr.make.footnote_item(all_items,&x->text,0,0,rndr.make.opaque);
27962810
g.ftntsIssues[1]++;
27972811
}
27982812
rndr.make.footnotes(ob, all_items, rndr.make.opaque);
27992813
release_work_buffer(&rndr, all_items);
28002814
--- src/markdown.c
+++ src/markdown.c
@@ -142,10 +142,13 @@
142 struct Blob id; /* must be the first field as in footnote struct */
143 struct Blob link;
144 struct Blob title;
145 };
146
 
 
 
147 struct footnote {
148 struct Blob id; /* must be the first field as in link_ref struct */
149 struct Blob text; /* footnote's content that is rendered at the end */
150 struct Blob upc; /* user-provided classes .ASCII-alnum.or-hypen: */
151 int bRndred; /* indicates if `text` holds a rendered content */
@@ -2505,12 +2508,12 @@
2505 size_t j;
2506 upc_size = is_footnote_classlist(data+i, end-i, 1);
2507 upc_offset = i; /* prevent further checks for a classlist */
2508 i += upc_size;
2509 j = i;
2510 do i++; while( i<end && data[i]!='\n' && data[i]!='\r' );
2511 blob_append(&fn.text, data+j, i-j);
2512 if( i<end ){
2513 blob_append_char(&fn.text, data[i]);
2514 i++;
2515 if( i<end && data[i]=='\n' && data[i-1]=='\r' ){
2516 blob_append_char(&fn.text, data[i]);
@@ -2524,14 +2527,13 @@
2524 if( i<end ){
2525
2526 /* compute the indentation from the 2nd line */
2527 size_t indent = i;
2528 const char *spaces = data+i;
2529 while( i<end && data[i]==' ' ){ i++; }
2530 if( i>=end ) goto footnote_finish;
2531 indent = i - indent;
2532 i -= indent;
2533 if( indent<2 ) goto footnote_finish;
2534
2535 /* process the 2nd and the following lines */
2536 while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){
2537 size_t j;
@@ -2543,12 +2545,12 @@
2543 if( upc_size ){
2544 i = upc_offset + upc_size;
2545 }
2546 }
2547 j = i;
2548 while( i<end && data[i]!='\n' && data[i]!='\r' ) i++;
2549 blob_append(&fn.text, data+j, i-j);
2550 if( i>=end ) break;
2551 blob_append_char(&fn.text, data[i]);
2552 i++;
2553 if( i<end && data[i]=='\n' && data[i-1]=='\r' ){
2554 blob_append_char(&fn.text, data[i]);
@@ -2675,13 +2677,13 @@
2675 qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id);
2676
2677 /* concatenate footnotes with equal labels */
2678 for(i=0; i<rndr.notes.nLbled ;){
2679 struct footnote *x = fn + i;
2680 size_t j = i+1, k = blob_size(&x->text) + 64;
2681 while(j<rndr.notes.nLbled && !blob_compare(&x->id, &fn[j].id)){
2682 k += blob_size(&fn[j].text) + 10;
2683 j++;
2684 nDups++;
2685 }
2686 if( i+1<j ){
2687 Blob list = empty_blob;
@@ -2689,10 +2691,14 @@
2689 /* must match _joined_footnote_indicator in html_footnote_item() */
2690 blob_append_string(&list, "<ul class='fn-joined'>\n");
2691 for(k=i; k<j; k++){
2692 struct footnote *y = fn + k;
2693 blob_append_string(&list, "<li>");
 
 
 
 
2694 blob_append(&list, blob_buffer(&y->text), blob_size(&y->text));
2695 blob_append_string(&list, "</li>\n");
2696
2697 /* free memory buffer */
2698 blob_reset(&y->text);
@@ -2771,29 +2777,37 @@
2771
2772 /* footnotes rendering */
2773 if( rndr.make.footnote_item && rndr.make.footnotes ){
2774 Blob *all_items = new_work_buffer(&rndr);
2775 int j = -1;
 
 
 
 
 
 
 
 
 
2776 for(i=0; i<COUNT_FOOTNOTES(notes); i++){
2777 const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i;
2778 if( x->iMark ){
2779 rndr.make.footnote_item(all_items, &x->text, x->iMark,
2780 x->bRndred ? x->nUsed : 0, rndr.make.opaque);
2781 j = i;
2782 }
2783 }
2784 if( rndr.notes.misref.nUsed ){
2785 rndr.make.footnote_item(all_items, 0, -1,
2786 rndr.notes.misref.nUsed, rndr.make.opaque);
2787 g.ftntsIssues[0] += rndr.notes.misref.nUsed;
2788 }
2789 while( ++j < COUNT_FOOTNOTES(notes) ){
2790 const struct footnote* x = CAST_AS_FOOTNOTES(notes) + j;
2791 assert( !x->iMark );
 
2792 assert( !x->bRndred );
2793 assert( (&x->id) + 1 == &x->text ); /* see html_footnote_item() */
2794 assert( (&x->upc)- 1 == &x->text );
2795 rndr.make.footnote_item(all_items,&x->text,0,0,rndr.make.opaque);
2796 g.ftntsIssues[1]++;
2797 }
2798 rndr.make.footnotes(ob, all_items, rndr.make.opaque);
2799 release_work_buffer(&rndr, all_items);
2800
--- src/markdown.c
+++ src/markdown.c
@@ -142,10 +142,13 @@
142 struct Blob id; /* must be the first field as in footnote struct */
143 struct Blob link;
144 struct Blob title;
145 };
146
147 /* A footnote's data.
148 ** id, text, and upc fields must be in that particular order.
149 */
150 struct footnote {
151 struct Blob id; /* must be the first field as in link_ref struct */
152 struct Blob text; /* footnote's content that is rendered at the end */
153 struct Blob upc; /* user-provided classes .ASCII-alnum.or-hypen: */
154 int bRndred; /* indicates if `text` holds a rendered content */
@@ -2505,12 +2508,12 @@
2508 size_t j;
2509 upc_size = is_footnote_classlist(data+i, end-i, 1);
2510 upc_offset = i; /* prevent further checks for a classlist */
2511 i += upc_size;
2512 j = i;
2513 while( i<end && data[i]!='\n' && data[i]!='\r' ){ i++; };
2514 if( i!=j )blob_append(&fn.text, data+j, i-j);
2515 if( i<end ){
2516 blob_append_char(&fn.text, data[i]);
2517 i++;
2518 if( i<end && data[i]=='\n' && data[i-1]=='\r' ){
2519 blob_append_char(&fn.text, data[i]);
@@ -2524,14 +2527,13 @@
2527 if( i<end ){
2528
2529 /* compute the indentation from the 2nd line */
2530 size_t indent = i;
2531 const char *spaces = data+i;
2532 while( indent<end && data[indent]==' ' ){ indent++; }
2533 if( indent>=end ) goto footnote_finish;
2534 indent -= i;
 
2535 if( indent<2 ) goto footnote_finish;
2536
2537 /* process the 2nd and the following lines */
2538 while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){
2539 size_t j;
@@ -2543,12 +2545,12 @@
2545 if( upc_size ){
2546 i = upc_offset + upc_size;
2547 }
2548 }
2549 j = i;
2550 while( i<end && data[i]!='\n' && data[i]!='\r' ){ i++; }
2551 if( i!=j ) blob_append(&fn.text, data+j, i-j);
2552 if( i>=end ) break;
2553 blob_append_char(&fn.text, data[i]);
2554 i++;
2555 if( i<end && data[i]=='\n' && data[i-1]=='\r' ){
2556 blob_append_char(&fn.text, data[i]);
@@ -2675,13 +2677,13 @@
2677 qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id);
2678
2679 /* concatenate footnotes with equal labels */
2680 for(i=0; i<rndr.notes.nLbled ;){
2681 struct footnote *x = fn + i;
2682 size_t j = i+1, k = blob_size(&x->text) + 64 + blob_size(&x->upc);
2683 while(j<rndr.notes.nLbled && !blob_compare(&x->id, &fn[j].id)){
2684 k += blob_size(&fn[j].text) + 10 + blob_size(&fn[j].upc);
2685 j++;
2686 nDups++;
2687 }
2688 if( i+1<j ){
2689 Blob list = empty_blob;
@@ -2689,10 +2691,14 @@
2691 /* must match _joined_footnote_indicator in html_footnote_item() */
2692 blob_append_string(&list, "<ul class='fn-joined'>\n");
2693 for(k=i; k<j; k++){
2694 struct footnote *y = fn + k;
2695 blob_append_string(&list, "<li>");
2696 if( blob_size(&y->upc) ){
2697 blob_append(&list, blob_buffer(&y->upc), blob_size(&y->upc));
2698 blob_reset(&y->upc);
2699 }
2700 blob_append(&list, blob_buffer(&y->text), blob_size(&y->text));
2701 blob_append_string(&list, "</li>\n");
2702
2703 /* free memory buffer */
2704 blob_reset(&y->text);
@@ -2771,29 +2777,37 @@
2777
2778 /* footnotes rendering */
2779 if( rndr.make.footnote_item && rndr.make.footnotes ){
2780 Blob *all_items = new_work_buffer(&rndr);
2781 int j = -1;
2782
2783 /* Assert that the in-memory layout of id, text and upc within
2784 ** footnote struct matches the expectations of html_footnote_item()
2785 ** If it doesn't then a compiler has done something very weird.
2786 */
2787 const struct footnote *dummy = 0;
2788 assert( &(dummy->id) == &(dummy->text) - 1 );
2789 assert( &(dummy->upc) == &(dummy->text) + 1 );
2790
2791 for(i=0; i<COUNT_FOOTNOTES(notes); i++){
2792 const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i;
2793 if( !x->iMark ) break;
2794 assert( x->nUsed );
2795 rndr.make.footnote_item(all_items, &x->text, x->iMark,
2796 x->bRndred ? x->nUsed : 0, rndr.make.opaque);
2797 j = i;
2798 }
2799 if( rndr.notes.misref.nUsed ){
2800 rndr.make.footnote_item(all_items, 0, -1,
2801 rndr.notes.misref.nUsed, rndr.make.opaque);
2802 g.ftntsIssues[0] += rndr.notes.misref.nUsed;
2803 }
2804 while( ++j < COUNT_FOOTNOTES(notes) ){
2805 const struct footnote* x = CAST_AS_FOOTNOTES(notes) + j;
2806 assert( !x->iMark );
2807 assert( !x->nUsed );
2808 assert( !x->bRndred );
 
 
2809 rndr.make.footnote_item(all_items,&x->text,0,0,rndr.make.opaque);
2810 g.ftntsIssues[1]++;
2811 }
2812 rndr.make.footnotes(ob, all_items, rndr.make.opaque);
2813 release_work_buffer(&rndr, all_items);
2814
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -447,16 +447,18 @@
447447
static void html_footnote_item(
448448
struct Blob *ob, const struct Blob *text, int iMark, int nUsed, void *opaque
449449
){
450450
const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque;
451451
const char * const unique = ctx->unique.c;
452
+ /* make.footnote_item() invocations should pass args accordingly */
453
+ const struct Blob *upc = text+1;
452454
assert( nUsed >= 0 );
453455
/* expect BUGs if the following yields compiler warnings */
454456
455457
if( iMark < 0 ){ /* misreferences */
456458
assert( iMark == -1 );
457
- if( !nUsed ) return;
459
+ assert( nUsed );
458460
BLOB_APPEND_LITERAL(ob,"<li class='fn-misreference'>"
459461
"<sup class='fn-backrefs'>");
460462
if( nUsed == 1 ){
461463
blob_appendf(ob,"<a id='misreference%s-a' href='", unique);
462464
BLOB_APPEND_URI(ob, ctx);
@@ -472,15 +474,13 @@
472474
}
473475
if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
474476
}
475477
BLOB_APPEND_LITERAL(ob,"</sup>\n<span>Misreference</span>");
476478
477
- }else if( iMark > 0 ){ /* a regular footnote */
479
+ }else if( iMark > 0 ){ /* regular, joined and overnested footnotes */
478480
char pos[24];
479481
int bJoin = 0;
480
- /* make.footnote_item() invocations should pass args accordingly */
481
- const struct Blob * upc = text+1;
482482
#define _joined_footnote_indicator "<ul class='fn-joined'>"
483483
#define _jfi_sz (sizeof(_joined_footnote_indicator)-1)
484484
assert( text );
485485
assert( blob_size(text) );
486486
memset(pos,0,24);
@@ -521,26 +521,28 @@
521521
blob_appendf(ob,"#noteref%s-%s'>%s</a>", pos,l.c, l.c);
522522
}
523523
if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
524524
}
525525
BLOB_APPEND_LITERAL(ob,"</sup>\n");
526
- append_footnote_upc(ob, upc, 1);
527526
if( bJoin ){
528527
BLOB_APPEND_LITERAL(ob,"<sup class='fn-joined'></sup><ul>");
529528
blob_append(ob,blob_buffer(text)+_jfi_sz,blob_size(text)-_jfi_sz);
530529
}else if( nUsed ){
530
+ append_footnote_upc(ob, upc, 1);
531531
BLOB_APPEND_BLOB(ob, text);
532532
}else{
533533
BLOB_APPEND_LITERAL(ob,"<i></i>\n"
534534
"<pre><code class='language-markdown'>");
535
+ if( blob_size(upc) ){
536
+ BLOB_APPEND_BLOB(ob, upc);
537
+ }
535538
html_escape(ob, blob_buffer(text), blob_size(text));
536539
BLOB_APPEND_LITERAL(ob,"</code></pre>");
537540
}
538541
#undef _joined_footnote_indicator
539542
#undef _jfi_sz
540
- }else{
541
- /* a footnote was defined but wasn't used */
543
+ }else{ /* a footnote was defined but wasn't referenced */
542544
/* make.footnote_item() invocations should pass args accordingly */
543545
const struct Blob * id = text-1;
544546
assert( !nUsed );
545547
assert( text );
546548
assert( blob_size(text) );
@@ -547,10 +549,13 @@
547549
assert( blob_size(id) );
548550
BLOB_APPEND_LITERAL(ob,"<li class='fn-unreferenced'>\n[^&nbsp;<code>");
549551
html_escape(ob, blob_buffer(id), blob_size(id));
550552
BLOB_APPEND_LITERAL(ob, "</code>&nbsp;]<i></i>\n"
551553
"<pre><code class='language-markdown'>");
554
+ if( blob_size(upc) ){
555
+ BLOB_APPEND_BLOB(ob, upc);
556
+ }
552557
html_escape(ob, blob_buffer(text), blob_size(text));
553558
BLOB_APPEND_LITERAL(ob,"</code></pre>");
554559
}
555560
BLOB_APPEND_LITERAL(ob, "\n</li>\n");
556561
}
557562
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -447,16 +447,18 @@
447 static void html_footnote_item(
448 struct Blob *ob, const struct Blob *text, int iMark, int nUsed, void *opaque
449 ){
450 const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque;
451 const char * const unique = ctx->unique.c;
 
 
452 assert( nUsed >= 0 );
453 /* expect BUGs if the following yields compiler warnings */
454
455 if( iMark < 0 ){ /* misreferences */
456 assert( iMark == -1 );
457 if( !nUsed ) return;
458 BLOB_APPEND_LITERAL(ob,"<li class='fn-misreference'>"
459 "<sup class='fn-backrefs'>");
460 if( nUsed == 1 ){
461 blob_appendf(ob,"<a id='misreference%s-a' href='", unique);
462 BLOB_APPEND_URI(ob, ctx);
@@ -472,15 +474,13 @@
472 }
473 if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
474 }
475 BLOB_APPEND_LITERAL(ob,"</sup>\n<span>Misreference</span>");
476
477 }else if( iMark > 0 ){ /* a regular footnote */
478 char pos[24];
479 int bJoin = 0;
480 /* make.footnote_item() invocations should pass args accordingly */
481 const struct Blob * upc = text+1;
482 #define _joined_footnote_indicator "<ul class='fn-joined'>"
483 #define _jfi_sz (sizeof(_joined_footnote_indicator)-1)
484 assert( text );
485 assert( blob_size(text) );
486 memset(pos,0,24);
@@ -521,26 +521,28 @@
521 blob_appendf(ob,"#noteref%s-%s'>%s</a>", pos,l.c, l.c);
522 }
523 if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
524 }
525 BLOB_APPEND_LITERAL(ob,"</sup>\n");
526 append_footnote_upc(ob, upc, 1);
527 if( bJoin ){
528 BLOB_APPEND_LITERAL(ob,"<sup class='fn-joined'></sup><ul>");
529 blob_append(ob,blob_buffer(text)+_jfi_sz,blob_size(text)-_jfi_sz);
530 }else if( nUsed ){
 
531 BLOB_APPEND_BLOB(ob, text);
532 }else{
533 BLOB_APPEND_LITERAL(ob,"<i></i>\n"
534 "<pre><code class='language-markdown'>");
 
 
 
535 html_escape(ob, blob_buffer(text), blob_size(text));
536 BLOB_APPEND_LITERAL(ob,"</code></pre>");
537 }
538 #undef _joined_footnote_indicator
539 #undef _jfi_sz
540 }else{
541 /* a footnote was defined but wasn't used */
542 /* make.footnote_item() invocations should pass args accordingly */
543 const struct Blob * id = text-1;
544 assert( !nUsed );
545 assert( text );
546 assert( blob_size(text) );
@@ -547,10 +549,13 @@
547 assert( blob_size(id) );
548 BLOB_APPEND_LITERAL(ob,"<li class='fn-unreferenced'>\n[^&nbsp;<code>");
549 html_escape(ob, blob_buffer(id), blob_size(id));
550 BLOB_APPEND_LITERAL(ob, "</code>&nbsp;]<i></i>\n"
551 "<pre><code class='language-markdown'>");
 
 
 
552 html_escape(ob, blob_buffer(text), blob_size(text));
553 BLOB_APPEND_LITERAL(ob,"</code></pre>");
554 }
555 BLOB_APPEND_LITERAL(ob, "\n</li>\n");
556 }
557
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -447,16 +447,18 @@
447 static void html_footnote_item(
448 struct Blob *ob, const struct Blob *text, int iMark, int nUsed, void *opaque
449 ){
450 const struct MarkdownToHtml* ctx = (struct MarkdownToHtml*)opaque;
451 const char * const unique = ctx->unique.c;
452 /* make.footnote_item() invocations should pass args accordingly */
453 const struct Blob *upc = text+1;
454 assert( nUsed >= 0 );
455 /* expect BUGs if the following yields compiler warnings */
456
457 if( iMark < 0 ){ /* misreferences */
458 assert( iMark == -1 );
459 assert( nUsed );
460 BLOB_APPEND_LITERAL(ob,"<li class='fn-misreference'>"
461 "<sup class='fn-backrefs'>");
462 if( nUsed == 1 ){
463 blob_appendf(ob,"<a id='misreference%s-a' href='", unique);
464 BLOB_APPEND_URI(ob, ctx);
@@ -472,15 +474,13 @@
474 }
475 if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
476 }
477 BLOB_APPEND_LITERAL(ob,"</sup>\n<span>Misreference</span>");
478
479 }else if( iMark > 0 ){ /* regular, joined and overnested footnotes */
480 char pos[24];
481 int bJoin = 0;
 
 
482 #define _joined_footnote_indicator "<ul class='fn-joined'>"
483 #define _jfi_sz (sizeof(_joined_footnote_indicator)-1)
484 assert( text );
485 assert( blob_size(text) );
486 memset(pos,0,24);
@@ -521,26 +521,28 @@
521 blob_appendf(ob,"#noteref%s-%s'>%s</a>", pos,l.c, l.c);
522 }
523 if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
524 }
525 BLOB_APPEND_LITERAL(ob,"</sup>\n");
 
526 if( bJoin ){
527 BLOB_APPEND_LITERAL(ob,"<sup class='fn-joined'></sup><ul>");
528 blob_append(ob,blob_buffer(text)+_jfi_sz,blob_size(text)-_jfi_sz);
529 }else if( nUsed ){
530 append_footnote_upc(ob, upc, 1);
531 BLOB_APPEND_BLOB(ob, text);
532 }else{
533 BLOB_APPEND_LITERAL(ob,"<i></i>\n"
534 "<pre><code class='language-markdown'>");
535 if( blob_size(upc) ){
536 BLOB_APPEND_BLOB(ob, upc);
537 }
538 html_escape(ob, blob_buffer(text), blob_size(text));
539 BLOB_APPEND_LITERAL(ob,"</code></pre>");
540 }
541 #undef _joined_footnote_indicator
542 #undef _jfi_sz
543 }else{ /* a footnote was defined but wasn't referenced */
 
544 /* make.footnote_item() invocations should pass args accordingly */
545 const struct Blob * id = text-1;
546 assert( !nUsed );
547 assert( text );
548 assert( blob_size(text) );
@@ -547,10 +549,13 @@
549 assert( blob_size(id) );
550 BLOB_APPEND_LITERAL(ob,"<li class='fn-unreferenced'>\n[^&nbsp;<code>");
551 html_escape(ob, blob_buffer(id), blob_size(id));
552 BLOB_APPEND_LITERAL(ob, "</code>&nbsp;]<i></i>\n"
553 "<pre><code class='language-markdown'>");
554 if( blob_size(upc) ){
555 BLOB_APPEND_BLOB(ob, upc);
556 }
557 html_escape(ob, blob_buffer(text), blob_size(text));
558 BLOB_APPEND_LITERAL(ob,"</code></pre>");
559 }
560 BLOB_APPEND_LITERAL(ob, "\n</li>\n");
561 }
562
--- test/markdown-test3.md
+++ test/markdown-test3.md
@@ -101,10 +101,17 @@
101101
}
102102
sup.noteref.fn-upc-example:hover::after,
103103
span.notescope.fn-upc-example sup.noteref:hover::after {
104104
content: " 👻";
105105
}
106
+ li.fn-upc-l span.fn-upc {
107
+ font-size: 60%;
108
+ color: orange;
109
+ }
110
+ li.fn-upc-l span.fn-upc span.fn-upcDot {
111
+ display: none;
112
+ }
106113
</style>
107114
108115
It is possible to provide a list of classes for a particular footnote and
109116
all its references. This is achieved by prepending a footnote's text with
110117
a special token that starts with dot and ends with colon.
@@ -124,10 +131,14 @@
124131
(^
125132
.bare.classlist.inside.inline.footnote:
126133
)[^bare1]
127134
[^bare2]
128135
136
+[^duplicate]: .with.UPC.token:
137
+ When duplicates are joined their UPC tokens are treated as plain-text.
138
+ Blank characters between token and main text must be preserved.
139
+
129140
<html>
130141
Click
131142
<a href="?a=B&quote='&nonASCII=😂&script=<script>alert('Broken!');</script>">
132143
here</a> and
133144
<a href='?a=B&quote="&nonASCII=😂&script=<script>alert("Broken!");</script>'>
@@ -135,23 +146,23 @@
135146
to test escaping of REQUEST_URI in the generated footnote markers.
136147
</html>
137148
138149
A depth of nesting must be limited.
139150
(^
140
- A long chain of nested inline footnotes...
141
- (^
142
- is a rather unusual thing...
143
- (^
144
- and requires extra CPU cycles for processing.
145
- (^
146
- Theoretically speaking O(n<sup>2</sup>).
147
- (^
148
- Thus it is worth dismissing those footnotes...
149
- (^
150
- that are nested deeper than on a certain level.
151
- (^
152
- A particular value for that limit...
151
+ .L.1: A long chain of nested inline footnotes...
152
+ (^
153
+ .L.2: is a rather unusual thing...
154
+ (^
155
+ .L.3: and requires extra CPU cycles for processing.
156
+ (^
157
+ .L.4: Theoretically speaking O(n<sup>2</sup>).
158
+ (^
159
+ .L.5: Thus it is worth dismissing those footnotes...
160
+ (^
161
+ .L.6: that are nested deeper than on a certain level.
162
+ (^
163
+ .L.7: A particular value for that limit...
153164
(^
154165
is hard-coded in src/markdown.c ...
155166
(^
156167
in function `markdown()` ...
157168
(^
@@ -216,10 +227,13 @@
216227
217228
218229
[^bare2]:
219230
.at.the.2nd.line.of.labeled.footnote.definition:
220231
232
+[^stray with UPC]: .UPC-token:
233
+ A token of user-provided classes must be rendered within strays.
234
+ Aslo: this and the previous line may not have extra indentation.
221235
222236
[^nostyle]:
223237
.unused.classes:
224238
In that case text of the footnote just looks like as if
225239
no special processing occured.
226240
--- test/markdown-test3.md
+++ test/markdown-test3.md
@@ -101,10 +101,17 @@
101 }
102 sup.noteref.fn-upc-example:hover::after,
103 span.notescope.fn-upc-example sup.noteref:hover::after {
104 content: " 👻";
105 }
 
 
 
 
 
 
 
106 </style>
107
108 It is possible to provide a list of classes for a particular footnote and
109 all its references. This is achieved by prepending a footnote's text with
110 a special token that starts with dot and ends with colon.
@@ -124,10 +131,14 @@
124 (^
125 .bare.classlist.inside.inline.footnote:
126 )[^bare1]
127 [^bare2]
128
 
 
 
 
129 <html>
130 Click
131 <a href="?a=B&quote='&nonASCII=😂&script=<script>alert('Broken!');</script>">
132 here</a> and
133 <a href='?a=B&quote="&nonASCII=😂&script=<script>alert("Broken!");</script>'>
@@ -135,23 +146,23 @@
135 to test escaping of REQUEST_URI in the generated footnote markers.
136 </html>
137
138 A depth of nesting must be limited.
139 (^
140 A long chain of nested inline footnotes...
141 (^
142 is a rather unusual thing...
143 (^
144 and requires extra CPU cycles for processing.
145 (^
146 Theoretically speaking O(n<sup>2</sup>).
147 (^
148 Thus it is worth dismissing those footnotes...
149 (^
150 that are nested deeper than on a certain level.
151 (^
152 A particular value for that limit...
153 (^
154 is hard-coded in src/markdown.c ...
155 (^
156 in function `markdown()` ...
157 (^
@@ -216,10 +227,13 @@
216
217
218 [^bare2]:
219 .at.the.2nd.line.of.labeled.footnote.definition:
220
 
 
 
221
222 [^nostyle]:
223 .unused.classes:
224 In that case text of the footnote just looks like as if
225 no special processing occured.
226
--- test/markdown-test3.md
+++ test/markdown-test3.md
@@ -101,10 +101,17 @@
101 }
102 sup.noteref.fn-upc-example:hover::after,
103 span.notescope.fn-upc-example sup.noteref:hover::after {
104 content: " 👻";
105 }
106 li.fn-upc-l span.fn-upc {
107 font-size: 60%;
108 color: orange;
109 }
110 li.fn-upc-l span.fn-upc span.fn-upcDot {
111 display: none;
112 }
113 </style>
114
115 It is possible to provide a list of classes for a particular footnote and
116 all its references. This is achieved by prepending a footnote's text with
117 a special token that starts with dot and ends with colon.
@@ -124,10 +131,14 @@
131 (^
132 .bare.classlist.inside.inline.footnote:
133 )[^bare1]
134 [^bare2]
135
136 [^duplicate]: .with.UPC.token:
137 When duplicates are joined their UPC tokens are treated as plain-text.
138 Blank characters between token and main text must be preserved.
139
140 <html>
141 Click
142 <a href="?a=B&quote='&nonASCII=😂&script=<script>alert('Broken!');</script>">
143 here</a> and
144 <a href='?a=B&quote="&nonASCII=😂&script=<script>alert("Broken!");</script>'>
@@ -135,23 +146,23 @@
146 to test escaping of REQUEST_URI in the generated footnote markers.
147 </html>
148
149 A depth of nesting must be limited.
150 (^
151 .L.1: A long chain of nested inline footnotes...
152 (^
153 .L.2: is a rather unusual thing...
154 (^
155 .L.3: and requires extra CPU cycles for processing.
156 (^
157 .L.4: Theoretically speaking O(n<sup>2</sup>).
158 (^
159 .L.5: Thus it is worth dismissing those footnotes...
160 (^
161 .L.6: that are nested deeper than on a certain level.
162 (^
163 .L.7: A particular value for that limit...
164 (^
165 is hard-coded in src/markdown.c ...
166 (^
167 in function `markdown()` ...
168 (^
@@ -216,10 +227,13 @@
227
228
229 [^bare2]:
230 .at.the.2nd.line.of.labeled.footnote.definition:
231
232 [^stray with UPC]: .UPC-token:
233 A token of user-provided classes must be rendered within strays.
234 Aslo: this and the previous line may not have extra indentation.
235
236 [^nostyle]:
237 .unused.classes:
238 In that case text of the footnote just looks like as if
239 no special processing occured.
240

Keyboard Shortcuts

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