/ Check-in [32c4fa25]
Login

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

Overview
Comment:Add support for the "--list" command. And for arguments to the "--extract" command.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | sqlar-shell-support
Files: files | file ages | folders
SHA3-256:32c4fa2552bb0fa7d7d143108457efae7a756d6cb14b1d59312e56efac3b2656
User & Date: dan 2017-12-13 20:04:53
Context
2017-12-13
20:17
Add the shell tool ".ar --update" command. check-in: 825e3c03 user: dan tags: sqlar-shell-support
20:04
Add support for the "--list" command. And for arguments to the "--extract" command. check-in: 32c4fa25 user: dan tags: sqlar-shell-support
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
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/shell.c.in.

  4087   4087         raw_printf(stderr, "sql error: %s (%d)\n", 
  4088   4088             sqlite3_errmsg(db), sqlite3_errcode(db)
  4089   4089         );
  4090   4090         *pRc = rc;
  4091   4091       }
  4092   4092     }
  4093   4093   }
         4094  +
         4095  +static void shellPrepare2(
         4096  +  sqlite3 *db, 
         4097  +  int *pRc, 
         4098  +  const char *zSql, 
         4099  +  const char *zTail, 
         4100  +  sqlite3_stmt **ppStmt
         4101  +){
         4102  +  if( *pRc==SQLITE_OK && zTail ){
         4103  +    char *z = sqlite3_mprintf("%s %s", zSql, zTail);
         4104  +    if( z==0 ){
         4105  +      *pRc = SQLITE_NOMEM;
         4106  +    }else{
         4107  +      shellPrepare(db, pRc, z, ppStmt);
         4108  +      sqlite3_free(z);
         4109  +    }
         4110  +  }else{
         4111  +    shellPrepare(db, pRc, zSql, ppStmt);
         4112  +  }
         4113  +}
  4094   4114   
  4095   4115   static void shellFinalize(
  4096   4116     int *pRc, 
  4097   4117     sqlite3_stmt *pStmt
  4098   4118   ){
  4099   4119     if( pStmt ){
  4100   4120       sqlite3 *db = sqlite3_db_handle(pStmt);
................................................................................
  4311   4331   /*
  4312   4332   ** Implementation of .ar "Update" command. 
  4313   4333   */
  4314   4334   static int arUpdateCmd(ShellState *p, sqlite3 *db, ArCommand *pAr){
  4315   4335     raw_printf(stderr, "todo...\n");
  4316   4336     return SQLITE_OK;
  4317   4337   }
         4338  +
         4339  +/*
         4340  +** This function assumes that all arguments within the ArCommand.azArg[]
         4341  +** array refer to archive members, as for the --extract or --list commands. 
         4342  +** It checks that each of them are present. If any specified file is not
         4343  +** present in the archive, an error is printed to stderr and an error
         4344  +** code returned. Otherwise, if all specified arguments are present in
         4345  +** the archive, SQLITE_OK is returned.
         4346  +**
         4347  +** This function strips any trailing '/' characters from each argument.
         4348  +** This is consistent with the way the [tar] command seems to work on
         4349  +** Linux.
         4350  +*/
         4351  +static int arCheckEntries(sqlite3 *db, ArCommand *pAr){
         4352  +  int rc = SQLITE_OK;
         4353  +  if( pAr->nArg ){
         4354  +    int i;
         4355  +    sqlite3_stmt *pTest = 0;
         4356  +
         4357  +    shellPrepare(db, &rc, "SELECT name FROM sqlar WHERE name=?", &pTest);
         4358  +    for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
         4359  +      char *z = pAr->azArg[i];
         4360  +      int n = strlen(z);
         4361  +      int bOk = 0;
         4362  +      while( n>0 && z[n-1]=='/' ) n--;
         4363  +      z[n] = '\0';
         4364  +      sqlite3_bind_text(pTest, 1, z, -1, SQLITE_STATIC);
         4365  +      if( SQLITE_ROW==sqlite3_step(pTest) ){
         4366  +        bOk = 1;
         4367  +      }
         4368  +      shellReset(&rc, pTest);
         4369  +      if( rc==SQLITE_OK && bOk==0 ){
         4370  +        raw_printf(stderr, "not found in archive: %s\n", z);
         4371  +        rc = SQLITE_ERROR;
         4372  +      }
         4373  +    }
         4374  +    shellFinalize(&rc, pTest);
         4375  +  }
         4376  +
         4377  +  return rc;
         4378  +}
         4379  +
         4380  +/*
         4381  +** Format a WHERE clause that can be used against the "sqlar" table to
         4382  +** identify all archive members that match the command arguments held
         4383  +** in (*pAr). Leave this WHERE clause in (*pzWhere) before returning.
         4384  +** The caller is responsible for eventually calling sqlite3_free() on
         4385  +** any non-NULL (*pzWhere) value.
         4386  +*/
         4387  +static void arWhereClause(
         4388  +  int *pRc, 
         4389  +  ArCommand *pAr, 
         4390  +  char **pzWhere                  /* OUT: New WHERE clause (or NULL) */
         4391  +){
         4392  +  char *zWhere = 0;
         4393  +  if( *pRc==SQLITE_OK ){
         4394  +    int i;
         4395  +    const char *zSep = "WHERE ";
         4396  +    for(i=0; i<pAr->nArg; i++){
         4397  +      const char *z = pAr->azArg[i];
         4398  +      zWhere = sqlite3_mprintf(
         4399  +          "%z%s name = '%q' OR name BETWEEN '%q/' AND '%q0'", 
         4400  +          zWhere, zSep, z, z, z
         4401  +      );
         4402  +      if( zWhere==0 ){
         4403  +        *pRc = SQLITE_NOMEM;
         4404  +        break;
         4405  +      }
         4406  +      zSep = " OR ";
         4407  +    }
         4408  +  }
         4409  +  *pzWhere = zWhere;
         4410  +}
  4318   4411   
  4319   4412   /*
  4320   4413   ** Implementation of .ar "lisT" command. 
  4321   4414   */
  4322   4415   static int arListCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
  4323         -  raw_printf(stderr, "todo...\n");
  4324         -  return SQLITE_OK;
         4416  +  const char *zSql = "SELECT name FROM sqlar"; 
         4417  +  char *zWhere = 0;
         4418  +  sqlite3_stmt *pSql = 0;
         4419  +  int rc;
         4420  +
         4421  +  rc = arCheckEntries(db, pAr);
         4422  +  arWhereClause(&rc, pAr, &zWhere);
         4423  +
         4424  +  shellPrepare2(db, &rc, zSql, zWhere, &pSql);
         4425  +  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
         4426  +    raw_printf(p->out, "%s\n", sqlite3_column_text(pSql, 0));
         4427  +  }
         4428  +  return rc;
  4325   4429   }
  4326   4430   
  4327   4431   
  4328   4432   /*
  4329   4433   ** Implementation of .ar "eXtract" command. 
  4330   4434   */
  4331   4435   static int arExtractCommand(ShellState *p, sqlite3 *db, ArCommand *pAr){
  4332   4436     const char *zSql1 = 
  4333   4437       "SELECT :1 || name, writefile(:1 || name, "
  4334         -    "CASE WHEN (data AND sz>=0 AND sz!=length(data)) THEN uncompress(data) "
  4335         -    "   ELSE data END, "
         4438  +    "CASE WHEN (data AND sz>=0 AND sz!=length(data)) THEN "
         4439  +    "    uncompress(data) "
         4440  +    "ELSE"
         4441  +    "    data "
         4442  +    "END, "
  4336   4443       "mode) FROM sqlar";
  4337   4444     const char *zSql2 = "SELECT :1 || name, mtime FROM sqlar"; 
  4338   4445   
  4339   4446     struct timespec times[2];
  4340   4447     sqlite3_stmt *pSql = 0;
  4341   4448     int rc = SQLITE_OK;
  4342   4449     char *zDir = 0;
         4450  +  char *zWhere = 0;
  4343   4451   
  4344         -  if( pAr->zDir ){
  4345         -    zDir = sqlite3_mprintf("%s/", pAr->zDir);
  4346         -  }else{
  4347         -    zDir = sqlite3_mprintf("");
         4452  +  /* If arguments are specified, check that they actually exist within
         4453  +  ** the archive before proceeding. And formulate a WHERE clause to
         4454  +  ** match them.  */
         4455  +  rc = arCheckEntries(db, pAr);
         4456  +  arWhereClause(&rc, pAr, &zWhere);
         4457  +
         4458  +  if( rc==SQLITE_OK ){
         4459  +    if( pAr->zDir ){
         4460  +      zDir = sqlite3_mprintf("%s/", pAr->zDir);
         4461  +    }else{
         4462  +      zDir = sqlite3_mprintf("");
         4463  +    }
         4464  +    if( zDir==0 ) rc = SQLITE_NOMEM;
  4348   4465     }
  4349   4466   
  4350   4467     memset(times, 0, sizeof(times));
  4351   4468     times[0].tv_sec = time(0);
  4352   4469   
  4353         -  shellPrepare(db, &rc, zSql1, &pSql);
         4470  +  shellPrepare2(db, &rc, zSql1, zWhere, &pSql);
  4354   4471     if( rc==SQLITE_OK ){
  4355   4472       sqlite3_bind_text(pSql, 1, zDir, -1, SQLITE_STATIC);
  4356   4473     }
  4357   4474     while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
  4358   4475       if( pAr->bVerbose ){
  4359   4476         raw_printf(stdout, "%s\n", sqlite3_column_text(pSql, 0));
  4360   4477       }
  4361   4478     }
  4362   4479     shellFinalize(&rc, pSql);
  4363   4480   
  4364         -  shellPrepare(db, &rc, zSql2, &pSql);
         4481  +  shellPrepare2(db, &rc, zSql2, zWhere, &pSql);
  4365   4482     if( rc==SQLITE_OK ){
  4366   4483       sqlite3_bind_text(pSql, 1, zDir, -1, SQLITE_STATIC);
  4367   4484     }
  4368   4485     while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){
  4369   4486       const char *zPath = (const char*)sqlite3_column_text(pSql, 0);
  4370   4487       times[1].tv_sec = (time_t)sqlite3_column_int64(pSql, 1);
  4371   4488       if( utimensat(AT_FDCWD, zPath, times, AT_SYMLINK_NOFOLLOW) ){
................................................................................
  4373   4490         rc = SQLITE_ERROR;
  4374   4491         break;
  4375   4492       }
  4376   4493     }
  4377   4494     shellFinalize(&rc, pSql);
  4378   4495   
  4379   4496     sqlite3_free(zDir);
         4497  +  sqlite3_free(zWhere);
  4380   4498     return rc;
  4381   4499   }
  4382   4500   
  4383   4501   
  4384   4502   /*
  4385   4503   ** Implementation of .ar "Create" command. 
  4386   4504   **