/ Check-in [38dbeb1e]
Login

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

Overview
Comment: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.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sqlar-shell-support
Files: files | file ages | folders
SHA3-256: 38dbeb1e777aa7ec742aa27002ad4dcee28af520dc43de96e5c56c39f16574ff
User & Date: dan 2017-12-12 20:04:59
Context
2017-12-12
20:28
Add tests and fixes for the shell ".ar" command -f option. check-in: 1a986797 user: dan tags: sqlar-shell-support
20:04
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. check-in: 38dbeb1e user: dan tags: sqlar-shell-support
2017-12-11
20:22
Enhance virtual table "fsdir" in ext/misc/fileio.c. Add support for "-C" to the shell command's ".ar c" command. check-in: 0394889a user: dan tags: sqlar-shell-support
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/misc/fileio.c.

    36     36   #include <sys/types.h>
    37     37   #include <sys/stat.h>
    38     38   #include <fcntl.h>
    39     39   #include <unistd.h>
    40     40   #include <dirent.h>
    41     41   #include <time.h>
    42     42   #include <utime.h>
           43  +#include <errno.h>
    43     44   
    44     45   
    45     46   #define FSDIR_SCHEMA "(name,mode,mtime,data,path HIDDEN,dir HIDDEN)"
    46     47   
    47     48   static void readFileContents(sqlite3_context *ctx, const char *zName){
    48     49     FILE *in;
    49     50     long nIn;
................................................................................
    85     86     va_list ap;
    86     87     va_start(ap, zFmt);
    87     88     zMsg = sqlite3_vmprintf(zFmt, ap);
    88     89     sqlite3_result_error(ctx, zMsg, -1);
    89     90     sqlite3_free(zMsg);
    90     91     va_end(ap);
    91     92   }
           93  +
           94  +/*
           95  +** Argument zFile is the name of a file that will be created and/or written
           96  +** by SQL function writefile(). This function ensures that the directory
           97  +** zFile will be written to exists, creating it if required. The permissions
           98  +** for any path components created by this function are set to (mode&0777).
           99  +**
          100  +** If an OOM condition is encountered, SQLITE_NOMEM is returned. Otherwise,
          101  +** SQLITE_OK is returned if the directory is successfully created, or
          102  +** SQLITE_ERROR otherwise.
          103  +*/
          104  +static int makeDirectory(
          105  +  const char *zFile,
          106  +  mode_t mode
          107  +){
          108  +  char *zCopy = sqlite3_mprintf("%s", zFile);
          109  +  int rc = SQLITE_OK;
          110  +
          111  +  if( zCopy==0 ){
          112  +    rc = SQLITE_NOMEM;
          113  +  }else{
          114  +    int nCopy = strlen(zCopy);
          115  +    int i = 1;
          116  +
          117  +    while( rc==SQLITE_OK ){
          118  +      struct stat sStat;
          119  +      int rc;
          120  +
          121  +      for(; zCopy[i]!='/' && i<nCopy; i++);
          122  +      if( i==nCopy ) break;
          123  +      zCopy[i] = '\0';
          124  +
          125  +      rc = stat(zCopy, &sStat);
          126  +      if( rc!=0 ){
          127  +        if( mkdir(zCopy, mode & 0777) ) rc = SQLITE_ERROR;
          128  +      }else{
          129  +        if( !S_ISDIR(sStat.st_mode) ) rc = SQLITE_ERROR;
          130  +      }
          131  +      zCopy[i] = '/';
          132  +      i++;
          133  +    }
          134  +
          135  +    sqlite3_free(zCopy);
          136  +  }
          137  +
          138  +  return rc;
          139  +}
          140  +
          141  +static int writeFile(
          142  +  sqlite3_context *pCtx, 
          143  +  const char *zFile, 
          144  +  mode_t mode, 
          145  +  sqlite3_value *pData
          146  +){
          147  +  if( S_ISLNK(mode) ){
          148  +    const char *zTo = (const char*)sqlite3_value_text(pData);
          149  +    if( symlink(zTo, zFile)<0 ) return 1;
          150  +  }else{
          151  +    if( S_ISDIR(mode) ){
          152  +      if( mkdir(zFile, mode) ){
          153  +        /* The mkdir() call to create the directory failed. This might not
          154  +        ** be an error though - if there is already a directory at the same
          155  +        ** path and either the permissions already match or can be changed
          156  +        ** to do so using chmod(), it is not an error.  */
          157  +        struct stat sStat;
          158  +        if( errno!=EEXIST
          159  +         || 0!=stat(zFile, &sStat)
          160  +         || !S_ISDIR(sStat.st_mode)
          161  +         || ((sStat.st_mode&0777)!=(mode&0777) && 0!=chmod(zFile, mode&0777))
          162  +        ){
          163  +          return 1;
          164  +        }
          165  +      }
          166  +    }else{
          167  +      sqlite3_int64 nWrite = 0;
          168  +      const char *z;
          169  +      int rc = 0;
          170  +      FILE *out = fopen(zFile, "wb");
          171  +      if( out==0 ) return 1;
          172  +      z = (const char*)sqlite3_value_blob(pData);
          173  +      if( z ){
          174  +        sqlite3_int64 n = fwrite(z, 1, sqlite3_value_bytes(pData), out);
          175  +        nWrite = sqlite3_value_bytes(pData);
          176  +        if( nWrite!=n ){
          177  +          rc = 1;
          178  +        }
          179  +      }
          180  +      fclose(out);
          181  +      if( rc==0 && chmod(zFile, mode & 0777) ){
          182  +        rc = 1;
          183  +      }
          184  +      if( rc ) return 2;
          185  +      sqlite3_result_int64(pCtx, nWrite);
          186  +    }
          187  +  }
          188  +  return 0;
          189  +}
    92    190   
    93    191   /*
    94    192   ** Implementation of the "writefile(W,X[,Y]])" SQL function.  
    95    193   **
    96    194   ** The argument X is written into file W.  The number of bytes written is
    97    195   ** returned. Or NULL is returned if something goes wrong, such as being unable
    98    196   ** to open file X for writing.
................................................................................
   100    198   static void writefileFunc(
   101    199     sqlite3_context *context,
   102    200     int argc,
   103    201     sqlite3_value **argv
   104    202   ){
   105    203     const char *zFile;
   106    204     mode_t mode = 0;
          205  +  int res;
   107    206   
   108    207     if( argc<2 || argc>3 ){
   109    208       sqlite3_result_error(context, 
   110    209           "wrong number of arguments to function writefile()", -1
   111    210       );
   112    211       return;
   113    212     }
................................................................................
   115    214     zFile = (const char*)sqlite3_value_text(argv[0]);
   116    215     if( zFile==0 ) return;
   117    216     if( argc>=3 ){
   118    217       sqlite3_result_int(context, 0);
   119    218       mode = sqlite3_value_int(argv[2]);
   120    219     }
   121    220   
   122         -  if( S_ISLNK(mode) ){
   123         -    const char *zTo = (const char*)sqlite3_value_text(argv[1]);
   124         -    if( symlink(zTo, zFile)<0 ){
   125         -      ctxErrorMsg(context, "failed to create symlink: %s", zFile);
   126         -      return;
          221  +  res = writeFile(context, zFile, mode, argv[1]);
          222  +  if( res==1 && errno==ENOENT ){
          223  +    if( makeDirectory(zFile, mode)==SQLITE_OK ){
          224  +      res = writeFile(context, zFile, mode, argv[1]);
   127    225       }
   128         -  }else{
   129         -    if( S_ISDIR(mode) ){
   130         -      if( mkdir(zFile, mode) ){
   131         -        ctxErrorMsg(context, "failed to create directory: %s", zFile);
   132         -        return;
   133         -      }
          226  +  }
          227  +
          228  +  if( res!=0 ){
          229  +    if( S_ISLNK(mode) ){
          230  +      ctxErrorMsg(context, "failed to create symlink: %s", zFile);
          231  +    }else if( S_ISDIR(mode) ){
          232  +      ctxErrorMsg(context, "failed to create directory: %s", zFile);
   134    233       }else{
   135         -      sqlite3_int64 nWrite = 0;
   136         -      const char *z;
   137         -      int rc = 0;
   138         -      FILE *out = fopen(zFile, "wb");
   139         -      if( out==0 ){
   140         -        if( argc>2 ){
   141         -          ctxErrorMsg(context, "failed to open file for writing: %s", zFile);
   142         -        }
   143         -        return;
   144         -      }
   145         -      z = (const char*)sqlite3_value_blob(argv[1]);
   146         -      if( z ){
   147         -        sqlite3_int64 n = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out);
   148         -        nWrite = sqlite3_value_bytes(argv[1]);
   149         -        if( nWrite!=n ){
   150         -          ctxErrorMsg(context, "failed to write file: %s", zFile);
   151         -          rc = 1;
   152         -        }
   153         -      }
   154         -      fclose(out);
   155         -      if( rc ) return;
   156         -      sqlite3_result_int64(context, nWrite);
   157         -    }
   158         -
   159         -    if( argc>2 && chmod(zFile, mode & 0777) ){
   160         -      ctxErrorMsg(context, "failed to chmod file: %s", zFile);
   161         -      return;
          234  +      ctxErrorMsg(context, "failed to write file: %s", zFile);
   162    235       }
   163    236     }
   164    237   }
   165    238   
   166    239   #ifndef SQLITE_OMIT_VIRTUALTABLE
   167    240   
   168    241   /* 

Changes to src/shell.c.in.

  4071   4071     raw_printf(stderr, "Usage %s sub-command ?switches...?\n", azArg[0]);
  4072   4072     raw_printf(stderr, "Where sub-commands are:\n");
  4073   4073     raw_printf(stderr, "    fkey-indexes\n");
  4074   4074     return SQLITE_ERROR;
  4075   4075   }
  4076   4076   
  4077   4077   static void shellPrepare(
  4078         -  ShellState *p, 
         4078  +  sqlite3 *db, 
  4079   4079     int *pRc, 
  4080   4080     const char *zSql, 
  4081   4081     sqlite3_stmt **ppStmt
  4082   4082   ){
  4083   4083     *ppStmt = 0;
  4084   4084     if( *pRc==SQLITE_OK ){
  4085         -    int rc = sqlite3_prepare_v2(p->db, zSql, -1, ppStmt, 0);
         4085  +    int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
  4086   4086       if( rc!=SQLITE_OK ){
  4087   4087         raw_printf(stderr, "sql error: %s (%d)\n", 
  4088         -          sqlite3_errmsg(p->db), sqlite3_errcode(p->db)
         4088  +          sqlite3_errmsg(db), sqlite3_errcode(db)
  4089   4089         );
  4090   4090         *pRc = rc;
  4091   4091       }
  4092   4092     }
  4093   4093   }
  4094   4094   
  4095   4095   static void shellFinalize(
................................................................................
  4137   4137     raw_printf(stderr, "error in .ar command line\n");
  4138   4138     return SQLITE_ERROR;
  4139   4139   }
  4140   4140   
  4141   4141   /*
  4142   4142   ** Values for ArCommand.eCmd.
  4143   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
         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  +** Other (non-command) switches.
         4151  +*/
         4152  +#define AR_SWITCH_VERBOSE   5
         4153  +#define AR_SWITCH_FILE      6
         4154  +#define AR_SWITCH_DIRECTORY 7
         4155  +
         4156  +static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){
         4157  +  switch( eSwitch ){
         4158  +    case AR_CMD_CREATE:
         4159  +    case AR_CMD_EXTRACT:
         4160  +    case AR_CMD_LIST:
         4161  +    case AR_CMD_UPDATE:
         4162  +      if( pAr->eCmd ) return arUsage();
         4163  +      pAr->eCmd = eSwitch;
         4164  +      break;
         4165  +
         4166  +    case AR_SWITCH_VERBOSE:
         4167  +      pAr->bVerbose = 1;
         4168  +      break;
         4169  +
         4170  +    case AR_SWITCH_FILE:
         4171  +      pAr->zFile = zArg;
         4172  +      break;
         4173  +    case AR_SWITCH_DIRECTORY:
         4174  +      pAr->zDir = zArg;
         4175  +      break;
         4176  +  }
         4177  +
         4178  +  return SQLITE_OK;
         4179  +}
  4148   4180   
  4149   4181   /*
  4150   4182   ** Parse the command line for an ".ar" command. The results are written into
  4151   4183   ** structure (*pAr). SQLITE_OK is returned if the command line is parsed
  4152   4184   ** successfully, otherwise an error message is written to stderr and 
  4153   4185   ** SQLITE_ERROR returned.
  4154   4186   */
  4155   4187   static int arParseCommand(
  4156   4188     char **azArg,                   /* Array of arguments passed to dot command */
  4157   4189     int nArg,                       /* Number of entries in azArg[] */
  4158   4190     ArCommand *pAr                  /* Populate this object */
  4159   4191   ){
         4192  +  struct ArSwitch {
         4193  +    char cShort;
         4194  +    const char *zLong;
         4195  +    int eSwitch;
         4196  +    int bArg;
         4197  +  } aSwitch[] = {
         4198  +    { 'c', "create",    AR_CMD_CREATE, 0 },
         4199  +    { 'x', "extract",   AR_CMD_EXTRACT, 0 },
         4200  +    { 't', "list",      AR_CMD_LIST, 0 },
         4201  +    { 'u', "update",    AR_CMD_UPDATE, 0 },
         4202  +    { 'v', "verbose",   AR_SWITCH_VERBOSE, 0 },
         4203  +    { 'f', "file",      AR_SWITCH_FILE, 1 },
         4204  +    { 'C', "directory", AR_SWITCH_DIRECTORY, 1 }
         4205  +  };
         4206  +  int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch);
         4207  +  struct ArSwitch *pEnd = &aSwitch[nSwitch];
         4208  +
  4160   4209     if( nArg<=1 ){
  4161   4210       return arUsage();
  4162   4211     }else{
  4163   4212       char *z = azArg[1];
  4164   4213       memset(pAr, 0, sizeof(ArCommand));
  4165   4214   
  4166   4215       if( z[0]!='-' ){
  4167   4216         /* Traditional style [tar] invocation */
  4168   4217         int i;
  4169   4218         int iArg = 2;
  4170   4219         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         -
         4220  +        const char *zArg = 0;
         4221  +        struct ArSwitch *pOpt;
         4222  +        for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){
         4223  +          if( z[i]==pOpt->cShort ) break;
         4224  +        }
         4225  +        if( pOpt==pEnd ) return arUsage();
         4226  +        if( pOpt->bArg ){
         4227  +          if( iArg>=nArg ) return arUsage();
         4228  +          zArg = azArg[iArg++];
         4229  +        }
         4230  +        if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR;
         4231  +      }
  4206   4232         pAr->nArg = nArg-iArg;
  4207   4233         if( pAr->nArg>0 ){
  4208   4234           pAr->azArg = &azArg[iArg];
         4235  +      }
         4236  +    }else{
         4237  +      /* Non-traditional invocation */
         4238  +      int iArg;
         4239  +      for(iArg=1; iArg<nArg; iArg++){
         4240  +        int n;
         4241  +        z = azArg[iArg];
         4242  +        if( z[0]!='-' ){
         4243  +          /* All remaining command line words are command arguments. */
         4244  +          pAr->azArg = &azArg[iArg];
         4245  +          pAr->nArg = nArg-iArg;
         4246  +          break;
         4247  +        }
         4248  +        n = strlen(z);
         4249  +
         4250  +        if( z[1]!='-' ){
         4251  +          int i;
         4252  +          /* One or more short options */
         4253  +          for(i=1; i<n; i++){
         4254  +            const char *zArg = 0;
         4255  +            struct ArSwitch *pOpt;
         4256  +            for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){
         4257  +              if( z[i]==pOpt->cShort ) break;
         4258  +            }
         4259  +            if( pOpt==pEnd ) return arUsage();
         4260  +            if( pOpt->bArg ){
         4261  +              if( i<(n-1) ){
         4262  +                zArg = &z[i+1];
         4263  +                i = n;
         4264  +              }else{
         4265  +                if( iArg>=(nArg-1) ) return arUsage();
         4266  +                zArg = azArg[++iArg];
         4267  +              }
         4268  +            }
         4269  +            if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR;
         4270  +          }
         4271  +        }else if( z[2]=='\0' ){
         4272  +          /* A -- option, indicating that all remaining command line words
         4273  +          ** are command arguments.  */
         4274  +          pAr->azArg = &azArg[iArg+1];
         4275  +          pAr->nArg = nArg-iArg-1;
         4276  +          break;
         4277  +        }else{
         4278  +          /* A long option */
         4279  +          const char *zArg = 0;             /* Argument for option, if any */
         4280  +          struct ArSwitch *pMatch = 0;      /* Matching option */
         4281  +          struct ArSwitch *pOpt;            /* Iterator */
         4282  +          for(pOpt=&aSwitch[0]; pOpt<pEnd; pOpt++){
         4283  +            const char *zLong = pOpt->zLong;
         4284  +            if( (n-2)<=strlen(zLong) && 0==memcmp(&z[2], zLong, n-2) ){
         4285  +              if( pMatch ){
         4286  +                /* ambiguous option */
         4287  +                return arUsage();
         4288  +              }else{
         4289  +                pMatch = pOpt;
         4290  +              }
         4291  +            }
         4292  +          }
         4293  +
         4294  +          if( pMatch==0 ){
         4295  +            /* no such option. */
         4296  +            return arUsage();
         4297  +          }
         4298  +          if( pMatch->bArg ){
         4299  +            if( iArg>=(nArg-1) ) return arUsage();
         4300  +            zArg = azArg[++iArg];
         4301  +          }
         4302  +          if( arProcessSwitch(pAr, pMatch->eSwitch, zArg) ) return SQLITE_ERROR;
         4303  +        }
  4209   4304         }
  4210   4305       }
  4211   4306     }
  4212   4307   
  4213   4308     return SQLITE_OK;
  4214   4309   }
  4215   4310   
  4216   4311   /*
  4217   4312   ** Implementation of .ar "Update" command. 
  4218   4313   */
  4219         -static int arUpdateCmd(ShellState *p, ArCommand *pAr){
         4314  +static int arUpdateCmd(ShellState *p, sqlite3 *db, ArCommand *pAr){
  4220   4315     raw_printf(stderr, "todo...\n");
  4221   4316     return SQLITE_OK;
  4222   4317   }
  4223   4318   
  4224   4319   /*
  4225   4320   ** Implementation of .ar "lisT" command. 
  4226   4321   */
  4227         -static int arListCommand(ShellState *p, ArCommand *pAr){
         4322  +static int arListCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
  4228   4323     raw_printf(stderr, "todo...\n");
  4229   4324     return SQLITE_OK;
  4230   4325   }
  4231   4326   
  4232   4327   
  4233   4328   /*
  4234   4329   ** Implementation of .ar "eXtract" command. 
  4235   4330   */
  4236         -static int arExtractCommand(ShellState *p, ArCommand *pAr){
         4331  +static int arExtractCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
  4237   4332     const char *zSql1 = 
  4238   4333       "SELECT :1 || name, writefile(:1 || name, "
  4239   4334       "CASE WHEN (data AND sz>=0 AND sz!=length(data)) THEN uncompress(data) "
  4240   4335       "   ELSE data END, "
  4241   4336       "mode) FROM sqlar";
  4242   4337     const char *zSql2 = "SELECT :1 || name, mtime FROM sqlar"; 
  4243   4338   
................................................................................
  4251   4346     }else{
  4252   4347       zDir = sqlite3_mprintf("");
  4253   4348     }
  4254   4349   
  4255   4350     memset(times, 0, sizeof(times));
  4256   4351     times[0].tv_sec = time(0);
  4257   4352   
  4258         -  shellPrepare(p, &rc, zSql1, &pSql);
         4353  +  shellPrepare(db, &rc, zSql1, &pSql);
  4259   4354     if( rc==SQLITE_OK ){
  4260   4355       sqlite3_bind_text(pSql, 1, zDir, -1, SQLITE_STATIC);
  4261   4356     }
  4262   4357     while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
  4263   4358       if( pAr->bVerbose ){
  4264   4359         raw_printf(stdout, "%s\n", sqlite3_column_text(pSql, 0));
  4265   4360       }
  4266   4361     }
  4267   4362     shellFinalize(&rc, pSql);
  4268   4363   
  4269         -  shellPrepare(p, &rc, zSql2, &pSql);
         4364  +  shellPrepare(db, &rc, zSql2, &pSql);
  4270   4365     if( rc==SQLITE_OK ){
  4271   4366       sqlite3_bind_text(pSql, 1, zDir, -1, SQLITE_STATIC);
  4272   4367     }
  4273   4368     while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
  4274   4369       const char *zPath = (const char*)sqlite3_column_text(pSql, 0);
  4275   4370       times[1].tv_sec = (time_t)sqlite3_column_int64(pSql, 1);
  4276   4371       if( utimensat(AT_FDCWD, zPath, times, AT_SYMLINK_NOFOLLOW) ){
................................................................................
  4292   4387   ** Create the "sqlar" table in the database if it does not already exist.
  4293   4388   ** Then add each file in the azFile[] array to the archive. Directories
  4294   4389   ** are added recursively. If argument bVerbose is non-zero, a message is
  4295   4390   ** printed on stdout for each file archived.
  4296   4391   */
  4297   4392   static int arCreateCommand(
  4298   4393     ShellState *p,                  /* Shell state pointer */
         4394  +  sqlite3 *db,
  4299   4395     ArCommand *pAr                  /* Command arguments and options */
  4300   4396   ){
  4301         -  const char *zSql = 
  4302         -    "SELECT name, mode, mtime, data FROM fsdir(?, ?)";
  4303         -
  4304         -  const char *zSqlar = 
         4397  +  const char *zSql = "SELECT name, mode, mtime, data FROM fsdir(?, ?)";
         4398  +  const char *zCreate = 
  4305   4399         "CREATE TABLE IF NOT EXISTS sqlar("
  4306   4400         "name TEXT PRIMARY KEY,  -- name of the file\n"
  4307   4401         "mode INT,               -- access permissions\n"
  4308   4402         "mtime INT,              -- last modification time\n"
  4309   4403         "sz INT,                 -- original file size\n"
  4310   4404         "data BLOB               -- compressed content\n"
  4311         -      ")";
  4312         -
         4405  +  ")";
         4406  +  const char *zDrop = "DROP TABLE IF EXISTS sqlar";
  4313   4407     const char *zInsert = "REPLACE INTO sqlar VALUES(?, ?, ?, ?, ?)";
  4314   4408   
  4315   4409     sqlite3_stmt *pStmt = 0;        /* Directory traverser */
  4316   4410     sqlite3_stmt *pInsert = 0;      /* Compilation of zInsert */
  4317   4411     int i;                          /* For iterating through azFile[] */
  4318   4412     int rc;                         /* Return code */
  4319   4413   
  4320   4414     Bytef *aCompress = 0;           /* Compression buffer */
  4321   4415     int nCompress = 0;              /* Size of compression buffer */
  4322   4416   
  4323         -  rc = sqlite3_exec(p->db, "SAVEPOINT ar;", 0, 0, 0);
         4417  +  rc = sqlite3_exec(db, "SAVEPOINT ar;", 0, 0, 0);
         4418  +  if( rc!=SQLITE_OK ) return rc;
         4419  +
         4420  +  rc = sqlite3_exec(db, zDrop, 0, 0, 0);
  4324   4421     if( rc!=SQLITE_OK ) return rc;
  4325   4422   
  4326         -  rc = sqlite3_exec(p->db, zSqlar, 0, 0, 0);
  4327         -  shellPrepare(p, &rc, zInsert, &pInsert);
  4328         -  shellPrepare(p, &rc, zSql, &pStmt);
         4423  +  rc = sqlite3_exec(db, zCreate, 0, 0, 0);
         4424  +  shellPrepare(db, &rc, zInsert, &pInsert);
         4425  +  shellPrepare(db, &rc, zSql, &pStmt);
  4329   4426     sqlite3_bind_text(pStmt, 2, pAr->zDir, -1, SQLITE_STATIC);
  4330   4427   
  4331   4428     for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
  4332   4429       sqlite3_bind_text(pStmt, 1, pAr->azArg[i], -1, SQLITE_STATIC);
  4333   4430       while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
  4334   4431         int sz;
  4335   4432         const char *zName = (const char*)sqlite3_column_text(pStmt, 0);
................................................................................
  4381   4478           rc = sqlite3_reset(pInsert);
  4382   4479         }
  4383   4480       }
  4384   4481       shellReset(&rc, pStmt);
  4385   4482     }
  4386   4483   
  4387   4484     if( rc!=SQLITE_OK ){
  4388         -    sqlite3_exec(p->db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
         4485  +    sqlite3_exec(db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
  4389   4486     }else{
  4390         -    rc = sqlite3_exec(p->db, "RELEASE ar;", 0, 0, 0);
         4487  +    rc = sqlite3_exec(db, "RELEASE ar;", 0, 0, 0);
  4391   4488     }
  4392   4489     shellFinalize(&rc, pStmt);
  4393   4490     shellFinalize(&rc, pInsert);
  4394   4491     sqlite3_free(aCompress);
  4395   4492     return rc;
  4396   4493   }
  4397   4494   
................................................................................
  4403   4500     char **azArg,                   /* Array of arguments passed to dot command */
  4404   4501     int nArg                        /* Number of entries in azArg[] */
  4405   4502   ){
  4406   4503     ArCommand cmd;
  4407   4504     int rc;
  4408   4505     rc = arParseCommand(azArg, nArg, &cmd);
  4409   4506     if( rc==SQLITE_OK ){
         4507  +    sqlite3 *db = 0;              /* Database handle to use as archive */
         4508  +
         4509  +    if( cmd.zFile ){
         4510  +      int flags;
         4511  +      if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_UPDATE ){
         4512  +        flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
         4513  +      }else{
         4514  +        flags = SQLITE_OPEN_READONLY;
         4515  +      }
         4516  +      rc = sqlite3_open_v2(cmd.zFile, &db, flags, 0);
         4517  +      if( rc!=SQLITE_OK ){
         4518  +        raw_printf(stderr, "cannot open file: %s (%s)\n", 
         4519  +            cmd.zFile, sqlite3_errmsg(db)
         4520  +        );
         4521  +        sqlite3_close(db);
         4522  +        return rc;
         4523  +      }
         4524  +    }else{
         4525  +      db = pState->db;
         4526  +    }
         4527  +
  4410   4528       switch( cmd.eCmd ){
  4411   4529         case AR_CMD_CREATE:
  4412         -        rc = arCreateCommand(pState, &cmd);
         4530  +        rc = arCreateCommand(pState, db, &cmd);
  4413   4531           break;
  4414   4532   
  4415   4533         case AR_CMD_EXTRACT:
  4416         -        rc = arExtractCommand(pState, &cmd);
         4534  +        rc = arExtractCommand(pState, db, &cmd);
  4417   4535           break;
  4418   4536   
  4419   4537         case AR_CMD_LIST:
  4420         -        rc = arListCommand(pState, &cmd);
         4538  +        rc = arListCommand(pState, db, &cmd);
  4421   4539           break;
  4422   4540   
  4423   4541         default:
  4424   4542           assert( cmd.eCmd==AR_CMD_UPDATE );
  4425         -        rc = arUpdateCmd(pState, &cmd);
         4543  +        rc = arUpdateCmd(pState, db, &cmd);
  4426   4544           break;
  4427   4545       }
         4546  +
         4547  +    if( cmd.zFile ){
         4548  +      sqlite3_close(db);
         4549  +    }
  4428   4550     }
  4429   4551   
  4430   4552     return rc;
  4431   4553   }
  4432   4554   
  4433   4555   
  4434   4556   /*

Changes to test/shell8.test.

    58     58   
    59     59   proc dir_compare {d1 d2} {
    60     60     set l1 [dir_to_list $d1]
    61     61     set l2 [dir_to_list $d1]
    62     62     string compare $l1 $l2
    63     63   }
    64     64   
    65         -populate_dir ar1 {
    66         -  file1 "abcd" 
    67         -  file2 "efgh"
    68         -  dir1/file3 "ijkl"
           65  +foreach {tn tcl} {
           66  +  1 {
           67  +    set c1 ".ar c ar1"
           68  +    set x1 ".ar x"
           69  +
           70  +    set c2 ".ar cC ar1 ."
           71  +    set x2 ".ar Cx ar3"
           72  +  }
           73  +
           74  +  2 {
           75  +    set c1 ".ar -c ar1"
           76  +    set x1 ".ar -x"
           77  +
           78  +    set c2 ".ar -cC ar1 ."
           79  +    set x2 ".ar -xC ar3"
           80  +  }
           81  +
           82  +  3 {
           83  +    set c1 ".ar --create ar1"
           84  +    set x1 ".ar --extract"
           85  +
           86  +    set c2 ".ar --directory ar1 --create ."
           87  +    set x2 ".ar --extract --dir ar3"
           88  +  }
           89  +
           90  +  4 {
           91  +    set c1 ".ar --cr ar1"
           92  +    set x1 ".ar --e"
           93  +
           94  +    set c2 ".ar -C ar1 -c ."
           95  +    set x2 ".ar -x -C ar3"
           96  +  }
           97  +} {
           98  +  eval $tcl
           99  +
          100  +  # Populate directory "ar1" with some files.
          101  +  #
          102  +  populate_dir ar1 {
          103  +    file1 "abcd" 
          104  +    file2 "efgh"
          105  +    dir1/file3 "ijkl"
          106  +  }
          107  +  set expected [dir_to_list ar1]
          108  +
          109  +  do_test 1.$tn.1 {
          110  +    catchcmd test_ar.db $c1
          111  +    file delete -force ar1
          112  +    catchcmd test_ar.db $x1
          113  +    dir_to_list ar1
          114  +  } $expected
          115  +
          116  +  do_test 1.$tn.2 {
          117  +    file delete -force ar3
          118  +    catchcmd test_ar.db $c2
          119  +    catchcmd test_ar.db $x2
          120  +    dir_to_list ar3
          121  +  } $expected
    69    122   }
    70    123   
    71         -set expected [dir_to_list ar1]
    72         -# puts "# $expected"
    73         -do_test 1.1 {
    74         -  forcedelete test_ar.db
    75         -
    76         -  catchcmd test_ar.db ".ar c ar1"
    77         -  file delete -force ar1
    78         -  catchcmd test_ar.db ".ar x"
    79         -
    80         -  dir_to_list ar1
    81         -} $expected
    82         -
    83         -do_test 1.2 {
    84         -  file delete -force ar3
    85         -  file mkdir ar3
    86         -  catchcmd test_ar.db ".ar xvC ar3"
    87         -  dir_to_list ar3/ar1
    88         -} $expected
    89         -
    90         -do_test 1.3 {
    91         -  file delete -force ar3
    92         -  file mkdir ar3
    93         -
    94         -  forcedelete test_ar.db
    95         -  catchcmd test_ar.db ".ar cC ar1 file1 file2 dir1"
    96         -  catchcmd test_ar.db ".ar xC ar3"
    97         -  dir_to_list ar3
    98         -} $expected
          124  +finish_test
    99    125   
   100    126   
   101    127   
   102    128   finish_test
          129  +