]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
CLI resmanage use to 100% by commands, 84% by LOC. WIP
authorlarrybr <larrybr@noemail.net>
Thu, 18 May 2023 23:42:43 +0000 (23:42 +0000)
committerlarrybr <larrybr@noemail.net>
Thu, 18 May 2023 23:42:43 +0000 (23:42 +0000)
FossilOrigin-Name: 48f81d8fc650bf85028c729968f5de894f9c7e96b6ea1ec58cab31a39cb78417

manifest
manifest.uuid
src/shell.c.in

index 1502b3aceea8e0400aa3b93c2c52eee01df9b5af..57797f450ef5921e47d3cf17acd2074c1caec2d4 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C More\sCLI\sresmanage\suse\s(75%\sLOC)
-D 2023-05-17T15:22:17.870
+C CLI\sresmanage\suse\sto\s100%\sby\scommands,\s84%\sby\sLOC.\sWIP
+D 2023-05-18T23:42:43.540
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -643,7 +643,7 @@ F src/resmanage.h e210da1810bb3e3ff000b4e6aa317cd5bc42a9e027ee5991144d9a998bee8b
 F src/resolve.c 3e53e02ce87c9582bd7e7d22f13f4094a271678d9dc72820fa257a2abb5e4032
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c 738c3a3d6929f8be66c319bad17f6b297bd60a4eb14006075c48a28487dc7786
-F src/shell.c.in df0b2965b3fea71ff4a4bc03a099c3d145ee0f343b43d0ce7059f2bc4650b6b0
+F src/shell.c.in a3d2cd270c5b7525811d984840406f7739cd46329dc43ed1132552ae4616bd5f
 F src/shext_linkage.h 27dcf7624df05b2a7a6d367834339a6db3636f3035157f641f7db2ec499f8f6d
 F src/sqlite.h.in c14a4471fcd897a03631ac7ad3d05505e895e7b6419ec5b96cae9bc4df7a9fc6
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
@@ -2081,8 +2081,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P c010e1a7b2120d44c096b7d4216a8a51601972581f232d537b577617dfa6195a
-R bb1e7d9087e2e9880da47ae6212d55b1
+P 3428ce82476e996c0bf06b623c93e6d5ba1a4542e1384350321405f8bd0d0e74
+R 4c4f46d47ea8ae2acb4edb9aaf04a323
 U larrybr
-Z 3b7afacf0d05d6b1d7df00452922197f
+Z 3ba5774db546cb7251b89a558e4743f3
 # Remove this line to create a well-formed Fossil manifest.
index 958564f6b5e8fd1318ac4ec6e310e6eab9aff68a..ed1eda97c139c4f81c2feb62de844e8d867f74d4 100644 (file)
@@ -1 +1 @@
-3428ce82476e996c0bf06b623c93e6d5ba1a4542e1384350321405f8bd0d0e74
\ No newline at end of file
+48f81d8fc650bf85028c729968f5de894f9c7e96b6ea1ec58cab31a39cb78417
\ No newline at end of file
index 3d5885073ae7890a2d87ec2f2abdd26395f41947..7ae7543d193ee10b67efbdf523faf3781a6d2e8a 100644 (file)
@@ -883,11 +883,8 @@ static void shell_check_ooms(const void *p){
   }
 }
 #else
-# define shell_check_ooms(p) shell_check_oom(p)
-# define shell_check_oomm(p) shell_check_oom(p)
-static void shell_check_oom(const void *p){
-  if( p==0 ) shell_out_of_memory();
-}
+# define shell_check_ooms(p) do{ if((p)==0) shell_out_of_memory(); }while 0
+# define shell_check_oomm(p) do{ if((p)==0) shell_out_of_memory(); }while 0
 #endif
 
 /* Check a SQLite result code for out-of-memory indication.
@@ -5261,15 +5258,18 @@ static int shell_exec(
 }
 
 /*
-** Release memory previously allocated by tableColumnList().
+** Release memory previously allocated by tableColumnList() and others.
+** The *azCol object is a zero-terminated sequence of char pointers,
+** all but first of which is to be sqlite3_free()'ed, after which the
+** azCol object is to be sqlite3_free()'ed.
 */
-static void freeColumnList(char **azCol){
+static void freeNameList(char **azCol){
   int i;
   if( azCol!=0 ){
+    /* azCol[0] is a static string, not to be freed. */
     for(i=1; azCol[i]; i++){
       sqlite3_free(azCol[i]);
     }
-    /* azCol[0] is a static string */
     sqlite3_free(azCol);
   }
 }
@@ -5278,7 +5278,7 @@ static void freeColumnList(char **azCol){
 ** Return a list of pointers to strings which are the names of all
 ** columns in table zTab. The memory to hold the names is dynamically
 ** allocated and must be released by the caller using a subsequent call
-** to freeColumnList().
+** to freeNameList().
 **
 ** The azCol[0] entry is usually NULL.  However, if zTab contains a rowid
 ** value that needs to be preserved, then azCol[0] is filled in with the
@@ -5286,7 +5286,7 @@ static void freeColumnList(char **azCol){
 **
 ** The first regular column in the table is azCol[1].  The list is terminated
 ** by an entry with azCol[i]==0 for i>0. This is an invariant, maintained
-** as the list is grown so that freeColumnList() can always deal with it.
+** as the list is grown so that freeNameList() can always deal with it.
 **
 ** This function can exit abruptly under OOM conditions.
 */
@@ -5299,7 +5299,7 @@ static char **tableColumnList(sqlite3 *db, const char *zTab, int preserveRowid){
   int nPK = 0;       /* Number of PRIMARY KEY columns seen */
   int isIPK = 0;     /* True if one PRIMARY KEY column of type INTEGER */
   ResourceMark mark = holder_mark();
-  AnyResourceHolder arh = { 0, (GenericFreer)freeColumnList };
+  AnyResourceHolder arh = { 0, (GenericFreer)freeNameList };
   int rc;
 
   zSql = smprintf("PRAGMA table_info=%Q", zTab);
@@ -5459,7 +5459,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){
   }
 
   if( cli_strcmp(zType, "table")==0 ){
-    AnyResourceHolder arh = { 0, (GenericFreer)freeColumnList };
+    AnyResourceHolder arh = { 0, (GenericFreer)freeNameList };
     ShellText sSelect;
     ShellText sTable;
     char **azCol;
@@ -12772,7 +12772,7 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){
     FILE *cs_out = 0;
     if( failIfSafeMode
         (p, "cannot run \".session %s\" in safe mode", azCmd[0]) ){
-      rc = DCR_AbortError;
+      return DCR_AbortError;
     }else{
       if( nCmd!=2 ) goto session_syntax_error;
       if( pSession->p==0 ) goto session_not_open;
@@ -12789,12 +12789,13 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){
           rc = sqlite3session_patchset(pSession->p, &szChng, &pChng);
         }
         if( rc ){
-          fprintf(out, "Error: error code %d\n", rc);
+          fprintf(out, "Error: error code %d (swallowed)\n", rc);
           rc = 0;
         }
         if( pChng && fwrite(pChng, szChng, 1, cs_out)!=1 ){
           raw_printf(STD_ERR, "ERROR: Failed to write entire %d-byte output\n",
                      szChng);
+          rc = 1;
         }
         sqlite3_free(pChng);
         fclose(cs_out);
@@ -12898,7 +12899,7 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){
     for(i=0; i<pAuxDb->nSession; i++){
       if( cli_strcmp(pAuxDb->aSession[i].zName,zName)==0 ){
         utf8_printf(STD_ERR, "Session \"%s\" already exists\n", zName);
-        return rc;
+        return DCR_Error;
       }
     }
     if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){
@@ -12925,7 +12926,7 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){
     showHelp(out, "session", p);
     return DCR_CmdErred;
   }
-  return DCR_Ok;
+  return DCR_Ok|(rc!=0);
 }
 
 DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){
@@ -12940,6 +12941,8 @@ DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){
   char *zSep;              /* Separator */
   ShellText sSql;          /* Complete SQL for the query to run the hash */
   ShellText sQuery;        /* Set of queries used to read all content */
+  RESOURCE_MARK(mark);
+
   open_db(p, 0);
   for(i=1; i<nArg; i++){
     const char *z = azArg[i];
@@ -12981,9 +12984,12 @@ DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){
       " AND name NOT LIKE 'sqlite_%'"
       " ORDER BY 1 collate nocase";
   }
-  sqlite3_prepare_v2(DBX(p), zSql, -1, &pStmt, 0);
+  shell_check_nomem(sqlite3_prepare_v2(DBX(p), zSql, -1, &pStmt, 0));
+  stmt_ptr_holder(&pStmt); /* +1 */
   initText(&sQuery);
+  text_ref_holder(&sQuery); /* +2 */
   initText(&sSql);
+  text_ref_holder(&sSql); /* +3 */
   appendText(&sSql, "WITH [sha3sum$query](a,b) AS(",0);
   zSep = "VALUES(";
   while( SQLITE_ROW==sqlite3_step(pStmt) ){
@@ -13015,7 +13021,6 @@ DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){
     appendText(&sSql, zTab, '\'');
     zSep = "),(";
   }
-  sqlite3_finalize(pStmt);
   if( bSeparate ){
     zSql = smprintf(
            "%s))"
@@ -13029,14 +13034,15 @@ DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){
            "   FROM [sha3sum$query]",
            sSql.z, iSize);
   }
+  release_holders(mark);
   shell_check_ooms(zSql);
-  freeText(&sQuery);
-  freeText(&sSql);
+  sstr_holder(zSql); /* +1 */
   if( bDebug ){
     utf8_printf(ISS(p)->out, "%s\n", zSql);
   }else{
     shell_exec(p, zSql, 0);
   }
+  release_holder(); /* 0 */
 #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && !defined(SQLITE_OMIT_VIRTUALTABLE)
   {
     int lrc;
@@ -13045,8 +13051,8 @@ DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){
       "WHERE type='table' AND coalesce(rootpage,0)>1\n"
       "AND name NOT LIKE 'sqlite_%%'%s\n"
       "ORDER BY 1 collate nocase";
-    zRevText = sqlite3_mprintf(zRevText, zLike? " AND name LIKE $tspec" : "");
-    zRevText = sqlite3_mprintf(
+    zRevText = smprintf(zRevText, zLike? " AND name LIKE $tspec" : "");
+    zRevText = smprintf(
         /* lower-case query is first run, producing upper-case query. */
         "with tabcols as materialized(\n"
         "select tname, cname\n"
@@ -13062,22 +13068,25 @@ DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){
         "' OR ') as query, tname from tabcols group by tname)"
         , zRevText);
     shell_check_ooms(zRevText);
+    sstr_holder(zRevText); /* +1 */
     if( bDebug ) utf8_printf(ISS(p)->out, "%s\n", zRevText);
-    lrc = sqlite3_prepare_v2(DBX(p), zRevText, -1, &pStmt, 0);
+    lrc = shell_check_nomem(sqlite3_prepare_v2(DBX(p), zRevText,-1,&pStmt,0));
     if( lrc!=SQLITE_OK ){
-      sqlite3_free(zRevText);
+      release_holders_mark(mark);
       return DCR_Error;
     }
+    stmt_holder(pStmt); /* +2 */
     if( zLike ) sqlite3_bind_text(pStmt,1,zLike,-1,SQLITE_STATIC);
-    lrc = SQLITE_ROW==sqlite3_step(pStmt);
+    lrc = SQLITE_ROW==shell_check_nomem(sqlite3_step(pStmt));
     if( lrc ){
       const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0);
       sqlite3_stmt *pCheckStmt;
       lrc = sqlite3_prepare_v2(DBX(p), zGenQuery, -1, &pCheckStmt, 0);
+      shell_check_nomem(lrc);
       if( bDebug ) utf8_printf(ISS(p)->out, "%s\n", zGenQuery);
+      stmt_holder(pCheckStmt); /* +3 */
       if( SQLITE_OK!=lrc ){
-        sqlite3_finalize(pStmt);
-        sqlite3_free(zRevText);
+        release_holders_mark(mark);
         return DCR_Error;
       }else{
         if( SQLITE_ROW==sqlite3_step(pCheckStmt) ){
@@ -13089,14 +13098,11 @@ DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){
                sz, (sz>1)? "s": "");
           }
         }
-        sqlite3_finalize(pCheckStmt);
       }
-      sqlite3_finalize(pStmt);
     }
-    sqlite3_free(zRevText);
   }
 #endif /* !defined(*_OMIT_SCHEMA_PRAGMAS) && !defined(*_OMIT_VIRTUALTABLE) */
-  sqlite3_free(zSql);
+  release_holders_mark(mark);
   return DCR_Ok;
 }
 
@@ -13143,7 +13149,8 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){
   int i, k;                /* Loop counters */
   int nTest = 0;           /* Number of tests runs */
   int nErr = 0;            /* Number of errors seen */
-  ShellText str;           /* Answer for a query */
+  RESOURCE_MARK(mark);
+  ShellText str = {0};     /* Answer for a query */
   sqlite3_stmt *pStmt = 0; /* Query against the SELFTEST table */
 
   for(i=1; i<nArg; i++){
@@ -13174,7 +13181,9 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){
     bSelftestExists = 1;
   }
   initText(&str);
+  text_ref_holder(&str);
   appendText(&str, "x", 0);
+  stmt_ptr_holder(&pStmt);
   for(k=bSelftestExists; k>=0; k--){
     if( k==1 ){
       rc = sqlite3_prepare_v2(DBX(p),
@@ -13188,10 +13197,10 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){
     }
     if( rc ){
       *pzErr = smprintf("Error querying the selftest table\n");
-      sqlite3_finalize(pStmt);
+      release_holders_mark(mark);
       return DCR_Error;
     }
-    for(i=1; sqlite3_step(pStmt)==SQLITE_ROW; i++){
+    for(i=1; shell_check_nomem(sqlite3_step(pStmt))==SQLITE_ROW; i++){
       int tno = sqlite3_column_int(pStmt, 0);
       const char *zOp = (const char*)sqlite3_column_text(pStmt, 1);
       const char *zSql = (const char*)sqlite3_column_text(pStmt, 2);
@@ -13207,6 +13216,7 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){
         utf8_printf(psi->out, "%s\n", zSql);
       }else if( cli_strcmp(zOp,"run")==0 ){
         char *zErrMsg = 0;
+        sstr_ptr_holder(&zErrMsg);
         str.n = 0;
         str.z[0] = 0;
         rc = sqlite3_exec(DBX(p), zSql, captureOutputCallback, &str, &zErrMsg);
@@ -13217,14 +13227,15 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){
         if( rc || zErrMsg ){
           nErr++;
           rc = 1;
-          utf8_printf(psi->out, "%d: error-code-%d: %s\n", tno, rc, zErrMsg);
-          sqlite3_free(zErrMsg);
+          utf8_printf(psi->out, "%d: error-code-%d: %s\n", tno, rc,
+                      (zErrMsg)? zErrMsg : "");
         }else if( cli_strcmp(zAns,str.z)!=0 ){
           nErr++;
           rc = 1;
           utf8_printf(psi->out, "%d: Expected: [%s]\n", tno, zAns);
           utf8_printf(psi->out, "%d:      Got: [%s]\n", tno, str.z);
         }
+        release_holder();
       }else{
         *pzErr = smprintf
           ("Unknown operation \"%s\" on selftest line %d\n", zOp, tno);
@@ -13232,9 +13243,8 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){
         break;
       }
     } /* End loop over rows of content from SELFTEST */
-    sqlite3_finalize(pStmt);
   } /* End loop over k */
-  freeText(&str);
+  release_holders_mark(mark);
   utf8_printf(psi->out, "%d errors out of %d tests\n", nErr, nTest);
   return rc > 0;
 }
@@ -13257,7 +13267,7 @@ COLLECT_HELP_TEXT[
 #if SHELLOUT_ENABLE
 static DotCmdRC shellOut(char *azArg[], int nArg,
                          ShellExState *psx, char **pzErr){
-  char *zCmd;
+  char *zCmd = 0;
   int i, x;
   if( ISS(psx)->bSafeMode ) return DCR_AbortError;
   zCmd = smprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]);
@@ -13266,8 +13276,9 @@ static DotCmdRC shellOut(char *azArg[], int nArg,
                     zCmd, azArg[i]);
   }
   shell_check_ooms(zCmd);
+  sstr_holder(zCmd);
   x = system(zCmd);
-  sqlite3_free(zCmd);
+  release_holder();
   if( x ) raw_printf(STD_ERR, "%s command returns %d\n", azArg[0], x);
   return DCR_Ok;
 }
@@ -13366,7 +13377,7 @@ DISPATCHABLE_COMMAND( shxopts 3 0 0 ){
   }
   return DCR_Ok;
  moan_error:
-  raw_printf(STD_ERR, "Error: %s %s\n", zAbout, zMoan);
+  *pzErr = smprintf("Error: %s %s\n", zAbout, zMoan);
   return DCR_CmdErred;
 }
 
@@ -13463,16 +13474,22 @@ COLLECT_HELP_TEXT[
 static int showTableLike(char *azArg[], int nArg, ShellExState *p,
                          char **pzErr, char ot){
   int rc;
-  sqlite3_stmt *pStmt;
+  sqlite3_stmt *pStmt = 0;
+  ShellText s = {0};
+  AnyResourceHolder arh = { 0, (GenericFreer)freeNameList };
   char **azResult;
   int nRow, nAlloc;
   int ii;
-  ShellText s;
+  RESOURCE_MARK(mark);
+
   initText(&s);
+  text_ref_holder(&s);
   open_db(p, 0);
   rc = sqlite3_prepare_v2(DBX(p), "PRAGMA database_list", -1, &pStmt, 0);
-  if( rc ){
-    sqlite3_finalize(pStmt);
+  stmt_ptr_holder(&pStmt);
+  if( shell_check_nomem(rc) ){
+  db_err_bail:
+    release_holders_mark(mark);
     return shellDatabaseError(DBX(p));
   }
 
@@ -13481,7 +13498,7 @@ static int showTableLike(char *azArg[], int nArg, ShellExState *p,
     ** when called with the wrong number of arguments whereas the .tables
     ** command does not. */
     *pzErr = smprintf("Usage: .indexes ?LIKE-PATTERN?\n");
-    sqlite3_finalize(pStmt);
+    release_holders_mark(mark);
     return DCR_SayUsage;
   }
   for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
@@ -13526,15 +13543,16 @@ static int showTableLike(char *azArg[], int nArg, ShellExState *p,
     appendText(&s, zSystem, 0);
   }
   rc = sqlite3_finalize(pStmt);
+  pStmt = 0;
   if( rc==SQLITE_OK ){
     appendText(&s, " ORDER BY 1", 0);
-    rc = sqlite3_prepare_v2(DBX(p), s.z, -1, &pStmt, 0);
+    rc = shell_check_nomem(sqlite3_prepare_v2(DBX(p), s.z, -1, &pStmt, 0));
   }
-  freeText(&s);
-  if( rc ) return shellDatabaseError(DBX(p));
+  if( rc ) goto db_err_bail;
 
   /* Run the SQL statement prepared by the above block. Store the results
-  ** as an array of nul-terminated strings in azResult[].  */
+  ** as an array of nul-terminated strings in azResult[]. The 0th element
+  ** of azResult[] is not used so that freeNameList() can free it. */
   nRow = nAlloc = 0;
   azResult = 0;
   if( nArg>1 ){
@@ -13542,47 +13560,49 @@ static int showTableLike(char *azArg[], int nArg, ShellExState *p,
   }else{
     sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC);
   }
-  while( sqlite3_step(pStmt)==SQLITE_ROW ){
-    if( nRow>=nAlloc ){
+  while( shell_check_nomem(sqlite3_step(pStmt))==SQLITE_ROW ){
+    if( nRow+2 > nAlloc ){
       char **azNew;
       int n2 = nAlloc*2 + 10;
       azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
       shell_check_ooms(azNew);
+      /* Keep the object usable by freeNameList at all times. */
+      memset(azNew+nAlloc, 0, (n2-nAlloc)*sizeof(azResult[0]));
       nAlloc = n2;
+      arh.pAny = azNew;
       azResult = azNew;
     }
+    ++nRow;
     azResult[nRow] = smprintf("%s", sqlite3_column_text(pStmt, 0));
     shell_check_ooms(azResult[nRow]);
-    nRow++;
   }
   if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
-    rc = shellDatabaseError(DBX(p));
+    pStmt = 0;
+    goto db_err_bail;
   }
-
+  pStmt = 0;
   /* Pretty-print the contents of array azResult[] to the output */
   if( rc==0 && nRow>0 ){
     int len, maxlen = 0;
     int i, j;
     int nPrintCol, nPrintRow;
-    for(i=0; i<nRow; i++){
+    for(i=1; i<=nRow; i++){
       len = strlen30(azResult[i]);
       if( len>maxlen ) maxlen = len;
     }
     nPrintCol = 80/(maxlen+2);
     if( nPrintCol<1 ) nPrintCol = 1;
     nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
-    for(i=0; i<nPrintRow; i++){
-      for(j=i; j<nRow; j+=nPrintRow){
-        char *zSp = j<nPrintRow ? "" : "  ";
+    for(i=1; i<=nPrintRow; i++){
+      for(j=i; j<=nRow; j+=nPrintRow){
+        char *zSp = j<=nPrintRow ? "" : "  ";
         utf8_printf(ISS(p)->out, "%s%-*s", zSp, maxlen,
                     azResult[j] ? azResult[j]:"");
       }
       raw_printf(ISS(p)->out, "\n");
     }
   }
-
-  for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]);
-  sqlite3_free(azResult);
+  release_holders_mark(mark);
   return DCR_Ok;
 }
 
@@ -13764,11 +13784,12 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){
   if( testctrl<0 ){
     utf8_printf(STD_ERR,"Error: unknown test-control: %s\n"
                 "Use \".testctrl --help\" for help\n", zCmd);
-  }else if( aCtrl[iCtrl].unSafe && ISS(p)->bSafeMode ){
-    utf8_printf(STD_ERR,
-                "line %d: \".testctrl %s\" may not be used in safe mode\n",
-                ISS(p)->pInSource->lineno, aCtrl[iCtrl].zCtrlName);
-    exit(1);
+  }else if( aCtrl[iCtrl].unSafe
+            && failIfSafeMode(p,"line %d: \".testctrl %s\" "
+                              "may not be used in safe mode\n",
+                              ISS(p)->pInSource->lineno,
+                              aCtrl[iCtrl].zCtrlName) ){
+    return DCR_Abort;
   }else{
     switch(testctrl){
 
@@ -14136,14 +14157,17 @@ DISPATCHABLE_COMMAND( vars 2 1 0 ){
     if( (nArg>2 || zCmd[0]=='c') ){
       sqlite3_str *sbZap = sqlite3_str_new(dbs);
       char *zSql;
+      sqst_ptr_holder(&sbZap);
       sqlite3_str_appendf
         (sbZap, "DELETE FROM "SHVAR_TABLE_SNAME" WHERE key ");
       append_in_clause(sbZap,
                        (const char **)&azArg[2], (const char **)&azArg[nArg]);
       zSql = sqlite3_str_finish(sbZap);
+      drop_holder();
       shell_check_ooms(zSql);
+      sstr_holder(zSql);
       rc = sqlite3_exec(dbs, zSql, 0, 0, 0);
-      sqlite3_free(zSql);
+      release_holder();
     }
 #ifndef SQLITE_NOHAVE_SYSTEM
   }else if( SUBCMD("edit") ){
@@ -14297,10 +14321,12 @@ DISPATCHABLE_COMMAND( vfsname ? 0 0 ){
  */
 static void setColumnWidths(ShellExState *p, char *azWidths[], int nWidths){
   int j;
+  int *pSW = p->pSpecWidths;
   p->numWidths = nWidths;
-  p->pSpecWidths = realloc(p->pSpecWidths, (nWidths+1)*sizeof(int)*2);
+  pSW = realloc(pSW, (nWidths+1)*sizeof(int)*2);;
+  shell_check_oomm(pSW);
+  p->pSpecWidths = pSW;
   if( nWidths>0 ){
-    shell_check_oomm(p->pSpecWidths);
     p->pHaveWidths = &p->pSpecWidths[nWidths];
     for(j=0; j<nWidths; j++){
       p->pSpecWidths[j] = (int)integerValue(azWidths[j]);
@@ -14410,35 +14436,31 @@ DISPATCHABLE_COMMAND( eval 3 1 0 ){
 DISPATCHABLE_COMMAND( read 3 2 2 ){
   DotCmdRC rc = DCR_Ok;
   ShellInState *psi = ISS(p);
-  FILE *inUse = 0;
-  int (*fCloser)(FILE *) = 0;
+  InSource inSourceRedir
+    = INSOURCE_FILE_REDIR(0, azArg[1], psi->pInSource);
+
   if( psi->bSafeMode ) return DCR_AbortError;
   if( azArg[1][0]=='|' ){
 #ifdef SQLITE_OMIT_POPEN
     *pzErr = smprintf("pipes are not supported in this OS\n");
     rc = DCR_Error;
-    /* p->out = STD_OUT; This was likely not needed. To be investigated. */
 #else
-    inUse = popen(azArg[1]+1, "r");
-    if( inUse==0 ){
+    if( (inSourceRedir.inFile = popen(azArg[1]+1, "r"))==0 ){
       *pzErr = smprintf("cannot open \"%s\"\n", azArg[1]);
       rc = DCR_Error;
     }else{
-      fCloser = pclose;
+      inSourceRedir.closer.stream = pclose;
     }
 #endif
-  }else if( (inUse = openChrSource(azArg[1]))==0 ){
+  }else if( (inSourceRedir.inFile = openChrSource(azArg[1]))==0 ){
     *pzErr = smprintf("cannot open \"%s\"\n", azArg[1]);
     rc = DCR_Error;
   }else{
-    fCloser = fclose;
+    inSourceRedir.closer.stream = fclose;
   }
-  if( inUse!=0 ){
-    InSource inSourceRedir
-      = INSOURCE_FILE_REDIR(inUse, azArg[1], psi->pInSource);
+  if( inSourceRedir.inFile!=0 ){
     AnyResourceHolder arh = { &(psi->pInSource),(GenericFreer)finish_InSource };
     psi->pInSource = &inSourceRedir;
-    inSourceRedir.closer.stream = fCloser;
     any_ref_holder(&arh);
     rc = process_input(psi);
     /* If error(s) occured during process, leave complaining to them. */