From: dan Date: Tue, 12 Dec 2017 20:04:59 +0000 (+0000) Subject: Add support for parsing options in non-traditional tar form to the ".ar" X-Git-Tag: version-3.22.0~108^2~29 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d4b56e59017d3f57afdc7c5f3d0b84904eca920d;p=thirdparty%2Fsqlite.git Add support for parsing options in non-traditional tar form to the ".ar" command. Have writefile() attempt to create any missing path components. And not to throw an exception if it is called to create a directory that already exists. FossilOrigin-Name: 38dbeb1e777aa7ec742aa27002ad4dcee28af520dc43de96e5c56c39f16574ff --- diff --git a/ext/misc/fileio.c b/ext/misc/fileio.c index d3c00b4b1f..2320173997 100644 --- a/ext/misc/fileio.c +++ b/ext/misc/fileio.c @@ -40,6 +40,7 @@ SQLITE_EXTENSION_INIT1 #include #include #include +#include #define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)" @@ -90,6 +91,103 @@ static void ctxErrorMsg(sqlite3_context *ctx, const char *zFmt, ...){ 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]!='/' && i3 ){ sqlite3_result_error(context, @@ -119,46 +218,20 @@ static void writefileFunc( 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); } } } diff --git a/manifest b/manifest index d95b1b4be9..af08ef11da 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -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 @@ -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 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 @@ -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 4c7a2c1226216dfad7978642c958143f0fdfee4f09aef2add1e73e3601dd7d33 +F src/shell.c.in 0ab6e3c1fa09e420e643628d55929422867ca053f05df67a4cae4a67e2a6cfc5 F src/sqlite.h.in 8fd97993d48b50b9bade38c52f12d175942c9497c960905610c7b03a3e4b5818 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h c02d628cca67f3889c689d82d25c3eb45e2c155db08e4c6089b5840d64687d34 @@ -1214,7 +1214,7 @@ F test/shell4.test 89ad573879a745974ff2df20ff97c5d6ffffbd5d 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 @@ -1682,7 +1682,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 8cd70960c5ddf0d0b2c40b8b6af4ce6b0277ffdaf04f33fcb33227d2b99ad515 -R ddf59f7efc51e054de53889e9f879e48 +P 0394889afed2479773af594e2d9659cf58b8959004ebcdeaff8e08e5dae684ef +R e0d6dbcefc8608142fe03f9ef6d2dee7 U dan -Z c8a2667de38f55deb8cffcf1545f4d0c +Z 5813e7d404a813775aab77275468b917 diff --git a/manifest.uuid b/manifest.uuid index 1e0e922cc2..9e345298fb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0394889afed2479773af594e2d9659cf58b8959004ebcdeaff8e08e5dae684ef \ No newline at end of file +38dbeb1e777aa7ec742aa27002ad4dcee28af520dc43de96e5c56c39f16574ff \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 0e32cbe1e2..fd8659297c 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -4075,17 +4075,17 @@ static int lintDotCommand( } 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; } @@ -4141,10 +4141,42 @@ static int arUsage(void){ /* ** 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 @@ -4157,6 +4189,23 @@ static int arParseCommand( 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{ @@ -4168,45 +4217,91 @@ static int arParseCommand( 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]; pOptcShort ) 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; iArgazArg = &azArg[iArg]; + pAr->nArg = nArg-iArg; + break; + } + n = strlen(z); + + if( z[1]!='-' ){ + int i; + /* One or more short options */ + for(i=1; icShort ) 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]; pOptzLong; + 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; + } + } } } @@ -4216,7 +4311,7 @@ static int arParseCommand( /* ** 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; } @@ -4224,7 +4319,7 @@ static int arUpdateCmd(ShellState *p, ArCommand *pAr){ /* ** 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; } @@ -4233,7 +4328,7 @@ static int arListCommand(ShellState *p, ArCommand *pAr){ /* ** 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) " @@ -4255,7 +4350,7 @@ static int arExtractCommand(ShellState *p, ArCommand *pAr){ 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); } @@ -4266,7 +4361,7 @@ static int arExtractCommand(ShellState *p, ArCommand *pAr){ } 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); } @@ -4296,20 +4391,19 @@ static int arExtractCommand(ShellState *p, ArCommand *pAr){ */ 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 */ @@ -4320,12 +4414,15 @@ static int arCreateCommand( 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; inArg && rc==SQLITE_OK; i++){ @@ -4385,9 +4482,9 @@ static int arCreateCommand( } 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); @@ -4407,24 +4504,49 @@ static int arDotCommand( 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; diff --git a/test/shell8.test b/test/shell8.test index b3189d4215..577452511a 100644 --- a/test/shell8.test +++ b/test/shell8.test @@ -62,41 +62,68 @@ proc dir_compare {d1 d2} { string compare $l1 $l2 } -populate_dir ar1 { - file1 "abcd" - file2 "efgh" - dir1/file3 "ijkl" -} +foreach {tn tcl} { + 1 { + set c1 ".ar c ar1" + set x1 ".ar x" -set expected [dir_to_list ar1] -# puts "# $expected" -do_test 1.1 { - forcedelete test_ar.db + set c2 ".ar cC ar1 ." + set x2 ".ar Cx ar3" + } - catchcmd test_ar.db ".ar c ar1" - file delete -force ar1 - catchcmd test_ar.db ".ar x" + 2 { + set c1 ".ar -c ar1" + set x1 ".ar -x" - dir_to_list ar1 -} $expected + set c2 ".ar -cC ar1 ." + set x2 ".ar -xC ar3" + } -do_test 1.2 { - file delete -force ar3 - file mkdir ar3 - catchcmd test_ar.db ".ar xvC ar3" - dir_to_list ar3/ar1 -} $expected + 3 { + set c1 ".ar --create ar1" + set x1 ".ar --extract" -do_test 1.3 { - file delete -force ar3 - file mkdir ar3 + set c2 ".ar --directory ar1 --create ." + set x2 ".ar --extract --dir ar3" + } - forcedelete test_ar.db - catchcmd test_ar.db ".ar cC ar1 file1 file2 dir1" - catchcmd test_ar.db ".ar xC ar3" - dir_to_list ar3 -} $expected + 4 { + set c1 ".ar --cr ar1" + set x1 ".ar --e" + + set c2 ".ar -C ar1 -c ." + set x2 ".ar -x -C ar3" + } +} { + eval $tcl + + # Populate directory "ar1" with some files. + # + populate_dir ar1 { + file1 "abcd" + file2 "efgh" + dir1/file3 "ijkl" + } + set expected [dir_to_list ar1] + + do_test 1.$tn.1 { + catchcmd test_ar.db $c1 + file delete -force ar1 + catchcmd test_ar.db $x1 + dir_to_list ar1 + } $expected + + do_test 1.$tn.2 { + file delete -force ar3 + catchcmd test_ar.db $c2 + catchcmd test_ar.db $x2 + dir_to_list ar3 + } $expected +} + +finish_test finish_test +