/ Check-in [840401cc]
Login

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

Overview
Comment:Improve parsing of ".ar" commands. Add new test file for the same.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sqlar-shell-support
Files: files | file ages | folders
SHA3-256: 840401cc8ce3a09e0663b46973ecd2856d9607be71d2d1e9b21f7df7a82dcbe5
User & Date: dan 2017-12-09 17:58:02
Context
2017-12-09
18:28
Add support for -C to ".ar x". check-in: 8cd70960 user: dan tags: sqlar-shell-support
17:58
Improve parsing of ".ar" commands. Add new test file for the same. check-in: 840401cc user: dan tags: sqlar-shell-support
2017-12-07
21:03
Add the ".ar x" command to the shell. For extracting the contents of sqlar archives. check-in: 0cc699d1 user: dan tags: sqlar-shell-support
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/shell.c.in.

  4111   4111   static void shellReset(
  4112   4112     int *pRc, 
  4113   4113     sqlite3_stmt *pStmt
  4114   4114   ){
  4115   4115     int rc = sqlite3_reset(pStmt);
  4116   4116     if( *pRc==SQLITE_OK ) *pRc = rc;
  4117   4117   }
         4118  +
         4119  +/* 
         4120  +** Structure representing a single ".ar" command.
         4121  +*/
         4122  +typedef struct ArCommand ArCommand;
         4123  +struct ArCommand {
         4124  +  int eCmd;                       /* An AR_CMD_* value */
         4125  +  const char *zFile;              /* --file argument, or NULL */
         4126  +  const char *zDir;               /* --directory argument, or NULL */
         4127  +  int bVerbose;                   /* True if --verbose */
         4128  +  int nArg;                       /* Number of command arguments */
         4129  +  char **azArg;                   /* Array of command arguments */
         4130  +};
         4131  +
         4132  +/*
         4133  +** Print a usage message for the .ar command to stderr and return SQLITE_ERROR.
         4134  +*/
         4135  +static int arUsage(void){
         4136  +  /* todo */
         4137  +  raw_printf(stderr, "error in .ar command line\n");
         4138  +  return SQLITE_ERROR;
         4139  +}
         4140  +
         4141  +/*
         4142  +** Values for ArCommand.eCmd.
         4143  +*/
         4144  +#define AR_CMD_CREATE  1
         4145  +#define AR_CMD_EXTRACT 2
         4146  +#define AR_CMD_LIST    3
         4147  +#define AR_CMD_UPDATE  4
         4148  +
         4149  +/*
         4150  +** Parse the command line for an ".ar" command. The results are written into
         4151  +** structure (*pAr). SQLITE_OK is returned if the command line is parsed
         4152  +** successfully, otherwise an error message is written to stderr and 
         4153  +** SQLITE_ERROR returned.
         4154  +*/
         4155  +static int arParseCommand(
         4156  +  char **azArg,                   /* Array of arguments passed to dot command */
         4157  +  int nArg,                       /* Number of entries in azArg[] */
         4158  +  ArCommand *pAr                  /* Populate this object */
         4159  +){
         4160  +  if( nArg<=1 ){
         4161  +    return arUsage();
         4162  +  }else{
         4163  +    char *z = azArg[1];
         4164  +    memset(pAr, 0, sizeof(ArCommand));
         4165  +
         4166  +    if( z[0]!='-' ){
         4167  +      /* Traditional style [tar] invocation */
         4168  +      int i;
         4169  +      int iArg = 2;
         4170  +      for(i=0; z[i]; i++){
         4171  +        switch( z[i] ){
         4172  +          case 'c':
         4173  +            if( pAr->eCmd ) return arUsage();
         4174  +            pAr->eCmd = AR_CMD_CREATE;
         4175  +            break;
         4176  +          case 'x':
         4177  +            if( pAr->eCmd ) return arUsage();
         4178  +            pAr->eCmd = AR_CMD_EXTRACT;
         4179  +            break;
         4180  +          case 't':
         4181  +            if( pAr->eCmd ) return arUsage();
         4182  +            pAr->eCmd = AR_CMD_LIST;
         4183  +            break;
         4184  +          case 'u':
         4185  +            if( pAr->eCmd ) return arUsage();
         4186  +            pAr->eCmd = AR_CMD_UPDATE;
         4187  +            break;
         4188  +
         4189  +          case 'v':
         4190  +            pAr->bVerbose = 1;
         4191  +            break;
         4192  +          case 'f':
         4193  +            if( iArg>=nArg ) return arUsage();
         4194  +            pAr->zFile = azArg[iArg++];
         4195  +            break;
         4196  +          case 'C':
         4197  +            if( iArg>=nArg ) return arUsage();
         4198  +            pAr->zDir = azArg[iArg++];
         4199  +            break;
         4200  +
         4201  +          default:
         4202  +            return arUsage();
         4203  +        }
         4204  +      }
         4205  +
         4206  +      pAr->nArg = nArg-iArg;
         4207  +      if( pAr->nArg>0 ){
         4208  +        pAr->azArg = &azArg[iArg];
         4209  +      }
         4210  +    }
         4211  +  }
         4212  +
         4213  +  return SQLITE_OK;
         4214  +}
         4215  +
         4216  +/*
         4217  +** Implementation of .ar "Update" command. 
         4218  +*/
         4219  +static int arUpdateCmd(ShellState *p, ArCommand *pAr){
         4220  +  raw_printf(stderr, "todo...\n");
         4221  +  return SQLITE_OK;
         4222  +}
         4223  +
         4224  +/*
         4225  +** Implementation of .ar "lisT" command. 
         4226  +*/
         4227  +static int arListCommand(ShellState *p, ArCommand *pAr){
         4228  +  raw_printf(stderr, "todo...\n");
         4229  +  return SQLITE_OK;
         4230  +}
         4231  +
  4118   4232   
  4119   4233   /*
  4120   4234   ** Implementation of .ar "eXtract" command. 
  4121   4235   */
  4122         -static int arExtractCommand(ShellState *p, int bVerbose){
         4236  +static int arExtractCommand(ShellState *p, ArCommand *pAr){
  4123   4237     const char *zSql1 = 
  4124   4238       "SELECT name, writefile(name, "
  4125   4239       "CASE WHEN (data AND sz>=0 AND sz!=length(data)) THEN uncompress(data) "
  4126   4240       "   ELSE data END, "
  4127   4241       "mode) FROM sqlar";
  4128   4242     const char *zSql2 = "SELECT name, mtime FROM sqlar"; 
  4129   4243   
................................................................................
  4132   4246     int rc = SQLITE_OK;
  4133   4247   
  4134   4248     memset(times, 0, sizeof(times));
  4135   4249     times[0].tv_sec = time(0);
  4136   4250   
  4137   4251     shellPrepare(p, &rc, zSql1, &pSql);
  4138   4252     while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
  4139         -    if( bVerbose ){
         4253  +    if( pAr->bVerbose ){
  4140   4254         raw_printf(stdout, "%s\n", sqlite3_column_text(pSql, 0));
  4141   4255       }
  4142   4256     }
  4143   4257     shellFinalize(&rc, pSql);
  4144   4258   
  4145   4259     shellPrepare(p, &rc, zSql2, &pSql);
  4146   4260     while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
................................................................................
  4163   4277   ** Create the "sqlar" table in the database if it does not already exist.
  4164   4278   ** Then add each file in the azFile[] array to the archive. Directories
  4165   4279   ** are added recursively. If argument bVerbose is non-zero, a message is
  4166   4280   ** printed on stdout for each file archived.
  4167   4281   */
  4168   4282   static int arCreateCommand(
  4169   4283     ShellState *p,                  /* Shell state pointer */
  4170         -  char **azFile,                  /* Array of files to add to archive */
  4171         -  int nFile,                      /* Number of entries in azFile[] */
  4172         -  int bVerbose                    /* True to be verbose on stdout */
         4284  +  ArCommand *pAr                  /* Command arguments and options */
  4173   4285   ){
  4174   4286     const char *zSql = 
  4175   4287       "WITH f(n, m, t, d) AS ("
  4176   4288       "  SELECT name, mode, mtime, data FROM fsentry(:1) UNION ALL "
  4177   4289       "  SELECT n || '/' || name, mode, mtime, data "
  4178   4290       "      FROM f, fsdir(n) WHERE (m&?)"
  4179   4291       ") SELECT * FROM f";
................................................................................
  4200   4312     rc = sqlite3_exec(p->db, "SAVEPOINT ar;", 0, 0, 0);
  4201   4313     if( rc!=SQLITE_OK ) return rc;
  4202   4314   
  4203   4315     rc = sqlite3_exec(p->db, zSqlar, 0, 0, 0);
  4204   4316     shellPrepare(p, &rc, zInsert, &pInsert);
  4205   4317     shellPrepare(p, &rc, zSql, &pStmt);
  4206   4318   
  4207         -  for(i=0; i<nFile && rc==SQLITE_OK; i++){
  4208         -    sqlite3_bind_text(pStmt, 1, azFile[i], -1, SQLITE_STATIC);
         4319  +  for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
         4320  +    sqlite3_bind_text(pStmt, 1, pAr->azArg[i], -1, SQLITE_STATIC);
  4209   4321       sqlite3_bind_int(pStmt, 2, S_IFDIR);
  4210   4322       while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
  4211   4323         int sz;
  4212   4324         const char *zName = (const char*)sqlite3_column_text(pStmt, 0);
  4213   4325         int mode = sqlite3_column_int(pStmt, 1);
  4214   4326         unsigned int mtime = sqlite3_column_int(pStmt, 2);
  4215   4327   
  4216         -      if( bVerbose ){
         4328  +      if( pAr->bVerbose ){
  4217   4329           raw_printf(stdout, "%s\n", zName);
  4218   4330         }
  4219   4331   
  4220   4332         sqlite3_bind_text(pInsert, 1, zName, -1, SQLITE_STATIC);
  4221   4333         sqlite3_bind_int(pInsert, 2, mode);
  4222   4334         sqlite3_bind_int64(pInsert, 3, (sqlite3_int64)mtime);
  4223   4335   
................................................................................
  4276   4388   ** Implementation of ".ar" dot command.
  4277   4389   */
  4278   4390   static int arDotCommand(
  4279   4391     ShellState *pState,             /* Current shell tool state */
  4280   4392     char **azArg,                   /* Array of arguments passed to dot command */
  4281   4393     int nArg                        /* Number of entries in azArg[] */
  4282   4394   ){
  4283         -  int bVerbose = 0;
  4284         -  char cmd = 0;
  4285         -  int i;
  4286         -  int n1;
  4287         -  if( nArg<=1 ) goto usage;
  4288         -
  4289         -  n1 = strlen(azArg[1]);
  4290         -  for(i=0; i<n1; i++){
  4291         -    char c = azArg[1][i];
  4292         -    if( c=='c' || c=='x' ){
  4293         -      if( cmd ) goto usage;
  4294         -      cmd = c;
  4295         -    }
  4296         -    else if( c=='v' ){
  4297         -      bVerbose = 1;
  4298         -    }else{
  4299         -      goto usage;
  4300         -    }
  4301         -  }
  4302         -
  4303         -  if( cmd=='c' ){
  4304         -    return arCreateCommand(pState, &azArg[2], nArg-2, bVerbose);
  4305         -  }
  4306         -
  4307         -  if( cmd=='x' ){
  4308         -    if( nArg!=2 ) goto usage;
  4309         -    return arExtractCommand(pState, bVerbose);
  4310         -  }
  4311         -
  4312         - usage:
  4313         -  raw_printf(stderr, "Usage %s sub-command ?args...?\n", azArg[0]);
  4314         -  return SQLITE_ERROR;
         4395  +  ArCommand cmd;
         4396  +  int rc;
         4397  +  rc = arParseCommand(azArg, nArg, &cmd);
         4398  +  if( rc==SQLITE_OK ){
         4399  +    switch( cmd.eCmd ){
         4400  +      case AR_CMD_CREATE:
         4401  +        rc = arCreateCommand(pState, &cmd);
         4402  +        break;
         4403  +
         4404  +      case AR_CMD_EXTRACT:
         4405  +        rc = arExtractCommand(pState, &cmd);
         4406  +        break;
         4407  +
         4408  +      case AR_CMD_LIST:
         4409  +        rc = arListCommand(pState, &cmd);
         4410  +        break;
         4411  +
         4412  +      default:
         4413  +        assert( cmd.eCmd==AR_CMD_UPDATE );
         4414  +        rc = arUpdateCmd(pState, &cmd);
         4415  +        break;
         4416  +    }
         4417  +  }
         4418  +
         4419  +  return rc;
  4315   4420   }
  4316   4421   
  4317   4422   
  4318   4423   /*
  4319   4424   ** If an input line begins with "." then invoke this routine to
  4320   4425   ** process that line.
  4321   4426   **

Added test/shell8.test.

            1  +# 2017 December 9
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +# Test the shell tool ".ar" command.
           13  +#
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +set testprefix shell8
           18  +set CLI [test_find_cli]
           19  +
           20  +proc populate_dir {dirname spec} {
           21  +  # First delete the current tree, if one exists.
           22  +  file delete -force $dirname
           23  +  
           24  +  # Recreate the root of the new tree.
           25  +  file mkdir $dirname
           26  +
           27  +  # Add each file to the new tree.
           28  +  foreach {f d} $spec {
           29  +    set path [file join $dirname $f]
           30  +    file mkdir [file dirname $path]
           31  +    set fd [open $path w]
           32  +    puts -nonewline $fd $d
           33  +    close $fd
           34  +  }
           35  +}
           36  +
           37  +proc dir_to_list {dirname} {
           38  +  set res [list]
           39  +  foreach f [glob -nocomplain $dirname/*] {
           40  +    set mtime [file mtime $f]
           41  +    set perm [file attributes $f -perm]
           42  +    set relpath [file join {*}[lrange [file split $f] 1 end]]
           43  +    lappend res 
           44  +    if {[file isdirectory $f]} {
           45  +      lappend res [list $relpath / $mtime $perm]
           46  +      lappend res {*}[dir_to_list $f]
           47  +    } else {
           48  +      set fd [open $f]
           49  +      set data [read $fd]
           50  +      close $fd
           51  +      lappend res [list $relpath $data $mtime $perm]
           52  +    }
           53  +  }
           54  +  lsort $res
           55  +}
           56  +
           57  +proc dir_compare {d1 d2} {
           58  +  set l1 [dir_to_list $d1]
           59  +  set l2 [dir_to_list $d1]
           60  +  string compare $l1 $l2
           61  +}
           62  +
           63  +populate_dir ar1 {
           64  +  file1 "abcd" 
           65  +  file2 "efgh"
           66  +  dir1/file3 "ijkl"
           67  +}
           68  +
           69  +set expected [dir_to_list ar1]
           70  +# puts "# $expected"
           71  +do_test 1.1 {
           72  +  forcedelete test_ar.db
           73  +
           74  +  catchcmd test_ar.db ".ar c ar1"
           75  +  file delete -force ar1
           76  +  catchcmd test_ar.db ".ar x"
           77  +
           78  +  dir_to_list ar1
           79  +} $expected
           80  +
           81  +
           82  +
           83  +finish_test