Fossil SCM

Handle some corner cases more thoroughly: dismiss empty footnotes, passthrough (more carefully) user-provided classlist if the token is not followed by a blank character or if a footnote's text consists just of such token and blank characters. Also simplify a little bit a few places inside of <code>is_footnote()</code> function.

george 2022-02-19 01:00 markdown-footnotes
Commit fe3157803f15b685910023749fbaf62c0aa5153ed6cea4c2dc55278144dc7a57
2 files changed +57 -57 +27 -1
+57 -57
--- src/markdown.c
+++ src/markdown.c
@@ -1104,18 +1104,23 @@
11041104
return fn;
11051105
}
11061106
11071107
/* Counts characters in the blank prefix within at most nHalfLines.
11081108
** A sequence of spaces and tabs counts as odd halfline,
1109
-** a newline counts as even halfline
1109
+** a newline counts as even halfline.
1110
+** If nHalfLines < 0 then procceed without constrains.
11101111
*/
1111
-static inline size_t count_whitespaces(
1112
+static inline size_t sizeof_blank_prefix(
11121113
const char *data, size_t size, int nHalfLines
11131114
){
11141115
const char *p = data;
11151116
const char * const end = data+size;
1116
- while( nHalfLines > 0 ){
1117
+ if( nHalfLines < 0 ){
1118
+ while( p!=end && fossil_isspace(*p) ){
1119
+ p++;
1120
+ }
1121
+ }else while( nHalfLines > 0 ){
11171122
while( p!=end && (*p==' ' || *p=='\t' ) ){ p++; }
11181123
if( p==end || --nHalfLines == 0 ) break;
11191124
if( *p=='\n' || *p=='\r' ){
11201125
p++;
11211126
if( p==end ) break;
@@ -1123,33 +1128,39 @@
11231128
p++;
11241129
}
11251130
}
11261131
nHalfLines--;
11271132
}
1128
- return (size_t)(p-data);
1133
+ return p-data;
11291134
}
11301135
11311136
/* Check if the data starts with a classlist token of the special form.
11321137
** If so then return the length of that token, otherwise return 0.
11331138
**
11341139
** The token must start with a dot and must end with a colon;
11351140
** in between of these it must be a dot-separated list of words;
11361141
** each word may contain only alphanumeric characters and hyphens.
1142
+**
1143
+** If `bBlank` is non-zero then a blank character must follow
1144
+** the token's ending colon: otherwise function returns 0
1145
+** despite of the well-formed token.
11371146
*/
1138
-size_t is_footnote_classlist(const char * const data, size_t size){
1147
+size_t is_footnote_classlist(const char * const data, size_t size, int bBlank){
11391148
const char *p;
11401149
const char * const end = data+size;
11411150
if( data==end || *data != '.' ) return 0;
11421151
for(p=data+1; p!=end; p++){
11431152
if( fossil_isalnum(*p) || *p=='-' ) continue;
11441153
if( *p==':' ){
1145
- return p[-1]!='.' ? (size_t)(p-data)+1 : 0;
1146
- }
1147
- if( *p=='.' ){
1148
- if( p[-1]!='.' ) continue;
1149
- else break;
1154
+ if( p[-1]=='.' ) break;
1155
+ p++;
1156
+ if( bBlank ){
1157
+ if( p==end || !fossil_isspace(*p) ) break;
1158
+ }
1159
+ return p-data;
11501160
}
1161
+ if( *p=='.' && p[-1]!='.' ) continue;
11511162
break;
11521163
}
11531164
return 0;
11541165
}
11551166
@@ -1161,33 +1172,27 @@
11611172
const char *text,
11621173
size_t size
11631174
){
11641175
struct footnote fn = FOOTNOTE_INITIALIZER;
11651176
const char *zUPC = 0;
1166
- size_t nUPC = 0, n = count_whitespaces(text, size, 3);
1177
+ size_t nUPC = 0, n = sizeof_blank_prefix(text, size, 3);
11671178
if( n >= size ) return 0;
11681179
text += n;
11691180
size -= n;
1170
- n = is_footnote_classlist(text, size);
1171
- if( n && n < size ){
1172
- nUPC = n;
1181
+ nUPC = is_footnote_classlist(text, size, 1);
1182
+ if( nUPC ){
1183
+ assert( nUPC<size );
11731184
zUPC = text;
11741185
text += nUPC;
11751186
size -= nUPC;
1176
- n = count_whitespaces(text, size, 3);
1177
- /* naked classlist is treated as plain text */
1178
- if( n >= size || text[n]=='\n' || text[n]=='\r' ){
1179
- size += nUPC;
1180
- nUPC = 0;
1181
- text = zUPC;
1182
- zUPC = 0;
1183
- }else{
1184
- text += n;
1185
- size -= n;
1186
- }
1187
- }
1188
- if(!size) return 0;
1187
+ }
1188
+ if( sizeof_blank_prefix(text,size,-1)==size ){
1189
+ if( !nUPC ) return 0; /* empty inline footnote */
1190
+ text = zUPC;
1191
+ size = nUPC; /* bare classlist is treated */
1192
+ nUPC = 0; /* as plain text */
1193
+ }
11891194
fn.iMark = ++(rndr->notes.nMarks);
11901195
fn.nUsed = 1;
11911196
fn.index = COUNT_FOOTNOTES(&rndr->notes.all);
11921197
assert( fn.iMark > 0 );
11931198
blob_append(&fn.text, text, size);
@@ -1237,11 +1242,12 @@
12371242
12381243
if( size<4 || data[1]!='^' || !rndr->make.footnote_ref ) return 0;
12391244
end = matching_bracket_offset(data, data+size);
12401245
if( !end ) return 0;
12411246
fn = add_inline_footnote(rndr, data+2, end-2);
1242
- if(fn) rndr->make.footnote_ref(ob,0,&fn->upc,fn->iMark,1,rndr->make.opaque);
1247
+ if( !fn ) return 0;
1248
+ rndr->make.footnote_ref(ob,0,&fn->upc,fn->iMark,1,rndr->make.opaque);
12431249
return end+1;
12441250
}
12451251
12461252
/* char_link -- '[': parsing a link or an image */
12471253
static size_t char_link(
@@ -2462,11 +2468,11 @@
24622468
size_t beg, /* offset of the beginning of the line */
24632469
size_t end, /* offset of the end of the text */
24642470
size_t *last, /* last character of the link */
24652471
struct Blob * footnotes
24662472
){
2467
- size_t i, id_offset, id_end, upc_offset = 0, upc_size = 0;
2473
+ size_t i, id_offset, id_end, upc_offset, upc_size;
24682474
struct footnote fn = FOOTNOTE_INITIALIZER;
24692475
24702476
/* failfast if data is too short */
24712477
if( beg+5>=end ) return 0;
24722478
i = beg;
@@ -2490,26 +2496,18 @@
24902496
/* passthrough truncated footnote definition */
24912497
if( i>=end ) return 0;
24922498
24932499
if( build_ref_id(&fn.id, data+id_offset, id_end-id_offset)<0 ) return 0;
24942500
2495
- /* footnote's text may start on the same line */
2496
- upc_offset = 0;
2497
- if( data[i]!='\n' && data[i]!='\r' ){
2498
- upc_offset = i; /* prevent a retry on the second line */
2499
- upc_size = is_footnote_classlist(data+i,end-i);
2500
- if( upc_size ){
2501
- i += upc_size;
2502
- while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
2503
- if( i>=end ){
2504
- upc_size = 0; /* naked classlist is treated as plain text */
2505
- i = upc_offset;
2506
- }
2507
- }
2508
- }
2509
- if( data[i]!='\n' && data[i]!='\r' ){
2510
- const size_t j = i;
2501
+ /* footnote's text may start on the same line after [^id]: */
2502
+ upc_offset = upc_size = 0;
2503
+ if( data[i]!='\n' && data[i]!='\r' ){
2504
+ size_t j;
2505
+ upc_size = is_footnote_classlist(data+i, end-i, 1);
2506
+ upc_offset = i; /* prevent further checks for a classlist */
2507
+ i += upc_size;
2508
+ j = i;
25112509
do i++; while( i<end && data[i]!='\n' && data[i]!='\r' );
25122510
blob_append(&fn.text, data+j, i-j);
25132511
if( i<end ){
25142512
blob_append_char(&fn.text, data[i]);
25152513
i++;
@@ -2535,26 +2533,19 @@
25352533
25362534
/* process the 2nd and the following lines */
25372535
while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){
25382536
size_t j;
25392537
i += indent;
2540
- j = i;
25412538
if( !upc_offset ){
25422539
/* a classlist must be provided no later than at the 2nd line */
2543
- upc_offset = i + count_whitespaces(data+i, end-i, 1);
2544
- upc_size = is_footnote_classlist(data+upc_offset, end-upc_offset);
2540
+ upc_offset = i + sizeof_blank_prefix(data+i, end-i, 1);
2541
+ upc_size = is_footnote_classlist(data+upc_offset, end-upc_offset, 1);
25452542
if( upc_size ){
25462543
i = upc_offset + upc_size;
2547
- while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
2548
- if( i>=end ){
2549
- upc_size = 0; /* naked classlist is treated as plain text */
2550
- i = j;
2551
- }else{
2552
- j = i;
2553
- }
25542544
}
25552545
}
2546
+ j = i;
25562547
while( i<end && data[i]!='\n' && data[i]!='\r' ) i++;
25572548
blob_append(&fn.text, data+j, i-j);
25582549
if( i>=end ) break;
25592550
blob_append_char(&fn.text, data[i]);
25602551
i++;
@@ -2566,18 +2557,27 @@
25662557
}
25672558
footnote_finish:
25682559
if( !blob_size(&fn.text) ){
25692560
blob_reset(&fn.id);
25702561
return 0;
2562
+ }
2563
+ if( !blob_trim(&fn.text) ){ /* if the content is all-blank */
2564
+ if( upc_size ){ /* interpret UPC as plain text */
2565
+ blob_append(&fn.text, data+upc_offset, upc_size);
2566
+ upc_size = 0;
2567
+ }else{
2568
+ blob_reset(&fn.id); /* or clean up and fail */
2569
+ blob_reset(&fn.text);
2570
+ return 0;
2571
+ }
25712572
}
25722573
/* a valid note has been found */
25732574
if( last ) *last = i;
25742575
if( footnotes ){
25752576
fn.defno = COUNT_FOOTNOTES( footnotes );
25762577
if( upc_size ){
2577
- assert( upc_offset );
2578
- assert( upc_offset+upc_size < end );
2578
+ assert( upc_offset && upc_offset+upc_size<end );
25792579
blob_append(&fn.upc, data+upc_offset, upc_size);
25802580
}
25812581
blob_append(footnotes, (char *)&fn, sizeof fn);
25822582
}
25832583
return 1;
25842584
--- src/markdown.c
+++ src/markdown.c
@@ -1104,18 +1104,23 @@
1104 return fn;
1105 }
1106
1107 /* Counts characters in the blank prefix within at most nHalfLines.
1108 ** A sequence of spaces and tabs counts as odd halfline,
1109 ** a newline counts as even halfline
 
1110 */
1111 static inline size_t count_whitespaces(
1112 const char *data, size_t size, int nHalfLines
1113 ){
1114 const char *p = data;
1115 const char * const end = data+size;
1116 while( nHalfLines > 0 ){
 
 
 
 
1117 while( p!=end && (*p==' ' || *p=='\t' ) ){ p++; }
1118 if( p==end || --nHalfLines == 0 ) break;
1119 if( *p=='\n' || *p=='\r' ){
1120 p++;
1121 if( p==end ) break;
@@ -1123,33 +1128,39 @@
1123 p++;
1124 }
1125 }
1126 nHalfLines--;
1127 }
1128 return (size_t)(p-data);
1129 }
1130
1131 /* Check if the data starts with a classlist token of the special form.
1132 ** If so then return the length of that token, otherwise return 0.
1133 **
1134 ** The token must start with a dot and must end with a colon;
1135 ** in between of these it must be a dot-separated list of words;
1136 ** each word may contain only alphanumeric characters and hyphens.
 
 
 
 
1137 */
1138 size_t is_footnote_classlist(const char * const data, size_t size){
1139 const char *p;
1140 const char * const end = data+size;
1141 if( data==end || *data != '.' ) return 0;
1142 for(p=data+1; p!=end; p++){
1143 if( fossil_isalnum(*p) || *p=='-' ) continue;
1144 if( *p==':' ){
1145 return p[-1]!='.' ? (size_t)(p-data)+1 : 0;
1146 }
1147 if( *p=='.' ){
1148 if( p[-1]!='.' ) continue;
1149 else break;
 
1150 }
 
1151 break;
1152 }
1153 return 0;
1154 }
1155
@@ -1161,33 +1172,27 @@
1161 const char *text,
1162 size_t size
1163 ){
1164 struct footnote fn = FOOTNOTE_INITIALIZER;
1165 const char *zUPC = 0;
1166 size_t nUPC = 0, n = count_whitespaces(text, size, 3);
1167 if( n >= size ) return 0;
1168 text += n;
1169 size -= n;
1170 n = is_footnote_classlist(text, size);
1171 if( n && n < size ){
1172 nUPC = n;
1173 zUPC = text;
1174 text += nUPC;
1175 size -= nUPC;
1176 n = count_whitespaces(text, size, 3);
1177 /* naked classlist is treated as plain text */
1178 if( n >= size || text[n]=='\n' || text[n]=='\r' ){
1179 size += nUPC;
1180 nUPC = 0;
1181 text = zUPC;
1182 zUPC = 0;
1183 }else{
1184 text += n;
1185 size -= n;
1186 }
1187 }
1188 if(!size) return 0;
1189 fn.iMark = ++(rndr->notes.nMarks);
1190 fn.nUsed = 1;
1191 fn.index = COUNT_FOOTNOTES(&rndr->notes.all);
1192 assert( fn.iMark > 0 );
1193 blob_append(&fn.text, text, size);
@@ -1237,11 +1242,12 @@
1237
1238 if( size<4 || data[1]!='^' || !rndr->make.footnote_ref ) return 0;
1239 end = matching_bracket_offset(data, data+size);
1240 if( !end ) return 0;
1241 fn = add_inline_footnote(rndr, data+2, end-2);
1242 if(fn) rndr->make.footnote_ref(ob,0,&fn->upc,fn->iMark,1,rndr->make.opaque);
 
1243 return end+1;
1244 }
1245
1246 /* char_link -- '[': parsing a link or an image */
1247 static size_t char_link(
@@ -2462,11 +2468,11 @@
2462 size_t beg, /* offset of the beginning of the line */
2463 size_t end, /* offset of the end of the text */
2464 size_t *last, /* last character of the link */
2465 struct Blob * footnotes
2466 ){
2467 size_t i, id_offset, id_end, upc_offset = 0, upc_size = 0;
2468 struct footnote fn = FOOTNOTE_INITIALIZER;
2469
2470 /* failfast if data is too short */
2471 if( beg+5>=end ) return 0;
2472 i = beg;
@@ -2490,26 +2496,18 @@
2490 /* passthrough truncated footnote definition */
2491 if( i>=end ) return 0;
2492
2493 if( build_ref_id(&fn.id, data+id_offset, id_end-id_offset)<0 ) return 0;
2494
2495 /* footnote's text may start on the same line */
2496 upc_offset = 0;
2497 if( data[i]!='\n' && data[i]!='\r' ){
2498 upc_offset = i; /* prevent a retry on the second line */
2499 upc_size = is_footnote_classlist(data+i,end-i);
2500 if( upc_size ){
2501 i += upc_size;
2502 while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
2503 if( i>=end ){
2504 upc_size = 0; /* naked classlist is treated as plain text */
2505 i = upc_offset;
2506 }
2507 }
2508 }
2509 if( data[i]!='\n' && data[i]!='\r' ){
2510 const size_t j = i;
2511 do i++; while( i<end && data[i]!='\n' && data[i]!='\r' );
2512 blob_append(&fn.text, data+j, i-j);
2513 if( i<end ){
2514 blob_append_char(&fn.text, data[i]);
2515 i++;
@@ -2535,26 +2533,19 @@
2535
2536 /* process the 2nd and the following lines */
2537 while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){
2538 size_t j;
2539 i += indent;
2540 j = i;
2541 if( !upc_offset ){
2542 /* a classlist must be provided no later than at the 2nd line */
2543 upc_offset = i + count_whitespaces(data+i, end-i, 1);
2544 upc_size = is_footnote_classlist(data+upc_offset, end-upc_offset);
2545 if( upc_size ){
2546 i = upc_offset + upc_size;
2547 while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
2548 if( i>=end ){
2549 upc_size = 0; /* naked classlist is treated as plain text */
2550 i = j;
2551 }else{
2552 j = i;
2553 }
2554 }
2555 }
 
2556 while( i<end && data[i]!='\n' && data[i]!='\r' ) i++;
2557 blob_append(&fn.text, data+j, i-j);
2558 if( i>=end ) break;
2559 blob_append_char(&fn.text, data[i]);
2560 i++;
@@ -2566,18 +2557,27 @@
2566 }
2567 footnote_finish:
2568 if( !blob_size(&fn.text) ){
2569 blob_reset(&fn.id);
2570 return 0;
 
 
 
 
 
 
 
 
 
 
2571 }
2572 /* a valid note has been found */
2573 if( last ) *last = i;
2574 if( footnotes ){
2575 fn.defno = COUNT_FOOTNOTES( footnotes );
2576 if( upc_size ){
2577 assert( upc_offset );
2578 assert( upc_offset+upc_size < end );
2579 blob_append(&fn.upc, data+upc_offset, upc_size);
2580 }
2581 blob_append(footnotes, (char *)&fn, sizeof fn);
2582 }
2583 return 1;
2584
--- src/markdown.c
+++ src/markdown.c
@@ -1104,18 +1104,23 @@
1104 return fn;
1105 }
1106
1107 /* Counts characters in the blank prefix within at most nHalfLines.
1108 ** A sequence of spaces and tabs counts as odd halfline,
1109 ** a newline counts as even halfline.
1110 ** If nHalfLines < 0 then procceed without constrains.
1111 */
1112 static inline size_t sizeof_blank_prefix(
1113 const char *data, size_t size, int nHalfLines
1114 ){
1115 const char *p = data;
1116 const char * const end = data+size;
1117 if( nHalfLines < 0 ){
1118 while( p!=end && fossil_isspace(*p) ){
1119 p++;
1120 }
1121 }else while( nHalfLines > 0 ){
1122 while( p!=end && (*p==' ' || *p=='\t' ) ){ p++; }
1123 if( p==end || --nHalfLines == 0 ) break;
1124 if( *p=='\n' || *p=='\r' ){
1125 p++;
1126 if( p==end ) break;
@@ -1123,33 +1128,39 @@
1128 p++;
1129 }
1130 }
1131 nHalfLines--;
1132 }
1133 return p-data;
1134 }
1135
1136 /* Check if the data starts with a classlist token of the special form.
1137 ** If so then return the length of that token, otherwise return 0.
1138 **
1139 ** The token must start with a dot and must end with a colon;
1140 ** in between of these it must be a dot-separated list of words;
1141 ** each word may contain only alphanumeric characters and hyphens.
1142 **
1143 ** If `bBlank` is non-zero then a blank character must follow
1144 ** the token's ending colon: otherwise function returns 0
1145 ** despite of the well-formed token.
1146 */
1147 size_t is_footnote_classlist(const char * const data, size_t size, int bBlank){
1148 const char *p;
1149 const char * const end = data+size;
1150 if( data==end || *data != '.' ) return 0;
1151 for(p=data+1; p!=end; p++){
1152 if( fossil_isalnum(*p) || *p=='-' ) continue;
1153 if( *p==':' ){
1154 if( p[-1]=='.' ) break;
1155 p++;
1156 if( bBlank ){
1157 if( p==end || !fossil_isspace(*p) ) break;
1158 }
1159 return p-data;
1160 }
1161 if( *p=='.' && p[-1]!='.' ) continue;
1162 break;
1163 }
1164 return 0;
1165 }
1166
@@ -1161,33 +1172,27 @@
1172 const char *text,
1173 size_t size
1174 ){
1175 struct footnote fn = FOOTNOTE_INITIALIZER;
1176 const char *zUPC = 0;
1177 size_t nUPC = 0, n = sizeof_blank_prefix(text, size, 3);
1178 if( n >= size ) return 0;
1179 text += n;
1180 size -= n;
1181 nUPC = is_footnote_classlist(text, size, 1);
1182 if( nUPC ){
1183 assert( nUPC<size );
1184 zUPC = text;
1185 text += nUPC;
1186 size -= nUPC;
1187 }
1188 if( sizeof_blank_prefix(text,size,-1)==size ){
1189 if( !nUPC ) return 0; /* empty inline footnote */
1190 text = zUPC;
1191 size = nUPC; /* bare classlist is treated */
1192 nUPC = 0; /* as plain text */
1193 }
 
 
 
 
 
 
1194 fn.iMark = ++(rndr->notes.nMarks);
1195 fn.nUsed = 1;
1196 fn.index = COUNT_FOOTNOTES(&rndr->notes.all);
1197 assert( fn.iMark > 0 );
1198 blob_append(&fn.text, text, size);
@@ -1237,11 +1242,12 @@
1242
1243 if( size<4 || data[1]!='^' || !rndr->make.footnote_ref ) return 0;
1244 end = matching_bracket_offset(data, data+size);
1245 if( !end ) return 0;
1246 fn = add_inline_footnote(rndr, data+2, end-2);
1247 if( !fn ) return 0;
1248 rndr->make.footnote_ref(ob,0,&fn->upc,fn->iMark,1,rndr->make.opaque);
1249 return end+1;
1250 }
1251
1252 /* char_link -- '[': parsing a link or an image */
1253 static size_t char_link(
@@ -2462,11 +2468,11 @@
2468 size_t beg, /* offset of the beginning of the line */
2469 size_t end, /* offset of the end of the text */
2470 size_t *last, /* last character of the link */
2471 struct Blob * footnotes
2472 ){
2473 size_t i, id_offset, id_end, upc_offset, upc_size;
2474 struct footnote fn = FOOTNOTE_INITIALIZER;
2475
2476 /* failfast if data is too short */
2477 if( beg+5>=end ) return 0;
2478 i = beg;
@@ -2490,26 +2496,18 @@
2496 /* passthrough truncated footnote definition */
2497 if( i>=end ) return 0;
2498
2499 if( build_ref_id(&fn.id, data+id_offset, id_end-id_offset)<0 ) return 0;
2500
2501 /* footnote's text may start on the same line after [^id]: */
2502 upc_offset = upc_size = 0;
2503 if( data[i]!='\n' && data[i]!='\r' ){
2504 size_t j;
2505 upc_size = is_footnote_classlist(data+i, end-i, 1);
2506 upc_offset = i; /* prevent further checks for a classlist */
2507 i += upc_size;
2508 j = i;
 
 
 
 
 
 
 
 
2509 do i++; while( i<end && data[i]!='\n' && data[i]!='\r' );
2510 blob_append(&fn.text, data+j, i-j);
2511 if( i<end ){
2512 blob_append_char(&fn.text, data[i]);
2513 i++;
@@ -2535,26 +2533,19 @@
2533
2534 /* process the 2nd and the following lines */
2535 while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){
2536 size_t j;
2537 i += indent;
 
2538 if( !upc_offset ){
2539 /* a classlist must be provided no later than at the 2nd line */
2540 upc_offset = i + sizeof_blank_prefix(data+i, end-i, 1);
2541 upc_size = is_footnote_classlist(data+upc_offset, end-upc_offset, 1);
2542 if( upc_size ){
2543 i = upc_offset + upc_size;
 
 
 
 
 
 
 
2544 }
2545 }
2546 j = i;
2547 while( i<end && data[i]!='\n' && data[i]!='\r' ) i++;
2548 blob_append(&fn.text, data+j, i-j);
2549 if( i>=end ) break;
2550 blob_append_char(&fn.text, data[i]);
2551 i++;
@@ -2566,18 +2557,27 @@
2557 }
2558 footnote_finish:
2559 if( !blob_size(&fn.text) ){
2560 blob_reset(&fn.id);
2561 return 0;
2562 }
2563 if( !blob_trim(&fn.text) ){ /* if the content is all-blank */
2564 if( upc_size ){ /* interpret UPC as plain text */
2565 blob_append(&fn.text, data+upc_offset, upc_size);
2566 upc_size = 0;
2567 }else{
2568 blob_reset(&fn.id); /* or clean up and fail */
2569 blob_reset(&fn.text);
2570 return 0;
2571 }
2572 }
2573 /* a valid note has been found */
2574 if( last ) *last = i;
2575 if( footnotes ){
2576 fn.defno = COUNT_FOOTNOTES( footnotes );
2577 if( upc_size ){
2578 assert( upc_offset && upc_offset+upc_size<end );
 
2579 blob_append(&fn.upc, data+upc_offset, upc_size);
2580 }
2581 blob_append(footnotes, (char *)&fn, sizeof fn);
2582 }
2583 return 1;
2584
--- test/markdown-test3.md
+++ test/markdown-test3.md
@@ -63,10 +63,20 @@
6363
This facilitates the usage in the usual case
6464
when several footnotes are refenrenced at the end
6565
of a phrase.[^scipub][^many-refs](^All these four should
6666
be parsed as "free-standing" footnotes)[^Coelurosauria]
6767
68
+A footnote may not be empty(^)
69
+or consist just of blank characters.(^
70
+ )
71
+
72
+The same holds for labeled footnotes. If definition of a labeled footnote
73
+is blank then it is not accepted by the first pass of the parser and
74
+is recognized during the second pass as misreference.
75
+[^ This definition consists of just blanks ]:
76
+
77
+
6878
<style>
6979
li.fn-upc-example span.fn-upc {
7080
border: solid 2px lightgreen;
7181
border-radius: 0.25em;
7282
padding-left: 2px;
@@ -77,10 +87,13 @@
7787
font-weight: bold;
7888
}
7989
sup.noteref.fn-upc-example,
8090
span.notescope.fn-upc-example sup.noteref {
8191
border: solid 2px lightgreen;
92
+[^duplicate]:
93
+ Labeled footnote definition may appear anywhere.
94
+ That part came from inside of an inline style definition.
8295
border-radius: 0.4em;
8396
padding: 2px;
8497
}
8598
sup.noteref.fn-upc-example::after,
8699
span.notescope.fn-upc-example sup.noteref::after {
@@ -100,14 +113,20 @@
100113
This token defines a dot-separated list of CSS classes
101114
which are added to that particular footnote and also to the
102115
corresponding reference(s). Hypens ('-') are also allowed.
103116
Classes from the token are tranformed to lowercase and are prepended
104117
with `"fn-upc-"` to avoid collisions.
105
-)
118
+)
106119
This feature is "*opt-in*": there is nothing wrong in starting a footnote's
107120
text with a token of that form while not defining any corresponding classes
108121
in the stylesheet.[^nostyle]
122
+If a footnote consists just of a valid userclass token then this token
123
+is not interpreted as such, instead it is emitted as plain text.
124
+(^
125
+ .bare.classlist.inside.inline.footnote:
126
+)[^bare1]
127
+[^bare2]
109128
110129
## Footnotes
111130
112131
[branch]: /timeline?r=markdown-footnotes&nowiki
113132
@@ -146,10 +165,17 @@
146165
[^undefined label is used]: For example due to a typo.
147166
148167
[^another stray]: Just to verify the correctness of ordering and styling.
149168
150169
[^scipub]: Which is common in the scientific publications.
170
+
171
+[^bare1]: .at.the.1st.line.of.labeled.footnote.definition:
172
+
173
+
174
+[^bare2]:
175
+ .at.the.2nd.line.of.labeled.footnote.definition:
176
+
151177
152178
[^nostyle]:
153179
.unused.classes:
154180
In that case text of the footnote just looks like as if
155181
no special processing occured.
156182
--- test/markdown-test3.md
+++ test/markdown-test3.md
@@ -63,10 +63,20 @@
63 This facilitates the usage in the usual case
64 when several footnotes are refenrenced at the end
65 of a phrase.[^scipub][^many-refs](^All these four should
66 be parsed as "free-standing" footnotes)[^Coelurosauria]
67
 
 
 
 
 
 
 
 
 
 
68 <style>
69 li.fn-upc-example span.fn-upc {
70 border: solid 2px lightgreen;
71 border-radius: 0.25em;
72 padding-left: 2px;
@@ -77,10 +87,13 @@
77 font-weight: bold;
78 }
79 sup.noteref.fn-upc-example,
80 span.notescope.fn-upc-example sup.noteref {
81 border: solid 2px lightgreen;
 
 
 
82 border-radius: 0.4em;
83 padding: 2px;
84 }
85 sup.noteref.fn-upc-example::after,
86 span.notescope.fn-upc-example sup.noteref::after {
@@ -100,14 +113,20 @@
100 This token defines a dot-separated list of CSS classes
101 which are added to that particular footnote and also to the
102 corresponding reference(s). Hypens ('-') are also allowed.
103 Classes from the token are tranformed to lowercase and are prepended
104 with `"fn-upc-"` to avoid collisions.
105 )
106 This feature is "*opt-in*": there is nothing wrong in starting a footnote's
107 text with a token of that form while not defining any corresponding classes
108 in the stylesheet.[^nostyle]
 
 
 
 
 
 
109
110 ## Footnotes
111
112 [branch]: /timeline?r=markdown-footnotes&nowiki
113
@@ -146,10 +165,17 @@
146 [^undefined label is used]: For example due to a typo.
147
148 [^another stray]: Just to verify the correctness of ordering and styling.
149
150 [^scipub]: Which is common in the scientific publications.
 
 
 
 
 
 
 
151
152 [^nostyle]:
153 .unused.classes:
154 In that case text of the footnote just looks like as if
155 no special processing occured.
156
--- test/markdown-test3.md
+++ test/markdown-test3.md
@@ -63,10 +63,20 @@
63 This facilitates the usage in the usual case
64 when several footnotes are refenrenced at the end
65 of a phrase.[^scipub][^many-refs](^All these four should
66 be parsed as "free-standing" footnotes)[^Coelurosauria]
67
68 A footnote may not be empty(^)
69 or consist just of blank characters.(^
70 )
71
72 The same holds for labeled footnotes. If definition of a labeled footnote
73 is blank then it is not accepted by the first pass of the parser and
74 is recognized during the second pass as misreference.
75 [^ This definition consists of just blanks ]:
76
77
78 <style>
79 li.fn-upc-example span.fn-upc {
80 border: solid 2px lightgreen;
81 border-radius: 0.25em;
82 padding-left: 2px;
@@ -77,10 +87,13 @@
87 font-weight: bold;
88 }
89 sup.noteref.fn-upc-example,
90 span.notescope.fn-upc-example sup.noteref {
91 border: solid 2px lightgreen;
92 [^duplicate]:
93 Labeled footnote definition may appear anywhere.
94 That part came from inside of an inline style definition.
95 border-radius: 0.4em;
96 padding: 2px;
97 }
98 sup.noteref.fn-upc-example::after,
99 span.notescope.fn-upc-example sup.noteref::after {
@@ -100,14 +113,20 @@
113 This token defines a dot-separated list of CSS classes
114 which are added to that particular footnote and also to the
115 corresponding reference(s). Hypens ('-') are also allowed.
116 Classes from the token are tranformed to lowercase and are prepended
117 with `"fn-upc-"` to avoid collisions.
118 )
119 This feature is "*opt-in*": there is nothing wrong in starting a footnote's
120 text with a token of that form while not defining any corresponding classes
121 in the stylesheet.[^nostyle]
122 If a footnote consists just of a valid userclass token then this token
123 is not interpreted as such, instead it is emitted as plain text.
124 (^
125 .bare.classlist.inside.inline.footnote:
126 )[^bare1]
127 [^bare2]
128
129 ## Footnotes
130
131 [branch]: /timeline?r=markdown-footnotes&nowiki
132
@@ -146,10 +165,17 @@
165 [^undefined label is used]: For example due to a typo.
166
167 [^another stray]: Just to verify the correctness of ordering and styling.
168
169 [^scipub]: Which is common in the scientific publications.
170
171 [^bare1]: .at.the.1st.line.of.labeled.footnote.definition:
172
173
174 [^bare2]:
175 .at.the.2nd.line.of.labeled.footnote.definition:
176
177
178 [^nostyle]:
179 .unused.classes:
180 In that case text of the footnote just looks like as if
181 no special processing occured.
182

Keyboard Shortcuts

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