| | @@ -368,10 +368,15 @@ |
| 368 | 368 | /* |
| 369 | 369 | ** Used to prevent warnings about unused parameters |
| 370 | 370 | */ |
| 371 | 371 | #define UNUSED_PARAMETER(x) (void)(x) |
| 372 | 372 | |
| 373 | +/* |
| 374 | +** Number of elements in an array |
| 375 | +*/ |
| 376 | +#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) |
| 377 | + |
| 373 | 378 | /* |
| 374 | 379 | ** If the following flag is set, then command execution stops |
| 375 | 380 | ** at an error if we are not interactive. |
| 376 | 381 | */ |
| 377 | 382 | static int bail_on_error = 0; |
| | @@ -640,10 +645,69 @@ |
| 640 | 645 | if( zResult && *zResult ) shell_add_history(zResult); |
| 641 | 646 | #endif |
| 642 | 647 | } |
| 643 | 648 | return zResult; |
| 644 | 649 | } |
| 650 | + |
| 651 | + |
| 652 | +/* |
| 653 | +** Return the value of a hexadecimal digit. Return -1 if the input |
| 654 | +** is not a hex digit. |
| 655 | +*/ |
| 656 | +static int hexDigitValue(char c){ |
| 657 | + if( c>='0' && c<='9' ) return c - '0'; |
| 658 | + if( c>='a' && c<='f' ) return c - 'a' + 10; |
| 659 | + if( c>='A' && c<='F' ) return c - 'A' + 10; |
| 660 | + return -1; |
| 661 | +} |
| 662 | + |
| 663 | +/* |
| 664 | +** Interpret zArg as an integer value, possibly with suffixes. |
| 665 | +*/ |
| 666 | +static sqlite3_int64 integerValue(const char *zArg){ |
| 667 | + sqlite3_int64 v = 0; |
| 668 | + static const struct { char *zSuffix; int iMult; } aMult[] = { |
| 669 | + { "KiB", 1024 }, |
| 670 | + { "MiB", 1024*1024 }, |
| 671 | + { "GiB", 1024*1024*1024 }, |
| 672 | + { "KB", 1000 }, |
| 673 | + { "MB", 1000000 }, |
| 674 | + { "GB", 1000000000 }, |
| 675 | + { "K", 1000 }, |
| 676 | + { "M", 1000000 }, |
| 677 | + { "G", 1000000000 }, |
| 678 | + }; |
| 679 | + int i; |
| 680 | + int isNeg = 0; |
| 681 | + if( zArg[0]=='-' ){ |
| 682 | + isNeg = 1; |
| 683 | + zArg++; |
| 684 | + }else if( zArg[0]=='+' ){ |
| 685 | + zArg++; |
| 686 | + } |
| 687 | + if( zArg[0]=='0' && zArg[1]=='x' ){ |
| 688 | + int x; |
| 689 | + zArg += 2; |
| 690 | + while( (x = hexDigitValue(zArg[0]))>=0 ){ |
| 691 | + v = (v<<4) + x; |
| 692 | + zArg++; |
| 693 | + } |
| 694 | + }else{ |
| 695 | + while( IsDigit(zArg[0]) ){ |
| 696 | + v = v*10 + zArg[0] - '0'; |
| 697 | + zArg++; |
| 698 | + } |
| 699 | + } |
| 700 | + for(i=0; i<ArraySize(aMult); i++){ |
| 701 | + if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){ |
| 702 | + v *= aMult[i].iMult; |
| 703 | + break; |
| 704 | + } |
| 705 | + } |
| 706 | + return isNeg? -v : v; |
| 707 | +} |
| 708 | + |
| 645 | 709 | /* |
| 646 | 710 | ** A variable length string to which one can append text. |
| 647 | 711 | */ |
| 648 | 712 | typedef struct ShellText ShellText; |
| 649 | 713 | struct ShellText { |
| | @@ -2334,10 +2398,44 @@ |
| 2334 | 2398 | }else{ |
| 2335 | 2399 | ctxErrorMsg(context, "failed to write file: %s", zFile); |
| 2336 | 2400 | } |
| 2337 | 2401 | } |
| 2338 | 2402 | } |
| 2403 | + |
| 2404 | +/* |
| 2405 | +** SQL function: lsmode(MODE) |
| 2406 | +** |
| 2407 | +** Given a numberic st_mode from stat(), convert it into a human-readable |
| 2408 | +** text string in the style of "ls -l". |
| 2409 | +*/ |
| 2410 | +static void lsModeFunc( |
| 2411 | + sqlite3_context *context, |
| 2412 | + int argc, |
| 2413 | + sqlite3_value **argv |
| 2414 | +){ |
| 2415 | + int i; |
| 2416 | + int iMode = sqlite3_value_int(argv[0]); |
| 2417 | + char z[16]; |
| 2418 | + if( S_ISLNK(iMode) ){ |
| 2419 | + z[0] = 'l'; |
| 2420 | + }else if( S_ISREG(iMode) ){ |
| 2421 | + z[0] = '-'; |
| 2422 | + }else if( S_ISDIR(iMode) ){ |
| 2423 | + z[0] = 'd'; |
| 2424 | + }else{ |
| 2425 | + z[0] = '?'; |
| 2426 | + } |
| 2427 | + for(i=0; i<3; i++){ |
| 2428 | + int m = (iMode >> ((2-i)*3)); |
| 2429 | + char *a = &z[1 + i*3]; |
| 2430 | + a[0] = (m & 0x4) ? 'r' : '-'; |
| 2431 | + a[1] = (m & 0x2) ? 'w' : '-'; |
| 2432 | + a[2] = (m & 0x1) ? 'x' : '-'; |
| 2433 | + } |
| 2434 | + z[10] = '\0'; |
| 2435 | + sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT); |
| 2436 | +} |
| 2339 | 2437 | |
| 2340 | 2438 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 2341 | 2439 | |
| 2342 | 2440 | /* |
| 2343 | 2441 | ** Cursor type for recursively iterating through a directory structure. |
| | @@ -2746,10 +2844,14 @@ |
| 2746 | 2844 | readfileFunc, 0, 0); |
| 2747 | 2845 | if( rc==SQLITE_OK ){ |
| 2748 | 2846 | rc = sqlite3_create_function(db, "writefile", -1, SQLITE_UTF8, 0, |
| 2749 | 2847 | writefileFunc, 0, 0); |
| 2750 | 2848 | } |
| 2849 | + if( rc==SQLITE_OK ){ |
| 2850 | + rc = sqlite3_create_function(db, "lsmode", 1, SQLITE_UTF8, 0, |
| 2851 | + lsModeFunc, 0, 0); |
| 2852 | + } |
| 2751 | 2853 | if( rc==SQLITE_OK ){ |
| 2752 | 2854 | rc = fsdirRegister(db); |
| 2753 | 2855 | } |
| 2754 | 2856 | return rc; |
| 2755 | 2857 | } |
| | @@ -4879,11 +4981,11 @@ |
| 4879 | 4981 | if( pNew==0 ){ |
| 4880 | 4982 | rc = SQLITE_NOMEM; |
| 4881 | 4983 | }else{ |
| 4882 | 4984 | memset(pNew, 0, sizeof(ZipfileEntry)); |
| 4883 | 4985 | pNew->zPath = (char*)&pNew[1]; |
| 4884 | | - memcpy(pNew->zPath, &aBuf[ZIPFILE_CDS_FIXED_SZ], nFile); |
| 4986 | + memcpy(pNew->zPath, &aRec[ZIPFILE_CDS_FIXED_SZ], nFile); |
| 4885 | 4987 | pNew->zPath[nFile] = '\0'; |
| 4886 | 4988 | pNew->aCdsEntry = (u8*)&pNew->zPath[nFile+1]; |
| 4887 | 4989 | pNew->nCdsEntry = ZIPFILE_CDS_FIXED_SZ+nFile+nExtra+nComment; |
| 4888 | 4990 | memcpy(pNew->aCdsEntry, aRec, pNew->nCdsEntry); |
| 4889 | 4991 | zipfileAddEntry(pTab, pNew); |
| | @@ -5034,10 +5136,22 @@ |
| 5034 | 5136 | |
| 5035 | 5137 | parse_error: |
| 5036 | 5138 | pTab->base.zErrMsg = sqlite3_mprintf("zipfile: parse error in mode: %s", z); |
| 5037 | 5139 | return SQLITE_ERROR; |
| 5038 | 5140 | } |
| 5141 | + |
| 5142 | +/* |
| 5143 | +** Both (const char*) arguments point to nul-terminated strings. Argument |
| 5144 | +** nB is the value of strlen(zB). This function returns 0 if the strings are |
| 5145 | +** identical, ignoring any trailing '/' character in either path. */ |
| 5146 | +static int zipfileComparePath(const char *zA, const char *zB, int nB){ |
| 5147 | + int nA = (int)strlen(zA); |
| 5148 | + if( zA[nA-1]=='/' ) nA--; |
| 5149 | + if( zB[nB-1]=='/' ) nB--; |
| 5150 | + if( nA==nB && memcmp(zA, zB, nA)==0 ) return 0; |
| 5151 | + return 1; |
| 5152 | +} |
| 5039 | 5153 | |
| 5040 | 5154 | /* |
| 5041 | 5155 | ** xUpdate method. |
| 5042 | 5156 | */ |
| 5043 | 5157 | static int zipfileUpdate( |
| | @@ -5048,19 +5162,20 @@ |
| 5048 | 5162 | ){ |
| 5049 | 5163 | ZipfileTab *pTab = (ZipfileTab*)pVtab; |
| 5050 | 5164 | int rc = SQLITE_OK; /* Return Code */ |
| 5051 | 5165 | ZipfileEntry *pNew = 0; /* New in-memory CDS entry */ |
| 5052 | 5166 | |
| 5053 | | - u32 mode; /* Mode for new entry */ |
| 5054 | | - i64 mTime; /* Modification time for new entry */ |
| 5167 | + u32 mode = 0; /* Mode for new entry */ |
| 5168 | + i64 mTime = 0; /* Modification time for new entry */ |
| 5055 | 5169 | i64 sz = 0; /* Uncompressed size */ |
| 5056 | | - const char *zPath; /* Path for new entry */ |
| 5057 | | - int nPath; /* strlen(zPath) */ |
| 5170 | + const char *zPath = 0; /* Path for new entry */ |
| 5171 | + int nPath = 0; /* strlen(zPath) */ |
| 5058 | 5172 | const u8 *pData = 0; /* Pointer to buffer containing content */ |
| 5059 | 5173 | int nData = 0; /* Size of pData buffer in bytes */ |
| 5060 | 5174 | int iMethod = 0; /* Compression method for new entry */ |
| 5061 | 5175 | u8 *pFree = 0; /* Free this */ |
| 5176 | + char *zFree = 0; /* Also free this */ |
| 5062 | 5177 | ZipfileCDS cds; /* New Central Directory Structure entry */ |
| 5063 | 5178 | |
| 5064 | 5179 | int bIsDir = 0; |
| 5065 | 5180 | |
| 5066 | 5181 | int mNull; |
| | @@ -5067,19 +5182,23 @@ |
| 5067 | 5182 | |
| 5068 | 5183 | assert( pTab->zFile ); |
| 5069 | 5184 | assert( pTab->pWriteFd ); |
| 5070 | 5185 | |
| 5071 | 5186 | if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ |
| 5072 | | - i64 iDelete = sqlite3_value_int64(apVal[0]); |
| 5073 | | - ZipfileEntry *p; |
| 5074 | | - for(p=pTab->pFirstEntry; p; p=p->pNext){ |
| 5075 | | - if( p->iRowid==iDelete ){ |
| 5076 | | - p->bDeleted = 1; |
| 5077 | | - break; |
| 5078 | | - } |
| 5079 | | - } |
| 5080 | | - if( nVal==1 ) return SQLITE_OK; |
| 5187 | + if( nVal>1 ){ |
| 5188 | + return SQLITE_CONSTRAINT; |
| 5189 | + }else{ |
| 5190 | + i64 iDelete = sqlite3_value_int64(apVal[0]); |
| 5191 | + ZipfileEntry *p; |
| 5192 | + for(p=pTab->pFirstEntry; p; p=p->pNext){ |
| 5193 | + if( p->iRowid==iDelete ){ |
| 5194 | + p->bDeleted = 1; |
| 5195 | + break; |
| 5196 | + } |
| 5197 | + } |
| 5198 | + return SQLITE_OK; |
| 5199 | + } |
| 5081 | 5200 | } |
| 5082 | 5201 | |
| 5083 | 5202 | mNull = (sqlite3_value_type(apVal[5])==SQLITE_NULL ? 0x0 : 0x8) /* sz */ |
| 5084 | 5203 | + (sqlite3_value_type(apVal[6])==SQLITE_NULL ? 0x0 : 0x4) /* rawdata */ |
| 5085 | 5204 | + (sqlite3_value_type(apVal[7])==SQLITE_NULL ? 0x0 : 0x2) /* data */ |
| | @@ -5148,10 +5267,34 @@ |
| 5148 | 5267 | mTime = (sqlite3_int64)time(0); |
| 5149 | 5268 | }else{ |
| 5150 | 5269 | mTime = sqlite3_value_int64(apVal[4]); |
| 5151 | 5270 | } |
| 5152 | 5271 | } |
| 5272 | + |
| 5273 | + if( rc==SQLITE_OK && bIsDir ){ |
| 5274 | + /* For a directory, check that the last character in the path is a |
| 5275 | + ** '/'. This appears to be required for compatibility with info-zip |
| 5276 | + ** (the unzip command on unix). It does not create directories |
| 5277 | + ** otherwise. */ |
| 5278 | + if( zPath[nPath-1]!='/' ){ |
| 5279 | + zFree = sqlite3_mprintf("%s/", zPath); |
| 5280 | + if( zFree==0 ){ rc = SQLITE_NOMEM; } |
| 5281 | + zPath = (const char*)zFree; |
| 5282 | + nPath++; |
| 5283 | + } |
| 5284 | + } |
| 5285 | + |
| 5286 | + /* Check that we're not inserting a duplicate entry */ |
| 5287 | + if( rc==SQLITE_OK ){ |
| 5288 | + ZipfileEntry *p; |
| 5289 | + for(p=pTab->pFirstEntry; p; p=p->pNext){ |
| 5290 | + if( zipfileComparePath(p->zPath, zPath, nPath)==0 ){ |
| 5291 | + rc = SQLITE_CONSTRAINT; |
| 5292 | + break; |
| 5293 | + } |
| 5294 | + } |
| 5295 | + } |
| 5153 | 5296 | |
| 5154 | 5297 | if( rc==SQLITE_OK ){ |
| 5155 | 5298 | /* Create the new CDS record. */ |
| 5156 | 5299 | memset(&cds, 0, sizeof(cds)); |
| 5157 | 5300 | cds.iVersionMadeBy = ZIPFILE_NEWENTRY_MADEBY; |
| | @@ -5176,10 +5319,11 @@ |
| 5176 | 5319 | if( rc==SQLITE_OK ){ |
| 5177 | 5320 | rc = zipfileAppendEntry(pTab, &cds, zPath, nPath, pData, nData, (u32)mTime); |
| 5178 | 5321 | } |
| 5179 | 5322 | |
| 5180 | 5323 | sqlite3_free(pFree); |
| 5324 | + sqlite3_free(zFree); |
| 5181 | 5325 | return rc; |
| 5182 | 5326 | } |
| 5183 | 5327 | |
| 5184 | 5328 | static int zipfileAppendEOCD(ZipfileTab *pTab, ZipfileEOCD *p){ |
| 5185 | 5329 | u8 *aBuf = pTab->aBuffer; |
| | @@ -5329,11 +5473,10 @@ |
| 5329 | 5473 | ){ |
| 5330 | 5474 | SQLITE_EXTENSION_INIT2(pApi); |
| 5331 | 5475 | (void)pzErrMsg; /* Unused parameter */ |
| 5332 | 5476 | return zipfileRegister(db); |
| 5333 | 5477 | } |
| 5334 | | - |
| 5335 | 5478 | |
| 5336 | 5479 | /************************* End ../ext/misc/zipfile.c ********************/ |
| 5337 | 5480 | /************************* Begin ../ext/misc/sqlar.c ******************/ |
| 5338 | 5481 | /* |
| 5339 | 5482 | ** 2017-12-17 |
| | @@ -5642,10 +5785,12 @@ |
| 5642 | 5785 | ************************************************************************* |
| 5643 | 5786 | */ |
| 5644 | 5787 | #include <assert.h> |
| 5645 | 5788 | #include <string.h> |
| 5646 | 5789 | #include <stdio.h> |
| 5790 | + |
| 5791 | +#ifndef SQLITE_OMIT_VIRTUALTABLE |
| 5647 | 5792 | |
| 5648 | 5793 | /* typedef sqlite3_int64 i64; */ |
| 5649 | 5794 | /* typedef sqlite3_uint64 u64; */ |
| 5650 | 5795 | |
| 5651 | 5796 | typedef struct IdxColumn IdxColumn; |
| | @@ -7151,10 +7296,11 @@ |
| 7151 | 7296 | zCols = idxAppendText(&rc, zCols, |
| 7152 | 7297 | "%sx.%Q IS rem(%d, x.%Q) COLLATE %s", zComma, zName, nCol, zName, zColl |
| 7153 | 7298 | ); |
| 7154 | 7299 | zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol); |
| 7155 | 7300 | } |
| 7301 | + sqlite3_reset(pIndexXInfo); |
| 7156 | 7302 | if( rc==SQLITE_OK ){ |
| 7157 | 7303 | if( p->iSample==100 ){ |
| 7158 | 7304 | zQuery = sqlite3_mprintf( |
| 7159 | 7305 | "SELECT %s FROM %Q x ORDER BY %s", zCols, zTab, zOrder |
| 7160 | 7306 | ); |
| | @@ -7560,10 +7706,12 @@ |
| 7560 | 7706 | idxHashClear(&p->hIdx); |
| 7561 | 7707 | sqlite3_free(p->zCandidates); |
| 7562 | 7708 | sqlite3_free(p); |
| 7563 | 7709 | } |
| 7564 | 7710 | } |
| 7711 | + |
| 7712 | +#endif /* ifndef SQLITE_OMIT_VIRTUAL_TABLE */ |
| 7565 | 7713 | |
| 7566 | 7714 | /************************* End ../ext/expert/sqlite3expert.c ********************/ |
| 7567 | 7715 | |
| 7568 | 7716 | #if defined(SQLITE_ENABLE_SESSION) |
| 7569 | 7717 | /* |
| | @@ -7606,26 +7754,31 @@ |
| 7606 | 7754 | u8 autoExplain; /* Automatically turn on .explain mode */ |
| 7607 | 7755 | u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */ |
| 7608 | 7756 | u8 statsOn; /* True to display memory stats before each finalize */ |
| 7609 | 7757 | u8 scanstatsOn; /* True to display scan stats before each finalize */ |
| 7610 | 7758 | u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */ |
| 7759 | + u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */ |
| 7611 | 7760 | int outCount; /* Revert to stdout when reaching zero */ |
| 7612 | 7761 | int cnt; /* Number of records displayed so far */ |
| 7613 | 7762 | FILE *out; /* Write results here */ |
| 7614 | 7763 | FILE *traceOut; /* Output for sqlite3_trace() */ |
| 7615 | 7764 | int nErr; /* Number of errors seen */ |
| 7616 | 7765 | int mode; /* An output mode setting */ |
| 7766 | + int modePrior; /* Saved mode */ |
| 7617 | 7767 | int cMode; /* temporary output mode for the current query */ |
| 7618 | 7768 | int normalMode; /* Output mode before ".explain on" */ |
| 7619 | 7769 | int writableSchema; /* True if PRAGMA writable_schema=ON */ |
| 7620 | 7770 | int showHeader; /* True to show column names in List or Column mode */ |
| 7621 | 7771 | int nCheck; /* Number of ".check" commands run */ |
| 7622 | 7772 | unsigned shellFlgs; /* Various flags */ |
| 7623 | 7773 | char *zDestTable; /* Name of destination table when MODE_Insert */ |
| 7774 | + char *zTempFile; /* Temporary file that might need deleting */ |
| 7624 | 7775 | char zTestcase[30]; /* Name of current test case */ |
| 7625 | 7776 | char colSeparator[20]; /* Column separator character for several modes */ |
| 7626 | 7777 | char rowSeparator[20]; /* Row separator character for MODE_Ascii */ |
| 7778 | + char colSepPrior[20]; /* Saved column separator */ |
| 7779 | + char rowSepPrior[20]; /* Saved row separator */ |
| 7627 | 7780 | int colWidth[100]; /* Requested width of each column when in column mode*/ |
| 7628 | 7781 | int actualWidth[100]; /* Actual width of each column */ |
| 7629 | 7782 | char nullValue[20]; /* The text to print when a NULL comes back from |
| 7630 | 7783 | ** the database */ |
| 7631 | 7784 | char outfile[FILENAME_MAX]; /* Filename for *out */ |
| | @@ -7719,24 +7872,175 @@ |
| 7719 | 7872 | #define SEP_Comma "," |
| 7720 | 7873 | #define SEP_CrLf "\r\n" |
| 7721 | 7874 | #define SEP_Unit "\x1F" |
| 7722 | 7875 | #define SEP_Record "\x1E" |
| 7723 | 7876 | |
| 7724 | | -/* |
| 7725 | | -** Number of elements in an array |
| 7726 | | -*/ |
| 7727 | | -#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) |
| 7728 | | - |
| 7729 | 7877 | /* |
| 7730 | 7878 | ** A callback for the sqlite3_log() interface. |
| 7731 | 7879 | */ |
| 7732 | 7880 | static void shellLog(void *pArg, int iErrCode, const char *zMsg){ |
| 7733 | 7881 | ShellState *p = (ShellState*)pArg; |
| 7734 | 7882 | if( p->pLog==0 ) return; |
| 7735 | 7883 | utf8_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg); |
| 7736 | 7884 | fflush(p->pLog); |
| 7737 | 7885 | } |
| 7886 | + |
| 7887 | +/* |
| 7888 | +** SQL function: shell_putsnl(X) |
| 7889 | +** |
| 7890 | +** Write the text X to the screen (or whatever output is being directed) |
| 7891 | +** adding a newline at the end, and then return X. |
| 7892 | +*/ |
| 7893 | +static void shellPutsFunc( |
| 7894 | + sqlite3_context *pCtx, |
| 7895 | + int nVal, |
| 7896 | + sqlite3_value **apVal |
| 7897 | +){ |
| 7898 | + ShellState *p = (ShellState*)sqlite3_user_data(pCtx); |
| 7899 | + utf8_printf(p->out, "%s\n", sqlite3_value_text(apVal[0])); |
| 7900 | + sqlite3_result_value(pCtx, apVal[0]); |
| 7901 | +} |
| 7902 | + |
| 7903 | +/* |
| 7904 | +** SQL function: edit(VALUE) |
| 7905 | +** edit(VALUE,EDITOR) |
| 7906 | +** |
| 7907 | +** These steps: |
| 7908 | +** |
| 7909 | +** (1) Write VALUE into a temporary file. |
| 7910 | +** (2) Run program EDITOR on that temporary file. |
| 7911 | +** (3) Read the temporary file back and return its content as the result. |
| 7912 | +** (4) Delete the temporary file |
| 7913 | +** |
| 7914 | +** If the EDITOR argument is omitted, use the value in the VISUAL |
| 7915 | +** environment variable. If still there is no EDITOR, through an error. |
| 7916 | +** |
| 7917 | +** Also throw an error if the EDITOR program returns a non-zero exit code. |
| 7918 | +*/ |
| 7919 | +static void editFunc( |
| 7920 | + sqlite3_context *context, |
| 7921 | + int argc, |
| 7922 | + sqlite3_value **argv |
| 7923 | +){ |
| 7924 | + const char *zEditor; |
| 7925 | + char *zTempFile = 0; |
| 7926 | + sqlite3 *db; |
| 7927 | + char *zCmd = 0; |
| 7928 | + int bBin; |
| 7929 | + int rc; |
| 7930 | + FILE *f = 0; |
| 7931 | + sqlite3_int64 sz; |
| 7932 | + sqlite3_int64 x; |
| 7933 | + unsigned char *p = 0; |
| 7934 | + |
| 7935 | + if( argc==2 ){ |
| 7936 | + zEditor = (const char*)sqlite3_value_text(argv[1]); |
| 7937 | + }else{ |
| 7938 | + zEditor = getenv("VISUAL"); |
| 7939 | + } |
| 7940 | + if( zEditor==0 ){ |
| 7941 | + sqlite3_result_error(context, "no editor for edit()", -1); |
| 7942 | + return; |
| 7943 | + } |
| 7944 | + if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ |
| 7945 | + sqlite3_result_error(context, "NULL input to edit()", -1); |
| 7946 | + return; |
| 7947 | + } |
| 7948 | + db = sqlite3_context_db_handle(context); |
| 7949 | + zTempFile = 0; |
| 7950 | + sqlite3_file_control(db, 0, SQLITE_FCNTL_TEMPFILENAME, &zTempFile); |
| 7951 | + if( zTempFile==0 ){ |
| 7952 | + sqlite3_uint64 r = 0; |
| 7953 | + sqlite3_randomness(sizeof(r), &r); |
| 7954 | + zTempFile = sqlite3_mprintf("temp%llx", r); |
| 7955 | + if( zTempFile==0 ){ |
| 7956 | + sqlite3_result_error_nomem(context); |
| 7957 | + return; |
| 7958 | + } |
| 7959 | + } |
| 7960 | + bBin = sqlite3_value_type(argv[0])==SQLITE_BLOB; |
| 7961 | + f = fopen(zTempFile, bBin ? "wb" : "w"); |
| 7962 | + if( f==0 ){ |
| 7963 | + sqlite3_result_error(context, "edit() cannot open temp file", -1); |
| 7964 | + goto edit_func_end; |
| 7965 | + } |
| 7966 | + sz = sqlite3_value_bytes(argv[0]); |
| 7967 | + if( bBin ){ |
| 7968 | + x = fwrite(sqlite3_value_blob(argv[0]), 1, sz, f); |
| 7969 | + }else{ |
| 7970 | + x = fwrite(sqlite3_value_text(argv[0]), 1, sz, f); |
| 7971 | + } |
| 7972 | + fclose(f); |
| 7973 | + f = 0; |
| 7974 | + if( x!=sz ){ |
| 7975 | + sqlite3_result_error(context, "edit() could not write the whole file", -1); |
| 7976 | + goto edit_func_end; |
| 7977 | + } |
| 7978 | + zCmd = sqlite3_mprintf("%s \"%s\"", zEditor, zTempFile); |
| 7979 | + if( zCmd==0 ){ |
| 7980 | + sqlite3_result_error_nomem(context); |
| 7981 | + goto edit_func_end; |
| 7982 | + } |
| 7983 | + rc = system(zCmd); |
| 7984 | + sqlite3_free(zCmd); |
| 7985 | + if( rc ){ |
| 7986 | + sqlite3_result_error(context, "EDITOR returned non-zero", -1); |
| 7987 | + goto edit_func_end; |
| 7988 | + } |
| 7989 | + f = fopen(zTempFile, bBin ? "rb" : "r"); |
| 7990 | + if( f==0 ){ |
| 7991 | + sqlite3_result_error(context, |
| 7992 | + "edit() cannot reopen temp file after edit", -1); |
| 7993 | + goto edit_func_end; |
| 7994 | + } |
| 7995 | + fseek(f, 0, SEEK_END); |
| 7996 | + sz = ftell(f); |
| 7997 | + rewind(f); |
| 7998 | + p = sqlite3_malloc64( sz+(bBin==0) ); |
| 7999 | + if( p==0 ){ |
| 8000 | + sqlite3_result_error_nomem(context); |
| 8001 | + goto edit_func_end; |
| 8002 | + } |
| 8003 | + if( bBin ){ |
| 8004 | + x = fread(p, 1, sz, f); |
| 8005 | + }else{ |
| 8006 | + x = fread(p, 1, sz, f); |
| 8007 | + p[sz] = 0; |
| 8008 | + } |
| 8009 | + fclose(f); |
| 8010 | + f = 0; |
| 8011 | + if( x!=sz ){ |
| 8012 | + sqlite3_result_error(context, "could not read back the whole file", -1); |
| 8013 | + goto edit_func_end; |
| 8014 | + } |
| 8015 | + if( bBin ){ |
| 8016 | + sqlite3_result_blob(context, p, sz, sqlite3_free); |
| 8017 | + }else{ |
| 8018 | + sqlite3_result_text(context, (const char*)p, sz, sqlite3_free); |
| 8019 | + } |
| 8020 | + p = 0; |
| 8021 | + |
| 8022 | +edit_func_end: |
| 8023 | + if( f ) fclose(f); |
| 8024 | + unlink(zTempFile); |
| 8025 | + sqlite3_free(zTempFile); |
| 8026 | + sqlite3_free(p); |
| 8027 | +} |
| 8028 | + |
| 8029 | +/* |
| 8030 | +** Save or restore the current output mode |
| 8031 | +*/ |
| 8032 | +static void outputModePush(ShellState *p){ |
| 8033 | + p->modePrior = p->mode; |
| 8034 | + memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator)); |
| 8035 | + memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator)); |
| 8036 | +} |
| 8037 | +static void outputModePop(ShellState *p){ |
| 8038 | + p->mode = p->modePrior; |
| 8039 | + memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator)); |
| 8040 | + memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator)); |
| 8041 | +} |
| 7738 | 8042 | |
| 7739 | 8043 | /* |
| 7740 | 8044 | ** Output the given string as a hex-encoded blob (eg. X'1234' ) |
| 7741 | 8045 | */ |
| 7742 | 8046 | static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ |
| | @@ -9058,10 +9362,11 @@ |
| 9058 | 9362 | } while( rc == SQLITE_ROW ); |
| 9059 | 9363 | } |
| 9060 | 9364 | } |
| 9061 | 9365 | } |
| 9062 | 9366 | |
| 9367 | +#ifndef SQLITE_OMIT_VIRTUALTABLE |
| 9063 | 9368 | /* |
| 9064 | 9369 | ** This function is called to process SQL if the previous shell command |
| 9065 | 9370 | ** was ".expert". It passes the SQL in the second argument directly to |
| 9066 | 9371 | ** the sqlite3expert object. |
| 9067 | 9372 | ** |
| | @@ -9130,10 +9435,67 @@ |
| 9130 | 9435 | sqlite3_expert_destroy(p); |
| 9131 | 9436 | pState->expert.pExpert = 0; |
| 9132 | 9437 | return rc; |
| 9133 | 9438 | } |
| 9134 | 9439 | |
| 9440 | +/* |
| 9441 | +** Implementation of ".expert" dot command. |
| 9442 | +*/ |
| 9443 | +static int expertDotCommand( |
| 9444 | + ShellState *pState, /* Current shell tool state */ |
| 9445 | + char **azArg, /* Array of arguments passed to dot command */ |
| 9446 | + int nArg /* Number of entries in azArg[] */ |
| 9447 | +){ |
| 9448 | + int rc = SQLITE_OK; |
| 9449 | + char *zErr = 0; |
| 9450 | + int i; |
| 9451 | + int iSample = 0; |
| 9452 | + |
| 9453 | + assert( pState->expert.pExpert==0 ); |
| 9454 | + memset(&pState->expert, 0, sizeof(ExpertInfo)); |
| 9455 | + |
| 9456 | + for(i=1; rc==SQLITE_OK && i<nArg; i++){ |
| 9457 | + char *z = azArg[i]; |
| 9458 | + int n; |
| 9459 | + if( z[0]=='-' && z[1]=='-' ) z++; |
| 9460 | + n = strlen30(z); |
| 9461 | + if( n>=2 && 0==strncmp(z, "-verbose", n) ){ |
| 9462 | + pState->expert.bVerbose = 1; |
| 9463 | + } |
| 9464 | + else if( n>=2 && 0==strncmp(z, "-sample", n) ){ |
| 9465 | + if( i==(nArg-1) ){ |
| 9466 | + raw_printf(stderr, "option requires an argument: %s\n", z); |
| 9467 | + rc = SQLITE_ERROR; |
| 9468 | + }else{ |
| 9469 | + iSample = (int)integerValue(azArg[++i]); |
| 9470 | + if( iSample<0 || iSample>100 ){ |
| 9471 | + raw_printf(stderr, "value out of range: %s\n", azArg[i]); |
| 9472 | + rc = SQLITE_ERROR; |
| 9473 | + } |
| 9474 | + } |
| 9475 | + } |
| 9476 | + else{ |
| 9477 | + raw_printf(stderr, "unknown option: %s\n", z); |
| 9478 | + rc = SQLITE_ERROR; |
| 9479 | + } |
| 9480 | + } |
| 9481 | + |
| 9482 | + if( rc==SQLITE_OK ){ |
| 9483 | + pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr); |
| 9484 | + if( pState->expert.pExpert==0 ){ |
| 9485 | + raw_printf(stderr, "sqlite3_expert_new: %s\n", zErr); |
| 9486 | + rc = SQLITE_ERROR; |
| 9487 | + }else{ |
| 9488 | + sqlite3_expert_config( |
| 9489 | + pState->expert.pExpert, EXPERT_CONFIG_SAMPLE, iSample |
| 9490 | + ); |
| 9491 | + } |
| 9492 | + } |
| 9493 | + |
| 9494 | + return rc; |
| 9495 | +} |
| 9496 | +#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ |
| 9135 | 9497 | |
| 9136 | 9498 | /* |
| 9137 | 9499 | ** Execute a statement or set of statements. Print |
| 9138 | 9500 | ** any result rows/columns depending on the current mode |
| 9139 | 9501 | ** set via the supplied callback. |
| | @@ -9157,14 +9519,16 @@ |
| 9157 | 9519 | |
| 9158 | 9520 | if( pzErrMsg ){ |
| 9159 | 9521 | *pzErrMsg = NULL; |
| 9160 | 9522 | } |
| 9161 | 9523 | |
| 9524 | +#ifndef SQLITE_OMIT_VIRTUALTABLE |
| 9162 | 9525 | if( pArg->expert.pExpert ){ |
| 9163 | 9526 | rc = expertHandleSQL(pArg, zSql, pzErrMsg); |
| 9164 | 9527 | return expertFinish(pArg, (rc!=SQLITE_OK), pzErrMsg); |
| 9165 | 9528 | } |
| 9529 | +#endif |
| 9166 | 9530 | |
| 9167 | 9531 | while( zSql[0] && (SQLITE_OK == rc) ){ |
| 9168 | 9532 | static const char *zStmtSql; |
| 9169 | 9533 | rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover); |
| 9170 | 9534 | if( SQLITE_OK != rc ){ |
| | @@ -9587,10 +9951,11 @@ |
| 9587 | 9951 | ".dump ?TABLE? ... Dump the database in an SQL text format\n" |
| 9588 | 9952 | " If TABLE specified, only dump tables matching\n" |
| 9589 | 9953 | " LIKE pattern TABLE.\n" |
| 9590 | 9954 | ".echo on|off Turn command echo on or off\n" |
| 9591 | 9955 | ".eqp on|off|full Enable or disable automatic EXPLAIN QUERY PLAN\n" |
| 9956 | + ".excel Display the output of next command in a spreadsheet\n" |
| 9592 | 9957 | ".exit Exit this program\n" |
| 9593 | 9958 | ".expert EXPERIMENTAL. Suggest indexes for specified queries\n" |
| 9594 | 9959 | /* Because explain mode comes on automatically now, the ".explain" mode |
| 9595 | 9960 | ** is removed from the help screen. It is still supported for legacy, however */ |
| 9596 | 9961 | /*".explain ?on|off|auto? Turn EXPLAIN output mode on or off or to automatic\n"*/ |
| | @@ -9624,14 +9989,16 @@ |
| 9624 | 9989 | " list Values delimited by \"|\"\n" |
| 9625 | 9990 | " quote Escape answers as for SQL\n" |
| 9626 | 9991 | " tabs Tab-separated values\n" |
| 9627 | 9992 | " tcl TCL list elements\n" |
| 9628 | 9993 | ".nullvalue STRING Use STRING in place of NULL values\n" |
| 9629 | | - ".once FILENAME Output for the next SQL command only to FILENAME\n" |
| 9994 | + ".once (-e|-x|FILE) Output for the next SQL command only to FILE\n" |
| 9995 | + " or invoke system text editor (-e) or spreadsheet (-x)\n" |
| 9996 | + " on the output.\n" |
| 9630 | 9997 | ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE\n" |
| 9631 | 9998 | " The --new option starts with an empty file\n" |
| 9632 | | - ".output ?FILENAME? Send output to FILENAME or stdout\n" |
| 9999 | + ".output ?FILE? Send output to FILE or stdout\n" |
| 9633 | 10000 | ".print STRING... Print literal STRING\n" |
| 9634 | 10001 | ".prompt MAIN CONTINUE Replace the standard prompts\n" |
| 9635 | 10002 | ".quit Exit this program\n" |
| 9636 | 10003 | ".read FILENAME Execute SQL in FILENAME\n" |
| 9637 | 10004 | ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE\n" |
| | @@ -9846,10 +10213,16 @@ |
| 9846 | 10213 | #endif |
| 9847 | 10214 | sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0, |
| 9848 | 10215 | shellAddSchemaName, 0, 0); |
| 9849 | 10216 | sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0, |
| 9850 | 10217 | shellModuleSchema, 0, 0); |
| 10218 | + sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p, |
| 10219 | + shellPutsFunc, 0, 0); |
| 10220 | + sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0, |
| 10221 | + editFunc, 0, 0); |
| 10222 | + sqlite3_create_function(p->db, "edit", 2, SQLITE_UTF8, 0, |
| 10223 | + editFunc, 0, 0); |
| 9851 | 10224 | if( p->openMode==SHELL_OPEN_ZIPFILE ){ |
| 9852 | 10225 | char *zSql = sqlite3_mprintf( |
| 9853 | 10226 | "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename); |
| 9854 | 10227 | sqlite3_exec(p->db, zSql, 0, 0, 0); |
| 9855 | 10228 | sqlite3_free(zSql); |
| | @@ -9979,67 +10352,10 @@ |
| 9979 | 10352 | z[j] = c; |
| 9980 | 10353 | } |
| 9981 | 10354 | if( j<i ) z[j] = 0; |
| 9982 | 10355 | } |
| 9983 | 10356 | |
| 9984 | | -/* |
| 9985 | | -** Return the value of a hexadecimal digit. Return -1 if the input |
| 9986 | | -** is not a hex digit. |
| 9987 | | -*/ |
| 9988 | | -static int hexDigitValue(char c){ |
| 9989 | | - if( c>='0' && c<='9' ) return c - '0'; |
| 9990 | | - if( c>='a' && c<='f' ) return c - 'a' + 10; |
| 9991 | | - if( c>='A' && c<='F' ) return c - 'A' + 10; |
| 9992 | | - return -1; |
| 9993 | | -} |
| 9994 | | - |
| 9995 | | -/* |
| 9996 | | -** Interpret zArg as an integer value, possibly with suffixes. |
| 9997 | | -*/ |
| 9998 | | -static sqlite3_int64 integerValue(const char *zArg){ |
| 9999 | | - sqlite3_int64 v = 0; |
| 10000 | | - static const struct { char *zSuffix; int iMult; } aMult[] = { |
| 10001 | | - { "KiB", 1024 }, |
| 10002 | | - { "MiB", 1024*1024 }, |
| 10003 | | - { "GiB", 1024*1024*1024 }, |
| 10004 | | - { "KB", 1000 }, |
| 10005 | | - { "MB", 1000000 }, |
| 10006 | | - { "GB", 1000000000 }, |
| 10007 | | - { "K", 1000 }, |
| 10008 | | - { "M", 1000000 }, |
| 10009 | | - { "G", 1000000000 }, |
| 10010 | | - }; |
| 10011 | | - int i; |
| 10012 | | - int isNeg = 0; |
| 10013 | | - if( zArg[0]=='-' ){ |
| 10014 | | - isNeg = 1; |
| 10015 | | - zArg++; |
| 10016 | | - }else if( zArg[0]=='+' ){ |
| 10017 | | - zArg++; |
| 10018 | | - } |
| 10019 | | - if( zArg[0]=='0' && zArg[1]=='x' ){ |
| 10020 | | - int x; |
| 10021 | | - zArg += 2; |
| 10022 | | - while( (x = hexDigitValue(zArg[0]))>=0 ){ |
| 10023 | | - v = (v<<4) + x; |
| 10024 | | - zArg++; |
| 10025 | | - } |
| 10026 | | - }else{ |
| 10027 | | - while( IsDigit(zArg[0]) ){ |
| 10028 | | - v = v*10 + zArg[0] - '0'; |
| 10029 | | - zArg++; |
| 10030 | | - } |
| 10031 | | - } |
| 10032 | | - for(i=0; i<ArraySize(aMult); i++){ |
| 10033 | | - if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){ |
| 10034 | | - v *= aMult[i].iMult; |
| 10035 | | - break; |
| 10036 | | - } |
| 10037 | | - } |
| 10038 | | - return isNeg? -v : v; |
| 10039 | | -} |
| 10040 | | - |
| 10041 | 10357 | /* |
| 10042 | 10358 | ** Interpret zArg as either an integer or a boolean value. Return 1 or 0 |
| 10043 | 10359 | ** for TRUE and FALSE. Return the integer value if appropriate. |
| 10044 | 10360 | */ |
| 10045 | 10361 | static int booleanValue(const char *zArg){ |
| | @@ -10082,20 +10398,20 @@ |
| 10082 | 10398 | /* |
| 10083 | 10399 | ** Try to open an output file. The names "stdout" and "stderr" are |
| 10084 | 10400 | ** recognized and do the right thing. NULL is returned if the output |
| 10085 | 10401 | ** filename is "off". |
| 10086 | 10402 | */ |
| 10087 | | -static FILE *output_file_open(const char *zFile){ |
| 10403 | +static FILE *output_file_open(const char *zFile, int bTextMode){ |
| 10088 | 10404 | FILE *f; |
| 10089 | 10405 | if( strcmp(zFile,"stdout")==0 ){ |
| 10090 | 10406 | f = stdout; |
| 10091 | 10407 | }else if( strcmp(zFile, "stderr")==0 ){ |
| 10092 | 10408 | f = stderr; |
| 10093 | 10409 | }else if( strcmp(zFile, "off")==0 ){ |
| 10094 | 10410 | f = 0; |
| 10095 | 10411 | }else{ |
| 10096 | | - f = fopen(zFile, "wb"); |
| 10412 | + f = fopen(zFile, bTextMode ? "w" : "wb"); |
| 10097 | 10413 | if( f==0 ){ |
| 10098 | 10414 | utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile); |
| 10099 | 10415 | } |
| 10100 | 10416 | } |
| 10101 | 10417 | return f; |
| | @@ -10504,19 +10820,41 @@ |
| 10504 | 10820 | } |
| 10505 | 10821 | sqlite3_close(newDb); |
| 10506 | 10822 | } |
| 10507 | 10823 | |
| 10508 | 10824 | /* |
| 10509 | | -** Change the output file back to stdout |
| 10825 | +** Change the output file back to stdout. |
| 10826 | +** |
| 10827 | +** If the p->doXdgOpen flag is set, that means the output was being |
| 10828 | +** redirected to a temporary file named by p->zTempFile. In that case, |
| 10829 | +** launch start/open/xdg-open on that temporary file. |
| 10510 | 10830 | */ |
| 10511 | 10831 | static void output_reset(ShellState *p){ |
| 10512 | 10832 | if( p->outfile[0]=='|' ){ |
| 10513 | 10833 | #ifndef SQLITE_OMIT_POPEN |
| 10514 | 10834 | pclose(p->out); |
| 10515 | 10835 | #endif |
| 10516 | 10836 | }else{ |
| 10517 | 10837 | output_file_close(p->out); |
| 10838 | + if( p->doXdgOpen ){ |
| 10839 | + const char *zXdgOpenCmd = |
| 10840 | +#if defined(_WIN32) |
| 10841 | + "start"; |
| 10842 | +#elif defined(__APPLE__) |
| 10843 | + "open"; |
| 10844 | +#else |
| 10845 | + "xdg-open"; |
| 10846 | +#endif |
| 10847 | + char *zCmd; |
| 10848 | + zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); |
| 10849 | + if( system(zCmd) ){ |
| 10850 | + utf8_printf(stderr, "Failed: [%s]\n", zCmd); |
| 10851 | + } |
| 10852 | + sqlite3_free(zCmd); |
| 10853 | + outputModePop(p); |
| 10854 | + p->doXdgOpen = 0; |
| 10855 | + } |
| 10518 | 10856 | } |
| 10519 | 10857 | p->outfile[0] = 0; |
| 10520 | 10858 | p->out = stdout; |
| 10521 | 10859 | } |
| 10522 | 10860 | |
| | @@ -10770,10 +11108,45 @@ |
| 10770 | 11108 | #else |
| 10771 | 11109 | rc = unlink(zFilename); |
| 10772 | 11110 | #endif |
| 10773 | 11111 | return rc; |
| 10774 | 11112 | } |
| 11113 | + |
| 11114 | +/* |
| 11115 | +** Try to delete the temporary file (if there is one) and free the |
| 11116 | +** memory used to hold the name of the temp file. |
| 11117 | +*/ |
| 11118 | +static void clearTempFile(ShellState *p){ |
| 11119 | + if( p->zTempFile==0 ) return; |
| 11120 | + if( p->doXdgOpen ) return; |
| 11121 | + if( shellDeleteFile(p->zTempFile) ) return; |
| 11122 | + sqlite3_free(p->zTempFile); |
| 11123 | + p->zTempFile = 0; |
| 11124 | +} |
| 11125 | + |
| 11126 | +/* |
| 11127 | +** Create a new temp file name with the given suffix. |
| 11128 | +*/ |
| 11129 | +static void newTempFile(ShellState *p, const char *zSuffix){ |
| 11130 | + clearTempFile(p); |
| 11131 | + sqlite3_free(p->zTempFile); |
| 11132 | + p->zTempFile = 0; |
| 11133 | + if( p->db ){ |
| 11134 | + sqlite3_file_control(p->db, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile); |
| 11135 | + } |
| 11136 | + if( p->zTempFile==0 ){ |
| 11137 | + sqlite3_uint64 r; |
| 11138 | + sqlite3_randomness(sizeof(r), &r); |
| 11139 | + p->zTempFile = sqlite3_mprintf("temp%llx.%s", r, zSuffix); |
| 11140 | + }else{ |
| 11141 | + p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix); |
| 11142 | + } |
| 11143 | + if( p->zTempFile==0 ){ |
| 11144 | + raw_printf(stderr, "out of memory\n"); |
| 11145 | + exit(1); |
| 11146 | + } |
| 11147 | +} |
| 10775 | 11148 | |
| 10776 | 11149 | |
| 10777 | 11150 | /* |
| 10778 | 11151 | ** The implementation of SQL scalar function fkey_collate_clause(), used |
| 10779 | 11152 | ** by the ".lint fkey-indexes" command. This scalar function is always |
| | @@ -11099,17 +11472,22 @@ |
| 11099 | 11472 | /* |
| 11100 | 11473 | ** Structure representing a single ".ar" command. |
| 11101 | 11474 | */ |
| 11102 | 11475 | typedef struct ArCommand ArCommand; |
| 11103 | 11476 | struct ArCommand { |
| 11104 | | - int eCmd; /* An AR_CMD_* value */ |
| 11477 | + u8 eCmd; /* An AR_CMD_* value */ |
| 11478 | + u8 bVerbose; /* True if --verbose */ |
| 11479 | + u8 bZip; /* True if the archive is a ZIP */ |
| 11480 | + u8 bDryRun; /* True if --dry-run */ |
| 11481 | + u8 bAppend; /* True if --append */ |
| 11482 | + int nArg; /* Number of command arguments */ |
| 11483 | + char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */ |
| 11105 | 11484 | const char *zFile; /* --file argument, or NULL */ |
| 11106 | 11485 | const char *zDir; /* --directory argument, or NULL */ |
| 11107 | | - int bVerbose; /* True if --verbose */ |
| 11108 | | - int bZip; /* True if --zip */ |
| 11109 | | - int nArg; /* Number of command arguments */ |
| 11110 | 11486 | char **azArg; /* Array of command arguments */ |
| 11487 | + ShellState *p; /* Shell state */ |
| 11488 | + sqlite3 *db; /* Database containing the archive */ |
| 11111 | 11489 | }; |
| 11112 | 11490 | |
| 11113 | 11491 | /* |
| 11114 | 11492 | ** Print a usage message for the .ar command to stderr and return SQLITE_ERROR. |
| 11115 | 11493 | */ |
| | @@ -11131,11 +11509,13 @@ |
| 11131 | 11509 | " -x, --extract Extract files from archive\n" |
| 11132 | 11510 | "\n" |
| 11133 | 11511 | "And zero or more optional options:\n" |
| 11134 | 11512 | " -v, --verbose Print each filename as it is processed\n" |
| 11135 | 11513 | " -f FILE, --file FILE Operate on archive FILE (default is current db)\n" |
| 11514 | +" -a FILE, --append FILE Operate on FILE opened using the apndvfs VFS\n" |
| 11136 | 11515 | " -C DIR, --directory DIR Change to directory DIR to read/extract files\n" |
| 11516 | +" -n, --dryrun Show the SQL that would have occurred\n" |
| 11137 | 11517 | "\n" |
| 11138 | 11518 | "See also: http://sqlite.org/cli.html#sqlar_archive_support\n" |
| 11139 | 11519 | "\n" |
| 11140 | 11520 | ); |
| 11141 | 11521 | return SQLITE_ERROR; |
| | @@ -11166,14 +11546,15 @@ |
| 11166 | 11546 | #define AR_CMD_HELP 5 |
| 11167 | 11547 | |
| 11168 | 11548 | /* |
| 11169 | 11549 | ** Other (non-command) switches. |
| 11170 | 11550 | */ |
| 11171 | | -#define AR_SWITCH_VERBOSE 6 |
| 11172 | | -#define AR_SWITCH_FILE 7 |
| 11173 | | -#define AR_SWITCH_DIRECTORY 8 |
| 11174 | | -#define AR_SWITCH_ZIP 9 |
| 11551 | +#define AR_SWITCH_VERBOSE 6 |
| 11552 | +#define AR_SWITCH_FILE 7 |
| 11553 | +#define AR_SWITCH_DIRECTORY 8 |
| 11554 | +#define AR_SWITCH_APPEND 9 |
| 11555 | +#define AR_SWITCH_DRYRUN 10 |
| 11175 | 11556 | |
| 11176 | 11557 | static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){ |
| 11177 | 11558 | switch( eSwitch ){ |
| 11178 | 11559 | case AR_CMD_CREATE: |
| 11179 | 11560 | case AR_CMD_EXTRACT: |
| | @@ -11184,17 +11565,19 @@ |
| 11184 | 11565 | return arErrorMsg("multiple command options"); |
| 11185 | 11566 | } |
| 11186 | 11567 | pAr->eCmd = eSwitch; |
| 11187 | 11568 | break; |
| 11188 | 11569 | |
| 11570 | + case AR_SWITCH_DRYRUN: |
| 11571 | + pAr->bDryRun = 1; |
| 11572 | + break; |
| 11189 | 11573 | case AR_SWITCH_VERBOSE: |
| 11190 | 11574 | pAr->bVerbose = 1; |
| 11191 | 11575 | break; |
| 11192 | | - case AR_SWITCH_ZIP: |
| 11193 | | - pAr->bZip = 1; |
| 11194 | | - break; |
| 11195 | | - |
| 11576 | + case AR_SWITCH_APPEND: |
| 11577 | + pAr->bAppend = 1; |
| 11578 | + /* Fall thru into --file */ |
| 11196 | 11579 | case AR_SWITCH_FILE: |
| 11197 | 11580 | pAr->zFile = zArg; |
| 11198 | 11581 | break; |
| 11199 | 11582 | case AR_SWITCH_DIRECTORY: |
| 11200 | 11583 | pAr->zDir = zArg; |
| | @@ -11214,24 +11597,25 @@ |
| 11214 | 11597 | char **azArg, /* Array of arguments passed to dot command */ |
| 11215 | 11598 | int nArg, /* Number of entries in azArg[] */ |
| 11216 | 11599 | ArCommand *pAr /* Populate this object */ |
| 11217 | 11600 | ){ |
| 11218 | 11601 | struct ArSwitch { |
| 11219 | | - char cShort; |
| 11220 | 11602 | const char *zLong; |
| 11221 | | - int eSwitch; |
| 11222 | | - int bArg; |
| 11603 | + char cShort; |
| 11604 | + u8 eSwitch; |
| 11605 | + u8 bArg; |
| 11223 | 11606 | } aSwitch[] = { |
| 11224 | | - { 'c', "create", AR_CMD_CREATE, 0 }, |
| 11225 | | - { 'x', "extract", AR_CMD_EXTRACT, 0 }, |
| 11226 | | - { 't', "list", AR_CMD_LIST, 0 }, |
| 11227 | | - { 'u', "update", AR_CMD_UPDATE, 0 }, |
| 11228 | | - { 'h', "help", AR_CMD_HELP, 0 }, |
| 11229 | | - { 'v', "verbose", AR_SWITCH_VERBOSE, 0 }, |
| 11230 | | - { 'f', "file", AR_SWITCH_FILE, 1 }, |
| 11231 | | - { 'C', "directory", AR_SWITCH_DIRECTORY, 1 }, |
| 11232 | | - { 'z', "zip", AR_SWITCH_ZIP, 0 } |
| 11607 | + { "create", 'c', AR_CMD_CREATE, 0 }, |
| 11608 | + { "extract", 'x', AR_CMD_EXTRACT, 0 }, |
| 11609 | + { "list", 't', AR_CMD_LIST, 0 }, |
| 11610 | + { "update", 'u', AR_CMD_UPDATE, 0 }, |
| 11611 | + { "help", 'h', AR_CMD_HELP, 0 }, |
| 11612 | + { "verbose", 'v', AR_SWITCH_VERBOSE, 0 }, |
| 11613 | + { "file", 'f', AR_SWITCH_FILE, 1 }, |
| 11614 | + { "append", 'a', AR_SWITCH_APPEND, 1 }, |
| 11615 | + { "directory", 'C', AR_SWITCH_DIRECTORY, 1 }, |
| 11616 | + { "dryrun", 'n', AR_SWITCH_DRYRUN, 0 }, |
| 11233 | 11617 | }; |
| 11234 | 11618 | int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch); |
| 11235 | 11619 | struct ArSwitch *pEnd = &aSwitch[nSwitch]; |
| 11236 | 11620 | |
| 11237 | 11621 | if( nArg<=1 ){ |
| | @@ -11354,41 +11738,39 @@ |
| 11354 | 11738 | ** |
| 11355 | 11739 | ** This function strips any trailing '/' characters from each argument. |
| 11356 | 11740 | ** This is consistent with the way the [tar] command seems to work on |
| 11357 | 11741 | ** Linux. |
| 11358 | 11742 | */ |
| 11359 | | -static int arCheckEntries(sqlite3 *db, ArCommand *pAr){ |
| 11743 | +static int arCheckEntries(ArCommand *pAr){ |
| 11360 | 11744 | int rc = SQLITE_OK; |
| 11361 | 11745 | if( pAr->nArg ){ |
| 11362 | | - int i; |
| 11746 | + int i, j; |
| 11363 | 11747 | sqlite3_stmt *pTest = 0; |
| 11364 | 11748 | |
| 11365 | | - shellPreparePrintf(db, &rc, &pTest, "SELECT name FROM %s WHERE name=?1", |
| 11366 | | - pAr->bZip ? "zipfile(?2)" : "sqlar" |
| 11749 | + shellPreparePrintf(pAr->db, &rc, &pTest, |
| 11750 | + "SELECT name FROM %s WHERE name=$name", |
| 11751 | + pAr->zSrcTable |
| 11367 | 11752 | ); |
| 11368 | | - if( rc==SQLITE_OK && pAr->bZip ){ |
| 11369 | | - sqlite3_bind_text(pTest, 2, pAr->zFile, -1, SQLITE_TRANSIENT); |
| 11370 | | - } |
| 11753 | + j = sqlite3_bind_parameter_index(pTest, "$name"); |
| 11371 | 11754 | for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){ |
| 11372 | 11755 | char *z = pAr->azArg[i]; |
| 11373 | 11756 | int n = strlen30(z); |
| 11374 | 11757 | int bOk = 0; |
| 11375 | 11758 | while( n>0 && z[n-1]=='/' ) n--; |
| 11376 | 11759 | z[n] = '\0'; |
| 11377 | | - sqlite3_bind_text(pTest, 1, z, -1, SQLITE_STATIC); |
| 11760 | + sqlite3_bind_text(pTest, j, z, -1, SQLITE_STATIC); |
| 11378 | 11761 | if( SQLITE_ROW==sqlite3_step(pTest) ){ |
| 11379 | 11762 | bOk = 1; |
| 11380 | 11763 | } |
| 11381 | 11764 | shellReset(&rc, pTest); |
| 11382 | 11765 | if( rc==SQLITE_OK && bOk==0 ){ |
| 11383 | | - raw_printf(stderr, "not found in archive: %s\n", z); |
| 11766 | + utf8_printf(stderr, "not found in archive: %s\n", z); |
| 11384 | 11767 | rc = SQLITE_ERROR; |
| 11385 | 11768 | } |
| 11386 | 11769 | } |
| 11387 | 11770 | shellFinalize(&rc, pTest); |
| 11388 | 11771 | } |
| 11389 | | - |
| 11390 | 11772 | return rc; |
| 11391 | 11773 | } |
| 11392 | 11774 | |
| 11393 | 11775 | /* |
| 11394 | 11776 | ** Format a WHERE clause that can be used against the "sqlar" table to |
| | @@ -11410,13 +11792,13 @@ |
| 11410 | 11792 | int i; |
| 11411 | 11793 | const char *zSep = ""; |
| 11412 | 11794 | for(i=0; i<pAr->nArg; i++){ |
| 11413 | 11795 | const char *z = pAr->azArg[i]; |
| 11414 | 11796 | zWhere = sqlite3_mprintf( |
| 11415 | | - "%z%s name = '%q' OR name BETWEEN '%q/' AND '%q0'", |
| 11416 | | - zWhere, zSep, z, z, z |
| 11417 | | - ); |
| 11797 | + "%z%s name = '%q' OR substr(name,1,%d) = '%q/'", |
| 11798 | + zWhere, zSep, z, strlen30(z)+1, z |
| 11799 | + ); |
| 11418 | 11800 | if( zWhere==0 ){ |
| 11419 | 11801 | *pRc = SQLITE_NOMEM; |
| 11420 | 11802 | break; |
| 11421 | 11803 | } |
| 11422 | 11804 | zSep = " OR "; |
| | @@ -11424,107 +11806,75 @@ |
| 11424 | 11806 | } |
| 11425 | 11807 | } |
| 11426 | 11808 | *pzWhere = zWhere; |
| 11427 | 11809 | } |
| 11428 | 11810 | |
| 11429 | | -/* |
| 11430 | | -** Argument zMode must point to a buffer at least 11 bytes in size. This |
| 11431 | | -** function populates this buffer with the string interpretation of |
| 11432 | | -** the unix file mode passed as the second argument (e.g. "drwxr-xr-x"). |
| 11433 | | -*/ |
| 11434 | | -static void shellModeToString(char *zMode, int mode){ |
| 11435 | | - int i; |
| 11436 | | - |
| 11437 | | - /* Magic numbers copied from [man 2 stat] */ |
| 11438 | | - if( mode & 0040000 ){ |
| 11439 | | - zMode[0] = 'd'; |
| 11440 | | - }else if( (mode & 0120000)==0120000 ){ |
| 11441 | | - zMode[0] = 'l'; |
| 11442 | | - }else{ |
| 11443 | | - zMode[0] = '-'; |
| 11444 | | - } |
| 11445 | | - |
| 11446 | | - for(i=0; i<3; i++){ |
| 11447 | | - int m = (mode >> ((2-i)*3)); |
| 11448 | | - char *a = &zMode[1 + i*3]; |
| 11449 | | - a[0] = (m & 0x4) ? 'r' : '-'; |
| 11450 | | - a[1] = (m & 0x2) ? 'w' : '-'; |
| 11451 | | - a[2] = (m & 0x1) ? 'x' : '-'; |
| 11452 | | - } |
| 11453 | | - zMode[10] = '\0'; |
| 11454 | | -} |
| 11455 | | - |
| 11456 | 11811 | /* |
| 11457 | 11812 | ** Implementation of .ar "lisT" command. |
| 11458 | 11813 | */ |
| 11459 | | -static int arListCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){ |
| 11814 | +static int arListCommand(ArCommand *pAr){ |
| 11460 | 11815 | const char *zSql = "SELECT %s FROM %s WHERE %s"; |
| 11461 | | - const char *zTbl = (pAr->bZip ? "zipfile(?)" : "sqlar"); |
| 11462 | 11816 | const char *azCols[] = { |
| 11463 | 11817 | "name", |
| 11464 | | - "mode, sz, datetime(mtime, 'unixepoch'), name" |
| 11818 | + "lsmode(mode), sz, datetime(mtime, 'unixepoch'), name" |
| 11465 | 11819 | }; |
| 11466 | 11820 | |
| 11467 | 11821 | char *zWhere = 0; |
| 11468 | 11822 | sqlite3_stmt *pSql = 0; |
| 11469 | 11823 | int rc; |
| 11470 | 11824 | |
| 11471 | | - rc = arCheckEntries(db, pAr); |
| 11825 | + rc = arCheckEntries(pAr); |
| 11472 | 11826 | arWhereClause(&rc, pAr, &zWhere); |
| 11473 | 11827 | |
| 11474 | | - shellPreparePrintf(db, &rc, &pSql, zSql, azCols[pAr->bVerbose], zTbl, zWhere); |
| 11475 | | - if( rc==SQLITE_OK && pAr->bZip ){ |
| 11476 | | - sqlite3_bind_text(pSql, 1, pAr->zFile, -1, SQLITE_TRANSIENT); |
| 11477 | | - } |
| 11478 | | - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 11479 | | - if( pAr->bVerbose ){ |
| 11480 | | - char zMode[11]; |
| 11481 | | - shellModeToString(zMode, sqlite3_column_int(pSql, 0)); |
| 11482 | | - |
| 11483 | | - raw_printf(p->out, "%s % 10d %s %s\n", zMode, |
| 11484 | | - sqlite3_column_int(pSql, 1), |
| 11485 | | - sqlite3_column_text(pSql, 2), |
| 11486 | | - sqlite3_column_text(pSql, 3) |
| 11487 | | - ); |
| 11488 | | - }else{ |
| 11489 | | - raw_printf(p->out, "%s\n", sqlite3_column_text(pSql, 0)); |
| 11490 | | - } |
| 11491 | | - } |
| 11492 | | - |
| 11828 | + shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose], |
| 11829 | + pAr->zSrcTable, zWhere); |
| 11830 | + if( pAr->bDryRun ){ |
| 11831 | + utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql)); |
| 11832 | + }else{ |
| 11833 | + while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 11834 | + if( pAr->bVerbose ){ |
| 11835 | + utf8_printf(pAr->p->out, "%s % 10d %s %s\n", |
| 11836 | + sqlite3_column_text(pSql, 0), |
| 11837 | + sqlite3_column_int(pSql, 1), |
| 11838 | + sqlite3_column_text(pSql, 2), |
| 11839 | + sqlite3_column_text(pSql, 3) |
| 11840 | + ); |
| 11841 | + }else{ |
| 11842 | + utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0)); |
| 11843 | + } |
| 11844 | + } |
| 11845 | + } |
| 11493 | 11846 | shellFinalize(&rc, pSql); |
| 11494 | 11847 | return rc; |
| 11495 | 11848 | } |
| 11496 | 11849 | |
| 11497 | 11850 | |
| 11498 | 11851 | /* |
| 11499 | 11852 | ** Implementation of .ar "eXtract" command. |
| 11500 | 11853 | */ |
| 11501 | | -static int arExtractCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){ |
| 11854 | +static int arExtractCommand(ArCommand *pAr){ |
| 11502 | 11855 | const char *zSql1 = |
| 11503 | 11856 | "SELECT " |
| 11504 | | - " :1 || name, " |
| 11505 | | - " writefile(?1 || name, %s, mode, mtime) " |
| 11506 | | - "FROM %s WHERE (%s) AND (data IS NULL OR ?2 = 0)"; |
| 11857 | + " ($dir || name)," |
| 11858 | + " writefile(($dir || name), %s, mode, mtime) " |
| 11859 | + "FROM %s WHERE (%s) AND (data IS NULL OR $dirOnly = 0)"; |
| 11507 | 11860 | |
| 11508 | 11861 | const char *azExtraArg[] = { |
| 11509 | 11862 | "sqlar_uncompress(data, sz)", |
| 11510 | 11863 | "data" |
| 11511 | 11864 | }; |
| 11512 | | - const char *azSource[] = { |
| 11513 | | - "sqlar", "zipfile(?3)" |
| 11514 | | - }; |
| 11515 | 11865 | |
| 11516 | 11866 | sqlite3_stmt *pSql = 0; |
| 11517 | 11867 | int rc = SQLITE_OK; |
| 11518 | 11868 | char *zDir = 0; |
| 11519 | 11869 | char *zWhere = 0; |
| 11520 | | - int i; |
| 11870 | + int i, j; |
| 11521 | 11871 | |
| 11522 | 11872 | /* If arguments are specified, check that they actually exist within |
| 11523 | 11873 | ** the archive before proceeding. And formulate a WHERE clause to |
| 11524 | 11874 | ** match them. */ |
| 11525 | | - rc = arCheckEntries(db, pAr); |
| 11875 | + rc = arCheckEntries(pAr); |
| 11526 | 11876 | arWhereClause(&rc, pAr, &zWhere); |
| 11527 | 11877 | |
| 11528 | 11878 | if( rc==SQLITE_OK ){ |
| 11529 | 11879 | if( pAr->zDir ){ |
| 11530 | 11880 | zDir = sqlite3_mprintf("%s/", pAr->zDir); |
| | @@ -11532,30 +11882,33 @@ |
| 11532 | 11882 | zDir = sqlite3_mprintf(""); |
| 11533 | 11883 | } |
| 11534 | 11884 | if( zDir==0 ) rc = SQLITE_NOMEM; |
| 11535 | 11885 | } |
| 11536 | 11886 | |
| 11537 | | - shellPreparePrintf(db, &rc, &pSql, zSql1, |
| 11538 | | - azExtraArg[pAr->bZip], azSource[pAr->bZip], zWhere |
| 11887 | + shellPreparePrintf(pAr->db, &rc, &pSql, zSql1, |
| 11888 | + azExtraArg[pAr->bZip], pAr->zSrcTable, zWhere |
| 11539 | 11889 | ); |
| 11540 | 11890 | |
| 11541 | 11891 | if( rc==SQLITE_OK ){ |
| 11542 | | - sqlite3_bind_text(pSql, 1, zDir, -1, SQLITE_STATIC); |
| 11543 | | - if( pAr->bZip ){ |
| 11544 | | - sqlite3_bind_text(pSql, 3, pAr->zFile, -1, SQLITE_STATIC); |
| 11545 | | - } |
| 11892 | + j = sqlite3_bind_parameter_index(pSql, "$dir"); |
| 11893 | + sqlite3_bind_text(pSql, j, zDir, -1, SQLITE_STATIC); |
| 11546 | 11894 | |
| 11547 | 11895 | /* Run the SELECT statement twice. The first time, writefile() is called |
| 11548 | 11896 | ** for all archive members that should be extracted. The second time, |
| 11549 | 11897 | ** only for the directories. This is because the timestamps for |
| 11550 | 11898 | ** extracted directories must be reset after they are populated (as |
| 11551 | 11899 | ** populating them changes the timestamp). */ |
| 11552 | 11900 | for(i=0; i<2; i++){ |
| 11553 | | - sqlite3_bind_int(pSql, 2, i); |
| 11554 | | - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 11555 | | - if( i==0 && pAr->bVerbose ){ |
| 11556 | | - raw_printf(p->out, "%s\n", sqlite3_column_text(pSql, 0)); |
| 11901 | + j = sqlite3_bind_parameter_index(pSql, "$dirOnly"); |
| 11902 | + sqlite3_bind_int(pSql, j, i); |
| 11903 | + if( pAr->bDryRun ){ |
| 11904 | + utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql)); |
| 11905 | + }else{ |
| 11906 | + while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ |
| 11907 | + if( i==0 && pAr->bVerbose ){ |
| 11908 | + utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0)); |
| 11909 | + } |
| 11557 | 11910 | } |
| 11558 | 11911 | } |
| 11559 | 11912 | shellReset(&rc, pSql); |
| 11560 | 11913 | } |
| 11561 | 11914 | shellFinalize(&rc, pSql); |
| | @@ -11563,10 +11916,29 @@ |
| 11563 | 11916 | |
| 11564 | 11917 | sqlite3_free(zDir); |
| 11565 | 11918 | sqlite3_free(zWhere); |
| 11566 | 11919 | return rc; |
| 11567 | 11920 | } |
| 11921 | + |
| 11922 | +/* |
| 11923 | +** Run the SQL statement in zSql. Or if doing a --dryrun, merely print it out. |
| 11924 | +*/ |
| 11925 | +static int arExecSql(ArCommand *pAr, const char *zSql){ |
| 11926 | + int rc; |
| 11927 | + if( pAr->bDryRun ){ |
| 11928 | + utf8_printf(pAr->p->out, "%s\n", zSql); |
| 11929 | + rc = SQLITE_OK; |
| 11930 | + }else{ |
| 11931 | + char *zErr = 0; |
| 11932 | + rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr); |
| 11933 | + if( zErr ){ |
| 11934 | + utf8_printf(stdout, "ERROR: %s\n", zErr); |
| 11935 | + sqlite3_free(zErr); |
| 11936 | + } |
| 11937 | + } |
| 11938 | + return rc; |
| 11939 | +} |
| 11568 | 11940 | |
| 11569 | 11941 | |
| 11570 | 11942 | /* |
| 11571 | 11943 | ** Implementation of .ar "create" and "update" commands. |
| 11572 | 11944 | ** |
| | @@ -11576,116 +11948,60 @@ |
| 11576 | 11948 | ** printed on stdout for each file archived. |
| 11577 | 11949 | ** |
| 11578 | 11950 | ** The create command is the same as update, except that it drops |
| 11579 | 11951 | ** any existing "sqlar" table before beginning. |
| 11580 | 11952 | */ |
| 11581 | | -static int arCreateUpdate( |
| 11582 | | - ShellState *p, /* Shell state pointer */ |
| 11583 | | - sqlite3 *db, |
| 11953 | +static int arCreateOrUpdateCommand( |
| 11584 | 11954 | ArCommand *pAr, /* Command arguments and options */ |
| 11585 | | - int bUpdate |
| 11955 | + int bUpdate /* true for a --create. false for --update */ |
| 11586 | 11956 | ){ |
| 11587 | | - const char *zSql = "SELECT name, mode, mtime, data FROM fsdir(?, ?)"; |
| 11588 | 11957 | const char *zCreate = |
| 11589 | 11958 | "CREATE TABLE IF NOT EXISTS sqlar(\n" |
| 11590 | 11959 | " name TEXT PRIMARY KEY, -- name of the file\n" |
| 11591 | 11960 | " mode INT, -- access permissions\n" |
| 11592 | 11961 | " mtime INT, -- last modification time\n" |
| 11593 | 11962 | " sz INT, -- original file size\n" |
| 11594 | 11963 | " data BLOB -- compressed content\n" |
| 11595 | 11964 | ")"; |
| 11596 | 11965 | const char *zDrop = "DROP TABLE IF EXISTS sqlar"; |
| 11597 | | - const char *zInsert = "REPLACE INTO sqlar VALUES(?,?,?,?,sqlar_compress(?))"; |
| 11598 | | - |
| 11599 | | - sqlite3_stmt *pStmt = 0; /* Directory traverser */ |
| 11600 | | - sqlite3_stmt *pInsert = 0; /* Compilation of zInsert */ |
| 11966 | + const char *zInsertFmt = |
| 11967 | + "REPLACE INTO sqlar(name,mode,mtime,sz,data)\n" |
| 11968 | + " SELECT\n" |
| 11969 | + " %s,\n" |
| 11970 | + " mode,\n" |
| 11971 | + " mtime,\n" |
| 11972 | + " CASE substr(lsmode(mode),1,1)\n" |
| 11973 | + " WHEN '-' THEN length(data)\n" |
| 11974 | + " WHEN 'd' THEN 0\n" |
| 11975 | + " ELSE -1 END,\n" |
| 11976 | + " CASE WHEN lsmode(mode) LIKE 'd%%' THEN NULL else data END\n" |
| 11977 | + " FROM fsdir(%Q,%Q)\n" |
| 11978 | + " WHERE lsmode(mode) NOT LIKE '?%%';"; |
| 11601 | 11979 | int i; /* For iterating through azFile[] */ |
| 11602 | 11980 | int rc; /* Return code */ |
| 11603 | 11981 | |
| 11604 | | - assert( pAr->bZip==0 ); |
| 11605 | | - |
| 11606 | | - rc = sqlite3_exec(db, "SAVEPOINT ar;", 0, 0, 0); |
| 11607 | | - if( rc!=SQLITE_OK ) return rc; |
| 11608 | | - |
| 11609 | | - if( bUpdate==0 ){ |
| 11610 | | - rc = sqlite3_exec(db, zDrop, 0, 0, 0); |
| 11611 | | - if( rc!=SQLITE_OK ) return rc; |
| 11612 | | - } |
| 11613 | | - |
| 11614 | | - rc = sqlite3_exec(db, zCreate, 0, 0, 0); |
| 11615 | | - shellPrepare(db, &rc, zInsert, &pInsert); |
| 11616 | | - shellPrepare(db, &rc, zSql, &pStmt); |
| 11617 | | - sqlite3_bind_text(pStmt, 2, pAr->zDir, -1, SQLITE_STATIC); |
| 11618 | | - |
| 11619 | | - for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){ |
| 11620 | | - sqlite3_bind_text(pStmt, 1, pAr->azArg[i], -1, SQLITE_STATIC); |
| 11621 | | - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ |
| 11622 | | - int sz; |
| 11623 | | - const char *zName = (const char*)sqlite3_column_text(pStmt, 0); |
| 11624 | | - int mode = sqlite3_column_int(pStmt, 1); |
| 11625 | | - unsigned int mtime = sqlite3_column_int(pStmt, 2); |
| 11626 | | - |
| 11627 | | - if( pAr->bVerbose ){ |
| 11628 | | - raw_printf(p->out, "%s\n", zName); |
| 11629 | | - } |
| 11630 | | - |
| 11631 | | - sqlite3_bind_text(pInsert, 1, zName, -1, SQLITE_STATIC); |
| 11632 | | - sqlite3_bind_int(pInsert, 2, mode); |
| 11633 | | - sqlite3_bind_int64(pInsert, 3, (sqlite3_int64)mtime); |
| 11634 | | - |
| 11635 | | - if( S_ISDIR(mode) ){ |
| 11636 | | - sz = 0; |
| 11637 | | - sqlite3_bind_null(pInsert, 5); |
| 11638 | | - }else{ |
| 11639 | | - sqlite3_bind_value(pInsert, 5, sqlite3_column_value(pStmt, 3)); |
| 11640 | | - if( S_ISLNK(mode) ){ |
| 11641 | | - sz = -1; |
| 11642 | | - }else{ |
| 11643 | | - sz = sqlite3_column_bytes(pStmt, 3); |
| 11644 | | - } |
| 11645 | | - } |
| 11646 | | - |
| 11647 | | - sqlite3_bind_int(pInsert, 4, sz); |
| 11648 | | - sqlite3_step(pInsert); |
| 11649 | | - rc = sqlite3_reset(pInsert); |
| 11650 | | - } |
| 11651 | | - shellReset(&rc, pStmt); |
| 11652 | | - } |
| 11653 | | - |
| 11654 | | - if( rc!=SQLITE_OK ){ |
| 11655 | | - sqlite3_exec(db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0); |
| 11656 | | - }else{ |
| 11657 | | - rc = sqlite3_exec(db, "RELEASE ar;", 0, 0, 0); |
| 11658 | | - } |
| 11659 | | - shellFinalize(&rc, pStmt); |
| 11660 | | - shellFinalize(&rc, pInsert); |
| 11661 | | - return rc; |
| 11662 | | -} |
| 11663 | | - |
| 11664 | | -/* |
| 11665 | | -** Implementation of .ar "Create" command. |
| 11666 | | -** |
| 11667 | | -** Create the "sqlar" table in the database if it does not already exist. |
| 11668 | | -** Then add each file in the azFile[] array to the archive. Directories |
| 11669 | | -** are added recursively. If argument bVerbose is non-zero, a message is |
| 11670 | | -** printed on stdout for each file archived. |
| 11671 | | -*/ |
| 11672 | | -static int arCreateCommand( |
| 11673 | | - ShellState *p, /* Shell state pointer */ |
| 11674 | | - sqlite3 *db, |
| 11675 | | - ArCommand *pAr /* Command arguments and options */ |
| 11676 | | -){ |
| 11677 | | - return arCreateUpdate(p, db, pAr, 0); |
| 11678 | | -} |
| 11679 | | - |
| 11680 | | -/* |
| 11681 | | -** Implementation of .ar "Update" command. |
| 11682 | | -*/ |
| 11683 | | -static int arUpdateCmd(ShellState *p, sqlite3 *db, ArCommand *pAr){ |
| 11684 | | - return arCreateUpdate(p, db, pAr, 1); |
| 11685 | | -} |
| 11686 | | - |
| 11982 | + rc = arExecSql(pAr, "SAVEPOINT ar;"); |
| 11983 | + if( rc!=SQLITE_OK ) return rc; |
| 11984 | + if( bUpdate==0 ){ |
| 11985 | + rc = arExecSql(pAr, zDrop); |
| 11986 | + if( rc!=SQLITE_OK ) return rc; |
| 11987 | + } |
| 11988 | + rc = arExecSql(pAr, zCreate); |
| 11989 | + for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){ |
| 11990 | + char *zSql = sqlite3_mprintf(zInsertFmt, |
| 11991 | + pAr->bVerbose ? "shell_putsnl(name)" : "name", |
| 11992 | + pAr->azArg[i], pAr->zDir); |
| 11993 | + rc = arExecSql(pAr, zSql); |
| 11994 | + sqlite3_free(zSql); |
| 11995 | + } |
| 11996 | + if( rc!=SQLITE_OK ){ |
| 11997 | + arExecSql(pAr, "ROLLBACK TO ar; RELEASE ar;"); |
| 11998 | + }else{ |
| 11999 | + rc = arExecSql(pAr, "RELEASE ar;"); |
| 12000 | + } |
| 12001 | + return rc; |
| 12002 | +} |
| 11687 | 12003 | |
| 11688 | 12004 | /* |
| 11689 | 12005 | ** Implementation of ".ar" dot command. |
| 11690 | 12006 | */ |
| 11691 | 12007 | static int arDotCommand( |
| | @@ -11693,138 +12009,108 @@ |
| 11693 | 12009 | char **azArg, /* Array of arguments passed to dot command */ |
| 11694 | 12010 | int nArg /* Number of entries in azArg[] */ |
| 11695 | 12011 | ){ |
| 11696 | 12012 | ArCommand cmd; |
| 11697 | 12013 | int rc; |
| 12014 | + memset(&cmd, 0, sizeof(cmd)); |
| 11698 | 12015 | rc = arParseCommand(azArg, nArg, &cmd); |
| 11699 | 12016 | if( rc==SQLITE_OK ){ |
| 11700 | | - sqlite3 *db = 0; /* Database handle to use as archive */ |
| 11701 | | - |
| 11702 | | - if( cmd.bZip ){ |
| 12017 | + int eDbType = SHELL_OPEN_UNSPEC; |
| 12018 | + cmd.p = pState; |
| 12019 | + cmd.db = pState->db; |
| 12020 | + if( cmd.zFile ){ |
| 12021 | + eDbType = deduceDatabaseType(cmd.zFile); |
| 12022 | + }else{ |
| 12023 | + eDbType = pState->openMode; |
| 12024 | + } |
| 12025 | + if( eDbType==SHELL_OPEN_ZIPFILE ){ |
| 11703 | 12026 | if( cmd.zFile==0 ){ |
| 11704 | | - raw_printf(stderr, "zip format requires a --file switch\n"); |
| 11705 | | - return SQLITE_ERROR; |
| 11706 | | - }else |
| 12027 | + cmd.zSrcTable = sqlite3_mprintf("zip"); |
| 12028 | + }else{ |
| 12029 | + cmd.zSrcTable = sqlite3_mprintf("zipfile(%Q)", cmd.zFile); |
| 12030 | + } |
| 11707 | 12031 | if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_UPDATE ){ |
| 11708 | | - raw_printf(stderr, "zip archives are read-only\n"); |
| 11709 | | - return SQLITE_ERROR; |
| 12032 | + utf8_printf(stderr, "zip archives are read-only\n"); |
| 12033 | + rc = SQLITE_ERROR; |
| 12034 | + goto end_ar_command; |
| 11710 | 12035 | } |
| 11711 | | - db = pState->db; |
| 12036 | + cmd.bZip = 1; |
| 11712 | 12037 | }else if( cmd.zFile ){ |
| 11713 | 12038 | int flags; |
| 12039 | + if( cmd.bAppend ) eDbType = SHELL_OPEN_APPENDVFS; |
| 11714 | 12040 | if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_UPDATE ){ |
| 11715 | 12041 | flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; |
| 11716 | 12042 | }else{ |
| 11717 | 12043 | flags = SQLITE_OPEN_READONLY; |
| 11718 | 12044 | } |
| 11719 | | - rc = sqlite3_open_v2(cmd.zFile, &db, flags, 0); |
| 12045 | + cmd.db = 0; |
| 12046 | + if( cmd.bDryRun ){ |
| 12047 | + utf8_printf(pState->out, "-- open database '%s'%s\n", cmd.zFile, |
| 12048 | + eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : ""); |
| 12049 | + } |
| 12050 | + rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags, |
| 12051 | + eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0); |
| 11720 | 12052 | if( rc!=SQLITE_OK ){ |
| 11721 | | - raw_printf(stderr, "cannot open file: %s (%s)\n", |
| 11722 | | - cmd.zFile, sqlite3_errmsg(db) |
| 12053 | + utf8_printf(stderr, "cannot open file: %s (%s)\n", |
| 12054 | + cmd.zFile, sqlite3_errmsg(cmd.db) |
| 11723 | 12055 | ); |
| 11724 | | - sqlite3_close(db); |
| 11725 | | - return rc; |
| 12056 | + goto end_ar_command; |
| 11726 | 12057 | } |
| 11727 | | - sqlite3_fileio_init(db, 0, 0); |
| 12058 | + sqlite3_fileio_init(cmd.db, 0, 0); |
| 11728 | 12059 | #ifdef SQLITE_HAVE_ZLIB |
| 11729 | | - sqlite3_sqlar_init(db, 0, 0); |
| 12060 | + sqlite3_sqlar_init(cmd.db, 0, 0); |
| 11730 | 12061 | #endif |
| 11731 | | - }else{ |
| 11732 | | - db = pState->db; |
| 12062 | + sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p, |
| 12063 | + shellPutsFunc, 0, 0); |
| 12064 | + |
| 12065 | + } |
| 12066 | + if( cmd.zSrcTable==0 ){ |
| 12067 | + if( cmd.eCmd!=AR_CMD_CREATE |
| 12068 | + && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) |
| 12069 | + ){ |
| 12070 | + utf8_printf(stderr, "database does not contain an 'sqlar' table\n"); |
| 12071 | + rc = SQLITE_ERROR; |
| 12072 | + goto end_ar_command; |
| 12073 | + } |
| 12074 | + cmd.zSrcTable = sqlite3_mprintf("sqlar"); |
| 11733 | 12075 | } |
| 11734 | 12076 | |
| 11735 | 12077 | switch( cmd.eCmd ){ |
| 11736 | 12078 | case AR_CMD_CREATE: |
| 11737 | | - rc = arCreateCommand(pState, db, &cmd); |
| 12079 | + rc = arCreateOrUpdateCommand(&cmd, 0); |
| 11738 | 12080 | break; |
| 11739 | 12081 | |
| 11740 | 12082 | case AR_CMD_EXTRACT: |
| 11741 | | - rc = arExtractCommand(pState, db, &cmd); |
| 12083 | + rc = arExtractCommand(&cmd); |
| 11742 | 12084 | break; |
| 11743 | 12085 | |
| 11744 | 12086 | case AR_CMD_LIST: |
| 11745 | | - rc = arListCommand(pState, db, &cmd); |
| 12087 | + rc = arListCommand(&cmd); |
| 11746 | 12088 | break; |
| 11747 | 12089 | |
| 11748 | 12090 | case AR_CMD_HELP: |
| 11749 | 12091 | arUsage(pState->out); |
| 11750 | 12092 | break; |
| 11751 | 12093 | |
| 11752 | 12094 | default: |
| 11753 | 12095 | assert( cmd.eCmd==AR_CMD_UPDATE ); |
| 11754 | | - rc = arUpdateCmd(pState, db, &cmd); |
| 12096 | + rc = arCreateOrUpdateCommand(&cmd, 1); |
| 11755 | 12097 | break; |
| 11756 | 12098 | } |
| 11757 | | - |
| 11758 | | - if( cmd.zFile ){ |
| 11759 | | - sqlite3_close(db); |
| 11760 | | - } |
| 12099 | + } |
| 12100 | +end_ar_command: |
| 12101 | + if( cmd.db!=pState->db ){ |
| 12102 | + sqlite3_close(cmd.db); |
| 11761 | 12103 | } |
| 12104 | + sqlite3_free(cmd.zSrcTable); |
| 11762 | 12105 | |
| 11763 | 12106 | return rc; |
| 11764 | 12107 | } |
| 11765 | 12108 | /* End of the ".archive" or ".ar" command logic |
| 11766 | 12109 | **********************************************************************************/ |
| 11767 | 12110 | #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */ |
| 11768 | 12111 | |
| 11769 | | -/* |
| 11770 | | -** Implementation of ".expert" dot command. |
| 11771 | | -*/ |
| 11772 | | -static int expertDotCommand( |
| 11773 | | - ShellState *pState, /* Current shell tool state */ |
| 11774 | | - char **azArg, /* Array of arguments passed to dot command */ |
| 11775 | | - int nArg /* Number of entries in azArg[] */ |
| 11776 | | -){ |
| 11777 | | - int rc = SQLITE_OK; |
| 11778 | | - char *zErr = 0; |
| 11779 | | - int i; |
| 11780 | | - int iSample = 0; |
| 11781 | | - |
| 11782 | | - assert( pState->expert.pExpert==0 ); |
| 11783 | | - memset(&pState->expert, 0, sizeof(ExpertInfo)); |
| 11784 | | - |
| 11785 | | - for(i=1; rc==SQLITE_OK && i<nArg; i++){ |
| 11786 | | - char *z = azArg[i]; |
| 11787 | | - int n; |
| 11788 | | - if( z[0]=='-' && z[1]=='-' ) z++; |
| 11789 | | - n = strlen30(z); |
| 11790 | | - if( n>=2 && 0==strncmp(z, "-verbose", n) ){ |
| 11791 | | - pState->expert.bVerbose = 1; |
| 11792 | | - } |
| 11793 | | - else if( n>=2 && 0==strncmp(z, "-sample", n) ){ |
| 11794 | | - if( i==(nArg-1) ){ |
| 11795 | | - raw_printf(stderr, "option requires an argument: %s\n", z); |
| 11796 | | - rc = SQLITE_ERROR; |
| 11797 | | - }else{ |
| 11798 | | - iSample = (int)integerValue(azArg[++i]); |
| 11799 | | - if( iSample<0 || iSample>100 ){ |
| 11800 | | - raw_printf(stderr, "value out of range: %s\n", azArg[i]); |
| 11801 | | - rc = SQLITE_ERROR; |
| 11802 | | - } |
| 11803 | | - } |
| 11804 | | - } |
| 11805 | | - else{ |
| 11806 | | - raw_printf(stderr, "unknown option: %s\n", z); |
| 11807 | | - rc = SQLITE_ERROR; |
| 11808 | | - } |
| 11809 | | - } |
| 11810 | | - |
| 11811 | | - if( rc==SQLITE_OK ){ |
| 11812 | | - pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr); |
| 11813 | | - if( pState->expert.pExpert==0 ){ |
| 11814 | | - raw_printf(stderr, "sqlite3_expert_new: %s\n", zErr); |
| 11815 | | - rc = SQLITE_ERROR; |
| 11816 | | - }else{ |
| 11817 | | - sqlite3_expert_config( |
| 11818 | | - pState->expert.pExpert, EXPERT_CONFIG_SAMPLE, iSample |
| 11819 | | - ); |
| 11820 | | - } |
| 11821 | | - } |
| 11822 | | - |
| 11823 | | - return rc; |
| 11824 | | -} |
| 11825 | | - |
| 11826 | 12112 | |
| 11827 | 12113 | /* |
| 11828 | 12114 | ** If an input line begins with "." then invoke this routine to |
| 11829 | 12115 | ** process that line. |
| 11830 | 12116 | ** |
| | @@ -11835,13 +12121,15 @@ |
| 11835 | 12121 | int nArg = 0; |
| 11836 | 12122 | int n, c; |
| 11837 | 12123 | int rc = 0; |
| 11838 | 12124 | char *azArg[50]; |
| 11839 | 12125 | |
| 12126 | +#ifndef SQLITE_OMIT_VIRTUALTABLE |
| 11840 | 12127 | if( p->expert.pExpert ){ |
| 11841 | 12128 | expertFinish(p, 1, 0); |
| 11842 | 12129 | } |
| 12130 | +#endif |
| 11843 | 12131 | |
| 11844 | 12132 | /* Parse the input line into tokens. |
| 11845 | 12133 | */ |
| 11846 | 12134 | while( zLine[h] && nArg<ArraySize(azArg) ){ |
| 11847 | 12135 | while( IsSpace(zLine[h]) ){ h++; } |
| | @@ -11868,10 +12156,11 @@ |
| 11868 | 12156 | /* Process the input line. |
| 11869 | 12157 | */ |
| 11870 | 12158 | if( nArg==0 ) return 0; /* no tokens, no error */ |
| 11871 | 12159 | n = strlen30(azArg[0]); |
| 11872 | 12160 | c = azArg[0][0]; |
| 12161 | + clearTempFile(p); |
| 11873 | 12162 | |
| 11874 | 12163 | #ifndef SQLITE_OMIT_AUTHORIZATION |
| 11875 | 12164 | if( c=='a' && strncmp(azArg[0], "auth", n)==0 ){ |
| 11876 | 12165 | if( nArg!=2 ){ |
| 11877 | 12166 | raw_printf(stderr, "Usage: .auth ON|OFF\n"); |
| | @@ -12202,14 +12491,16 @@ |
| 12202 | 12491 | if( p->mode==MODE_Explain ) p->mode = p->normalMode; |
| 12203 | 12492 | p->autoExplain = 1; |
| 12204 | 12493 | } |
| 12205 | 12494 | }else |
| 12206 | 12495 | |
| 12496 | +#ifndef SQLITE_OMIT_VIRTUALTABLE |
| 12207 | 12497 | if( c=='e' && strncmp(azArg[0], "expert", n)==0 ){ |
| 12208 | 12498 | open_db(p, 0); |
| 12209 | 12499 | expertDotCommand(p, azArg, nArg); |
| 12210 | 12500 | }else |
| 12501 | +#endif |
| 12211 | 12502 | |
| 12212 | 12503 | if( c=='f' && strncmp(azArg[0], "fullschema", n)==0 ){ |
| 12213 | 12504 | ShellState data; |
| 12214 | 12505 | char *zErrMsg = 0; |
| 12215 | 12506 | int doStats = 0; |
| | @@ -12661,11 +12952,11 @@ |
| 12661 | 12952 | raw_printf(stderr, "Usage: .log FILENAME\n"); |
| 12662 | 12953 | rc = 1; |
| 12663 | 12954 | }else{ |
| 12664 | 12955 | const char *zFile = azArg[1]; |
| 12665 | 12956 | output_file_close(p->pLog); |
| 12666 | | - p->pLog = output_file_open(zFile); |
| 12957 | + p->pLog = output_file_open(zFile, 0); |
| 12667 | 12958 | } |
| 12668 | 12959 | }else |
| 12669 | 12960 | |
| 12670 | 12961 | if( c=='m' && strncmp(azArg[0], "mode", n)==0 ){ |
| 12671 | 12962 | const char *zMode = nArg>=2 ? azArg[1] : ""; |
| | @@ -12770,30 +13061,54 @@ |
| 12770 | 13061 | p->zDbFilename = 0; |
| 12771 | 13062 | open_db(p, 0); |
| 12772 | 13063 | } |
| 12773 | 13064 | }else |
| 12774 | 13065 | |
| 12775 | | - if( c=='o' |
| 12776 | | - && (strncmp(azArg[0], "output", n)==0 || strncmp(azArg[0], "once", n)==0) |
| 13066 | + if( (c=='o' |
| 13067 | + && (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0)) |
| 13068 | + || (c=='e' && n==5 && strcmp(azArg[0],"excel")==0) |
| 12777 | 13069 | ){ |
| 12778 | 13070 | const char *zFile = nArg>=2 ? azArg[1] : "stdout"; |
| 13071 | + int bTxtMode = 0; |
| 13072 | + if( azArg[0][0]=='e' ){ |
| 13073 | + /* Transform the ".excel" command into ".once -x" */ |
| 13074 | + nArg = 2; |
| 13075 | + azArg[0] = "once"; |
| 13076 | + zFile = azArg[1] = "-x"; |
| 13077 | + n = 4; |
| 13078 | + } |
| 12779 | 13079 | if( nArg>2 ){ |
| 12780 | | - utf8_printf(stderr, "Usage: .%s FILE\n", azArg[0]); |
| 13080 | + utf8_printf(stderr, "Usage: .%s [-e|-x|FILE]\n", azArg[0]); |
| 12781 | 13081 | rc = 1; |
| 12782 | 13082 | goto meta_command_exit; |
| 12783 | 13083 | } |
| 12784 | 13084 | if( n>1 && strncmp(azArg[0], "once", n)==0 ){ |
| 12785 | 13085 | if( nArg<2 ){ |
| 12786 | | - raw_printf(stderr, "Usage: .once FILE\n"); |
| 13086 | + raw_printf(stderr, "Usage: .once (-e|-x|FILE)\n"); |
| 12787 | 13087 | rc = 1; |
| 12788 | 13088 | goto meta_command_exit; |
| 12789 | 13089 | } |
| 12790 | 13090 | p->outCount = 2; |
| 12791 | 13091 | }else{ |
| 12792 | 13092 | p->outCount = 0; |
| 12793 | 13093 | } |
| 12794 | 13094 | output_reset(p); |
| 13095 | + if( zFile[0]=='-' && zFile[1]=='-' ) zFile++; |
| 13096 | + if( strcmp(zFile, "-e")==0 || strcmp(zFile, "-x")==0 ){ |
| 13097 | + p->doXdgOpen = 1; |
| 13098 | + outputModePush(p); |
| 13099 | + if( zFile[1]=='x' ){ |
| 13100 | + newTempFile(p, "csv"); |
| 13101 | + p->mode = MODE_Csv; |
| 13102 | + sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 13103 | + sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); |
| 13104 | + }else{ |
| 13105 | + newTempFile(p, "txt"); |
| 13106 | + bTxtMode = 1; |
| 13107 | + } |
| 13108 | + zFile = p->zTempFile; |
| 13109 | + } |
| 12795 | 13110 | if( zFile[0]=='|' ){ |
| 12796 | 13111 | #ifdef SQLITE_OMIT_POPEN |
| 12797 | 13112 | raw_printf(stderr, "Error: pipes are not supported in this OS\n"); |
| 12798 | 13113 | rc = 1; |
| 12799 | 13114 | p->out = stdout; |
| | @@ -12806,11 +13121,11 @@ |
| 12806 | 13121 | }else{ |
| 12807 | 13122 | sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); |
| 12808 | 13123 | } |
| 12809 | 13124 | #endif |
| 12810 | 13125 | }else{ |
| 12811 | | - p->out = output_file_open(zFile); |
| 13126 | + p->out = output_file_open(zFile, bTxtMode); |
| 12812 | 13127 | if( p->out==0 ){ |
| 12813 | 13128 | if( strcmp(zFile,"off")!=0 ){ |
| 12814 | 13129 | utf8_printf(stderr,"Error: cannot write to \"%s\"\n", zFile); |
| 12815 | 13130 | } |
| 12816 | 13131 | p->out = stdout; |
| | @@ -13683,11 +13998,11 @@ |
| 13683 | 13998 | }else |
| 13684 | 13999 | |
| 13685 | 14000 | /* Begin redirecting output to the file "testcase-out.txt" */ |
| 13686 | 14001 | if( c=='t' && strcmp(azArg[0],"testcase")==0 ){ |
| 13687 | 14002 | output_reset(p); |
| 13688 | | - p->out = output_file_open("testcase-out.txt"); |
| 14003 | + p->out = output_file_open("testcase-out.txt", 0); |
| 13689 | 14004 | if( p->out==0 ){ |
| 13690 | 14005 | raw_printf(stderr, "Error: cannot open 'testcase-out.txt'\n"); |
| 13691 | 14006 | } |
| 13692 | 14007 | if( nArg>=2 ){ |
| 13693 | 14008 | sqlite3_snprintf(sizeof(p->zTestcase), p->zTestcase, "%s", azArg[1]); |
| | @@ -13889,11 +14204,11 @@ |
| 13889 | 14204 | raw_printf(stderr, "Usage: .trace FILE|off\n"); |
| 13890 | 14205 | rc = 1; |
| 13891 | 14206 | goto meta_command_exit; |
| 13892 | 14207 | } |
| 13893 | 14208 | output_file_close(p->traceOut); |
| 13894 | | - p->traceOut = output_file_open(azArg[1]); |
| 14209 | + p->traceOut = output_file_open(azArg[1], 0); |
| 13895 | 14210 | #if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) |
| 13896 | 14211 | if( p->traceOut==0 ){ |
| 13897 | 14212 | sqlite3_trace_v2(p->db, 0, 0, 0); |
| 13898 | 14213 | }else{ |
| 13899 | 14214 | sqlite3_trace_v2(p->db, SQLITE_TRACE_STMT, sql_trace_callback,p->traceOut); |
| | @@ -14219,10 +14534,12 @@ |
| 14219 | 14534 | errCnt += runOneSqlLine(p, zSql, in, startline); |
| 14220 | 14535 | nSql = 0; |
| 14221 | 14536 | if( p->outCount ){ |
| 14222 | 14537 | output_reset(p); |
| 14223 | 14538 | p->outCount = 0; |
| 14539 | + }else{ |
| 14540 | + clearTempFile(p); |
| 14224 | 14541 | } |
| 14225 | 14542 | }else if( nSql && _all_whitespace(zSql) ){ |
| 14226 | 14543 | if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql); |
| 14227 | 14544 | nSql = 0; |
| 14228 | 14545 | } |
| | @@ -14841,11 +15158,14 @@ |
| 14841 | 15158 | session_close_all(&data); |
| 14842 | 15159 | sqlite3_close(data.db); |
| 14843 | 15160 | } |
| 14844 | 15161 | sqlite3_free(data.zFreeOnClose); |
| 14845 | 15162 | find_home_dir(1); |
| 15163 | + output_reset(&data); |
| 15164 | + data.doXdgOpen = 0; |
| 15165 | + clearTempFile(&data); |
| 14846 | 15166 | #if !SQLITE_SHELL_IS_UTF8 |
| 14847 | 15167 | for(i=0; i<argc; i++) sqlite3_free(argv[i]); |
| 14848 | 15168 | sqlite3_free(argv); |
| 14849 | 15169 | #endif |
| 14850 | 15170 | return rc; |
| 14851 | 15171 | } |
| 14852 | 15172 | |