#include <dirent.h>
#include <time.h>
#include <utime.h>
+#include <errno.h>
#define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
va_end(ap);
}
+/*
+** Argument zFile is the name of a file that will be created and/or written
+** by SQL function writefile(). This function ensures that the directory
+** zFile will be written to exists, creating it if required. The permissions
+** for any path components created by this function are set to (mode&0777).
+**
+** If an OOM condition is encountered, SQLITE_NOMEM is returned. Otherwise,
+** SQLITE_OK is returned if the directory is successfully created, or
+** SQLITE_ERROR otherwise.
+*/
+static int makeDirectory(
+ const char *zFile,
+ mode_t mode
+){
+ char *zCopy = sqlite3_mprintf("%s", zFile);
+ int rc = SQLITE_OK;
+
+ if( zCopy==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int nCopy = strlen(zCopy);
+ int i = 1;
+
+ while( rc==SQLITE_OK ){
+ struct stat sStat;
+ int rc;
+
+ for(; zCopy[i]!='/' && i<nCopy; i++);
+ if( i==nCopy ) break;
+ zCopy[i] = '\0';
+
+ rc = stat(zCopy, &sStat);
+ if( rc!=0 ){
+ if( mkdir(zCopy, mode & 0777) ) rc = SQLITE_ERROR;
+ }else{
+ if( !S_ISDIR(sStat.st_mode) ) rc = SQLITE_ERROR;
+ }
+ zCopy[i] = '/';
+ i++;
+ }
+
+ sqlite3_free(zCopy);
+ }
+
+ return rc;
+}
+
+static int writeFile(
+ sqlite3_context *pCtx,
+ const char *zFile,
+ mode_t mode,
+ sqlite3_value *pData
+){
+ if( S_ISLNK(mode) ){
+ const char *zTo = (const char*)sqlite3_value_text(pData);
+ if( symlink(zTo, zFile)<0 ) return 1;
+ }else{
+ if( S_ISDIR(mode) ){
+ if( mkdir(zFile, mode) ){
+ /* The mkdir() call to create the directory failed. This might not
+ ** be an error though - if there is already a directory at the same
+ ** path and either the permissions already match or can be changed
+ ** to do so using chmod(), it is not an error. */
+ struct stat sStat;
+ if( errno!=EEXIST
+ || 0!=stat(zFile, &sStat)
+ || !S_ISDIR(sStat.st_mode)
+ || ((sStat.st_mode&0777)!=(mode&0777) && 0!=chmod(zFile, mode&0777))
+ ){
+ return 1;
+ }
+ }
+ }else{
+ sqlite3_int64 nWrite = 0;
+ const char *z;
+ int rc = 0;
+ FILE *out = fopen(zFile, "wb");
+ if( out==0 ) return 1;
+ z = (const char*)sqlite3_value_blob(pData);
+ if( z ){
+ sqlite3_int64 n = fwrite(z, 1, sqlite3_value_bytes(pData), out);
+ nWrite = sqlite3_value_bytes(pData);
+ if( nWrite!=n ){
+ rc = 1;
+ }
+ }
+ fclose(out);
+ if( rc==0 && chmod(zFile, mode & 0777) ){
+ rc = 1;
+ }
+ if( rc ) return 2;
+ sqlite3_result_int64(pCtx, nWrite);
+ }
+ }
+ return 0;
+}
+
/*
** Implementation of the "writefile(W,X[,Y]])" SQL function.
**
){
const char *zFile;
mode_t mode = 0;
+ int res;
if( argc<2 || argc>3 ){
sqlite3_result_error(context,
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);
+ res = writeFile(context, zFile, mode, argv[1]);
+ if( res==1 && errno==ENOENT ){
+ if( makeDirectory(zFile, mode)==SQLITE_OK ){
+ res = writeFile(context, zFile, mode, argv[1]);
}
+ }
- if( argc>2 && chmod(zFile, mode & 0777) ){
- ctxErrorMsg(context, "failed to chmod file: %s", zFile);
- return;
+ if( res!=0 ){
+ if( S_ISLNK(mode) ){
+ ctxErrorMsg(context, "failed to create symlink: %s", zFile);
+ }else if( S_ISDIR(mode) ){
+ ctxErrorMsg(context, "failed to create directory: %s", zFile);
+ }else{
+ ctxErrorMsg(context, "failed to write file: %s", zFile);
}
}
}
-C Enhance\svirtual\stable\s"fsdir"\sin\sext/misc/fileio.c.\sAdd\ssupport\sfor\s"-C"\sto\nthe\sshell\scommand's\s".ar\sc"\scommand.
-D 2017-12-11T20:22:02.045
+C Add\ssupport\sfor\sparsing\soptions\sin\snon-traditional\star\sform\sto\sthe\s".ar"\ncommand.\sHave\swritefile()\sattempt\sto\screate\sany\smissing\spath\scomponents.\sAnd\nnot\sto\sthrow\san\sexception\sif\sit\sis\scalled\sto\screate\sa\sdirectory\sthat\salready\nexists.
+D 2017-12-12T20:04:59.331
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 6a879cbf01e37f9eac131414955f71774b566502d9a57ded1b8585b507503cb8
F ext/misc/csv.c 1a009b93650732e22334edc92459c4630b9fa703397cbb3c8ca279921a36ca11
F ext/misc/dbdump.c 3509fa6b8932d04e932d6b6b827b6a82ca362781b8e8f3c77336f416793e215e
F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
-F ext/misc/fileio.c 238d0c65a14bda99748285527543273e0248bc428c01c3955ec45acb8738db3b
+F ext/misc/fileio.c 29b7fc94752fff6245cf4a81455f98cf6778ec1102ca7e67bf693d41a7db4307
F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
F ext/misc/json1.c dbe086615b9546c156bf32b9378fc09383b58bd17513b866cfd24c1e15281984
F src/resolve.c bbee7e31d369a18a2f4836644769882e9c5d40ef4a3af911db06410b65cb3730
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c 17e220191860a64a18c084141e1a8b7309e166a6f2d42c02021af27ea080d157
-F src/shell.c.in 4c7a2c1226216dfad7978642c958143f0fdfee4f09aef2add1e73e3601dd7d33
+F src/shell.c.in 0ab6e3c1fa09e420e643628d55929422867ca053f05df67a4cae4a67e2a6cfc5
F src/sqlite.h.in 8fd97993d48b50b9bade38c52f12d175942c9497c960905610c7b03a3e4b5818
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h c02d628cca67f3889c689d82d25c3eb45e2c155db08e4c6089b5840d64687d34
F test/shell5.test 23939a4c51f0421330ea61dbd3c74f9c215f5f8d3d1a94846da6ffc777a35458
F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3
F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f
-F test/shell8.test d04773f4e8cdad62d42a1b13bf4c1182109880441689a2f5d5001e190ec04831
+F test/shell8.test 5c5a9d100d34b125e0f46d259ea76cf074ac60719b722b2a2c63d759c63fc113
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
F test/shrink.test 1b4330b1fd9e818c04726d45cb28db73087535ce
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 8cd70960c5ddf0d0b2c40b8b6af4ce6b0277ffdaf04f33fcb33227d2b99ad515
-R ddf59f7efc51e054de53889e9f879e48
+P 0394889afed2479773af594e2d9659cf58b8959004ebcdeaff8e08e5dae684ef
+R e0d6dbcefc8608142fe03f9ef6d2dee7
U dan
-Z c8a2667de38f55deb8cffcf1545f4d0c
+Z 5813e7d404a813775aab77275468b917
}
static void shellPrepare(
- ShellState *p,
+ sqlite3 *db,
int *pRc,
const char *zSql,
sqlite3_stmt **ppStmt
){
*ppStmt = 0;
if( *pRc==SQLITE_OK ){
- int rc = sqlite3_prepare_v2(p->db, zSql, -1, ppStmt, 0);
+ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
if( rc!=SQLITE_OK ){
raw_printf(stderr, "sql error: %s (%d)\n",
- sqlite3_errmsg(p->db), sqlite3_errcode(p->db)
+ sqlite3_errmsg(db), sqlite3_errcode(db)
);
*pRc = rc;
}
/*
** Values for ArCommand.eCmd.
*/
-#define AR_CMD_CREATE 1
-#define AR_CMD_EXTRACT 2
-#define AR_CMD_LIST 3
-#define AR_CMD_UPDATE 4
+#define AR_CMD_CREATE 1
+#define AR_CMD_EXTRACT 2
+#define AR_CMD_LIST 3
+#define AR_CMD_UPDATE 4
+
+/*
+** Other (non-command) switches.
+*/
+#define AR_SWITCH_VERBOSE 5
+#define AR_SWITCH_FILE 6
+#define AR_SWITCH_DIRECTORY 7
+
+static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){
+ switch( eSwitch ){
+ case AR_CMD_CREATE:
+ case AR_CMD_EXTRACT:
+ case AR_CMD_LIST:
+ case AR_CMD_UPDATE:
+ if( pAr->eCmd ) return arUsage();
+ pAr->eCmd = eSwitch;
+ break;
+
+ case AR_SWITCH_VERBOSE:
+ pAr->bVerbose = 1;
+ break;
+
+ case AR_SWITCH_FILE:
+ pAr->zFile = zArg;
+ break;
+ case AR_SWITCH_DIRECTORY:
+ pAr->zDir = zArg;
+ break;
+ }
+
+ return SQLITE_OK;
+}
/*
** Parse the command line for an ".ar" command. The results are written into
int nArg, /* Number of entries in azArg[] */
ArCommand *pAr /* Populate this object */
){
+ struct ArSwitch {
+ char cShort;
+ const char *zLong;
+ int eSwitch;
+ int bArg;
+ } aSwitch[] = {
+ { 'c', "create", AR_CMD_CREATE, 0 },
+ { 'x', "extract", AR_CMD_EXTRACT, 0 },
+ { 't', "list", AR_CMD_LIST, 0 },
+ { 'u', "update", AR_CMD_UPDATE, 0 },
+ { 'v', "verbose", AR_SWITCH_VERBOSE, 0 },
+ { 'f', "file", AR_SWITCH_FILE, 1 },
+ { 'C', "directory", AR_SWITCH_DIRECTORY, 1 }
+ };
+ int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch);
+ struct ArSwitch *pEnd = &aSwitch[nSwitch];
+
if( nArg<=1 ){
return arUsage();
}else{
int i;
int iArg = 2;
for(i=0; z[i]; i++){
- switch( z[i] ){
- case 'c':
- if( pAr->eCmd ) return arUsage();
- pAr->eCmd = AR_CMD_CREATE;
- break;
- case 'x':
- if( pAr->eCmd ) return arUsage();
- pAr->eCmd = AR_CMD_EXTRACT;
- break;
- case 't':
- if( pAr->eCmd ) return arUsage();
- pAr->eCmd = AR_CMD_LIST;
- break;
- case 'u':
- if( pAr->eCmd ) return arUsage();
- pAr->eCmd = AR_CMD_UPDATE;
- break;
-
- case 'v':
- pAr->bVerbose = 1;
- break;
- case 'f':
- if( iArg>=nArg ) return arUsage();
- pAr->zFile = azArg[iArg++];
- break;
- case 'C':
- if( iArg>=nArg ) return arUsage();
- pAr->zDir = azArg[iArg++];
- break;
-
- default:
- return arUsage();
+ const char *zArg = 0;
+ struct ArSwitch *pOpt;
+ for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){
+ if( z[i]==pOpt->cShort ) break;
+ }
+ if( pOpt==pEnd ) return arUsage();
+ if( pOpt->bArg ){
+ if( iArg>=nArg ) return arUsage();
+ zArg = azArg[iArg++];
}
+ if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR;
}
-
pAr->nArg = nArg-iArg;
if( pAr->nArg>0 ){
pAr->azArg = &azArg[iArg];
}
+ }else{
+ /* Non-traditional invocation */
+ int iArg;
+ for(iArg=1; iArg<nArg; iArg++){
+ int n;
+ z = azArg[iArg];
+ if( z[0]!='-' ){
+ /* All remaining command line words are command arguments. */
+ pAr->azArg = &azArg[iArg];
+ pAr->nArg = nArg-iArg;
+ break;
+ }
+ n = strlen(z);
+
+ if( z[1]!='-' ){
+ int i;
+ /* One or more short options */
+ for(i=1; i<n; i++){
+ const char *zArg = 0;
+ struct ArSwitch *pOpt;
+ for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){
+ if( z[i]==pOpt->cShort ) break;
+ }
+ if( pOpt==pEnd ) return arUsage();
+ if( pOpt->bArg ){
+ if( i<(n-1) ){
+ zArg = &z[i+1];
+ i = n;
+ }else{
+ if( iArg>=(nArg-1) ) return arUsage();
+ zArg = azArg[++iArg];
+ }
+ }
+ if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR;
+ }
+ }else if( z[2]=='\0' ){
+ /* A -- option, indicating that all remaining command line words
+ ** are command arguments. */
+ pAr->azArg = &azArg[iArg+1];
+ pAr->nArg = nArg-iArg-1;
+ break;
+ }else{
+ /* A long option */
+ const char *zArg = 0; /* Argument for option, if any */
+ struct ArSwitch *pMatch = 0; /* Matching option */
+ struct ArSwitch *pOpt; /* Iterator */
+ for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){
+ const char *zLong = pOpt->zLong;
+ if( (n-2)<=strlen(zLong) && 0==memcmp(&z[2], zLong, n-2) ){
+ if( pMatch ){
+ /* ambiguous option */
+ return arUsage();
+ }else{
+ pMatch = pOpt;
+ }
+ }
+ }
+
+ if( pMatch==0 ){
+ /* no such option. */
+ return arUsage();
+ }
+ if( pMatch->bArg ){
+ if( iArg>=(nArg-1) ) return arUsage();
+ zArg = azArg[++iArg];
+ }
+ if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR;
+ }
+ }
}
}
/*
** Implementation of .ar "Update" command.
*/
-static int arUpdateCmd(ShellState *p, ArCommand *pAr){
+static int arUpdateCmd(ShellState *p, sqlite3 *db, ArCommand *pAr){
raw_printf(stderr, "todo...\n");
return SQLITE_OK;
}
/*
** Implementation of .ar "lisT" command.
*/
-static int arListCommand(ShellState *p, ArCommand *pAr){
+static int arListCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
raw_printf(stderr, "todo...\n");
return SQLITE_OK;
}
/*
** Implementation of .ar "eXtract" command.
*/
-static int arExtractCommand(ShellState *p, ArCommand *pAr){
+static int arExtractCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
const char *zSql1 =
"SELECT :1 || name, writefile(:1 || name, "
"CASE WHEN (data AND sz>=0 AND sz!=length(data)) THEN uncompress(data) "
memset(times, 0, sizeof(times));
times[0].tv_sec = time(0);
- shellPrepare(p, &rc, zSql1, &pSql);
+ shellPrepare(db, &rc, zSql1, &pSql);
if( rc==SQLITE_OK ){
sqlite3_bind_text(pSql, 1, zDir, -1, SQLITE_STATIC);
}
}
shellFinalize(&rc, pSql);
- shellPrepare(p, &rc, zSql2, &pSql);
+ shellPrepare(db, &rc, zSql2, &pSql);
if( rc==SQLITE_OK ){
sqlite3_bind_text(pSql, 1, zDir, -1, SQLITE_STATIC);
}
*/
static int arCreateCommand(
ShellState *p, /* Shell state pointer */
+ sqlite3 *db,
ArCommand *pAr /* Command arguments and options */
){
- const char *zSql =
- "SELECT name, mode, mtime, data FROM fsdir(?, ?)";
-
- const char *zSqlar =
+ const char *zSql = "SELECT name, mode, mtime, data FROM fsdir(?, ?)";
+ const char *zCreate =
"CREATE TABLE IF NOT EXISTS sqlar("
"name TEXT PRIMARY KEY, -- name of the file\n"
"mode INT, -- access permissions\n"
"mtime INT, -- last modification time\n"
"sz INT, -- original file size\n"
"data BLOB -- compressed content\n"
- ")";
-
+ ")";
+ const char *zDrop = "DROP TABLE IF EXISTS sqlar";
const char *zInsert = "REPLACE INTO sqlar VALUES(?, ?, ?, ?, ?)";
sqlite3_stmt *pStmt = 0; /* Directory traverser */
Bytef *aCompress = 0; /* Compression buffer */
int nCompress = 0; /* Size of compression buffer */
- rc = sqlite3_exec(p->db, "SAVEPOINT ar;", 0, 0, 0);
+ rc = sqlite3_exec(db, "SAVEPOINT ar;", 0, 0, 0);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3_exec(db, zDrop, 0, 0, 0);
if( rc!=SQLITE_OK ) return rc;
- rc = sqlite3_exec(p->db, zSqlar, 0, 0, 0);
- shellPrepare(p, &rc, zInsert, &pInsert);
- shellPrepare(p, &rc, zSql, &pStmt);
+ rc = sqlite3_exec(db, zCreate, 0, 0, 0);
+ shellPrepare(db, &rc, zInsert, &pInsert);
+ shellPrepare(db, &rc, zSql, &pStmt);
sqlite3_bind_text(pStmt, 2, pAr->zDir, -1, SQLITE_STATIC);
for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
}
if( rc!=SQLITE_OK ){
- sqlite3_exec(p->db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
+ sqlite3_exec(db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
}else{
- rc = sqlite3_exec(p->db, "RELEASE ar;", 0, 0, 0);
+ rc = sqlite3_exec(db, "RELEASE ar;", 0, 0, 0);
}
shellFinalize(&rc, pStmt);
shellFinalize(&rc, pInsert);
int rc;
rc = arParseCommand(azArg, nArg, &cmd);
if( rc==SQLITE_OK ){
+ sqlite3 *db = 0; /* Database handle to use as archive */
+
+ if( cmd.zFile ){
+ int flags;
+ if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_UPDATE ){
+ flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
+ }else{
+ flags = SQLITE_OPEN_READONLY;
+ }
+ rc = sqlite3_open_v2(cmd.zFile, &db, flags, 0);
+ if( rc!=SQLITE_OK ){
+ raw_printf(stderr, "cannot open file: %s (%s)\n",
+ cmd.zFile, sqlite3_errmsg(db)
+ );
+ sqlite3_close(db);
+ return rc;
+ }
+ }else{
+ db = pState->db;
+ }
+
switch( cmd.eCmd ){
case AR_CMD_CREATE:
- rc = arCreateCommand(pState, &cmd);
+ rc = arCreateCommand(pState, db, &cmd);
break;
case AR_CMD_EXTRACT:
- rc = arExtractCommand(pState, &cmd);
+ rc = arExtractCommand(pState, db, &cmd);
break;
case AR_CMD_LIST:
- rc = arListCommand(pState, &cmd);
+ rc = arListCommand(pState, db, &cmd);
break;
default:
assert( cmd.eCmd==AR_CMD_UPDATE );
- rc = arUpdateCmd(pState, &cmd);
+ rc = arUpdateCmd(pState, db, &cmd);
break;
}
+
+ if( cmd.zFile ){
+ sqlite3_close(db);
+ }
}
return rc;