/ Check-in [38f28029]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add the lsmode(MODE) function in the fileio.c extension and use that function in the .archive implementation. Add the --append and --dryrun options to the .archive command and remove the --zip option, making it automatic.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 38f28029d16df8489772c27867de8888dd1c26b170861acdc977fd4023939dde
User & Date: drh 2018-01-10 17:44:03
Context
2018-01-10
18:09
Modify the new "--append" option to the .archive so that it takes a filename argument, the same as "--file". check-in: 58e3b07c user: drh tags: trunk
17:44
Add the lsmode(MODE) function in the fileio.c extension and use that function in the .archive implementation. Add the --append and --dryrun options to the .archive command and remove the --zip option, making it automatic. check-in: 38f28029 user: drh tags: trunk
17:39
Fix minor problems with the new ".archive" command changes. Closed-Leaf check-in: 612b30c9 user: drh tags: archive-improvements
17:37
Return an SQLITE_CONSTRAINT error if an attempt is made to insert duplicate entries into a zip archive. check-in: 1f099b2b user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ext/misc/fileio.c.

352
353
354
355
356
357
358


































359
360
361
362
363
364
365
...
764
765
766
767
768
769
770




771
772
773
774
775
    }else if( S_ISDIR(mode) ){
      ctxErrorMsg(context, "failed to create directory: %s", zFile);
    }else{
      ctxErrorMsg(context, "failed to write file: %s", zFile);
    }
  }
}



































#ifndef SQLITE_OMIT_VIRTUALTABLE

/* 
** Cursor type for recursively iterating through a directory structure.
*/
typedef struct fsdir_cursor fsdir_cursor;
................................................................................
  (void)pzErrMsg;  /* Unused parameter */
  rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
                               readfileFunc, 0, 0);
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_function(db, "writefile", -1, SQLITE_UTF8, 0,
                                 writefileFunc, 0, 0);
  }




  if( rc==SQLITE_OK ){
    rc = fsdirRegister(db);
  }
  return rc;
}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>





352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
...
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
    }else if( S_ISDIR(mode) ){
      ctxErrorMsg(context, "failed to create directory: %s", zFile);
    }else{
      ctxErrorMsg(context, "failed to write file: %s", zFile);
    }
  }
}

/*
** SQL function:   lsmode(MODE)
**
** Given a numberic st_mode from stat(), convert it into a human-readable
** text string in the style of "ls -l".
*/
static void lsModeFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  int i;
  int iMode = sqlite3_value_int(argv[0]);
  char z[16];
  if( S_ISLNK(iMode) ){
    z[0] = 'l';
  }else if( S_ISREG(iMode) ){
    z[0] = '-';
  }else if( S_ISDIR(iMode) ){
    z[0] = 'd';
  }else{
    z[0] = '?';
  }
  for(i=0; i<3; i++){
    int m = (iMode >> ((2-i)*3));
    char *a = &z[1 + i*3];
    a[0] = (m & 0x4) ? 'r' : '-';
    a[1] = (m & 0x2) ? 'w' : '-';
    a[2] = (m & 0x1) ? 'x' : '-';
  }
  z[10] = '\0';
  sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT);
}

#ifndef SQLITE_OMIT_VIRTUALTABLE

/* 
** Cursor type for recursively iterating through a directory structure.
*/
typedef struct fsdir_cursor fsdir_cursor;
................................................................................
  (void)pzErrMsg;  /* Unused parameter */
  rc = sqlite3_create_function(db, "readfile", 1, SQLITE_UTF8, 0,
                               readfileFunc, 0, 0);
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_function(db, "writefile", -1, SQLITE_UTF8, 0,
                                 writefileFunc, 0, 0);
  }
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_function(db, "lsmode", 1, SQLITE_UTF8, 0,
                                 lsModeFunc, 0, 0);
  }
  if( rc==SQLITE_OK ){
    rc = fsdirRegister(db);
  }
  return rc;
}

Changes to src/shell.c.in.

1130
1131
1132
1133
1134
1135
1136
















1137
1138
1139
1140
1141
1142
1143
....
3303
3304
3305
3306
3307
3308
3309


3310
3311
3312
3313
3314
3315
3316
....
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511



4512


4513
4514
4515
4516
4517
4518
4519
....
4532
4533
4534
4535
4536
4537
4538


4539
4540
4541
4542
4543
4544
4545
....
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
4576

4577
4578
4579
4580
4581
4582
4583
....
4584
4585
4586
4587
4588
4589
4590



4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
....
4614
4615
4616
4617
4618
4619
4620
4621
4622

4623
4624
4625
4626
4627
4628
4629
4630
4631
4632
4633
4634

4635
4636
4637
4638
4639
4640
4641
....
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766

4767
4768

4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
....
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879


4880
4881
4882
4883
4884
4885

4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954

4955



4956
4957
4958

4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969



















4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987

4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000

5001
5002









5003
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
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099

5100
5101
5102







5103
5104

5105
5106
5107

5108


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
*/
static void shellLog(void *pArg, int iErrCode, const char *zMsg){
  ShellState *p = (ShellState*)pArg;
  if( p->pLog==0 ) return;
  utf8_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
  fflush(p->pLog);
}

















/*
** Output the given string as a hex-encoded blob (eg. X'1234' )
*/
static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
  int i;
  char *zBlob = (char *)pBlob;
................................................................................
    sqlite3_zipfile_init(p->db, 0, 0);
    sqlite3_sqlar_init(p->db, 0, 0);
#endif
    sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
                            shellAddSchemaName, 0, 0);
    sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
                            shellModuleSchema, 0, 0);


    if( p->openMode==SHELL_OPEN_ZIPFILE ){
      char *zSql = sqlite3_mprintf(
         "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename);
      sqlite3_exec(p->db, zSql, 0, 0, 0);
      sqlite3_free(zSql);
    }
  }
................................................................................
  }
}
/*
** Structure representing a single ".ar" command.
*/
typedef struct ArCommand ArCommand;
struct ArCommand {
  int eCmd;                       /* An AR_CMD_* value */
  const char *zFile;              /* --file argument, or NULL */
  const char *zDir;               /* --directory argument, or NULL */
  int bVerbose;                   /* True if --verbose */
  int bZip;                       /* True if --zip */
  int nArg;                       /* Number of command arguments */



  char **azArg;                   /* Array of command arguments */


};

/*
** Print a usage message for the .ar command to stderr and return SQLITE_ERROR.
*/
static int arUsage(FILE *f){
  raw_printf(f,
................................................................................
"  -t, --list                 List contents of archive\n"
"  -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"
"See also: http://sqlite.org/cli.html#sqlar_archive_support\n"
"\n"
);
  return SQLITE_ERROR;
}

................................................................................
#define AR_CMD_LIST         3
#define AR_CMD_UPDATE       4
#define AR_CMD_HELP         5

/*
** Other (non-command) switches.
*/
#define AR_SWITCH_VERBOSE   6
#define AR_SWITCH_FILE      7
#define AR_SWITCH_DIRECTORY 8
#define AR_SWITCH_ZIP       9


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:
................................................................................
    case AR_CMD_HELP:
      if( pAr->eCmd ){
        return arErrorMsg("multiple command options");
      }
      pAr->eCmd = eSwitch;
      break;




    case AR_SWITCH_VERBOSE:
      pAr->bVerbose = 1;
      break;
    case AR_SWITCH_ZIP:
      pAr->bZip = 1;
      break;

    case AR_SWITCH_FILE:
      pAr->zFile = zArg;
      break;
    case AR_SWITCH_DIRECTORY:
      pAr->zDir = zArg;
................................................................................
*/
static int arParseCommand(
  char **azArg,                   /* Array of arguments passed to dot command */
  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 },
    { 'h', "help",      AR_CMD_HELP, 0 },
    { 'v', "verbose",   AR_SWITCH_VERBOSE, 0 },
    { 'f', "file",      AR_SWITCH_FILE, 1 },
    { 'C', "directory", AR_SWITCH_DIRECTORY, 1 },
    { 'z', "zip",       AR_SWITCH_ZIP, 0 }

  };
  int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch);
  struct ArSwitch *pEnd = &aSwitch[nSwitch];

  if( nArg<=1 ){
    return arUsage(stderr);
  }else{
................................................................................
** code returned. Otherwise, if all specified arguments are present in
** the archive, SQLITE_OK is returned.
**
** This function strips any trailing '/' characters from each argument.
** This is consistent with the way the [tar] command seems to work on
** Linux.
*/
static int arCheckEntries(sqlite3 *db, ArCommand *pAr){
  int rc = SQLITE_OK;
  if( pAr->nArg ){
    int i;
    sqlite3_stmt *pTest = 0;


    shellPreparePrintf(db, &rc, &pTest, "SELECT name FROM %s WHERE name=?1", 
        pAr->bZip ? "zipfile(?2)" : "sqlar"

    );
    if( rc==SQLITE_OK && pAr->bZip ){
      sqlite3_bind_text(pTest, 2, pAr->zFile, -1, SQLITE_TRANSIENT);
    }
    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';
      sqlite3_bind_text(pTest, 1, z, -1, SQLITE_STATIC);
      if( SQLITE_ROW==sqlite3_step(pTest) ){
        bOk = 1;
      }
      shellReset(&rc, pTest);
      if( rc==SQLITE_OK && bOk==0 ){
        raw_printf(stderr, "not found in archive: %s\n", z);
        rc = SQLITE_ERROR;
      }
    }
    shellFinalize(&rc, pTest);
  }

  return rc;
}

/*
** Format a WHERE clause that can be used against the "sqlar" table to
** identify all archive members that match the command arguments held
** in (*pAr). Leave this WHERE clause in (*pzWhere) before returning.
................................................................................
      zWhere = sqlite3_mprintf("1");
    }else{
      int i;
      const char *zSep = "";
      for(i=0; i<pAr->nArg; i++){
        const char *z = pAr->azArg[i];
        zWhere = sqlite3_mprintf(
            "%z%s name = '%q' OR name BETWEEN '%q/' AND '%q0'", 
            zWhere, zSep, z, z, z
            );
        if( zWhere==0 ){
          *pRc = SQLITE_NOMEM;
          break;
        }
        zSep = " OR ";
      }
    }
  }
  *pzWhere = zWhere;
}

/*
** Argument zMode must point to a buffer at least 11 bytes in size. This
** function populates this buffer with the string interpretation of
** the unix file mode passed as the second argument (e.g. "drwxr-xr-x").
*/
static void shellModeToString(char *zMode, int mode){
  int i;

  /* Magic numbers copied from [man 2 stat] */
  if( mode & 0040000 ){
    zMode[0] = 'd';
  }else if( (mode & 0120000)==0120000 ){
    zMode[0] = 'l';
  }else{
    zMode[0] = '-';
  }

  for(i=0; i<3; i++){
    int m = (mode >> ((2-i)*3));
    char *a = &zMode[1 + i*3];
    a[0] = (m & 0x4) ? 'r' : '-';
    a[1] = (m & 0x2) ? 'w' : '-';
    a[2] = (m & 0x1) ? 'x' : '-';
  }
  zMode[10] = '\0';
}

/*
** Implementation of .ar "lisT" command. 
*/
static int arListCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
  const char *zSql = "SELECT %s FROM %s WHERE %s"; 
  const char *zTbl = (pAr->bZip ? "zipfile(?)" : "sqlar");
  const char *azCols[] = {
    "name",
    "mode, sz, datetime(mtime, 'unixepoch'), name"
  };

  char *zWhere = 0;
  sqlite3_stmt *pSql = 0;
  int rc;

  rc = arCheckEntries(db, pAr);
  arWhereClause(&rc, pAr, &zWhere);

  shellPreparePrintf(db, &rc, &pSql, zSql, azCols[pAr->bVerbose], zTbl, zWhere);
  if( rc==SQLITE_OK && pAr->bZip ){
    sqlite3_bind_text(pSql, 1, pAr->zFile, -1, SQLITE_TRANSIENT);
  }


  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
    if( pAr->bVerbose ){
      char zMode[11];
      shellModeToString(zMode, sqlite3_column_int(pSql, 0));

      raw_printf(p->out, "%s % 10d  %s  %s\n", zMode,

          sqlite3_column_int(pSql, 1), 
          sqlite3_column_text(pSql, 2),
          sqlite3_column_text(pSql, 3)
      );
    }else{
      raw_printf(p->out, "%s\n", sqlite3_column_text(pSql, 0));
    }
  }

  shellFinalize(&rc, pSql);
  return rc;
}


/*
** Implementation of .ar "eXtract" command. 
*/
static int arExtractCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
  const char *zSql1 = 
    "SELECT "
    "  :1 || name, "
    "  writefile(?1 || name, %s, mode, mtime) "
    "FROM %s WHERE (%s) AND (data IS NULL OR ?2 = 0)";

  const char *azExtraArg[] = { 
    "sqlar_uncompress(data, sz)",
    "data"
  };
  const char *azSource[] = {
    "sqlar", "zipfile(?3)"
  };

  sqlite3_stmt *pSql = 0;
  int rc = SQLITE_OK;
  char *zDir = 0;
  char *zWhere = 0;
  int i;

  /* If arguments are specified, check that they actually exist within
  ** the archive before proceeding. And formulate a WHERE clause to
  ** match them.  */
  rc = arCheckEntries(db, pAr);
  arWhereClause(&rc, pAr, &zWhere);

  if( rc==SQLITE_OK ){
    if( pAr->zDir ){
      zDir = sqlite3_mprintf("%s/", pAr->zDir);
    }else{
      zDir = sqlite3_mprintf("");
    }
    if( zDir==0 ) rc = SQLITE_NOMEM;
  }

  shellPreparePrintf(db, &rc, &pSql, zSql1, 
      azExtraArg[pAr->bZip], azSource[pAr->bZip], zWhere
  );

  if( rc==SQLITE_OK ){
    sqlite3_bind_text(pSql, 1, zDir, -1, SQLITE_STATIC);
    if( pAr->bZip ){
      sqlite3_bind_text(pSql, 3, pAr->zFile, -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++){

      sqlite3_bind_int(pSql, 2, i);



      while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
        if( i==0 && pAr->bVerbose ){
          raw_printf(p->out, "%s\n", sqlite3_column_text(pSql, 0));

        }
      }
      shellReset(&rc, pSql);
    }
    shellFinalize(&rc, pSql);
  }

  sqlite3_free(zDir);
  sqlite3_free(zWhere);
  return rc;
}





















/*
** Implementation of .ar "create" and "update" commands.
**
** Create the "sqlar" table in the database if it does not already exist.
** Then add each file in the azFile[] array to the archive. Directories
** are added recursively. If argument bVerbose is non-zero, a message is
** printed on stdout for each file archived.
**
** The create command is the same as update, except that it drops
** any existing "sqlar" table before beginning.
*/
static int arCreateUpdate(
  ShellState *p,                  /* Shell state pointer */
  sqlite3 *db,
  ArCommand *pAr,                 /* Command arguments and options */
  int bUpdate

){
  const char *zSql = "SELECT name, mode, mtime, data FROM fsdir(?, ?)";
  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 rc;                         /* Return code */

  assert( pAr->bZip==0 );

  rc = sqlite3_exec(db, "SAVEPOINT ar;", 0, 0, 0);
  if( rc!=SQLITE_OK ) return rc;

  if( bUpdate==0 ){
    rc = sqlite3_exec(db, zDrop, 0, 0, 0);

    if( rc!=SQLITE_OK ) return rc;
  }

  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++){
    sqlite3_bind_text(pStmt, 1, 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 ){
        raw_printf(p->out, "%s\n", zName);
      }

      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) ){
        sz = 0;
        sqlite3_bind_null(pInsert, 5);
      }else{
        sqlite3_bind_value(pInsert, 5, sqlite3_column_value(pStmt, 3));
        if( S_ISLNK(mode) ){
          sz = -1;
        }else{
          sz = sqlite3_column_bytes(pStmt, 3);
        }
      }

      sqlite3_bind_int(pInsert, 4, sz);


      sqlite3_step(pInsert);
      rc = sqlite3_reset(pInsert);
    }
    shellReset(&rc, pStmt);
  }

  if( rc!=SQLITE_OK ){
    sqlite3_exec(db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
  }else{
    rc = sqlite3_exec(db, "RELEASE ar;", 0, 0, 0);
  }
  shellFinalize(&rc, pStmt);
  shellFinalize(&rc, pInsert);
  return rc;
}

/*
** Implementation of .ar "Create" command. 
**
** Create the "sqlar" table in the database if it does not already exist.
** Then add each file in the azFile[] array to the archive. Directories
** are added recursively. If argument bVerbose is non-zero, a message is
** printed on stdout for each file archived.
*/
static int arCreateCommand(
  ShellState *p,                  /* Shell state pointer */
  sqlite3 *db,
  ArCommand *pAr                  /* Command arguments and options */
){
  return arCreateUpdate(p, db, pAr, 0);
}

/*
** Implementation of .ar "Update" command. 
*/
static int arUpdateCmd(ShellState *p, sqlite3 *db, ArCommand *pAr){
  return arCreateUpdate(p, db, pAr, 1);
}


/*
** Implementation of ".ar" dot command.
*/
static int arDotCommand(
  ShellState *pState,             /* Current shell tool state */
  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 ){
    sqlite3 *db = 0;              /* Database handle to use as archive */








    if( cmd.bZip ){

      if( cmd.zFile==0 ){
        raw_printf(stderr, "zip format requires a --file switch\n");
        return SQLITE_ERROR;

      }else


      if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_UPDATE ){
        raw_printf(stderr, "zip archives are read-only\n");
        return SQLITE_ERROR;

      }
      db = pState->db;

    }else 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;

      }
      sqlite3_fileio_init(db, 0, 0);
#ifdef SQLITE_HAVE_ZLIB
      sqlite3_sqlar_init(db, 0, 0);
#endif
    }else{
      db = pState->db;













    }

    switch( cmd.eCmd ){
      case AR_CMD_CREATE:
        rc = arCreateCommand(pState, db, &cmd);
        break;

      case AR_CMD_EXTRACT:
        rc = arExtractCommand(pState, db, &cmd);
        break;

      case AR_CMD_LIST:
        rc = arListCommand(pState, db, &cmd);
        break;

      case AR_CMD_HELP:
        arUsage(pState->out);
        break;

      default:
        assert( cmd.eCmd==AR_CMD_UPDATE );
        rc = arUpdateCmd(pState, db, &cmd);
        break;
    }


    if( cmd.zFile ){
      sqlite3_close(db);
    }
  }


  return rc;
}
/* End of the ".archive" or ".ar" command logic
**********************************************************************************/
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>







 







|
|
|
|
|

>
>
>

>
>







 







>
>







 







|
|
|
|
>







 







>
>
>



|
|







 







<

>
|
|

|
|
|
|
|
|
|
|
|
>







 







|


|


>
|
<
>

<
|
<






|





|





<







 







|
|
|












<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


|

<


|






|


|
|
|
<
>
>
|
|
<
<
<
|
>
|
|
|
|
|
|
|
|
|








|


|
|
|





<
<
<





|




|











|
|



|
<
|
<







>
|
>
>
>
|
|
|
>











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>













|
<
<

<
>

<









|
<
>
|
|
>
>
>
>
>
>
>
>
>



<
<
|

<

<
>


<
<
<
<
<
<
>

<
<
<
<
<
<
<
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
>
|
<
|
<
<
<

|

|

<
<


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<











>


<
>
>
>
>
>
>
>
|
<
>

<
<
>
|
>
>

|
|
>

<
>


>





>
>
>
>
>
|
>

|
|

<
<
>

|

|

<
<
>
>
>
>
>
>
>
>
>
>
>
>
>




|



|



|








|


|
>
|
|
|
<
>







1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
....
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
....
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
4533
4534
4535
4536
4537
4538
4539
4540
4541
4542
....
4555
4556
4557
4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
....
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
....
4610
4611
4612
4613
4614
4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627
4628
4629
4630
4631
....
4643
4644
4645
4646
4647
4648
4649

4650
4651
4652
4653
4654
4655
4656
4657
4658
4659
4660
4661
4662
4663
4664
4665
4666
4667
4668
4669
4670
4671
....
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798

4799
4800

4801

4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819

4820
4821
4822
4823
4824
4825
4826
....
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859



























4860
4861
4862
4863

4864
4865
4866
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878

4879
4880
4881
4882



4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912



4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
4923
4924
4925
4926
4927
4928
4929
4930
4931
4932
4933
4934
4935
4936
4937
4938
4939
4940

4941

4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001


5002

5003
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
5072

5073
5074


5075
5076
5077
5078
5079
5080
5081
5082
5083

5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103


5104
5105
5106
5107
5108
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
*/
static void shellLog(void *pArg, int iErrCode, const char *zMsg){
  ShellState *p = (ShellState*)pArg;
  if( p->pLog==0 ) return;
  utf8_printf(p->pLog, "(%d) %s\n", iErrCode, zMsg);
  fflush(p->pLog);
}

/*
** SQL function:  shell_putsnl(X)
**
** Write the text X to the screen (or whatever output is being directed)
** adding a newline at the end, and then return X.
*/
static void shellPutsFunc(
  sqlite3_context *pCtx,
  int nVal,
  sqlite3_value **apVal
){
  ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
  utf8_printf(p->out, "%s\n", sqlite3_value_text(apVal[0]));
  sqlite3_result_value(pCtx, apVal[0]);
}

/*
** Output the given string as a hex-encoded blob (eg. X'1234' )
*/
static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
  int i;
  char *zBlob = (char *)pBlob;
................................................................................
    sqlite3_zipfile_init(p->db, 0, 0);
    sqlite3_sqlar_init(p->db, 0, 0);
#endif
    sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
                            shellAddSchemaName, 0, 0);
    sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
                            shellModuleSchema, 0, 0);
    sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
                            shellPutsFunc, 0, 0);
    if( p->openMode==SHELL_OPEN_ZIPFILE ){
      char *zSql = sqlite3_mprintf(
         "CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename);
      sqlite3_exec(p->db, zSql, 0, 0, 0);
      sqlite3_free(zSql);
    }
  }
................................................................................
  }
}
/*
** 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 */
};

/*
** Print a usage message for the .ar command to stderr and return SQLITE_ERROR.
*/
static int arUsage(FILE *f){
  raw_printf(f,
................................................................................
"  -t, --list                 List contents of archive\n"
"  -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;
}

................................................................................
#define AR_CMD_LIST         3
#define AR_CMD_UPDATE       4
#define AR_CMD_HELP         5

/*
** 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:
    case AR_CMD_UPDATE:
................................................................................
    case AR_CMD_HELP:
      if( pAr->eCmd ){
        return arErrorMsg("multiple command options");
      }
      pAr->eCmd = eSwitch;
      break;

    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;
................................................................................
*/
static int arParseCommand(
  char **azArg,                   /* Array of arguments passed to dot command */
  int nArg,                       /* Number of entries in azArg[] */
  ArCommand *pAr                  /* Populate this object */
){
  struct ArSwitch {

    const char *zLong;
    char cShort;
    u8 eSwitch;
    u8 bArg;
  } aSwitch[] = {
    { "create",    'c', AR_CMD_CREATE,       0 },
    { "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);
  }else{
................................................................................
** code returned. Otherwise, if all specified arguments are present in
** the archive, SQLITE_OK is returned.
**
** This function strips any trailing '/' characters from each argument.
** This is consistent with the way the [tar] command seems to work on
** Linux.
*/
static int arCheckEntries(ArCommand *pAr){
  int rc = SQLITE_OK;
  if( pAr->nArg ){
    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';
      sqlite3_bind_text(pTest, j, z, -1, SQLITE_STATIC);
      if( SQLITE_ROW==sqlite3_step(pTest) ){
        bOk = 1;
      }
      shellReset(&rc, pTest);
      if( rc==SQLITE_OK && bOk==0 ){
        utf8_printf(stderr, "not found in archive: %s\n", z);
        rc = SQLITE_ERROR;
      }
    }
    shellFinalize(&rc, pTest);
  }

  return rc;
}

/*
** Format a WHERE clause that can be used against the "sqlar" table to
** identify all archive members that match the command arguments held
** in (*pAr). Leave this WHERE clause in (*pzWhere) before returning.
................................................................................
      zWhere = sqlite3_mprintf("1");
    }else{
      int i;
      const char *zSep = "";
      for(i=0; i<pAr->nArg; i++){
        const char *z = pAr->azArg[i];
        zWhere = sqlite3_mprintf(
          "%z%s name = '%q' OR substr(name,1,%d) = '%q/'", 
          zWhere, zSep, z, strlen30(z)+1, z
        );
        if( zWhere==0 ){
          *pRc = SQLITE_NOMEM;
          break;
        }
        zSep = " OR ";
      }
    }
  }
  *pzWhere = zWhere;
}

/*



























** Implementation of .ar "lisT" command. 
*/
static int arListCommand(ArCommand *pAr){
  const char *zSql = "SELECT %s FROM %s WHERE %s"; 

  const char *azCols[] = {
    "name",
    "lsmode(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 ){



        utf8_printf(pAr->p->out, "%s % 10d  %s  %s\n",
            sqlite3_column_text(pSql, 0),
            sqlite3_column_int(pSql, 1), 
            sqlite3_column_text(pSql, 2),
            sqlite3_column_text(pSql, 3)
        );
      }else{
        utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0));
      }
    }
  }
  shellFinalize(&rc, pSql);
  return rc;
}


/*
** Implementation of .ar "eXtract" command. 
*/
static int arExtractCommand(ArCommand *pAr){
  const char *zSql1 = 
    "SELECT "
    " ($dir || name),"
    " writefile(($dir || name), %s, mode, mtime) "
    "FROM %s WHERE (%s) AND (data IS NULL OR $dirOnly = 0)";

  const char *azExtraArg[] = { 
    "sqlar_uncompress(data, sz)",
    "data"
  };




  sqlite3_stmt *pSql = 0;
  int rc = SQLITE_OK;
  char *zDir = 0;
  char *zWhere = 0;
  int i, j;

  /* If arguments are specified, check that they actually exist within
  ** the archive before proceeding. And formulate a WHERE clause to
  ** match them.  */
  rc = arCheckEntries(pAr);
  arWhereClause(&rc, pAr, &zWhere);

  if( rc==SQLITE_OK ){
    if( pAr->zDir ){
      zDir = sqlite3_mprintf("%s/", pAr->zDir);
    }else{
      zDir = sqlite3_mprintf("");
    }
    if( zDir==0 ) rc = SQLITE_NOMEM;
  }

  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++){
      j = sqlite3_bind_parameter_index(pSql, "$dirOnly");
      sqlite3_bind_int(pSql, j, i);
      if( pAr->bDryRun ){
        utf8_printf(pAr->p->out, "%s\n", sqlite3_sql(pSql));
      }else{
        while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
          if( i==0 && pAr->bVerbose ){
            utf8_printf(pAr->p->out, "%s\n", sqlite3_column_text(pSql, 0));
          }
        }
      }
      shellReset(&rc, pSql);
    }
    shellFinalize(&rc, pSql);
  }

  sqlite3_free(zDir);
  sqlite3_free(zWhere);
  return rc;
}

/*
** Run the SQL statement in zSql.  Or if doing a --dryrun, merely print it out.
*/
static int arExecSql(ArCommand *pAr, const char *zSql){
  int rc;
  if( pAr->bDryRun ){
    utf8_printf(pAr->p->out, "%s\n", zSql);
    rc = SQLITE_OK;
  }else{
    char *zErr = 0;
    rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr);
    if( zErr ){
      utf8_printf(stdout, "ERROR: %s\n", zErr);
      sqlite3_free(zErr);
    }
  }
  return rc;
}


/*
** Implementation of .ar "create" and "update" commands.
**
** Create the "sqlar" table in the database if it does not already exist.
** Then add each file in the azFile[] array to the archive. Directories
** are added recursively. If argument bVerbose is non-zero, a message is
** printed on stdout for each file archived.
**
** 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 *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 *zInsertFmt = 

     "REPLACE INTO sqlar(name,mode,mtime,sz,data)\n"
     "  SELECT\n"
     "    %s,\n"
     "    mode,\n"
     "    mtime,\n"
     "    CASE substr(lsmode(mode),1,1)\n"
     "      WHEN '-' THEN length(data)\n"
     "      WHEN 'd' THEN 0\n"
     "      ELSE -1 END,\n"
     "    CASE WHEN lsmode(mode) LIKE 'd%%' THEN NULL else data END\n"
     "  FROM fsdir(%Q,%Q)\n"
     "  WHERE lsmode(mode) NOT LIKE '?%%';";
  int i;                          /* For iterating through azFile[] */
  int rc;                         /* Return code */



  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);
  for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){







    char *zSql = sqlite3_mprintf(zInsertFmt,
        pAr->bVerbose ? "shell_putsnl(name)" : "name",




















        pAr->azArg[i], pAr->zDir);
    rc = arExecSql(pAr, zSql);
    sqlite3_free(zSql);

  }



  if( rc!=SQLITE_OK ){
    arExecSql(pAr, "ROLLBACK TO ar; RELEASE ar;");
  }else{
    rc = arExecSql(pAr, "RELEASE ar;");
  }


  return rc;
}

























/*
** Implementation of ".ar" dot command.
*/
static int arDotCommand(
  ShellState *pState,             /* Current shell tool state */
  char **azArg,                   /* Array of arguments passed to dot command */
  int nArg                        /* Number of entries in azArg[] */
){
  ArCommand cmd;
  int rc;
  memset(&cmd, 0, sizeof(cmd));
  rc = arParseCommand(azArg, nArg, &cmd);
  if( rc==SQLITE_OK ){

    int eDbType = SHELL_OPEN_UNSPEC;
    cmd.p = pState;
    cmd.db = pState->db;
    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


      sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p,
                              shellPutsFunc, 0, 0);

    }
    if( cmd.zSrcTable==0 ){
      if( cmd.eCmd!=AR_CMD_CREATE
       && 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:
        rc = arExtractCommand(&cmd);
        break;

      case AR_CMD_LIST:
        rc = arListCommand(&cmd);
        break;

      case AR_CMD_HELP:
        arUsage(pState->out);
        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
104
105
106
107
108
109
110
111
...
165
166
167
168
169
170
171
172
    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  --d ar3 --f test_xyz.db"
  }

  4 {
    set c1 ".ar --cr ar1"
    set x1 ".ar --e"

    set c2 ".ar -C ar1 -c ."
................................................................................
}

finish_test



finish_test








|







 







<
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
...
165
166
167
168
169
170
171

    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 ."
................................................................................
}

finish_test



finish_test