Fossil SCM

For embedded documentation, if the content file has mimetype text/html but it begins with a <div> element that has class=fossil-doc, then add the usual header and footer to the content before displaying it. Also, if the <div> element has a data-title=TITLE attribute, then use TITLE as the title of the document.

drh 2015-02-01 18:22 trunk
Commit ace8016f297a1d0591d7569e8ed7a289444baa7b
1 file changed +86 -8
+86 -8
--- src/doc.c
+++ src/doc.c
@@ -122,13 +122,15 @@
122122
{ "deb", 3, "application/x-debian-package" },
123123
{ "dir", 3, "application/x-director" },
124124
{ "dl", 2, "video/dl" },
125125
{ "dms", 3, "application/octet-stream" },
126126
{ "doc", 3, "application/msword" },
127
- { "docx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
127
+ { "docx", 4, "application/vnd.openxmlformats-"
128
+ "officedocument.wordprocessingml.document"},
128129
{ "dot", 3, "application/msword" },
129
- { "dotx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
130
+ { "dotx", 4, "application/vnd.openxmlformats-"
131
+ "officedocument.wordprocessingml.template"},
130132
{ "drw", 3, "application/drafting" },
131133
{ "dvi", 3, "application/x-dvi" },
132134
{ "dwg", 3, "application/acad" },
133135
{ "dxf", 3, "application/dxf" },
134136
{ "dxr", 3, "application/x-director" },
@@ -203,16 +205,19 @@
203205
{ "pl", 2, "application/x-perl" },
204206
{ "pm", 2, "application/x-perl" },
205207
{ "png", 3, "image/png" },
206208
{ "pnm", 3, "image/x-portable-anymap" },
207209
{ "pot", 3, "application/mspowerpoint" },
208
- { "potx", 4, "application/vnd.openxmlformats-officedocument.presentationml.template"},
210
+ { "potx", 4, "application/vnd.openxmlformats-"
211
+ "officedocument.presentationml.template"},
209212
{ "ppm", 3, "image/x-portable-pixmap" },
210213
{ "pps", 3, "application/mspowerpoint" },
211
- { "ppsx", 4, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
214
+ { "ppsx", 4, "application/vnd.openxmlformats-"
215
+ "officedocument.presentationml.slideshow"},
212216
{ "ppt", 3, "application/mspowerpoint" },
213
- { "pptx", 4, "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
217
+ { "pptx", 4, "application/vnd.openxmlformats-"
218
+ "officedocument.presentationml.presentation"},
214219
{ "ppz", 3, "application/mspowerpoint" },
215220
{ "pre", 3, "application/x-freelance" },
216221
{ "prt", 3, "application/pro_eng" },
217222
{ "ps", 2, "application/postscript" },
218223
{ "qt", 2, "video/quicktime" },
@@ -284,11 +289,12 @@
284289
{ "xbm", 3, "image/x-xbitmap" },
285290
{ "xlc", 3, "application/vnd.ms-excel" },
286291
{ "xll", 3, "application/vnd.ms-excel" },
287292
{ "xlm", 3, "application/vnd.ms-excel" },
288293
{ "xls", 3, "application/vnd.ms-excel" },
289
- { "xlsx", 4, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
294
+ { "xlsx", 4, "application/vnd.openxmlformats-"
295
+ "officedocument.spreadsheetml.sheet"},
290296
{ "xlw", 3, "application/vnd.ms-excel" },
291297
{ "xml", 3, "text/xml" },
292298
{ "xpm", 3, "image/x-xpixmap" },
293299
{ "xwd", 3, "image/x-xwindowdump" },
294300
{ "xyz", 3, "chemical/x-pdb" },
@@ -351,10 +357,75 @@
351357
for(i=2; i<g.argc; i++){
352358
fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i]));
353359
}
354360
}
355361
362
+
363
+
364
+/*
365
+** Check to see if the file in the pContent blob is "embedded HTML". Return
366
+** true if it is, and fill pTitle with the document title.
367
+**
368
+** An "embedded HTML" file is HTML that lacks a header and a footer. The
369
+** standard Fossil header is prepended and the standard Fossil footer is
370
+** appended. Otherwise, the file is displayed without change.
371
+**
372
+** Embedded HTML must be contained in a <div class='fossil-doc'> element.
373
+** If that <div> also contains a data-title attribute, then the
374
+** value of that attribute is extracted into pTitle and becomes the title
375
+** of the document.
376
+*/
377
+int doc_is_embedded_html(Blob *pContent, Blob *pTitle){
378
+ const char *zIn = blob_str(pContent);
379
+ const char *zAttr;
380
+ const char *zValue;
381
+ int nAttr, nValue;
382
+ int seenClass = 0;
383
+ int seenTitle = 0;
384
+
385
+ while( fossil_isspace(zIn[0]) ) zIn++;
386
+ if( fossil_strnicmp(zIn,"<div",4)!=0 ) return 0;
387
+ zIn += 4;
388
+ while( zIn[0] ){
389
+ if( fossil_isspace(zIn[0]) ) zIn++;
390
+ if( zIn[0]=='>' ) return 0;
391
+ zAttr = zIn;
392
+ while( fossil_isalnum(zIn[0]) || zIn[0]=='-' ) zIn++;
393
+ nAttr = (int)(zIn - zAttr);
394
+ while( fossil_isspace(zIn[0]) ) zIn++;
395
+ if( zIn[0]!='=' ) continue;
396
+ zIn++;
397
+ while( fossil_isspace(zIn[0]) ) zIn++;
398
+ if( zIn[0]=='"' || zIn[0]=='\'' ){
399
+ char cDelim = zIn[0];
400
+ zIn++;
401
+ zValue = zIn;
402
+ while( zIn[0] && zIn[0]!=cDelim ) zIn++;
403
+ if( zIn[0]==0 ) return 0;
404
+ nValue = (int)(zIn - zValue);
405
+ zIn++;
406
+ }else{
407
+ zValue = zIn;
408
+ while( zIn[0]!=0 && zIn[0]!='>' && zIn[0]!='/'
409
+ && !fossil_isspace(zIn[0]) ) zIn++;
410
+ if( zIn[0]==0 ) return 0;
411
+ nValue = (int)(zIn - zValue);
412
+ }
413
+ if( nAttr==5 && fossil_strnicmp(zAttr,"class",5)==0 ){
414
+ if( nValue!=10 || fossil_strnicmp(zValue,"fossil-doc",10)!=0 ) return 0;
415
+ seenClass = 1;
416
+ if( seenTitle ) return 1;
417
+ }
418
+ if( nAttr==10 && fossil_strnicmp(zAttr,"data-title",10)==0 ){
419
+ blob_append(pTitle, zValue, nValue);
420
+ seenTitle = 1;
421
+ if( seenClass ) return 1;
422
+ }
423
+ }
424
+ return seenClass;
425
+}
426
+
356427
/*
357428
** Look for a file named zName in the checkin with RID=vid. Load the content
358429
** of that file into pContent and return the RID for the file. Or return 0
359430
** if the file is not found or could not be loaded.
360431
*/
@@ -426,17 +497,19 @@
426497
char *zCheckin = "tip"; /* The checkin holding the document */
427498
int vid = 0; /* Artifact of checkin */
428499
int rid = 0; /* Artifact of file */
429500
int i; /* Loop counter */
430501
Blob filebody; /* Content of the documentation file */
502
+ Blob title; /* Document title */
431503
int nMiss = (-1); /* Failed attempts to find the document */
432504
static const char *const azSuffix[] = {
433505
"index.html", "index.wiki", "index.md"
434506
};
435507
436508
login_check_credentials();
437509
if( !g.perm.Read ){ login_needed(); return; }
510
+ blob_init(&title, 0, 0);
438511
db_begin_transaction();
439512
while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){
440513
zName = PD("name", "tip/index.wiki");
441514
for(i=0; zName[i] && zName[i]!='/'; i++){}
442515
zCheckin = mprintf("%.*s", i, zName);
@@ -494,11 +567,11 @@
494567
Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
495568
" FROM blob WHERE rid=%d", vid));
496569
Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
497570
" WHERE objid=%d AND type='ci'", vid));
498571
if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
499
- Blob title, tail;
572
+ Blob tail;
500573
style_adunit_config(ADUNIT_RIGHT_OK);
501574
if( wiki_find_title(&filebody, &title, &tail) ){
502575
style_header("%s", blob_str(&title));
503576
wiki_convert(&tail, 0, WIKI_BUTTONS);
504577
}else{
@@ -505,11 +578,10 @@
505578
style_header("Documentation");
506579
wiki_convert(&filebody, 0, WIKI_BUTTONS);
507580
}
508581
style_footer();
509582
}else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
510
- Blob title = BLOB_INITIALIZER;
511583
Blob tail = BLOB_INITIALIZER;
512584
markdown_to_html(&filebody, &title, &tail);
513585
if( blob_size(&title)>0 ){
514586
style_header("%s", blob_str(&title));
515587
}else{
@@ -522,10 +594,16 @@
522594
style_header("Documentation");
523595
@ <blockquote><pre>
524596
@ %h(blob_str(&filebody))
525597
@ </pre></blockquote>
526598
style_footer();
599
+ }else if( fossil_strcmp(zMime, "text/html")==0
600
+ && doc_is_embedded_html(&filebody, &title) ){
601
+ if( blob_size(&title)==0 ) blob_append(&title,zName,-1);
602
+ style_header("%s", blob_str(&title));
603
+ blob_append(cgi_output_blob(), blob_buffer(&filebody),blob_size(&filebody));
604
+ style_footer();
527605
#ifdef FOSSIL_ENABLE_TH1_DOCS
528606
}else if( db_get_boolean("th1-docs", 0) &&
529607
fossil_strcmp(zMime, "application/x-th1")==0 ){
530608
style_header("%h", zName);
531609
Th_Render(blob_str(&filebody));
532610
--- src/doc.c
+++ src/doc.c
@@ -122,13 +122,15 @@
122 { "deb", 3, "application/x-debian-package" },
123 { "dir", 3, "application/x-director" },
124 { "dl", 2, "video/dl" },
125 { "dms", 3, "application/octet-stream" },
126 { "doc", 3, "application/msword" },
127 { "docx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
 
128 { "dot", 3, "application/msword" },
129 { "dotx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
 
130 { "drw", 3, "application/drafting" },
131 { "dvi", 3, "application/x-dvi" },
132 { "dwg", 3, "application/acad" },
133 { "dxf", 3, "application/dxf" },
134 { "dxr", 3, "application/x-director" },
@@ -203,16 +205,19 @@
203 { "pl", 2, "application/x-perl" },
204 { "pm", 2, "application/x-perl" },
205 { "png", 3, "image/png" },
206 { "pnm", 3, "image/x-portable-anymap" },
207 { "pot", 3, "application/mspowerpoint" },
208 { "potx", 4, "application/vnd.openxmlformats-officedocument.presentationml.template"},
 
209 { "ppm", 3, "image/x-portable-pixmap" },
210 { "pps", 3, "application/mspowerpoint" },
211 { "ppsx", 4, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
 
212 { "ppt", 3, "application/mspowerpoint" },
213 { "pptx", 4, "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
 
214 { "ppz", 3, "application/mspowerpoint" },
215 { "pre", 3, "application/x-freelance" },
216 { "prt", 3, "application/pro_eng" },
217 { "ps", 2, "application/postscript" },
218 { "qt", 2, "video/quicktime" },
@@ -284,11 +289,12 @@
284 { "xbm", 3, "image/x-xbitmap" },
285 { "xlc", 3, "application/vnd.ms-excel" },
286 { "xll", 3, "application/vnd.ms-excel" },
287 { "xlm", 3, "application/vnd.ms-excel" },
288 { "xls", 3, "application/vnd.ms-excel" },
289 { "xlsx", 4, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
 
290 { "xlw", 3, "application/vnd.ms-excel" },
291 { "xml", 3, "text/xml" },
292 { "xpm", 3, "image/x-xpixmap" },
293 { "xwd", 3, "image/x-xwindowdump" },
294 { "xyz", 3, "chemical/x-pdb" },
@@ -351,10 +357,75 @@
351 for(i=2; i<g.argc; i++){
352 fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i]));
353 }
354 }
355
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356 /*
357 ** Look for a file named zName in the checkin with RID=vid. Load the content
358 ** of that file into pContent and return the RID for the file. Or return 0
359 ** if the file is not found or could not be loaded.
360 */
@@ -426,17 +497,19 @@
426 char *zCheckin = "tip"; /* The checkin holding the document */
427 int vid = 0; /* Artifact of checkin */
428 int rid = 0; /* Artifact of file */
429 int i; /* Loop counter */
430 Blob filebody; /* Content of the documentation file */
 
431 int nMiss = (-1); /* Failed attempts to find the document */
432 static const char *const azSuffix[] = {
433 "index.html", "index.wiki", "index.md"
434 };
435
436 login_check_credentials();
437 if( !g.perm.Read ){ login_needed(); return; }
 
438 db_begin_transaction();
439 while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){
440 zName = PD("name", "tip/index.wiki");
441 for(i=0; zName[i] && zName[i]!='/'; i++){}
442 zCheckin = mprintf("%.*s", i, zName);
@@ -494,11 +567,11 @@
494 Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
495 " FROM blob WHERE rid=%d", vid));
496 Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
497 " WHERE objid=%d AND type='ci'", vid));
498 if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
499 Blob title, tail;
500 style_adunit_config(ADUNIT_RIGHT_OK);
501 if( wiki_find_title(&filebody, &title, &tail) ){
502 style_header("%s", blob_str(&title));
503 wiki_convert(&tail, 0, WIKI_BUTTONS);
504 }else{
@@ -505,11 +578,10 @@
505 style_header("Documentation");
506 wiki_convert(&filebody, 0, WIKI_BUTTONS);
507 }
508 style_footer();
509 }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
510 Blob title = BLOB_INITIALIZER;
511 Blob tail = BLOB_INITIALIZER;
512 markdown_to_html(&filebody, &title, &tail);
513 if( blob_size(&title)>0 ){
514 style_header("%s", blob_str(&title));
515 }else{
@@ -522,10 +594,16 @@
522 style_header("Documentation");
523 @ <blockquote><pre>
524 @ %h(blob_str(&filebody))
525 @ </pre></blockquote>
526 style_footer();
 
 
 
 
 
 
527 #ifdef FOSSIL_ENABLE_TH1_DOCS
528 }else if( db_get_boolean("th1-docs", 0) &&
529 fossil_strcmp(zMime, "application/x-th1")==0 ){
530 style_header("%h", zName);
531 Th_Render(blob_str(&filebody));
532
--- src/doc.c
+++ src/doc.c
@@ -122,13 +122,15 @@
122 { "deb", 3, "application/x-debian-package" },
123 { "dir", 3, "application/x-director" },
124 { "dl", 2, "video/dl" },
125 { "dms", 3, "application/octet-stream" },
126 { "doc", 3, "application/msword" },
127 { "docx", 4, "application/vnd.openxmlformats-"
128 "officedocument.wordprocessingml.document"},
129 { "dot", 3, "application/msword" },
130 { "dotx", 4, "application/vnd.openxmlformats-"
131 "officedocument.wordprocessingml.template"},
132 { "drw", 3, "application/drafting" },
133 { "dvi", 3, "application/x-dvi" },
134 { "dwg", 3, "application/acad" },
135 { "dxf", 3, "application/dxf" },
136 { "dxr", 3, "application/x-director" },
@@ -203,16 +205,19 @@
205 { "pl", 2, "application/x-perl" },
206 { "pm", 2, "application/x-perl" },
207 { "png", 3, "image/png" },
208 { "pnm", 3, "image/x-portable-anymap" },
209 { "pot", 3, "application/mspowerpoint" },
210 { "potx", 4, "application/vnd.openxmlformats-"
211 "officedocument.presentationml.template"},
212 { "ppm", 3, "image/x-portable-pixmap" },
213 { "pps", 3, "application/mspowerpoint" },
214 { "ppsx", 4, "application/vnd.openxmlformats-"
215 "officedocument.presentationml.slideshow"},
216 { "ppt", 3, "application/mspowerpoint" },
217 { "pptx", 4, "application/vnd.openxmlformats-"
218 "officedocument.presentationml.presentation"},
219 { "ppz", 3, "application/mspowerpoint" },
220 { "pre", 3, "application/x-freelance" },
221 { "prt", 3, "application/pro_eng" },
222 { "ps", 2, "application/postscript" },
223 { "qt", 2, "video/quicktime" },
@@ -284,11 +289,12 @@
289 { "xbm", 3, "image/x-xbitmap" },
290 { "xlc", 3, "application/vnd.ms-excel" },
291 { "xll", 3, "application/vnd.ms-excel" },
292 { "xlm", 3, "application/vnd.ms-excel" },
293 { "xls", 3, "application/vnd.ms-excel" },
294 { "xlsx", 4, "application/vnd.openxmlformats-"
295 "officedocument.spreadsheetml.sheet"},
296 { "xlw", 3, "application/vnd.ms-excel" },
297 { "xml", 3, "text/xml" },
298 { "xpm", 3, "image/x-xpixmap" },
299 { "xwd", 3, "image/x-xwindowdump" },
300 { "xyz", 3, "chemical/x-pdb" },
@@ -351,10 +357,75 @@
357 for(i=2; i<g.argc; i++){
358 fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i]));
359 }
360 }
361
362
363
364 /*
365 ** Check to see if the file in the pContent blob is "embedded HTML". Return
366 ** true if it is, and fill pTitle with the document title.
367 **
368 ** An "embedded HTML" file is HTML that lacks a header and a footer. The
369 ** standard Fossil header is prepended and the standard Fossil footer is
370 ** appended. Otherwise, the file is displayed without change.
371 **
372 ** Embedded HTML must be contained in a <div class='fossil-doc'> element.
373 ** If that <div> also contains a data-title attribute, then the
374 ** value of that attribute is extracted into pTitle and becomes the title
375 ** of the document.
376 */
377 int doc_is_embedded_html(Blob *pContent, Blob *pTitle){
378 const char *zIn = blob_str(pContent);
379 const char *zAttr;
380 const char *zValue;
381 int nAttr, nValue;
382 int seenClass = 0;
383 int seenTitle = 0;
384
385 while( fossil_isspace(zIn[0]) ) zIn++;
386 if( fossil_strnicmp(zIn,"<div",4)!=0 ) return 0;
387 zIn += 4;
388 while( zIn[0] ){
389 if( fossil_isspace(zIn[0]) ) zIn++;
390 if( zIn[0]=='>' ) return 0;
391 zAttr = zIn;
392 while( fossil_isalnum(zIn[0]) || zIn[0]=='-' ) zIn++;
393 nAttr = (int)(zIn - zAttr);
394 while( fossil_isspace(zIn[0]) ) zIn++;
395 if( zIn[0]!='=' ) continue;
396 zIn++;
397 while( fossil_isspace(zIn[0]) ) zIn++;
398 if( zIn[0]=='"' || zIn[0]=='\'' ){
399 char cDelim = zIn[0];
400 zIn++;
401 zValue = zIn;
402 while( zIn[0] && zIn[0]!=cDelim ) zIn++;
403 if( zIn[0]==0 ) return 0;
404 nValue = (int)(zIn - zValue);
405 zIn++;
406 }else{
407 zValue = zIn;
408 while( zIn[0]!=0 && zIn[0]!='>' && zIn[0]!='/'
409 && !fossil_isspace(zIn[0]) ) zIn++;
410 if( zIn[0]==0 ) return 0;
411 nValue = (int)(zIn - zValue);
412 }
413 if( nAttr==5 && fossil_strnicmp(zAttr,"class",5)==0 ){
414 if( nValue!=10 || fossil_strnicmp(zValue,"fossil-doc",10)!=0 ) return 0;
415 seenClass = 1;
416 if( seenTitle ) return 1;
417 }
418 if( nAttr==10 && fossil_strnicmp(zAttr,"data-title",10)==0 ){
419 blob_append(pTitle, zValue, nValue);
420 seenTitle = 1;
421 if( seenClass ) return 1;
422 }
423 }
424 return seenClass;
425 }
426
427 /*
428 ** Look for a file named zName in the checkin with RID=vid. Load the content
429 ** of that file into pContent and return the RID for the file. Or return 0
430 ** if the file is not found or could not be loaded.
431 */
@@ -426,17 +497,19 @@
497 char *zCheckin = "tip"; /* The checkin holding the document */
498 int vid = 0; /* Artifact of checkin */
499 int rid = 0; /* Artifact of file */
500 int i; /* Loop counter */
501 Blob filebody; /* Content of the documentation file */
502 Blob title; /* Document title */
503 int nMiss = (-1); /* Failed attempts to find the document */
504 static const char *const azSuffix[] = {
505 "index.html", "index.wiki", "index.md"
506 };
507
508 login_check_credentials();
509 if( !g.perm.Read ){ login_needed(); return; }
510 blob_init(&title, 0, 0);
511 db_begin_transaction();
512 while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){
513 zName = PD("name", "tip/index.wiki");
514 for(i=0; zName[i] && zName[i]!='/'; i++){}
515 zCheckin = mprintf("%.*s", i, zName);
@@ -494,11 +567,11 @@
567 Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'"
568 " FROM blob WHERE rid=%d", vid));
569 Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
570 " WHERE objid=%d AND type='ci'", vid));
571 if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
572 Blob tail;
573 style_adunit_config(ADUNIT_RIGHT_OK);
574 if( wiki_find_title(&filebody, &title, &tail) ){
575 style_header("%s", blob_str(&title));
576 wiki_convert(&tail, 0, WIKI_BUTTONS);
577 }else{
@@ -505,11 +578,10 @@
578 style_header("Documentation");
579 wiki_convert(&filebody, 0, WIKI_BUTTONS);
580 }
581 style_footer();
582 }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
 
583 Blob tail = BLOB_INITIALIZER;
584 markdown_to_html(&filebody, &title, &tail);
585 if( blob_size(&title)>0 ){
586 style_header("%s", blob_str(&title));
587 }else{
@@ -522,10 +594,16 @@
594 style_header("Documentation");
595 @ <blockquote><pre>
596 @ %h(blob_str(&filebody))
597 @ </pre></blockquote>
598 style_footer();
599 }else if( fossil_strcmp(zMime, "text/html")==0
600 && doc_is_embedded_html(&filebody, &title) ){
601 if( blob_size(&title)==0 ) blob_append(&title,zName,-1);
602 style_header("%s", blob_str(&title));
603 blob_append(cgi_output_blob(), blob_buffer(&filebody),blob_size(&filebody));
604 style_footer();
605 #ifdef FOSSIL_ENABLE_TH1_DOCS
606 }else if( db_get_boolean("th1-docs", 0) &&
607 fossil_strcmp(zMime, "application/x-th1")==0 ){
608 style_header("%h", zName);
609 Th_Render(blob_str(&filebody));
610

Keyboard Shortcuts

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