Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | The ".ar" command deduces whether or not the target file is a ZIP or SQLAR and does the appropropriate thing. The "-z" option is omitted. The "--append" option is added to open auxiliary databases using apndvfs. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | archive-improvements |
Files: | files | file ages | folders |
SHA3-256: |
430d1a7daa823ae53606b7a158af4e7c |
User & Date: | drh 2018-01-10 15:17:34.832 |
Context
2018-01-10
| ||
15:53 | Add the "filetype()" SQL function for interpreting file modes to the fileio.c extension. (check-in: 58c0c74c40 user: drh tags: archive-improvements) | |
15:17 | The ".ar" command deduces whether or not the target file is a ZIP or SQLAR and does the appropropriate thing. The "-z" option is omitted. The "--append" option is added to open auxiliary databases using apndvfs. (check-in: 430d1a7daa user: drh tags: archive-improvements) | |
14:00 | Allow the use of ".ar -t" without specifying an archive file or the "-z" option when the command-line shell is opened on a ZIP archive. (check-in: 9340a2c145 user: drh tags: archive-improvements) | |
Changes
Changes to src/shell.c.in.
︙ | ︙ | |||
4501 4502 4503 4504 4505 4506 4507 | /* ** Structure representing a single ".ar" command. */ typedef struct ArCommand ArCommand; struct ArCommand { u8 eCmd; /* An AR_CMD_* value */ u8 bVerbose; /* True if --verbose */ | | > | | 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 | /* ** Structure representing a single ".ar" command. */ typedef struct ArCommand ArCommand; struct ArCommand { u8 eCmd; /* An AR_CMD_* value */ u8 bVerbose; /* True if --verbose */ u8 bZip; /* True if the archive is a ZIP */ u8 bDryRun; /* True if --dry-run */ u8 bAppend; /* True if --append */ int nArg; /* Number of command arguments */ char *zSrcTable; /* "sqlar", "zipfile($file)" or "zip" */ const char *zFile; /* --file argument, or NULL */ const char *zDir; /* --directory argument, or NULL */ char **azArg; /* Array of command arguments */ ShellState *p; /* Shell state */ sqlite3 *db; /* Database containing the archive */ }; |
︙ | ︙ | |||
4537 4538 4539 4540 4541 4542 4543 | " -x, --extract Extract files from archive\n" "\n" "And zero or more optional options:\n" " -v, --verbose Print each filename as it is processed\n" " -f FILE, --file FILE Operate on archive FILE (default is current db)\n" " -C DIR, --directory DIR Change to directory DIR to read/extract files\n" " -n, --dryrun Show the SQL that would have occurred\n" | | | 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 | " -x, --extract Extract files from archive\n" "\n" "And zero or more optional options:\n" " -v, --verbose Print each filename as it is processed\n" " -f FILE, --file FILE Operate on archive FILE (default is current db)\n" " -C DIR, --directory DIR Change to directory DIR to read/extract files\n" " -n, --dryrun Show the SQL that would have occurred\n" " -a, --append Append the SQLAR to an existing file\n" "\n" "See also: http://sqlite.org/cli.html#sqlar_archive_support\n" "\n" ); return SQLITE_ERROR; } |
︙ | ︙ | |||
4575 4576 4577 4578 4579 4580 4581 | /* ** Other (non-command) switches. */ #define AR_SWITCH_VERBOSE 6 #define AR_SWITCH_FILE 7 #define AR_SWITCH_DIRECTORY 8 | | | 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 | /* ** Other (non-command) switches. */ #define AR_SWITCH_VERBOSE 6 #define AR_SWITCH_FILE 7 #define AR_SWITCH_DIRECTORY 8 #define AR_SWITCH_APPEND 9 #define AR_SWITCH_DRYRUN 10 static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){ switch( eSwitch ){ case AR_CMD_CREATE: case AR_CMD_EXTRACT: case AR_CMD_LIST: |
︙ | ︙ | |||
4597 4598 4599 4600 4601 4602 4603 | case AR_SWITCH_DRYRUN: pAr->bDryRun = 1; break; case AR_SWITCH_VERBOSE: pAr->bVerbose = 1; break; | | | | 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 | case AR_SWITCH_DRYRUN: pAr->bDryRun = 1; break; case AR_SWITCH_VERBOSE: pAr->bVerbose = 1; break; case AR_SWITCH_APPEND: pAr->bAppend = 1; break; case AR_SWITCH_FILE: pAr->zFile = zArg; break; case AR_SWITCH_DIRECTORY: pAr->zDir = zArg; |
︙ | ︙ | |||
4637 4638 4639 4640 4641 4642 4643 | { "extract", 'x', AR_CMD_EXTRACT, 0 }, { "list", 't', AR_CMD_LIST, 0 }, { "update", 'u', AR_CMD_UPDATE, 0 }, { "help", 'h', AR_CMD_HELP, 0 }, { "verbose", 'v', AR_SWITCH_VERBOSE, 0 }, { "file", 'f', AR_SWITCH_FILE, 1 }, { "directory", 'C', AR_SWITCH_DIRECTORY, 1 }, | | | 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 | { "extract", 'x', AR_CMD_EXTRACT, 0 }, { "list", 't', AR_CMD_LIST, 0 }, { "update", 'u', AR_CMD_UPDATE, 0 }, { "help", 'h', AR_CMD_HELP, 0 }, { "verbose", 'v', AR_SWITCH_VERBOSE, 0 }, { "file", 'f', AR_SWITCH_FILE, 1 }, { "directory", 'C', AR_SWITCH_DIRECTORY, 1 }, { "append", 'a', AR_SWITCH_APPEND, 0 }, { "dryrun", 'n', AR_SWITCH_DRYRUN, 0 }, }; int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch); struct ArSwitch *pEnd = &aSwitch[nSwitch]; if( nArg<=1 ){ return arUsage(stderr); |
︙ | ︙ | |||
4775 4776 4777 4778 4779 4780 4781 | int i, j; sqlite3_stmt *pTest = 0; shellPreparePrintf(pAr->db, &rc, &pTest, "SELECT name FROM %s WHERE name=$name", pAr->zSrcTable ); | < < < < < | 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 | int i, j; sqlite3_stmt *pTest = 0; shellPreparePrintf(pAr->db, &rc, &pTest, "SELECT name FROM %s WHERE name=$name", pAr->zSrcTable ); j = sqlite3_bind_parameter_index(pTest, "$name"); for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){ char *z = pAr->azArg[i]; int n = strlen30(z); int bOk = 0; while( n>0 && z[n-1]=='/' ) n--; z[n] = '\0'; |
︙ | ︙ | |||
4878 4879 4880 4881 4882 4883 4884 | "name", "mode, sz, datetime(mtime, 'unixepoch'), name" }; char *zWhere = 0; sqlite3_stmt *pSql = 0; int rc; | < < < < < < | 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 | "name", "mode, sz, datetime(mtime, 'unixepoch'), name" }; char *zWhere = 0; sqlite3_stmt *pSql = 0; int rc; rc = arCheckEntries(pAr); arWhereClause(&rc, pAr, &zWhere); shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose], pAr->zSrcTable, zWhere); if( pAr->bDryRun ){ utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql)); }else{ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ if( pAr->bVerbose ){ char zMode[11]; shellModeToString(zMode, sqlite3_column_int(pSql, 0)); |
︙ | ︙ | |||
4956 4957 4958 4959 4960 4961 4962 | shellPreparePrintf(pAr->db, &rc, &pSql, zSql1, azExtraArg[pAr->bZip], pAr->zSrcTable, zWhere ); if( rc==SQLITE_OK ){ j = sqlite3_bind_parameter_index(pSql, "$dir"); sqlite3_bind_text(pSql, j, zDir, -1, SQLITE_STATIC); | < < < < | 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 | shellPreparePrintf(pAr->db, &rc, &pSql, zSql1, azExtraArg[pAr->bZip], pAr->zSrcTable, zWhere ); if( rc==SQLITE_OK ){ j = sqlite3_bind_parameter_index(pSql, "$dir"); sqlite3_bind_text(pSql, j, zDir, -1, SQLITE_STATIC); /* Run the SELECT statement twice. The first time, writefile() is called ** for all archive members that should be extracted. The second time, ** only for the directories. This is because the timestamps for ** extracted directories must be reset after they are populated (as ** populating them changes the timestamp). */ for(i=0; i<2; i++){ |
︙ | ︙ | |||
5018 5019 5020 5021 5022 5023 5024 | ** The create command is the same as update, except that it drops ** any existing "sqlar" table before beginning. */ static int arCreateOrUpdateCommand( ArCommand *pAr, /* Command arguments and options */ int bUpdate /* true for a --create. false for --update */ ){ | | > > | > > | > > | > > | > > > > | 5004 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 5056 5057 5058 5059 5060 5061 5062 5063 5064 5065 5066 5067 5068 5069 5070 5071 | ** The create command is the same as update, except that it drops ** any existing "sqlar" table before beginning. */ static int arCreateOrUpdateCommand( ArCommand *pAr, /* Command arguments and options */ int bUpdate /* true for a --create. false for --update */ ){ const char *zSql = "SELECT name, mode, mtime, data FROM fsdir($name, $dir)"; const char *zCreate = "CREATE TABLE IF NOT EXISTS sqlar(\n" " 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(?,?,?,?,sqlar_compress(?))"; sqlite3_stmt *pStmt = 0; /* Directory traverser */ sqlite3_stmt *pInsert = 0; /* Compilation of zInsert */ int i; /* For iterating through azFile[] */ int j; /* Parameter index */ int rc; /* Return code */ assert( pAr->bZip==0 ); rc = arExecSql(pAr, "SAVEPOINT ar;"); if( rc!=SQLITE_OK ) return rc; if( bUpdate==0 ){ rc = arExecSql(pAr, zDrop); if( rc!=SQLITE_OK ) return rc; } rc = arExecSql(pAr, zCreate); if( !pAr->bDryRun ){ shellPrepare(pAr->db, &rc, zInsert, &pInsert); } shellPrepare(pAr->db, &rc, zSql, &pStmt); j = sqlite3_bind_parameter_index(pStmt, "$dir"); sqlite3_bind_text(pStmt, j, pAr->zDir, -1, SQLITE_STATIC); if( pAr->bDryRun ){ utf8_printf(pAr->p->out, "%s;\n", sqlite3_sql(pStmt)); } for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){ j = sqlite3_bind_parameter_index(pStmt, "$name"); sqlite3_bind_text(pStmt, j, pAr->azArg[i], -1, SQLITE_STATIC); while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ int sz; const char *zName = (const char*)sqlite3_column_text(pStmt, 0); int mode = sqlite3_column_int(pStmt, 1); unsigned int mtime = sqlite3_column_int(pStmt, 2); if( pAr->bVerbose ){ utf8_printf(pAr->p->out, "%s\n", zName); } if( pAr->bDryRun ){ utf8_printf(pAr->p->out, "%s;\n", zInsert); continue; } sqlite3_bind_text(pInsert, 1, zName, -1, SQLITE_STATIC); sqlite3_bind_int(pInsert, 2, mode); sqlite3_bind_int64(pInsert, 3, (sqlite3_int64)mtime); if( S_ISDIR(mode) ){ |
︙ | ︙ | |||
5111 5112 5113 5114 5115 5116 5117 5118 5119 | char **azArg, /* Array of arguments passed to dot command */ int nArg /* Number of entries in azArg[] */ ){ ArCommand cmd; int rc; rc = arParseCommand(azArg, nArg, &cmd); if( rc==SQLITE_OK ){ cmd.p = pState; cmd.db = pState->db; | > | > > > > > | | < < | < < | < | > > > > > > > | > | < > > > > > > > > | 5109 5110 5111 5112 5113 5114 5115 5116 5117 5118 5119 5120 5121 5122 5123 5124 5125 5126 5127 5128 5129 5130 5131 5132 5133 5134 5135 5136 5137 5138 5139 5140 5141 5142 5143 5144 5145 5146 5147 5148 5149 5150 5151 5152 5153 5154 5155 5156 5157 5158 5159 5160 5161 5162 5163 5164 5165 5166 5167 5168 5169 5170 5171 5172 5173 5174 5175 5176 5177 | char **azArg, /* Array of arguments passed to dot command */ int nArg /* Number of entries in azArg[] */ ){ ArCommand cmd; int rc; rc = arParseCommand(azArg, nArg, &cmd); if( rc==SQLITE_OK ){ int eDbType = SHELL_OPEN_UNSPEC; cmd.p = pState; cmd.db = pState->db; cmd.zSrcTable = 0; if( cmd.zFile ){ eDbType = deduceDatabaseType(cmd.zFile); }else{ eDbType = pState->openMode; } if( eDbType==SHELL_OPEN_ZIPFILE ){ if( cmd.zFile==0 ){ cmd.zSrcTable = sqlite3_mprintf("zip"); }else{ cmd.zSrcTable = sqlite3_mprintf("zipfile(%Q)", cmd.zFile); } if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_UPDATE ){ utf8_printf(stderr, "zip archives are read-only\n"); rc = SQLITE_ERROR; goto end_ar_command; } cmd.bZip = 1; }else if( cmd.zFile ){ int flags; if( cmd.bAppend ) eDbType = SHELL_OPEN_APPENDVFS; if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_UPDATE ){ flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE; }else{ flags = SQLITE_OPEN_READONLY; } cmd.db = 0; if( cmd.bDryRun ){ utf8_printf(pState->out, "-- open database '%s'%s\n", cmd.zFile, eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : ""); } rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags, eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0); if( rc!=SQLITE_OK ){ utf8_printf(stderr, "cannot open file: %s (%s)\n", cmd.zFile, sqlite3_errmsg(cmd.db) ); goto end_ar_command; } sqlite3_fileio_init(cmd.db, 0, 0); #ifdef SQLITE_HAVE_ZLIB sqlite3_sqlar_init(cmd.db, 0, 0); #endif } if( cmd.zSrcTable==0 ){ if( sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) ){ utf8_printf(stderr, "database does not contain an 'sqlar' table\n"); rc = SQLITE_ERROR; goto end_ar_command; } cmd.zSrcTable = sqlite3_mprintf("sqlar"); } switch( cmd.eCmd ){ case AR_CMD_CREATE: rc = arCreateOrUpdateCommand(&cmd, 0); break; case AR_CMD_EXTRACT: |
︙ | ︙ | |||
5173 5174 5175 5176 5177 5178 5179 | break; default: assert( cmd.eCmd==AR_CMD_UPDATE ); rc = arCreateOrUpdateCommand(&cmd, 1); break; } | | > | | | < > | 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 | break; default: assert( cmd.eCmd==AR_CMD_UPDATE ); rc = arCreateOrUpdateCommand(&cmd, 1); break; } } end_ar_command: if( cmd.db!=pState->db ){ sqlite3_close(cmd.db); } sqlite3_free(cmd.zSrcTable); return rc; } /* End of the ".archive" or ".ar" command logic **********************************************************************************/ #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */ |
︙ | ︙ |
Changes to test/shell8.test.
︙ | ︙ | |||
97 98 99 100 101 102 103 | set c1 ".ar --create ar1" set x1 ".ar --extract" set c2 ".ar --directory ar1 --create ." set x2 ".ar --extract --dir ar3" set c3 ".ar --creat --dir ar1 --file test_xyz.db ." | | | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | set c1 ".ar --create ar1" set x1 ".ar --extract" set c2 ".ar --directory ar1 --create ." set x2 ".ar --extract --dir ar3" set c3 ".ar --creat --dir ar1 --file test_xyz.db ." set x3 ".ar --e --dir ar3 --f test_xyz.db" } 4 { set c1 ".ar --cr ar1" set x1 ".ar --e" set c2 ".ar -C ar1 -c ." |
︙ | ︙ | |||
165 166 167 168 169 170 171 | } finish_test finish_test | < | 165 166 167 168 169 170 171 | } finish_test finish_test |