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.
Commit
ace8016f297a1d0591d7569e8ed7a289444baa7b
Parent
8e02c26ad2baf85…
1 file changed
+86
-8
+86
-8
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -122,13 +122,15 @@ | ||
| 122 | 122 | { "deb", 3, "application/x-debian-package" }, |
| 123 | 123 | { "dir", 3, "application/x-director" }, |
| 124 | 124 | { "dl", 2, "video/dl" }, |
| 125 | 125 | { "dms", 3, "application/octet-stream" }, |
| 126 | 126 | { "doc", 3, "application/msword" }, |
| 127 | - { "docx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, | |
| 127 | + { "docx", 4, "application/vnd.openxmlformats-" | |
| 128 | + "officedocument.wordprocessingml.document"}, | |
| 128 | 129 | { "dot", 3, "application/msword" }, |
| 129 | - { "dotx", 4, "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, | |
| 130 | + { "dotx", 4, "application/vnd.openxmlformats-" | |
| 131 | + "officedocument.wordprocessingml.template"}, | |
| 130 | 132 | { "drw", 3, "application/drafting" }, |
| 131 | 133 | { "dvi", 3, "application/x-dvi" }, |
| 132 | 134 | { "dwg", 3, "application/acad" }, |
| 133 | 135 | { "dxf", 3, "application/dxf" }, |
| 134 | 136 | { "dxr", 3, "application/x-director" }, |
| @@ -203,16 +205,19 @@ | ||
| 203 | 205 | { "pl", 2, "application/x-perl" }, |
| 204 | 206 | { "pm", 2, "application/x-perl" }, |
| 205 | 207 | { "png", 3, "image/png" }, |
| 206 | 208 | { "pnm", 3, "image/x-portable-anymap" }, |
| 207 | 209 | { "pot", 3, "application/mspowerpoint" }, |
| 208 | - { "potx", 4, "application/vnd.openxmlformats-officedocument.presentationml.template"}, | |
| 210 | + { "potx", 4, "application/vnd.openxmlformats-" | |
| 211 | + "officedocument.presentationml.template"}, | |
| 209 | 212 | { "ppm", 3, "image/x-portable-pixmap" }, |
| 210 | 213 | { "pps", 3, "application/mspowerpoint" }, |
| 211 | - { "ppsx", 4, "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, | |
| 214 | + { "ppsx", 4, "application/vnd.openxmlformats-" | |
| 215 | + "officedocument.presentationml.slideshow"}, | |
| 212 | 216 | { "ppt", 3, "application/mspowerpoint" }, |
| 213 | - { "pptx", 4, "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, | |
| 217 | + { "pptx", 4, "application/vnd.openxmlformats-" | |
| 218 | + "officedocument.presentationml.presentation"}, | |
| 214 | 219 | { "ppz", 3, "application/mspowerpoint" }, |
| 215 | 220 | { "pre", 3, "application/x-freelance" }, |
| 216 | 221 | { "prt", 3, "application/pro_eng" }, |
| 217 | 222 | { "ps", 2, "application/postscript" }, |
| 218 | 223 | { "qt", 2, "video/quicktime" }, |
| @@ -284,11 +289,12 @@ | ||
| 284 | 289 | { "xbm", 3, "image/x-xbitmap" }, |
| 285 | 290 | { "xlc", 3, "application/vnd.ms-excel" }, |
| 286 | 291 | { "xll", 3, "application/vnd.ms-excel" }, |
| 287 | 292 | { "xlm", 3, "application/vnd.ms-excel" }, |
| 288 | 293 | { "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"}, | |
| 290 | 296 | { "xlw", 3, "application/vnd.ms-excel" }, |
| 291 | 297 | { "xml", 3, "text/xml" }, |
| 292 | 298 | { "xpm", 3, "image/x-xpixmap" }, |
| 293 | 299 | { "xwd", 3, "image/x-xwindowdump" }, |
| 294 | 300 | { "xyz", 3, "chemical/x-pdb" }, |
| @@ -351,10 +357,75 @@ | ||
| 351 | 357 | for(i=2; i<g.argc; i++){ |
| 352 | 358 | fossil_print("%-20s -> %s\n", g.argv[i], mimetype_from_name(g.argv[i])); |
| 353 | 359 | } |
| 354 | 360 | } |
| 355 | 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 | + | |
| 356 | 427 | /* |
| 357 | 428 | ** Look for a file named zName in the checkin with RID=vid. Load the content |
| 358 | 429 | ** of that file into pContent and return the RID for the file. Or return 0 |
| 359 | 430 | ** if the file is not found or could not be loaded. |
| 360 | 431 | */ |
| @@ -426,17 +497,19 @@ | ||
| 426 | 497 | char *zCheckin = "tip"; /* The checkin holding the document */ |
| 427 | 498 | int vid = 0; /* Artifact of checkin */ |
| 428 | 499 | int rid = 0; /* Artifact of file */ |
| 429 | 500 | int i; /* Loop counter */ |
| 430 | 501 | Blob filebody; /* Content of the documentation file */ |
| 502 | + Blob title; /* Document title */ | |
| 431 | 503 | int nMiss = (-1); /* Failed attempts to find the document */ |
| 432 | 504 | static const char *const azSuffix[] = { |
| 433 | 505 | "index.html", "index.wiki", "index.md" |
| 434 | 506 | }; |
| 435 | 507 | |
| 436 | 508 | login_check_credentials(); |
| 437 | 509 | if( !g.perm.Read ){ login_needed(); return; } |
| 510 | + blob_init(&title, 0, 0); | |
| 438 | 511 | db_begin_transaction(); |
| 439 | 512 | while( rid==0 && (++nMiss)<=ArraySize(azSuffix) ){ |
| 440 | 513 | zName = PD("name", "tip/index.wiki"); |
| 441 | 514 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 442 | 515 | zCheckin = mprintf("%.*s", i, zName); |
| @@ -494,11 +567,11 @@ | ||
| 494 | 567 | Th_Store("doc_version", db_text(0, "SELECT '[' || substr(uuid,1,10) || ']'" |
| 495 | 568 | " FROM blob WHERE rid=%d", vid)); |
| 496 | 569 | Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event" |
| 497 | 570 | " WHERE objid=%d AND type='ci'", vid)); |
| 498 | 571 | if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){ |
| 499 | - Blob title, tail; | |
| 572 | + Blob tail; | |
| 500 | 573 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 501 | 574 | if( wiki_find_title(&filebody, &title, &tail) ){ |
| 502 | 575 | style_header("%s", blob_str(&title)); |
| 503 | 576 | wiki_convert(&tail, 0, WIKI_BUTTONS); |
| 504 | 577 | }else{ |
| @@ -505,11 +578,10 @@ | ||
| 505 | 578 | style_header("Documentation"); |
| 506 | 579 | wiki_convert(&filebody, 0, WIKI_BUTTONS); |
| 507 | 580 | } |
| 508 | 581 | style_footer(); |
| 509 | 582 | }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){ |
| 510 | - Blob title = BLOB_INITIALIZER; | |
| 511 | 583 | Blob tail = BLOB_INITIALIZER; |
| 512 | 584 | markdown_to_html(&filebody, &title, &tail); |
| 513 | 585 | if( blob_size(&title)>0 ){ |
| 514 | 586 | style_header("%s", blob_str(&title)); |
| 515 | 587 | }else{ |
| @@ -522,10 +594,16 @@ | ||
| 522 | 594 | style_header("Documentation"); |
| 523 | 595 | @ <blockquote><pre> |
| 524 | 596 | @ %h(blob_str(&filebody)) |
| 525 | 597 | @ </pre></blockquote> |
| 526 | 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(); | |
| 527 | 605 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 528 | 606 | }else if( db_get_boolean("th1-docs", 0) && |
| 529 | 607 | fossil_strcmp(zMime, "application/x-th1")==0 ){ |
| 530 | 608 | style_header("%h", zName); |
| 531 | 609 | Th_Render(blob_str(&filebody)); |
| 532 | 610 |
| --- 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 |