Fossil

Check-in [78b7846b8e]
Login

Check-in [78b7846b8e]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Support multiline footnote definitions and inline footnotes via <tt>^[...]</tt> syntax (this syntax is not settled yet). Fix overall link support that was broken by [e3710ccd3a5a].
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | markdown-footnotes
Files: files | file ages | folders
SHA3-256: 78b7846b8eff7c66df5c81a5d3bb02ea071ca97f2474f4c29cc30963be76de20
User & Date: george 2022-02-01 20:12:15.043
Original Comment: Support multiline footnote definitions (<tt>is_footnote()</tt> function) and inline footnotes via <verbatim>^[...]</verbatim> syntax (this syntax is not settled yet). Fix overall link support that was broken by [e3710ccd3a5a].
Context
2022-02-02
17:50
Bug-fix: Do not loose referenced footnotes when inline footnotes are also used. ... (check-in: a8f0f1e7ff user: george tags: markdown-footnotes)
2022-02-01
20:12
Support multiline footnote definitions and inline footnotes via <tt>^[...]</tt> syntax (this syntax is not settled yet). Fix overall link support that was broken by [e3710ccd3a5a]. ... (check-in: 78b7846b8e user: george tags: markdown-footnotes)
2022-01-30
17:38
Do not assume little-endian architecture within <tt>to_base26()</tt> function. ... (check-in: 4b63b1ee55 user: george tags: markdown-footnotes)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/backlink.c.
253
254
255
256
257
258
259


260
261
262
263
264
265
266
267
268
269
270


271
272
273
274
275
276
277
278
279


280
281
282
283
284
285
286
void markdown_extract_links(
  char *zInputText,
  Backlink *p
){
  struct mkd_renderer html_renderer = {
    /* prolog     */ (void(*)(Blob*,void*))mkdn_noop0,
    /* epilog     */ (void(*)(Blob*,void*))mkdn_noop0,


    /* blockcode  */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
    /* blockquote */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
    /* blockhtml  */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
    /* header     */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
    /* hrule      */ (void(*)(Blob*,void*))mkdn_noop0,
    /* list       */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
    /* listitem   */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
    /* paragraph  */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
    /* table      */ (void(*)(Blob*,Blob*,Blob*,void*))mkdn_noop0,
    /* table_cell */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
    /* table_row  */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,


    /* autolink   */ (int(*)(Blob*,Blob*,enum mkd_autolink,void*))mkdn_noop1,
    /* codespan   */ (int(*)(Blob*,Blob*,int,void*))mkdn_noop1,
    /* dbl_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
    /* emphasis   */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
    /* image      */ (int(*)(Blob*,Blob*,Blob*,Blob*,void*))mkdn_noop1,
    /* linebreak  */ (int(*)(Blob*,void*))mkdn_noop1,
    /* link       */ backlink_md_link,
    /* r_html_tag */ (int(*)(Blob*,Blob*,void*))mkdn_noop1,
    /* tri_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,


    0,  /* entity */
    0,  /* normal_text */
    "*_", /* emphasis characters */
    0   /* client data */
  };
  Blob out, in;
  html_renderer.opaque = (void*)p;







>
>











>
>









>
>







253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
void markdown_extract_links(
  char *zInputText,
  Backlink *p
){
  struct mkd_renderer html_renderer = {
    /* prolog     */ (void(*)(Blob*,void*))mkdn_noop0,
    /* epilog     */ (void(*)(Blob*,void*))mkdn_noop0,
    /* footnotes  */ (void(*)(Blob*,const Blob*, void*))mkdn_noop0,

    /* blockcode  */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
    /* blockquote */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
    /* blockhtml  */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
    /* header     */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
    /* hrule      */ (void(*)(Blob*,void*))mkdn_noop0,
    /* list       */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
    /* listitem   */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
    /* paragraph  */ (void(*)(Blob*,Blob*,void*))mkdn_noop0,
    /* table      */ (void(*)(Blob*,Blob*,Blob*,void*))mkdn_noop0,
    /* table_cell */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
    /* table_row  */ (void(*)(Blob*,Blob*,int,void*))mkdn_noop0,
    /* footnoteitm*/ (void(*)(Blob*,const Blob*,int,int,void*))mkdn_noop0,

    /* autolink   */ (int(*)(Blob*,Blob*,enum mkd_autolink,void*))mkdn_noop1,
    /* codespan   */ (int(*)(Blob*,Blob*,int,void*))mkdn_noop1,
    /* dbl_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
    /* emphasis   */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
    /* image      */ (int(*)(Blob*,Blob*,Blob*,Blob*,void*))mkdn_noop1,
    /* linebreak  */ (int(*)(Blob*,void*))mkdn_noop1,
    /* link       */ backlink_md_link,
    /* r_html_tag */ (int(*)(Blob*,Blob*,void*))mkdn_noop1,
    /* tri_emphas */ (int(*)(Blob*,Blob*,char,void*))mkdn_noop1,
    /* footnoteref*/ (int(*)(Blob*,int,int,void*))mkdn_noop1,

    0,  /* entity */
    0,  /* normal_text */
    "*_", /* emphasis characters */
    0   /* client data */
  };
  Blob out, in;
  html_renderer.opaque = (void*)p;
Changes to src/default.css.
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
div.content  div.markdown > ol.footnotes {
  font-size: 90%;
}
div.content  div.markdown > ol.footnotes > li {
  margin-bottom: 0.5em;
}
div.content  div.markdown > ol.footnotes > li > .footnote-backrefs {
  margin-right: 1em;
  font-weight: bold;
}
div.markdown > ol.footnotes > li > .footnote-backrefs > a:target {
  background: gold;
}
div.markdown a.noteref:target > sup {
  background: gold;







|







1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
div.content  div.markdown > ol.footnotes {
  font-size: 90%;
}
div.content  div.markdown > ol.footnotes > li {
  margin-bottom: 0.5em;
}
div.content  div.markdown > ol.footnotes > li > .footnote-backrefs {
  margin-right: 0.5em;
  font-weight: bold;
}
div.markdown > ol.footnotes > li > .footnote-backrefs > a:target {
  background: gold;
}
div.markdown a.noteref:target > sup {
  background: gold;
Changes to src/markdown.c.
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078

1079
1080
1081
1082
1083
1084
1085
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t offset,
  size_t size
){
  const int is_img = (offset && data[-1] == '!');
  const int is_inline = (offset && data[-1]=='^');
  const int is_note = !is_img && (is_inline || (size>1 && data[1]=='^'));
  size_t i = 1, txt_e;
  struct Blob *content = 0;
  struct Blob *link = 0;
  struct Blob *title = 0;
  const struct footnote *fn = 0;
  int level, ret;


  /* checking whether the correct renderer exists */
  if( (is_img && !rndr->make.image) || (!is_img && !rndr->make.link) ){
    return 0;
  }

  /* looking for the matching closing bracket */







<
<






>







1064
1065
1066
1067
1068
1069
1070


1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t offset,
  size_t size
){
  const int is_img = (offset && data[-1] == '!');


  size_t i = 1, txt_e;
  struct Blob *content = 0;
  struct Blob *link = 0;
  struct Blob *title = 0;
  const struct footnote *fn = 0;
  int level, ret;
  /* ? FIXME: assert( size>0 ); */

  /* checking whether the correct renderer exists */
  if( (is_img && !rndr->make.image) || (!is_img && !rndr->make.link) ){
    return 0;
  }

  /* looking for the matching closing bracket */
1137
1138
1139
1140
1141
1142
1143

1144
1145
1146

1147
1148
1149
1150

1151
1152
1153

1154


1155


1156


1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176


1177
1178
1179
1180
1181
1182
1183
      id_data = data+1;
      id_size = txt_e-1;
    }else{
      /* explicit id - between brackets */
      id_data = data+i+1;
      id_size = id_end-(i+1);
    }

    if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
      goto char_link_cleanup;
    }

    i = id_end+1;

  /* shortcut reference style link */
  }else{

    if( is_note ){
      if( is_inline ){
        //fn = put_footnote(rndr, data+1, txt_e-1);

      }else{


        fn = get_footnote(rndr, data+1, txt_e-1);


      }


      if( !fn ) goto char_link_cleanup;
    }else if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){
      goto char_link_cleanup;
    }

    /* rewinding the whitespace */
    i = txt_e+1;
  }

  /* building content: img alt is escaped, link content is parsed */
  if( txt_e>1 ){
    if( is_img ) blob_append(content, data+1, txt_e-1);
    else if(is_inline) parse_inline(content, rndr, data+1, txt_e-1);
  }

  /* calling the relevant rendering function */
  if( is_img ){
    if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ) ob->nUsed--;
    ret = rndr->make.image(ob, link, title, content, rndr->make.opaque);
  }else if(fn){


    if(rndr->make.footnote_ref){
      ret = rndr->make.footnote_ref(ob,fn->index,fn->nUsed,rndr->make.opaque);
    }
  }else{
    ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
  }








>



>




>
|
|
|
>
|
>
>
|
>
>
|
>
>












|







>
>







1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
      id_data = data+1;
      id_size = txt_e-1;
    }else{
      /* explicit id - between brackets */
      id_data = data+i+1;
      id_size = id_end-(i+1);
    }

    if( get_link_ref(rndr, link, title, id_data, id_size)<0 ){
      goto char_link_cleanup;
    }

    i = id_end+1;

  /* shortcut reference style link */
  }else{
    if( offset && data[-1]=='^' ){

      /* free-standing inline note */
      struct footnote note = {empty_blob,empty_blob,0,0};
      note.index = ++(rndr->iNotesCount);
      note.nUsed = 1;
      blob_append(&note.text,data+1,txt_e-1);
      blob_append(&rndr->notes, (char *)&note, sizeof note);
      fn = (struct footnote*)(blob_buffer(&rndr->notes)
              + blob_size(&rndr->notes) - sizeof(note));
    }else if(!is_img && size>2 && data[1]=='^'){

      /* free-standing reference */
      fn = get_footnote(rndr, data+1, txt_e-1);
      if( !fn ) goto char_link_cleanup;
    }else if( get_link_ref(rndr, link, title, data+1, txt_e-1)<0 ){
      goto char_link_cleanup;
    }

    /* rewinding the whitespace */
    i = txt_e+1;
  }

  /* building content: img alt is escaped, link content is parsed */
  if( txt_e>1 ){
    if( is_img ) blob_append(content, data+1, txt_e-1);
    else if(!fn) parse_inline(content, rndr, data+1, txt_e-1);
  }

  /* calling the relevant rendering function */
  if( is_img ){
    if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='!' ) ob->nUsed--;
    ret = rndr->make.image(ob, link, title, content, rndr->make.opaque);
  }else if(fn){
    if( blob_size(ob)>0 && blob_buffer(ob)[blob_size(ob)-1]=='^' ) ob->nUsed--;
    /* ? FIXME: the above line looks like a hack */
    if(rndr->make.footnote_ref){
      ret = rndr->make.footnote_ref(ob,fn->index,fn->nUsed,rndr->make.opaque);
    }
  }else{
    ret = rndr->make.link(ob, link, title, content, rndr->make.opaque);
  }

2158
2159
2160
2161
2162
2163
2164
2165
2166


2167
2168
2169
2170
2171
2172
2173
        if( data[beg+3]==' ' ) return 0;
      }
    }
  }
  i += beg;

  /* id part: anything but a newline between brackets */
  if( data[i]!='[' || data[i+1]=='^' ) return 0;
  i++;


  id_offset = i;
  while( i<end && data[i]!='\n' && data[i]!='\r' && data[i]!=']' ){ i++; }
  if( i>=end || data[i]!=']' ) return 0;
  id_end = i;

  /* spacer: colon (space | tab)* newline? (space | tab)* */
  i++;







|

>
>







2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
        if( data[beg+3]==' ' ) return 0;
      }
    }
  }
  i += beg;

  /* id part: anything but a newline between brackets */
  if( data[i]!='[' ) return 0;
  i++;
  if( i>=end || data[i]=='^' ) return 0;  /* see is_footnote() */

  id_offset = i;
  while( i<end && data[i]!='\n' && data[i]!='\r' && data[i]!=']' ){ i++; }
  if( i>=end || data[i]!=']' ) return 0;
  id_end = i;

  /* spacer: colon (space | tab)* newline? (space | tab)* */
  i++;
2188
2189
2190
2191
2192
2193
2194

2195
2196
2197
2198
2199
2200
2201
   && data[i]!=' '
   && data[i]!='\t'
   && data[i]!='\n'
   && data[i]!='\r'
  ){
    i += 1;
  }

  if( data[i-1]=='>' ) link_end = i-1; else link_end = i;

  /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
  while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
  if( i<end
   && data[i]!='\n'
   && data[i]!='\r'







>







2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
   && data[i]!=' '
   && data[i]!='\t'
   && data[i]!='\n'
   && data[i]!='\r'
  ){
    i += 1;
  }
  /* ? FIXME: if( data[i-1]=='>' && data[link_offset-1]!='<' ) */
  if( data[i-1]=='>' ) link_end = i-1; else link_end = i;

  /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
  while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
  if( i<end
   && data[i]!='\n'
   && data[i]!='\r'
2251
2252
2253
2254
2255
2256
2257
2258

2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272

2273
2274
2275
2276

2277
2278

2279


2280
2281
2282
2283
2284
2285
2286
2287
2288












2289








2290
2291
2292
2293
2294
2295
2296

2297

2298

2299


2300
2301



2302



2303

2304


2305





2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
  return 1;
}

/*********************
 * FOOTNOTE PARSING  *
 *********************/

/* is_footnote -- returns whether a line is a footnote or not */

static int is_footnote(
  const char *data,   /* input text */
  size_t beg,         /* offset of the beginning of the line */
  size_t end,         /* offset of the end of the text */
  size_t *last,       /* last character of the link */
  struct Blob * footnotes
){
  size_t i = 0;
  size_t id_offset, id_end;
  size_t note_offset, note_end;
  size_t line_end;
  struct footnote fn = { empty_blob, empty_blob, 0, 0 };

  /* footnote definition must start at the begining of a line */

  if( beg+4>=end ) return 0;
  i += beg;

  /* id part: anything but a newline between brackets */

  if( data[i]!='[' || data[i+1]!='^' ) return 0;
  i++;

  id_offset = i;


  while( i<end && data[i]!=']' && data[i]!='\n' && data[i]!='\r' ){ i++; }
  if( i>=end || data[i]!=']' ) return 0;
  id_end = i;

  /* spacer: colon (space | tab)* newline? (space | tab)* */
  i++;
  if( i>=end || data[i]!=':' ) return 0;
  i++;
  while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }












  if( i<end && (data[i]=='\n' || data[i]=='\r') ){








    i++;
    if( i<end && data[i]=='\r' && data[i-1] == '\n' ) i++;
  }
  while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }
  if( i>=end ) return 0;

  /* note is a single line of text (FIXME: support multiline notes) */

  note_offset = i;

  while( i<end && data[i]!='\r' && data[i]!='\n' ){ i++; }

  note_end = i;



  /* computing end-of-line */



  line_end = 0;



  if( i >=end || data[i]=='\r' || data[ i ]=='\n' ) line_end = i;

  if( i+1<end && data[i]=='\n' && data[i+1]=='\r' ) line_end = i+1;








  if( !line_end ) return 0; /* garbage after the link */

  /* a valid note has been found, filling-in note's text */
  if( last ) *last = line_end;
  if( !footnotes ) return 1;
  if( build_ref_id(&fn.id, data+id_offset, id_end-id_offset)<0 ) return 0;
  blob_append(&fn.text, data+note_offset, note_end-note_offset);
  blob_append(footnotes, (char *)&fn, sizeof fn);
  return 1;
}

/**********************
 * EXPORTED FUNCTIONS *
 **********************/








|
>







<
|
<
<


<
>
|
|

<
>
|

>
|
>
>


|

|
<



>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>



<
|

<
>
|
>
|
>
|
>
>

|
>
>
>
|
>
>
>
|
>
|
>
>
|
>
>
>
>
>
|
|
|
|
<
<
<
|







2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280

2281


2282
2283

2284
2285
2286
2287

2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299

2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326

2327
2328

2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360



2361
2362
2363
2364
2365
2366
2367
2368
  return 1;
}

/*********************
 * FOOTNOTE PARSING  *
 *********************/

/* is_footnote -- check if data holds a definition of a labeled footnote.
 * If so then append the corresponding element to `footnotes` array */
static int is_footnote(
  const char *data,   /* input text */
  size_t beg,         /* offset of the beginning of the line */
  size_t end,         /* offset of the end of the text */
  size_t *last,       /* last character of the link */
  struct Blob * footnotes
){

  size_t i, id_offset, id_end;


  struct footnote fn = { empty_blob, empty_blob, 0, 0 };


  /* failfast if data is too short */
  if( beg+5>=end ) return 0;
  i = beg;


  /* footnote definition must start at the begining of a line */
  if( data[i]!='[' ) return 0;
  i++;
  if( data[i]!='^' ) return 0;
  id_offset = i++;

  /* id part: anything but a newline between brackets */
  while( i<end && data[i]!=']' && data[i]!='\n' && data[i]!='\r' ){ i++; }
  if( i>=end || data[i]!=']' ) return 0;
  id_end = i++;

  /* spacer: colon (space | tab)* */

  if( i>=end || data[i]!=':' ) return 0;
  i++;
  while( i<end && (data[i]==' ' || data[i]=='\t') ){ i++; }

  /* passthrough truncated footnote definition
   * FIXME: maybe omit it? */
  if( i>=end ) return 0;

  if( build_ref_id(&fn.id, data+id_offset, id_end-id_offset)<0 ) return 0;

  /* footnote's text may start on the same line */
  if( data[i]!='\n' && data[i]!='\r' ){
    const size_t j = i;
    do i++; while( i<end && data[i]!='\n' && data[i]!='\r' );
    blob_append(&fn.text, data+j, i-j);
    if( i<end ){
      blob_append_char(&fn.text, data[i]);
      i++;
      if( i<end && data[i]=='\r' && data[i-1] == '\n' ){
        blob_append_char(&fn.text, data[i]);
        i++;
      }
    }
  }else{
    i++;
    if( i<end && data[i]=='\r' && data[i-1] == '\n' ) i++;
  }

  if( i<end ){


    /* compute the indentation from the 2nd line  */
    size_t indent = i;
    const char *spaces = data+i;
    while( i<end && data[i]==' ' ){ i++; }
    if( i>=end )   goto footnote_finish;
    indent = i - indent;
    i -= indent;
    if( indent<2 ) goto footnote_finish;

    /* process the 2nd and the following lines */
    while( i+indent<end && memcmp(data+i,spaces,indent)==0 ){
      size_t j;
      i += indent;
      j = i;
      while( i<end && data[i]!='\n' && data[i]!='\r' ) i++;
      blob_append(&fn.text, data+j, i-j);
      if( i>=end ) break;
      blob_append_char(&fn.text, data[i]);
      i++;
      if( i<end && data[i]=='\r' && data[i-1] == '\n' ){
        blob_append_char(&fn.text, data[i]);
        i++;
      }
    }
  }
footnote_finish:
  if( !blob_size(&fn.text) ){
    blob_reset(&fn.id);
    return 0;
  }
  /* a valid note has been found */
  if( last ) *last = i;



  if( footnotes ) blob_append(footnotes, (char *)&fn, sizeof fn);
  return 1;
}

/**********************
 * EXPORTED FUNCTIONS *
 **********************/

2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
    blob_reset(&lr[i].id);
    blob_reset(&lr[i].link);
    blob_reset(&lr[i].title);
  }
  blob_reset(&rndr.refs);
  end = COUNT_FOOTNOTES(&rndr.notes);
  for(i=0; i<end; i++){
    blob_reset(&fn[i].id);
    blob_reset(&fn[i].text);
  }
  blob_reset(&rndr.notes);
  for(i=0; i<rndr.nBlobCache; i++){
    fossil_free(rndr.aBlobCache[i]);
  }
}







|







2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
    blob_reset(&lr[i].id);
    blob_reset(&lr[i].link);
    blob_reset(&lr[i].title);
  }
  blob_reset(&rndr.refs);
  end = COUNT_FOOTNOTES(&rndr.notes);
  for(i=0; i<end; i++){
    if(blob_size(&fn[i].id)) blob_reset(&fn[i].id);
    blob_reset(&fn[i].text);
  }
  blob_reset(&rndr.notes);
  for(i=0; i<rndr.nBlobCache; i++){
    fossil_free(rndr.aBlobCache[i]);
  }
}
Changes to src/markdown_html.c.
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
      const bitfield64_t l = to_base26(i,0);
      blob_appendf(ob," <a id='footnote-%s-%s'"
                       " href='#noteref-%s-%s'>%s</a>",
                       pos,l.c, pos,l.c, l.c);
    }
    if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
  }
  BLOB_APPEND_LITERAL(ob,"</sup>\n\t");
  BLOB_APPEND_BLOB(ob, text);
  BLOB_APPEND_LITERAL(ob, "\n</li>\n");
}
static void html_footnotes(
  struct Blob *ob, const struct Blob *items, void *opaque
){
  if( items && blob_size(items) ){







|







377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
      const bitfield64_t l = to_base26(i,0);
      blob_appendf(ob," <a id='footnote-%s-%s'"
                       " href='#noteref-%s-%s'>%s</a>",
                       pos,l.c, pos,l.c, l.c);
    }
    if( i < nUsed ) BLOB_APPEND_LITERAL(ob," &hellip;");
  }
  BLOB_APPEND_LITERAL(ob,"</sup>\n");
  BLOB_APPEND_BLOB(ob, text);
  BLOB_APPEND_LITERAL(ob, "\n</li>\n");
}
static void html_footnotes(
  struct Blob *ob, const struct Blob *items, void *opaque
){
  if( items && blob_size(items) ){