Fossil SCM
If a /doc filename ends with "/", then try appending "index.html", "index.wiki", and "index.md" in that order. If none are found, then a 404 error will be generated. Try to find a file named 404.md in the root directory for the text of the 404, or generate a default 404 if no 404.md file is found.
Commit
bdfbbddc8f170f406b30692d9780a368bf0efc10
Parent
2c1677aa176e06d…
1 file changed
+72
-68
+72
-68
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -375,10 +375,15 @@ | ||
| 375 | 375 | ** The "ckout" CHECKIN is intended for development - to provide a mechanism |
| 376 | 376 | ** for looking at what a file will look like using the /doc webpage after |
| 377 | 377 | ** it gets checked in. |
| 378 | 378 | ** |
| 379 | 379 | ** The file extension is used to decide how to render the file. |
| 380 | +** | |
| 381 | +** If FILE ends in "/" then names "FILE/index.html", "FILE/index.wiki", | |
| 382 | +** and "FILE/index.md" are in that order. If none of those are found, | |
| 383 | +** then FILE is completely replaced by "404.md" and tried. If that is | |
| 384 | +** not found, then a default 404 screen is generated. | |
| 380 | 385 | */ |
| 381 | 386 | void doc_page(void){ |
| 382 | 387 | const char *zName; /* Argument to the /doc page */ |
| 383 | 388 | const char *zOrigName; /* Original document name */ |
| 384 | 389 | const char *zMime; /* Document MIME type */ |
| @@ -386,86 +391,85 @@ | ||
| 386 | 391 | int vid = 0; /* Artifact of checkin */ |
| 387 | 392 | int rid = 0; /* Artifact of file */ |
| 388 | 393 | int i; /* Loop counter */ |
| 389 | 394 | Blob filebody; /* Content of the documentation file */ |
| 390 | 395 | int nMiss = 0; /* Failed attempts to find the document */ |
| 396 | + static const char *azSuffix[] = { | |
| 397 | + "index.html", "index.wiki", "index.md" | |
| 398 | + }; | |
| 391 | 399 | |
| 392 | 400 | login_check_credentials(); |
| 393 | 401 | if( !g.perm.Read ){ login_needed(); return; } |
| 394 | - zName = PD("name", "tip/index.wiki"); | |
| 395 | - for(i=0; zName[i] && zName[i]!='/'; i++){} | |
| 396 | - zCheckin = mprintf("%.*s", i, zName); | |
| 397 | - if( zName[i]==0 ){ | |
| 398 | - zName = "index.html"; | |
| 399 | - }else{ | |
| 400 | - zName += i; | |
| 401 | - } | |
| 402 | - while( zName[0]=='/' ){ zName++; } | |
| 403 | - g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName); | |
| 404 | - zOrigName = zName; | |
| 405 | - if( !file_is_simple_pathname(zName, 1) ){ | |
| 406 | - if( sqlite3_strglob("*/", zName)==0 ){ | |
| 407 | - zOrigName = zName = mprintf("%sindex.html", zName); | |
| 408 | - if( !file_is_simple_pathname(zName, 1) ){ | |
| 409 | - goto doc_not_found; | |
| 410 | - } | |
| 411 | - }else{ | |
| 412 | - goto doc_not_found; | |
| 413 | - } | |
| 414 | - } | |
| 415 | - if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){ | |
| 416 | - sqlite3_snprintf(sizeof(zCheckin), zCheckin, "tip"); | |
| 417 | - } | |
| 418 | - if( fossil_strcmp(zCheckin,"ckout")==0 ){ | |
| 419 | - /* Read from the local checkout */ | |
| 420 | - char *zFullpath; | |
| 421 | - db_must_be_within_tree(); | |
| 422 | - while( rid==0 && nMiss<2 ){ | |
| 423 | - zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); | |
| 424 | - if( file_isfile(zFullpath) | |
| 425 | - && blob_read_from_file(&filebody, zFullpath)<0 ){ | |
| 402 | + for(nMiss=0; rid==0 && nMiss<=ArraySize(azSuffix); nMiss++){ | |
| 403 | + zName = PD("name", "tip/index.wiki"); | |
| 404 | + for(i=0; zName[i] && zName[i]!='/'; i++){} | |
| 405 | + zCheckin = mprintf("%.*s", i, zName); | |
| 406 | + if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){ | |
| 407 | + zCheckin = "tip"; | |
| 408 | + } | |
| 409 | + if( nMiss==ArraySize(azSuffix) ){ | |
| 410 | + zName = "404.md"; | |
| 411 | + }else if( zName[i]==0 ){ | |
| 412 | + zName = azSuffix[nMiss]; | |
| 413 | + }else{ | |
| 414 | + zName += i; | |
| 415 | + } | |
| 416 | + while( zName[0]=='/' ){ zName++; } | |
| 417 | + g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName); | |
| 418 | + if( nMiss==0 ) zOrigName = zName; | |
| 419 | + if( !file_is_simple_pathname(zName, 1) ){ | |
| 420 | + if( sqlite3_strglob("*/", zName)==0 ){ | |
| 421 | + zName = mprintf("%s%s", zName, azSuffix[nMiss]); | |
| 422 | + if( !file_is_simple_pathname(zName, 1) ){ | |
| 423 | + goto doc_not_found; | |
| 424 | + } | |
| 425 | + }else{ | |
| 426 | + goto doc_not_found; | |
| 427 | + } | |
| 428 | + } | |
| 429 | + if( fossil_strcmp(zCheckin,"ckout")==0 ){ | |
| 430 | + /* Read from the local checkout */ | |
| 431 | + char *zFullpath; | |
| 432 | + db_must_be_within_tree(); | |
| 433 | + zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); | |
| 434 | + if( file_isfile(zFullpath) | |
| 435 | + && blob_read_from_file(&filebody, zFullpath)>0 ){ | |
| 426 | 436 | rid = 1; /* Fake RID just to get the loop to end */ |
| 427 | 437 | } |
| 428 | 438 | fossil_free(zFullpath); |
| 429 | - if( rid ) break; | |
| 430 | - nMiss++; | |
| 431 | - zName = "404.md"; | |
| 432 | - } | |
| 433 | - }else{ | |
| 434 | - db_begin_transaction(); | |
| 435 | - vid = name_to_typed_rid(zCheckin, "ci"); | |
| 436 | - db_multi_exec( | |
| 437 | - "CREATE TABLE IF NOT EXISTS vcache(\n" | |
| 438 | - " vid INTEGER, -- checkin ID\n" | |
| 439 | - " fname TEXT, -- filename\n" | |
| 440 | - " rid INTEGER, -- artifact ID\n" | |
| 441 | - " PRIMARY KEY(vid,fname)\n" | |
| 442 | - ") WITHOUT ROWID" | |
| 443 | - ); | |
| 444 | - if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ | |
| 445 | - db_multi_exec( | |
| 446 | - "DELETE FROM vcache;\n" | |
| 447 | - "CREATE VIRTUAL TABLE temp.foci USING files_of_checkin;\n" | |
| 448 | - "INSERT INTO vcache(vid,fname,rid)" | |
| 449 | - " SELECT checkinID, filename, blob.rid FROM foci, blob" | |
| 450 | - " WHERE blob.uuid=foci.uuid" | |
| 451 | - " AND foci.checkinID=%d;", | |
| 452 | - vid | |
| 453 | - ); | |
| 454 | - } | |
| 455 | - while( rid==0 && nMiss<2 ){ | |
| 439 | + }else{ | |
| 440 | + db_begin_transaction(); | |
| 441 | + vid = name_to_typed_rid(zCheckin, "ci"); | |
| 442 | + db_multi_exec( | |
| 443 | + "CREATE TABLE IF NOT EXISTS vcache(\n" | |
| 444 | + " vid INTEGER, -- checkin ID\n" | |
| 445 | + " fname TEXT, -- filename\n" | |
| 446 | + " rid INTEGER, -- artifact ID\n" | |
| 447 | + " PRIMARY KEY(vid,fname)\n" | |
| 448 | + ") WITHOUT ROWID" | |
| 449 | + ); | |
| 450 | + if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ | |
| 451 | + db_multi_exec( | |
| 452 | + "DELETE FROM vcache;\n" | |
| 453 | + "CREATE VIRTUAL TABLE temp.foci USING files_of_checkin;\n" | |
| 454 | + "INSERT INTO vcache(vid,fname,rid)" | |
| 455 | + " SELECT checkinID, filename, blob.rid FROM foci, blob" | |
| 456 | + " WHERE blob.uuid=foci.uuid" | |
| 457 | + " AND foci.checkinID=%d;", | |
| 458 | + vid | |
| 459 | + ); | |
| 460 | + } | |
| 456 | 461 | rid = db_int(0, "SELECT rid FROM vcache" |
| 457 | 462 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| 458 | - if( rid ) break; | |
| 459 | - nMiss++; | |
| 460 | - zName = "404.md"; | |
| 461 | - } | |
| 462 | - if( rid==0 || content_get(rid, &filebody)==0 ){ | |
| 463 | - goto doc_not_found; | |
| 464 | - } | |
| 465 | - db_end_transaction(0); | |
| 466 | - } | |
| 463 | + nMiss++; | |
| 464 | + if( rid==0 || content_get(rid, &filebody)==0 ){ | |
| 465 | + goto doc_not_found; | |
| 466 | + } | |
| 467 | + db_end_transaction(0); | |
| 468 | + } | |
| 469 | + } | |
| 470 | + if( rid==0 ) goto doc_not_found; | |
| 467 | 471 | blob_to_utf8_no_bom(&filebody, 0); |
| 468 | 472 | |
| 469 | 473 | /* The file is now contained in the filebody blob. Deliver the |
| 470 | 474 | ** file to the user |
| 471 | 475 | */ |
| 472 | 476 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -375,10 +375,15 @@ | |
| 375 | ** The "ckout" CHECKIN is intended for development - to provide a mechanism |
| 376 | ** for looking at what a file will look like using the /doc webpage after |
| 377 | ** it gets checked in. |
| 378 | ** |
| 379 | ** The file extension is used to decide how to render the file. |
| 380 | */ |
| 381 | void doc_page(void){ |
| 382 | const char *zName; /* Argument to the /doc page */ |
| 383 | const char *zOrigName; /* Original document name */ |
| 384 | const char *zMime; /* Document MIME type */ |
| @@ -386,86 +391,85 @@ | |
| 386 | int vid = 0; /* Artifact of checkin */ |
| 387 | int rid = 0; /* Artifact of file */ |
| 388 | int i; /* Loop counter */ |
| 389 | Blob filebody; /* Content of the documentation file */ |
| 390 | int nMiss = 0; /* Failed attempts to find the document */ |
| 391 | |
| 392 | login_check_credentials(); |
| 393 | if( !g.perm.Read ){ login_needed(); return; } |
| 394 | zName = PD("name", "tip/index.wiki"); |
| 395 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 396 | zCheckin = mprintf("%.*s", i, zName); |
| 397 | if( zName[i]==0 ){ |
| 398 | zName = "index.html"; |
| 399 | }else{ |
| 400 | zName += i; |
| 401 | } |
| 402 | while( zName[0]=='/' ){ zName++; } |
| 403 | g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName); |
| 404 | zOrigName = zName; |
| 405 | if( !file_is_simple_pathname(zName, 1) ){ |
| 406 | if( sqlite3_strglob("*/", zName)==0 ){ |
| 407 | zOrigName = zName = mprintf("%sindex.html", zName); |
| 408 | if( !file_is_simple_pathname(zName, 1) ){ |
| 409 | goto doc_not_found; |
| 410 | } |
| 411 | }else{ |
| 412 | goto doc_not_found; |
| 413 | } |
| 414 | } |
| 415 | if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){ |
| 416 | sqlite3_snprintf(sizeof(zCheckin), zCheckin, "tip"); |
| 417 | } |
| 418 | if( fossil_strcmp(zCheckin,"ckout")==0 ){ |
| 419 | /* Read from the local checkout */ |
| 420 | char *zFullpath; |
| 421 | db_must_be_within_tree(); |
| 422 | while( rid==0 && nMiss<2 ){ |
| 423 | zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 424 | if( file_isfile(zFullpath) |
| 425 | && blob_read_from_file(&filebody, zFullpath)<0 ){ |
| 426 | rid = 1; /* Fake RID just to get the loop to end */ |
| 427 | } |
| 428 | fossil_free(zFullpath); |
| 429 | if( rid ) break; |
| 430 | nMiss++; |
| 431 | zName = "404.md"; |
| 432 | } |
| 433 | }else{ |
| 434 | db_begin_transaction(); |
| 435 | vid = name_to_typed_rid(zCheckin, "ci"); |
| 436 | db_multi_exec( |
| 437 | "CREATE TABLE IF NOT EXISTS vcache(\n" |
| 438 | " vid INTEGER, -- checkin ID\n" |
| 439 | " fname TEXT, -- filename\n" |
| 440 | " rid INTEGER, -- artifact ID\n" |
| 441 | " PRIMARY KEY(vid,fname)\n" |
| 442 | ") WITHOUT ROWID" |
| 443 | ); |
| 444 | if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ |
| 445 | db_multi_exec( |
| 446 | "DELETE FROM vcache;\n" |
| 447 | "CREATE VIRTUAL TABLE temp.foci USING files_of_checkin;\n" |
| 448 | "INSERT INTO vcache(vid,fname,rid)" |
| 449 | " SELECT checkinID, filename, blob.rid FROM foci, blob" |
| 450 | " WHERE blob.uuid=foci.uuid" |
| 451 | " AND foci.checkinID=%d;", |
| 452 | vid |
| 453 | ); |
| 454 | } |
| 455 | while( rid==0 && nMiss<2 ){ |
| 456 | rid = db_int(0, "SELECT rid FROM vcache" |
| 457 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| 458 | if( rid ) break; |
| 459 | nMiss++; |
| 460 | zName = "404.md"; |
| 461 | } |
| 462 | if( rid==0 || content_get(rid, &filebody)==0 ){ |
| 463 | goto doc_not_found; |
| 464 | } |
| 465 | db_end_transaction(0); |
| 466 | } |
| 467 | blob_to_utf8_no_bom(&filebody, 0); |
| 468 | |
| 469 | /* The file is now contained in the filebody blob. Deliver the |
| 470 | ** file to the user |
| 471 | */ |
| 472 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -375,10 +375,15 @@ | |
| 375 | ** The "ckout" CHECKIN is intended for development - to provide a mechanism |
| 376 | ** for looking at what a file will look like using the /doc webpage after |
| 377 | ** it gets checked in. |
| 378 | ** |
| 379 | ** The file extension is used to decide how to render the file. |
| 380 | ** |
| 381 | ** If FILE ends in "/" then names "FILE/index.html", "FILE/index.wiki", |
| 382 | ** and "FILE/index.md" are in that order. If none of those are found, |
| 383 | ** then FILE is completely replaced by "404.md" and tried. If that is |
| 384 | ** not found, then a default 404 screen is generated. |
| 385 | */ |
| 386 | void doc_page(void){ |
| 387 | const char *zName; /* Argument to the /doc page */ |
| 388 | const char *zOrigName; /* Original document name */ |
| 389 | const char *zMime; /* Document MIME type */ |
| @@ -386,86 +391,85 @@ | |
| 391 | int vid = 0; /* Artifact of checkin */ |
| 392 | int rid = 0; /* Artifact of file */ |
| 393 | int i; /* Loop counter */ |
| 394 | Blob filebody; /* Content of the documentation file */ |
| 395 | int nMiss = 0; /* Failed attempts to find the document */ |
| 396 | static const char *azSuffix[] = { |
| 397 | "index.html", "index.wiki", "index.md" |
| 398 | }; |
| 399 | |
| 400 | login_check_credentials(); |
| 401 | if( !g.perm.Read ){ login_needed(); return; } |
| 402 | for(nMiss=0; rid==0 && nMiss<=ArraySize(azSuffix); nMiss++){ |
| 403 | zName = PD("name", "tip/index.wiki"); |
| 404 | for(i=0; zName[i] && zName[i]!='/'; i++){} |
| 405 | zCheckin = mprintf("%.*s", i, zName); |
| 406 | if( fossil_strcmp(zCheckin,"ckout")==0 && db_open_local(0)==0 ){ |
| 407 | zCheckin = "tip"; |
| 408 | } |
| 409 | if( nMiss==ArraySize(azSuffix) ){ |
| 410 | zName = "404.md"; |
| 411 | }else if( zName[i]==0 ){ |
| 412 | zName = azSuffix[nMiss]; |
| 413 | }else{ |
| 414 | zName += i; |
| 415 | } |
| 416 | while( zName[0]=='/' ){ zName++; } |
| 417 | g.zPath = mprintf("%s/%s/%s", g.zPath, zCheckin, zName); |
| 418 | if( nMiss==0 ) zOrigName = zName; |
| 419 | if( !file_is_simple_pathname(zName, 1) ){ |
| 420 | if( sqlite3_strglob("*/", zName)==0 ){ |
| 421 | zName = mprintf("%s%s", zName, azSuffix[nMiss]); |
| 422 | if( !file_is_simple_pathname(zName, 1) ){ |
| 423 | goto doc_not_found; |
| 424 | } |
| 425 | }else{ |
| 426 | goto doc_not_found; |
| 427 | } |
| 428 | } |
| 429 | if( fossil_strcmp(zCheckin,"ckout")==0 ){ |
| 430 | /* Read from the local checkout */ |
| 431 | char *zFullpath; |
| 432 | db_must_be_within_tree(); |
| 433 | zFullpath = mprintf("%s/%s", g.zLocalRoot, zName); |
| 434 | if( file_isfile(zFullpath) |
| 435 | && blob_read_from_file(&filebody, zFullpath)>0 ){ |
| 436 | rid = 1; /* Fake RID just to get the loop to end */ |
| 437 | } |
| 438 | fossil_free(zFullpath); |
| 439 | }else{ |
| 440 | db_begin_transaction(); |
| 441 | vid = name_to_typed_rid(zCheckin, "ci"); |
| 442 | db_multi_exec( |
| 443 | "CREATE TABLE IF NOT EXISTS vcache(\n" |
| 444 | " vid INTEGER, -- checkin ID\n" |
| 445 | " fname TEXT, -- filename\n" |
| 446 | " rid INTEGER, -- artifact ID\n" |
| 447 | " PRIMARY KEY(vid,fname)\n" |
| 448 | ") WITHOUT ROWID" |
| 449 | ); |
| 450 | if( !db_exists("SELECT 1 FROM vcache WHERE vid=%d", vid) ){ |
| 451 | db_multi_exec( |
| 452 | "DELETE FROM vcache;\n" |
| 453 | "CREATE VIRTUAL TABLE temp.foci USING files_of_checkin;\n" |
| 454 | "INSERT INTO vcache(vid,fname,rid)" |
| 455 | " SELECT checkinID, filename, blob.rid FROM foci, blob" |
| 456 | " WHERE blob.uuid=foci.uuid" |
| 457 | " AND foci.checkinID=%d;", |
| 458 | vid |
| 459 | ); |
| 460 | } |
| 461 | rid = db_int(0, "SELECT rid FROM vcache" |
| 462 | " WHERE vid=%d AND fname=%Q", vid, zName); |
| 463 | nMiss++; |
| 464 | if( rid==0 || content_get(rid, &filebody)==0 ){ |
| 465 | goto doc_not_found; |
| 466 | } |
| 467 | db_end_transaction(0); |
| 468 | } |
| 469 | } |
| 470 | if( rid==0 ) goto doc_not_found; |
| 471 | blob_to_utf8_no_bom(&filebody, 0); |
| 472 | |
| 473 | /* The file is now contained in the filebody blob. Deliver the |
| 474 | ** file to the user |
| 475 | */ |
| 476 |