diff --git a/ext/misc/fileio.c b/ext/misc/fileio.c index 475f3713ce..db69357008 100644 --- a/ext/misc/fileio.c +++ b/ext/misc/fileio.c @@ -30,6 +30,17 @@ #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 #include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + #define FSDIR_SCHEMA "CREATE TABLE x(name,mode,mtime,data,dir HIDDEN)" @@ -69,44 +80,91 @@ static void readfileFunc( readFileContents(context, zName); } +static void ctxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){ + char *zMsg = 0; + va_list ap; + va_start(ap, zFmt); + zMsg = sqlite3_vmprintf(zFmt, ap); + sqlite3_result_error(ctx, zMsg, -1); + sqlite3_free(zMsg); + va_end(ap); +} + /* -** Implementation of the "writefile(X,Y)" SQL function. The argument Y -** is written into file X. The number of bytes written is returned. Or -** NULL is returned if something goes wrong, such as being unable to open -** file X for writing. +** Implementation of the "writefile(W,X[,Y]])" SQL function. +** +** The argument X is written into file W. The number of bytes written is +** returned. Or NULL is returned if something goes wrong, such as being unable +** to open file X for writing. */ static void writefileFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ - FILE *out; - const char *z; - sqlite3_int64 rc; const char *zFile; + mode_t mode = 0; + + if( argc<2 || argc>3 ){ + sqlite3_result_error(context, + "wrong number of arguments to function writefile()", -1 + ); + return; + } - (void)(argc); /* Unused parameter */ zFile = (const char*)sqlite3_value_text(argv[0]); if( zFile==0 ) return; - out = fopen(zFile, "wb"); - if( out==0 ) return; - z = (const char*)sqlite3_value_blob(argv[1]); - if( z==0 ){ - rc = 0; - }else{ - rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out); + if( argc>=3 ){ + sqlite3_result_int(context, 0); + mode = sqlite3_value_int(argv[2]); + } + + if( S_ISLNK(mode) ){ + const char *zTo = (const char*)sqlite3_value_text(argv[1]); + if( symlink(zTo, zFile)<0 ){ + ctxErrorMsg(context, "failed to create symlink: %s", zFile); + return; + } + }else{ + if( S_ISDIR(mode) ){ + if( mkdir(zFile, mode) ){ + ctxErrorMsg(context, "failed to create directory: %s", zFile); + return; + } + }else{ + sqlite3_int64 nWrite = 0; + const char *z; + int rc = 0; + FILE *out = fopen(zFile, "wb"); + if( out==0 ){ + if( argc>2 ){ + ctxErrorMsg(context, "failed to open file for writing: %s", zFile); + } + return; + } + z = (const char*)sqlite3_value_blob(argv[1]); + if( z ){ + sqlite3_int64 n = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out); + nWrite = sqlite3_value_bytes(argv[1]); + if( nWrite!=n ){ + ctxErrorMsg(context, "failed to write file: %s", zFile); + rc = 1; + } + } + fclose(out); + if( rc ) return; + sqlite3_result_int64(context, nWrite); + } + + if( argc>2 && chmod(zFile, mode & 0777) ){ + ctxErrorMsg(context, "failed to chmod file: %s", zFile); + return; + } } - fclose(out); - sqlite3_result_int64(context, rc); } #ifndef SQLITE_OMIT_VIRTUALTABLE -#include -#include -#include -#include - /* */ typedef struct fsdir_cursor fsdir_cursor; @@ -463,7 +521,7 @@ int sqlite3_fileio_init( rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0, readfileFunc, 0, 0); if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0, + rc = sqlite3_create_function(db, "writefile", -1, SQLITE_UTF8, 0, writefileFunc, 0, 0); } if( rc==SQLITE_OK ){ diff --git a/manifest b/manifest index dc93367667..ab01f7195c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Begin\sadding\ssupport\sfor\sthe\ssqlar\sarchive\sformat\sto\sshell.c.\sThere\sis\sno\n"extract"\scommand\sso\sfar,\sonly\s"create". -D 2017-12-07T15:44:29.604 +C Add\sthe\s".ar\sx"\scommand\sto\sthe\sshell.\sFor\sextracting\sthe\scontents\sof\ssqlar\narchives. +D 2017-12-07T21:03:33.903 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 6a879cbf01e37f9eac131414955f71774b566502d9a57ded1b8585b507503cb8 @@ -269,7 +269,7 @@ F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83 F ext/misc/csv.c 1a009b93650732e22334edc92459c4630b9fa703397cbb3c8ca279921a36ca11 F ext/misc/dbdump.c 3509fa6b8932d04e932d6b6b827b6a82ca362781b8e8f3c77336f416793e215e F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2 -F ext/misc/fileio.c bd2dd9bd22a509f972a4658be18bbfef80aec3cbc3e18948c5e8c5e29ece9939 +F ext/misc/fileio.c c84ec9c399657bd95914e7c4e9aefcc148cb86fe3ab7f2102e9557c861dd0d51 F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25 F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c F ext/misc/json1.c dbe086615b9546c156bf32b9378fc09383b58bd17513b866cfd24c1e15281984 @@ -474,7 +474,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c bbee7e31d369a18a2f4836644769882e9c5d40ef4a3af911db06410b65cb3730 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c 17e220191860a64a18c084141e1a8b7309e166a6f2d42c02021af27ea080d157 -F src/shell.c.in 56c4c091c74af2c7858f2d8af962caa632889596435b17bffbc308058fb305cc +F src/shell.c.in 2f9ae0bee09bdd35922ab7ed264d88e1d7fb34d39d37fc633e6a3a1af60036be F src/sqlite.h.in 8fd97993d48b50b9bade38c52f12d175942c9497c960905610c7b03a3e4b5818 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h c02d628cca67f3889c689d82d25c3eb45e2c155db08e4c6089b5840d64687d34 @@ -1681,7 +1681,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 8155b5ac850327ea76aba2adf624132f3e05024c973afd218b12f186fc7630e8 -R 1ffe38726fc0bf4dcb6cd1bf88405c9b +P c9827a01a6e107f38f85c2b2c1c7a599e443067b106217e965b6936441ca619d +R 4b2bb930e9456062f8914cba7a04cf63 U dan -Z f98960131c8a51264c03e5b1a40bd1b7 +Z 4726613d4458219c808274490c55a044 diff --git a/manifest.uuid b/manifest.uuid index 3b745e1dd5..245ee49a8f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c9827a01a6e107f38f85c2b2c1c7a599e443067b106217e965b6936441ca619d \ No newline at end of file +0cc699d14adfe8c7b7be50c180186562861806c47425c80c935bce43ee5c5c12 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 9015a4330a..fe58460fe1 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -4096,8 +4096,16 @@ static void shellFinalize( int *pRc, sqlite3_stmt *pStmt ){ - int rc = sqlite3_finalize(pStmt); - if( *pRc==SQLITE_OK ) *pRc = rc; + if( pStmt ){ + sqlite3 *db = sqlite3_db_handle(pStmt); + int rc = sqlite3_finalize(pStmt); + if( *pRc==SQLITE_OK ){ + if( rc!=SQLITE_OK ){ + raw_printf(stderr, "SQL error: %s\n", sqlite3_errmsg(db)); + } + *pRc = rc; + } + } } static void shellReset( @@ -4112,16 +4120,40 @@ static void shellReset( ** Implementation of .ar "eXtract" command. */ static int arExtractCommand(ShellState *p, int bVerbose){ - const char *zSql = - "SELECT name, mode, mtime, sz, data FROM sqlar"; + const char *zSql1 = + "SELECT name, writefile(name, " + "CASE WHEN (data AND sz>=0 AND sz!=length(data)) THEN uncompress(data) " + " ELSE data END, " + "mode) FROM sqlar"; + const char *zSql2 = "SELECT name, mtime FROM sqlar"; + + struct timespec times[2]; sqlite3_stmt *pSql = 0; int rc = SQLITE_OK; - shellPrepare(p, &rc, zSql, &pSql); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ - } + memset(times, 0, sizeof(times)); + times[0].tv_sec = time(0); + shellPrepare(p, &rc, zSql1, &pSql); + while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ + if( bVerbose ){ + raw_printf(stdout, "%s\n", sqlite3_column_text(pSql, 0)); + } + } shellFinalize(&rc, pSql); + + shellPrepare(p, &rc, zSql2, &pSql); + while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ + const char *zPath = (const char*)sqlite3_column_text(pSql, 0); + times[1].tv_sec = (time_t)sqlite3_column_int64(pSql, 1); + if( utimensat(AT_FDCWD, zPath, times, AT_SYMLINK_NOFOLLOW) ){ + raw_printf(stderr, "failed to set timestamp for %s\n", zPath); + rc = SQLITE_ERROR; + break; + } + } + shellFinalize(&rc, pSql); + return rc; }