Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | If several footnotes are defined with the same label then join them into a single footnote. Text from each definition becomes an item in the list. This solution makes such situations noticable for the usual case (when this is an oversight) but also not obtrusive for the rare cases (when this is intentional). The list is provided with a special class to enable styling via skin customization. This check-in is known to cause crash, see the forthcoming check-in. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | markdown-footnotes |
Files: | files | file ages | folders |
SHA3-256: |
544df852b2d9a1e8326b7792f5f9b6d1 |
User & Date: | george 2022-02-08 14:04:49 |
References
2022-02-09
| ||
19:29 | Fix a bug in the blob_reserve() function that was introduced by [1243bf39996b8a]. The current mainline is not affected because this function is not used anywhere. However it was causing memory corruption on the 'markdown-footnotes' branch since it was employed in [544df852b2d9a1]. ... (check-in: 7283ae6e user: george tags: markdown-footnotes) | |
Context
2022-02-08
| ||
14:09 | An attempt to fix a "double free crash" from the previous check-in. ... (check-in: 18c9d103 user: george tags: markdown-footnotes) | |
14:04 |
If several footnotes are defined with the same label then join them into a single footnote. Text from each definition becomes an item in the list. This solution makes such situations noticable for the usual case (when this is an oversight) but also not obtrusive for the rare cases (when this is intentional). The list is provided with a special class to enable styling via skin customization. This check-in is known to cause crash, see the forthcoming check-in. ... (check-in: 544df852 user: george tags: markdown-footnotes) | |
13:39 | Add const qualifier to the arguments of the blob_compare() function. ... (check-in: 2822b63b user: george tags: markdown-footnotes) | |
Changes
Changes to src/markdown.c.
︙ | ︙ | |||
178 179 180 181 182 183 184 | int nBlobCache; /* Number of entries in aBlobCache */ struct Blob *aBlobCache[20]; /* Cache of Blobs available for reuse */ struct { Blob all; /* array of footnotes */ int nLbled; /* number of labeled footnotes found during the first pass */ int nMarks; /* counts distinct indices found during the second pass */ | | | 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | int nBlobCache; /* Number of entries in aBlobCache */ struct Blob *aBlobCache[20]; /* Cache of Blobs available for reuse */ struct { Blob all; /* array of footnotes */ int nLbled; /* number of labeled footnotes found during the first pass */ int nMarks; /* counts distinct indices found during the second pass */ struct footnote misref; /* nUsed counts misreferences, iMark must be -1 */ } notes; }; /* html_tag -- structure for quick HTML tag search (inspired from discount) */ struct html_tag { const char *text; int size; |
︙ | ︙ | |||
269 270 271 272 273 274 275 276 277 278 279 280 281 282 | /* cmp_link_ref_sort -- comparison function for link_ref qsort */ static int cmp_link_ref_sort(const void *a, const void *b){ struct link_ref *lra = (void *)a; struct link_ref *lrb = (void *)b; return blob_compare(&lra->id, &lrb->id); } /* cmp_footnote_sort -- comparison function for footnotes qsort. * Unreferenced footnotes (when nUsed == 0) sort last and * are sorted in the order of definition in the source */ static int cmp_footnote_sort(const void *fna, const void *fnb){ const struct footnote *a = fna, *b = fnb; int i, j; | > > > > > > > > > > > > > > > > > > | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | /* cmp_link_ref_sort -- comparison function for link_ref qsort */ static int cmp_link_ref_sort(const void *a, const void *b){ struct link_ref *lra = (void *)a; struct link_ref *lrb = (void *)b; return blob_compare(&lra->id, &lrb->id); } /* cmp_footnote_id -- comparison function for footnotes qsort. * Empty IDs sort last (in undetermined order). * Equal IDs are sorted in the REVERSED order of definition in the source */ static int cmp_footnote_id(const void *fna, const void *fnb){ const struct footnote *a = fna, *b = fnb; const int szA = blob_size(&a->id), szB = blob_size(&b->id); if( szA ){ if( szB ){ int cmp = blob_compare(&a->id, &b->id); if( cmp ) return cmp; }else return -1; }else return szB ? 1 : 0; /* ids are equal and non-empty */ if( a->defno < b->defno ) return -1; if( a->defno > b->defno ) return 1; return 0; /* should never reach here */ } /* cmp_footnote_sort -- comparison function for footnotes qsort. * Unreferenced footnotes (when nUsed == 0) sort last and * are sorted in the order of definition in the source */ static int cmp_footnote_sort(const void *fna, const void *fnb){ const struct footnote *a = fna, *b = fnb; int i, j; |
︙ | ︙ | |||
1051 1052 1053 1054 1055 1056 1057 | blob_reset(link); blob_reset(title); blob_append(link, blob_buffer(&lr->link), blob_size(&lr->link)); blob_append(title, blob_buffer(&lr->title), blob_size(&lr->title)); return 0; } | | | | | 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 | blob_reset(link); blob_reset(title); blob_append(link, blob_buffer(&lr->link), blob_size(&lr->link)); blob_append(title, blob_buffer(&lr->title), blob_size(&lr->title)); return 0; } /* get_footnote() -- find a footnote by label, invoked during the 2nd pass. * On success returns a footnote (after incrementing its nUsed field), * otherwise returns NULL */ static const struct footnote* get_footnote( struct render *rndr, const char *data, size_t size ){ struct footnote *fn = NULL; struct Blob *id = new_work_buffer(rndr); |
︙ | ︙ | |||
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 | fn->nUsed++; assert( fn->iMark > 0 ); assert( fn->nUsed > 0 ); cleanup: release_work_buffer( rndr, id ); return fn; } /* Adds unlabeled footnote to the rndr. * If text is blank then returns 0, * otherwise returns the address of the added footnote. */ static inline const struct footnote* add_inline_footnote( struct render *rndr, const char *text, size_t size | > | 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 | fn->nUsed++; assert( fn->iMark > 0 ); assert( fn->nUsed > 0 ); cleanup: release_work_buffer( rndr, id ); return fn; } /* Adds unlabeled footnote to the rndr. * If text is blank then returns 0, * otherwise returns the address of the added footnote. */ static inline const struct footnote* add_inline_footnote( struct render *rndr, const char *text, size_t size |
︙ | ︙ | |||
2536 2537 2538 2539 2540 2541 2542 | blob_append_char(&text, '\n'); } end += 1; } beg = end; } } | | | > | | | > | > > > > > > > > > > > > > > > | > > > > > > > | > > > > > > | > > > > > > > > > > > > | 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 | blob_append_char(&text, '\n'); } end += 1; } beg = end; } } /* sorting the reference array */ if( blob_size(&rndr.refs) ){ qsort(blob_buffer(&rndr.refs), blob_size(&rndr.refs)/sizeof(struct link_ref), sizeof(struct link_ref), cmp_link_ref_sort); } rndr.notes.nLbled = COUNT_FOOTNOTES(&rndr.notes.all); /* sort footnotes by ID and join duplicates */ if( rndr.notes.nLbled > 1 ){ fn = CAST_AS_FOOTNOTES(&rndr.notes.all); qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id); /* concatenate footnotes with equal labels */ for(i=0; i<rndr.notes.nLbled ;){ struct footnote *x = fn + i; size_t j = i+1, k = blob_size(&x->text) + 64; while(j<rndr.notes.nLbled && !blob_compare(&x->id, &fn[j].id)){ k += blob_size(&fn[j].text) + 10; j++; } if( i+1<j ){ Blob tmp = empty_blob; blob_reserve(&tmp, k); blob_append_string(&tmp, "<ul class='footnote-joined'>\n"); for(k=i; k<j; k++){ struct footnote *y = fn + k; blob_append_string(&tmp, "<li>"); blob_append(&tmp, blob_buffer(&y->text), blob_size(&y->text)); blob_append_string(&tmp, "</li>\n"); /* free memory buffer */ blob_reset(&y->text); if( k!=i ){ blob_reset(&y->id); /* invalidate redundant elements (this is optional) */ memset(y,0,sizeof(struct footnote)); y->index = y->defno = y->iMark = y->nUsed = -42; } } blob_append_string(&tmp, "</ul>\n"); x->text = tmp; } i = j; } /* move redundant elements to the end of array and truncate/resize */ qsort(fn, rndr.notes.nLbled, sizeof(struct footnote), cmp_footnote_id); i = rndr.notes.nLbled; while( i && !blob_size(&fn[i-1].id) ){ i--; } rndr.notes.nLbled = i; blob_truncate( &rndr.notes.all, i*sizeof(struct footnote) ); } assert( COUNT_FOOTNOTES(&rndr.notes.all) == rndr.notes.nLbled ); fn = CAST_AS_FOOTNOTES(&rndr.notes.all); for(i=0; i<rndr.notes.nLbled; i++){ fn[i].index = i; } assert( rndr.notes.nMarks==0 ); /* second pass: actual rendering */ if( rndr.make.prolog ) rndr.make.prolog(ob, rndr.make.opaque); parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text)); if( (blob_size(&rndr.notes.all) || rndr.notes.misref.nUsed) ){ |
︙ | ︙ |
Changes to test/markdown-test3.md.
︙ | ︙ | |||
8 9 10 11 12 13 14 15 16 17 18 19 20 21 | executable that incorporates the abovementioned branch.**[^1] Developers are invited to add test cases here[^here]. It is suggested that the more simple is a test case the earlier it should appear in this document.[^ if glitch occurs ] [^lost3]: This note was defined at the begining of the document. A footnote's label should be case insensitive[^ case INSENSITIVE ], it is whitespace-savvy and can even contain newlines.[^ a multiline label] A labeled footnote may be [referenced several times][^many-refs]. | > > | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | executable that incorporates the abovementioned branch.**[^1] Developers are invited to add test cases here[^here]. It is suggested that the more simple is a test case the earlier it should appear in this document.[^ if glitch occurs ] [^lost3]: This note was defined at the begining of the document. [^duplicate]: This came from the begining of the document. A footnote's label should be case insensitive[^ case INSENSITIVE ], it is whitespace-savvy and can even contain newlines.[^ a multiline label] A labeled footnote may be [referenced several times][^many-refs]. |
︙ | ︙ | |||
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | If [undefined label is used][^] then red "`misref`" is emited instead of a numeric marker.[^ see it yourself ] This can be overridden by the skin though. The refenrence at the end of this sentence is the sole reason of rendering of <s>`lost1` and</s> [lost2][^]. ## Footnotes [branch]: /timeline?r=markdown-footnotes&nowiki [^ 1]: Footnotes is a Fossil' extention of Markdown. Your other tools may have limited support for these. [^here]: [History of test/markdown-test3.md](/finfo/test/markdown-test3.md) [^if glitch occurs]: So that simple cases are processed even if a glitch happens for more tricky cases. [^ CASE insensitive ]: And also tolerate whitespaces. [^ a multiline label ]: But at a footnote's definition it should still be written within square brackets on a single line. [^many-refs]: Each letter on the left is a back-reference to the place of use. Highlighted back-reference indicates a place from which navigation occurred[^lost1]. [^lost1]: This note was defined at the end of the document. | > > > > | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | If [undefined label is used][^] then red "`misref`" is emited instead of a numeric marker.[^ see it yourself ] This can be overridden by the skin though. The refenrence at the end of this sentence is the sole reason of rendering of <s>`lost1` and</s> [lost2][^]. If several labeled footnote definitions have the same equal label then texts from all these definitions are joined.[^duplicate] ## Footnotes [branch]: /timeline?r=markdown-footnotes&nowiki [^ 1]: Footnotes is a Fossil' extention of Markdown. Your other tools may have limited support for these. [^here]: [History of test/markdown-test3.md](/finfo/test/markdown-test3.md) [^if glitch occurs]: So that simple cases are processed even if a glitch happens for more tricky cases. [^ CASE insensitive ]: And also tolerate whitespaces. [^ a multiline label ]: But at a footnote's definition it should still be written within square brackets on a single line. [^duplicate]: And that came from the end of the document. [^many-refs]: Each letter on the left is a back-reference to the place of use. Highlighted back-reference indicates a place from which navigation occurred[^lost1]. [^lost1]: This note was defined at the end of the document. |
︙ | ︙ |