| | @@ -95,14 +95,21 @@ |
| 95 | 95 | cgi_append_content(z, n); |
| 96 | 96 | }else{ |
| 97 | 97 | fwrite(z, 1, n, stdout); |
| 98 | 98 | fflush(stdout); |
| 99 | 99 | } |
| 100 | | - if( encode ) free((char*)z); |
| 100 | + if( encode ) fossil_free((char*)z); |
| 101 | 101 | } |
| 102 | 102 | } |
| 103 | 103 | |
| 104 | +struct PutsCmdData { |
| 105 | + char escapeHtml; |
| 106 | + char const * sep; |
| 107 | + char const * eol; |
| 108 | +}; |
| 109 | +typedef struct PutsCmdData PutsCmdData; |
| 110 | + |
| 104 | 111 | /* |
| 105 | 112 | ** TH command: puts STRING |
| 106 | 113 | ** TH command: html STRING |
| 107 | 114 | ** |
| 108 | 115 | ** Output STRING as HTML (html) or unchanged (puts). |
| | @@ -112,14 +119,25 @@ |
| 112 | 119 | void *pConvert, |
| 113 | 120 | int argc, |
| 114 | 121 | const char **argv, |
| 115 | 122 | int *argl |
| 116 | 123 | ){ |
| 117 | | - if( argc!=2 ){ |
| 118 | | - return Th_WrongNumArgs(interp, "puts STRING"); |
| 124 | + PutsCmdData const * fmt = (PutsCmdData const *)pConvert; |
| 125 | + const int sepLen = fmt->sep ? strlen(fmt->sep) : 0; |
| 126 | + int i; |
| 127 | + if( argc<2 ){ |
| 128 | + return Th_WrongNumArgs(interp, "puts STRING ...STRING_N"); |
| 129 | + } |
| 130 | + for( i = 1; i < argc; ++i ){ |
| 131 | + if(sepLen && (i>1)){ |
| 132 | + sendText(fmt->sep, sepLen, 0); |
| 133 | + } |
| 134 | + sendText((char const*)argv[i], argl[i], fmt->escapeHtml); |
| 119 | 135 | } |
| 120 | | - sendText((char*)argv[1], argl[1], pConvert!=0); |
| 136 | + if(fmt->eol){ |
| 137 | + sendText(fmt->eol, strlen(fmt->eol), 0); |
| 138 | + } |
| 121 | 139 | return TH_OK; |
| 122 | 140 | } |
| 123 | 141 | |
| 124 | 142 | /* |
| 125 | 143 | ** TH command: wiki STRING |
| | @@ -415,17 +433,222 @@ |
| 415 | 433 | } |
| 416 | 434 | Th_SetResult(interp, g.zRepositoryName, -1); |
| 417 | 435 | return TH_OK; |
| 418 | 436 | } |
| 419 | 437 | |
| 438 | +#ifdef TH_USE_SQLITE |
| 439 | +static int queryPrepareCmd( |
| 440 | + Th_Interp *interp, |
| 441 | + void *p, |
| 442 | + int argc, |
| 443 | + const char **argv, |
| 444 | + int *argl |
| 445 | +){ |
| 446 | + char const * zSql; |
| 447 | + sqlite3_stmt * pStmt = NULL; |
| 448 | + int rc; |
| 449 | + char const * errMsg = NULL; |
| 450 | + if( argc!=2 ){ |
| 451 | + return Th_WrongNumArgs(interp, "query_prepare STRING"); |
| 452 | + } |
| 453 | + zSql = argv[1]; |
| 454 | + rc = sqlite3_prepare( g.db, zSql, strlen(zSql), &pStmt, NULL ); |
| 455 | + if(SQLITE_OK==rc){ |
| 456 | + if(sqlite3_column_count( pStmt ) < 1){ |
| 457 | + errMsg = "Only SELECT-like queries are supported."; |
| 458 | + rc = SQLITE_ERROR; |
| 459 | + sqlite3_finalize( pStmt ); |
| 460 | + pStmt = NULL; |
| 461 | + } |
| 462 | + }else{ |
| 463 | + errMsg = sqlite3_errmsg( g.db ); |
| 464 | + } |
| 465 | + if(SQLITE_OK!=rc){ |
| 466 | + assert(NULL != errMsg); |
| 467 | + assert(NULL == pStmt); |
| 468 | + Th_ErrorMessage(interp, "error preparing SQL:", errMsg, -1); |
| 469 | + return TH_ERROR; |
| 470 | + } |
| 471 | + rc = Th_AddStmt( interp, pStmt ); |
| 472 | + assert( rc >= 0 && "AddStmt failed."); |
| 473 | + Th_SetResultInt( interp, rc ); |
| 474 | + return TH_OK; |
| 475 | +} |
| 476 | + |
| 477 | +static sqlite3_stmt * queryStmtHandle(Th_Interp *interp, char const * arg, int argLen, int * stmtId ){ |
| 478 | + int rc = 0; |
| 479 | + sqlite3_stmt * pStmt = NULL; |
| 480 | + if( 0 == Th_ToInt( interp, arg, argLen, &rc ) ){ |
| 481 | + if(stmtId){ |
| 482 | + *stmtId = rc; |
| 483 | + } |
| 484 | + pStmt = Th_GetStmt( interp, rc ); |
| 485 | + if(NULL==pStmt){ |
| 486 | + Th_ErrorMessage(interp, "no such statement handle:", arg, -1); |
| 487 | + } |
| 488 | + } |
| 489 | + return pStmt; |
| 490 | + |
| 491 | +} |
| 492 | + |
| 493 | +static int queryFinalizeCmd( |
| 494 | + Th_Interp *interp, |
| 495 | + void *p, |
| 496 | + int argc, |
| 497 | + const char **argv, |
| 498 | + int *argl |
| 499 | +){ |
| 500 | + char * zSql; |
| 501 | + sqlite3_stmt * pStmt = NULL; |
| 502 | + int rc = 0; |
| 503 | + char const * arg; |
| 504 | + if( argc!=2 ){ |
| 505 | + return Th_WrongNumArgs(interp, "query_finalize StmtHandle"); |
| 506 | + } |
| 507 | + arg = argv[1]; |
| 508 | + pStmt = queryStmtHandle(interp, arg, argl[1], &rc); |
| 509 | + if( rc < 1 ){ |
| 510 | + return TH_ERROR; |
| 511 | + } |
| 512 | + assert( NULL != pStmt ); |
| 513 | + rc = Th_FinalizeStmt( interp, rc ); |
| 514 | + Th_SetResultInt( interp, rc ); |
| 515 | + return TH_OK; |
| 516 | +} |
| 517 | + |
| 518 | +static void queryReportDbErr( Th_Interp * interp, int rc ){ |
| 519 | + char const * msg = sqlite3_errmsg( g.db ); |
| 520 | + Th_ErrorMessage(interp, "db error:", msg, -1); |
| 521 | +} |
| 522 | + |
| 523 | +static int queryStepCmd( |
| 524 | + Th_Interp *interp, |
| 525 | + void *p, |
| 526 | + int argc, |
| 527 | + const char **argv, |
| 528 | + int *argl |
| 529 | +){ |
| 530 | + sqlite3_stmt * pStmt = NULL; |
| 531 | + int rc = 0; |
| 532 | + if( argc!=2 ){ |
| 533 | + return Th_WrongNumArgs(interp, "query_step StmtHandle"); |
| 534 | + } |
| 535 | + pStmt = queryStmtHandle(interp, argv[1], argl[1], &rc); |
| 536 | + if( rc < 1 ){ |
| 537 | + return TH_ERROR; |
| 538 | + } |
| 539 | + rc = sqlite3_step( pStmt ); |
| 540 | + switch(rc){ |
| 541 | + case SQLITE_ROW: |
| 542 | + rc = 1; |
| 543 | + break; |
| 544 | + case SQLITE_DONE: |
| 545 | + rc = 0; |
| 546 | + break; |
| 547 | + default: |
| 548 | + queryReportDbErr( interp, rc ); |
| 549 | + return TH_ERROR; |
| 550 | + } |
| 551 | + Th_SetResultInt( interp, rc ); |
| 552 | + return TH_OK; |
| 553 | +} |
| 554 | + |
| 555 | +static int queryColStringCmd( |
| 556 | + Th_Interp *interp, |
| 557 | + void *p, |
| 558 | + int argc, |
| 559 | + const char **argv, |
| 560 | + int *argl |
| 561 | +){ |
| 562 | + sqlite3_stmt * pStmt = NULL; |
| 563 | + char const * val; |
| 564 | + int index; |
| 565 | + int rc = 0; |
| 566 | + int valLen; |
| 567 | + if( argc!=3 ){ |
| 568 | + return Th_WrongNumArgs(interp, "query_column_string StmtHandle Index"); |
| 569 | + } |
| 570 | + pStmt = queryStmtHandle(interp, argv[1], argl[1], &rc); |
| 571 | + if( rc < 1 ){ |
| 572 | + return TH_ERROR; |
| 573 | + } |
| 574 | + if( 0 != Th_ToInt( interp, argv[2], argl[2], &index ) ){ |
| 575 | + return TH_ERROR; |
| 576 | + } |
| 577 | + val = sqlite3_column_text( pStmt, index ); |
| 578 | + valLen = val ? sqlite3_column_bytes( pStmt, index ) : 0; |
| 579 | + Th_SetResult( interp, val, valLen ); |
| 580 | + return TH_OK; |
| 581 | +} |
| 582 | + |
| 583 | + |
| 584 | +static int queryColCountCmd( |
| 585 | + Th_Interp *interp, |
| 586 | + void *p, |
| 587 | + int argc, |
| 588 | + const char **argv, |
| 589 | + int *argl |
| 590 | +){ |
| 591 | + int rc; |
| 592 | + sqlite3_stmt * pStmt = NULL; |
| 593 | + if( argc!=2 ){ |
| 594 | + return Th_WrongNumArgs(interp, "query_column_count StmtHandle"); |
| 595 | + } |
| 596 | + pStmt = queryStmtHandle(interp, argv[1], argl[1], NULL); |
| 597 | + if( NULL == pStmt ){ |
| 598 | + return TH_ERROR; |
| 599 | + } |
| 600 | + rc = sqlite3_column_count( pStmt ); |
| 601 | + Th_SetResultInt( interp, rc ); |
| 602 | + return TH_OK; |
| 603 | +} |
| 604 | + |
| 605 | +static int queryColNameCmd( |
| 606 | + Th_Interp *interp, |
| 607 | + void *p, |
| 608 | + int argc, |
| 609 | + const char **argv, |
| 610 | + int *argl |
| 611 | +){ |
| 612 | + sqlite3_stmt * pStmt = NULL; |
| 613 | + char const * val; |
| 614 | + int index; |
| 615 | + int rc = 0; |
| 616 | + int valLen; |
| 617 | + if( argc!=3 ){ |
| 618 | + return Th_WrongNumArgs(interp, "query_column_name StmtHandle Index"); |
| 619 | + } |
| 620 | + pStmt = queryStmtHandle(interp, argv[1], argl[1], &rc); |
| 621 | + if( rc < 1 ){ |
| 622 | + return TH_ERROR; |
| 623 | + } |
| 624 | + if( 0 != Th_ToInt( interp, argv[2], argl[2], &index ) ){ |
| 625 | + return TH_ERROR; |
| 626 | + } |
| 627 | + val = sqlite3_column_name( pStmt, index ); |
| 628 | + if(NULL==val){ |
| 629 | + Th_ErrorMessage(interp, "Column index out of bounds(?):", argv[2], -1); |
| 630 | + return TH_ERROR; |
| 631 | + }else{ |
| 632 | + Th_SetResult( interp, val, strlen( val ) ); |
| 633 | + return TH_OK; |
| 634 | + } |
| 635 | +} |
| 636 | + |
| 637 | +#endif |
| 638 | +/* end TH_USE_SQLITE */ |
| 639 | + |
| 420 | 640 | /* |
| 421 | 641 | ** Make sure the interpreter has been initialized. Initialize it if |
| 422 | 642 | ** it has not been already. |
| 423 | 643 | ** |
| 424 | 644 | ** The interpreter is stored in the g.interp global variable. |
| 425 | 645 | */ |
| 426 | 646 | void Th_FossilInit(void){ |
| 647 | + static PutsCmdData puts_Html = {0, 0, 0}; |
| 648 | + static PutsCmdData puts_Normal = {1, 0, 0}; |
| 649 | + static PutsCmdData puts_Ext = {1, " ", "\n"}; |
| 427 | 650 | static struct _Command { |
| 428 | 651 | const char *zName; |
| 429 | 652 | Th_CommandProc xProc; |
| 430 | 653 | void *pContext; |
| 431 | 654 | } aCommand[] = { |
| | @@ -435,12 +658,21 @@ |
| 435 | 658 | {"linecount", linecntCmd, 0}, |
| 436 | 659 | {"hascap", hascapCmd, 0}, |
| 437 | 660 | {"hasfeature", hasfeatureCmd, 0}, |
| 438 | 661 | {"htmlize", htmlizeCmd, 0}, |
| 439 | 662 | {"date", dateCmd, 0}, |
| 440 | | - {"html", putsCmd, 0}, |
| 441 | | - {"puts", putsCmd, (void*)1}, |
| 663 | + {"html", putsCmd, &puts_Html}, |
| 664 | + {"puts", putsCmd, &puts_Normal}, |
| 665 | + {"putsl", putsCmd, &puts_Ext}, |
| 666 | +#ifdef TH_USE_SQLITE |
| 667 | + {"query_column_count", queryColCountCmd, 0}, |
| 668 | + {"query_column_name", queryColNameCmd, 0}, |
| 669 | + {"query_finalize", queryFinalizeCmd, 0}, |
| 670 | + {"query_prepare", queryPrepareCmd, 0}, |
| 671 | + {"query_step", queryStepCmd, 0}, |
| 672 | + {"query_column_string", queryColStringCmd, 0}, |
| 673 | +#endif |
| 442 | 674 | {"wiki", wikiCmd, 0}, |
| 443 | 675 | {"repository", repositoryCmd, 0}, |
| 444 | 676 | {0, 0, 0} |
| 445 | 677 | }; |
| 446 | 678 | if( g.interp==0 ){ |
| | @@ -622,8 +854,9 @@ |
| 622 | 854 | if( g.argc<3 ){ |
| 623 | 855 | usage("FILE"); |
| 624 | 856 | } |
| 625 | 857 | db_open_config(0); /* Needed for global "tcl" setting. */ |
| 626 | 858 | blob_zero(&in); |
| 859 | + db_find_and_open_repository(OPEN_ANY_SCHEMA,0) /* for query_xxx tests. */; |
| 627 | 860 | blob_read_from_file(&in, g.argv[2]); |
| 628 | 861 | Th_Render(blob_str(&in)); |
| 629 | 862 | } |
| 630 | 863 | |
| 631 | 864 | ADDED test/th1-query-api-1.th1 |