]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Use CLI resmanage to ensure input redirection is unwound on abort. Remove unused...
authorlarrybr <larrybr@noemail.net>
Tue, 16 May 2023 14:45:19 +0000 (14:45 +0000)
committerlarrybr <larrybr@noemail.net>
Tue, 16 May 2023 14:45:19 +0000 (14:45 +0000)
FossilOrigin-Name: 243085279c6f8c51ad85bdc1e7c07ef6f858067640a7330f77d0bf94ed11b438

manifest
manifest.uuid
src/resmanage.c
src/resmanage.h
src/shell.c.in

index f70d040be5a96d781af17210be4534afb2196427..f9811b8f695f2592df593816093820f6a6dd1f7d 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C CLI\sdebug\sbuild\sready\sfor\stesting\swhether\sOOM\shandling\sis\swell-behaved.
-D 2023-05-15T23:43:26.974
+C Use\sCLI\sresmanage\sto\sensure\sinput\sredirection\sis\sunwound\son\sabort.\sRemove\sunused\sresmanage\sfeature.\sExtend\sno-leak-on-abort\sfraction.\sFix\sunlikely\s(and\sold)\sGPF\supon\sOOM.
+D 2023-05-16T14:45:19.983
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -638,12 +638,12 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
 F src/prepare.c 6350675966bd0e7ac3a464af9dbfe26db6f0d4237f4e1f1acdb17b12ad371e6e
 F src/printf.c b9320cdbeca0b336c3f139fd36dd121e4167dd62b35fbe9ccaa9bab44c0af38d
 F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
-F src/resmanage.c 0d0525e595998ef59b9e4bcc0a29674730be921e63b1d6decdd90e294258feca
-F src/resmanage.h 7caffb310388a25d147018752a25a5b1a85a2c21478ec723eddc5def64a9148a
+F src/resmanage.c 6b1e468adaa4343537e5b3b6746325b111961d7ff6122735a73f49e208d01df0
+F src/resmanage.h e4fd319ae38bc1c363190b3fb374788c40b5986f92183db779269cb8b9340c15
 F src/resolve.c 3e53e02ce87c9582bd7e7d22f13f4094a271678d9dc72820fa257a2abb5e4032
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c 738c3a3d6929f8be66c319bad17f6b297bd60a4eb14006075c48a28487dc7786
-F src/shell.c.in 5ef3ad16114a5bbb4e4c765f6ab718e48fc53e4f819a5ed86626dc79145543e8
+F src/shell.c.in 071bd4ae867d42a372e517fd101b77cb10372a914eee5dd0c9dec4f4c753fe85
 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 b91cec479d1b43598863d7b15927054cd089f51a385e86a4e511ffef64f6cfad
-R 8129360d19ca72d5540a042049a13e26
+P 3cec1488f4f1a375d9c97e073a4fe2e2099113e03a88a401a26e9331c783da86
+R 30833777a1a668ffdffa491e9ac5d3af
 U larrybr
-Z d6535bcd86ae75656b3143470547f39d
+Z 5728435be9055029d5aa1c859c1dfc28
 # Remove this line to create a well-formed Fossil manifest.
index e6cdc5dd74d90ddfeca247b8c66f1d12dec04ad9..afa2e36c44833c039d293975beddb59bdd651443 100644 (file)
@@ -1 +1 @@
-3cec1488f4f1a375d9c97e073a4fe2e2099113e03a88a401a26e9331c783da86
\ No newline at end of file
+243085279c6f8c51ad85bdc1e7c07ef6f858067640a7330f77d0bf94ed11b438
\ No newline at end of file
index 9f967df370c6380f1bb6851e283ff0aeb7594e12..40ffe132a0a8805bfe6a7f0c1f78155aee151e2e 100644 (file)
@@ -30,8 +30,8 @@ typedef enum FreeableResourceKind {
 #ifdef SHELL_MANAGE_TEXT
   FRK_Text,
 #endif
-  FRK_AnyRef,
-  FRK_CustomBase /* series of values for custom freers */
+  FRK_AnyRef, FRK_VdtorRef,
+  FRK_CountOf
 } FreeableResourceKind;
 
 #if defined(_WIN32) || defined(WIN32)
@@ -57,8 +57,10 @@ typedef struct ResourceHeld {
     ShellText *p_text;
 #endif
     AnyResourceHolder *p_any_rh;
+    VirtualDtorNthObject *p_vdfo;
   } held;
-  FreeableResourceKind frk;
+  unsigned short frk; /* a FreeableResourceKind value */
+  unsigned short offset;
 } ResourceHeld;
 
 /* The held-resource stack. This is for single-threaded use only. */
@@ -66,15 +68,6 @@ static ResourceHeld *pResHold = 0;
 static ResourceCount numResHold = 0;
 static ResourceCount numResAlloc = 0;
 
-/* A small set of custom freers. It is linearly searched, used for
-** layered heap-allocated (and other-allocated) data structures, so
-** tends to have use limited to where slow things are happening.
-*/
-static ResourceCount numCustom = 0; /* number of the set */
-static ResourceCount numCustomAlloc = 0; /* allocated space */
-typedef void (*FreerFunction)(void *);
-static FreerFunction *aCustomFreers = 0; /* content of set */
-
 const char *resmanage_oom_message = "out of memory, aborting";
 
 /* Info recorded in support of quit_moan(...) and stack-ripping */
@@ -144,14 +137,14 @@ static int free_rk( ResourceHeld *pRH ){
   case FRK_AnyRef:
     (pRH->held.p_any_rh->its_freer)(pRH->held.p_any_rh->pAny);
     break;
-  default:
+  case FRK_VdtorRef:
     {
-      int ck = pRH->frk - FRK_CustomBase;
-      assert(ck>=0);
-      if( ck < numCustom ){
-        aCustomFreers[ck]( pRH->held.p_any );
-      }else --rv;
+      VirtualDtorNthObject *po = pRH->held.p_vdfo;
+      (po->p_its_freer[pRH->offset])(po);
     }
+    break;
+  default:
+    assert(pRH->frk < FRK_CountOf);
   }
   pRH->held.p_any = 0;
   return rv;
@@ -209,7 +202,7 @@ void release_holder(void){
 
 /* Shared resource-stack pushing code */
 static void res_hold(void *pv, FreeableResourceKind frk){
-  ResourceHeld rh = { pv, frk };
+  ResourceHeld rh = { pv, frk, 0 };
   if( numResHold == numResAlloc ){
     ResourceCount nrn = (ResourceCount)((numResAlloc>>2) + 5);
     if( !more_holders_try(nrn) ){
@@ -261,29 +254,7 @@ static void text_ref_holder(ShellText *pt){
   res_hold(pt, FRK_Text);
 }
 #endif
-/* Hold anything together with arbitrary freeing function */
-void* any_holder(void *pm, void (*its_freer)(void*)){
-  int i = 0;
-  while( i < numCustom ){
-    if( its_freer == aCustomFreers[i] ) break;
-    ++i;
-  }
-  if( i == numCustom ){
-    size_t ncf = numCustom + 2;
-    FreerFunction *pcf;
-    pcf = (FreerFunction *)realloc(aCustomFreers, ncf*sizeof(FreerFunction));
-    if( pcf!=0 ){
-      assert(ncf < (1<<16));
-      numCustomAlloc = (ResourceCount)ncf;
-      aCustomFreers = pcf;
-      aCustomFreers[numCustom++] = its_freer;
-    }else{
-      quit_moan(resmanage_oom_message,1);
-    }
-  }
-  res_hold(pm, i + FRK_CustomBase);
-  return pm;
-}
+
 /* Hold some SQLite-allocated memory */
 void* smem_holder(void *pm){
   res_hold(pm, FRK_DbMem);
@@ -307,6 +278,19 @@ void any_ref_holder(AnyResourceHolder *parh){
   assert(parh!=0 && parh->its_freer!=0);
   res_hold(parh, FRK_AnyRef);
 }
+/* Hold a reference to a VirtualDtorNthObject (in stack frame) */
+void dtor_ref_holder(VirtualDtorNthObject *pvdfo, unsigned char n){
+  ResourceHeld rh = { (void*)pvdfo, FRK_VdtorRef, n };
+  assert(pvdfo!=0 && pvdfo->p_its_freer!=0 && *(pvdfo->p_its_freer)!=0);
+  if( numResHold == numResAlloc ){
+    ResourceCount nrn = (ResourceCount)((numResAlloc>>2) + 5);
+    if( !more_holders_try(nrn) ){
+      free_rk(&rh);
+      quit_moan(resmanage_oom_message,1);
+    }
+  }
+  pResHold[numResHold++] = rh;
+}
 
 /* Free all held resources in excess of given resource stack mark,
 ** then return how many needed freeing. */
@@ -321,12 +305,6 @@ int holder_free(ResourceMark mark){
       pResHold = 0;
       numResAlloc = 0;
     }
-    if( numCustomAlloc>0 ){
-      free(aCustomFreers);
-      aCustomFreers = 0;
-      numCustom = 0;
-      numCustomAlloc = 0;
-    }
   }
   return rv;
 }
index f1cd1558235d8ceccdeae8d76b0631fc24e4e860..147821c90af96236646180adb3be036ab4e3b30e 100644 (file)
@@ -97,8 +97,6 @@ extern void release_holder(void);
 ** The referenced objects are directly freed; they are stored in
 ** the heap with lifetime not bound to the caller's activation.
 */
-/* anything together with arbitrary freeing function */
-extern void* any_holder(void *pm, void (*its_freer)(void*));
 /* anything in the malloc() heap */
 extern void* mmem_holder(void *pm);
 /* a C string in the malloc() heap */
@@ -126,17 +124,29 @@ extern void pipe_holder(FILE *);
 ** exits, this condition is met because holder_free() is called
 ** by the abrupt exiter before the execution stack is stripped.
 */
+
+/* An arbitrary data pointer paired with its freer function */
 typedef struct AnyResourceHolder {
   void *pAny;
   GenericFreer its_freer;
 } AnyResourceHolder;
 
+/* An object of a class having its dtor as the Nth v-table entry.
+** This is only useful when it is embedded in a bigger object. */
+typedef struct VirtualDtorNthObject VirtualDtorNthObject;
+typedef void (*VirtualDtorNth)(VirtualDtorNthObject *);
+struct VirtualDtorNthObject {
+  VirtualDtorNth *p_its_freer;
+};
+
 /* a reference to an AnyResourceHolder (whose storage is not managed) */
 extern void any_ref_holder(AnyResourceHolder *parh);
 /* a C string in the SQLite heap, reference to */
 extern void sstr_ptr_holder(char **pz);
 /* a SQLite prepared statement, reference to */
 extern void stmt_ptr_holder(sqlite3_stmt **ppstmt);
+/* an object with v-table (ref) whose dtor is the 0th member, reference to */
+extern void dtor_ref_holder(VirtualDtorNthObject *pvdfo, unsigned char n);
 #ifdef SHELL_MANAGE_TEXT
 /* a ShellText object, reference to (storage for which not managed) */
 static void text_ref_holder(ShellText *);
index c9443ca5f65dd07f8d0e6213a624c060de1a1a43..108ea78d59d58325accb20ad4a77da0f2d4f7248 100644 (file)
@@ -1039,40 +1039,67 @@ static FILE * openChrSource(const char *zFile){
 /*
 ** Arrange for shell input from either a FILE or a string.
 ** For applicable invariants, see strLineGet(...) which is
-** the only modifier of this struct. (3rd and 4th members)
+** the only modifier of this struct. (4rd and 5th members)
 ** All other members are simply initialized to select the
 ** input stream and track input sources, set by whatever
 ** routines redirect the input.
 */
 typedef struct InSource {
+  struct InSource *pFrom; /* Aid redirect tracking and auto-unnesting.   */
   FILE *inFile;     /* Will be 0 when input is to be taken from string.  */
   char *zStrIn;     /* Will be 0 when no input is available from string. */
-  int iReadOffset;  /* Offset into zStrIn where next "read" to be done.  */
+  int iReadOffset;  /* Offset into zStrIn where next "read" to be done   */
   int lineno;       /* How many lines have been read from this source    */
-  const char *zSourceSay; /* For complaints, keep a name for this source */
-  struct InSource *pFrom; /* and redirect tracking to aid unraveling.    */
+  const char *zSourceSay; /* For complaints, a name kept for this source */
+  union {
+    int (*stream)(FILE *);
+    void (*ptext)(char *);
+  } closer;  /* Closer for the file or freer for the text, set by opener */
 } InSource;
-#define INSOURCE_STR_REDIR(str, tagTo, isFrom) {0, str, 0, 0, tagTo, isFrom}
-#define INSOURCE_FILE_REDIR(fh, tagTo, isFrom) {fh, 0, 0, 0, tagTo, isFrom}
+#define INSOURCE_STR_REDIR(str, tagTo, isFrom) {isFrom, 0, str, 0, 0, tagTo }
+#define INSOURCE_FILE_REDIR(fh, tagTo, isFrom) {isFrom, fh,  0, 0, 0, tagTo }
 #define INSOURCE_IS_INTERACTIVE(pIS) \
   ((pIS)==&termInSource && stdin_is_interactive )
 #define INSOURCE_IS_INVOKEARG(pIS) \
   ((pIS)==&cmdInSource)
-/* This instance's address is taken as part of interactive input test. */
-static InSource termInSource = { 0, 0, 0, 0, "<terminal>", 0};
-static InSource stdInSource = { 0, 0, 0, 0, "<stdin>", 0};
-static InSource cmdInSource = { 0, 0, 0, 0, "<cmdLine>", 0};
+/* These instances' addresses are taken as part of interactive input test
+ * or test for other special handling as a command-line argument. */
+static InSource termInSource = { 0, 0, 0, 0, 0, "<terminal>", 0 };
+static InSource stdInSource = { 0, 0, 0, 0, 0, "<stdin>", 0 };
+static InSource cmdInSource = { 0, 0, 0, 0, 0, "<cmdLine>", 0 };
+
+/* Initializer for just above 3 InSource objects */
 static void init_std_inputs(FILE *pIn ){
   termInSource.inFile = pIn;
   stdInSource.inFile = pIn;
   cmdInSource.lineno = 0;
 }
+/* Setup cmdInSource to supply given text as input. */
 static void set_invocation_cmd(char *zDo){
   cmdInSource.iReadOffset = 0;
   cmdInSource.zStrIn = zDo;
   ++cmdInSource.lineno;
 }
 
+/* Close an InSource object and unlink it from redirect nesting. */
+static void finish_InSource( InSource **ppIS ){
+  if( ppIS!=0 && *ppIS!=0 ){
+    InSource *pIS = *ppIS;
+    if( pIS->closer.stream != 0 ){
+      if( pIS->inFile != 0 ){
+        (pIS->closer.stream)(pIS->inFile);
+        pIS->inFile = 0;
+      }else if( pIS->zStrIn != 0 ){
+        (pIS->closer.ptext)(pIS->zStrIn);
+        pIS->zStrIn = 0;
+      }
+    }
+    *ppIS = pIS->pFrom;
+    pIS->pFrom = 0;
+  }
+}
+
+/* Similar to fgets(buffer, limit, file) but for InSource holding a string. */
 static char *strLineGet(char *zBuf, int ncMax, InSource *pInSrc){
   if( pInSrc->inFile!=0 ){
     char *zRet = fgets(zBuf, ncMax, pInSrc->inFile );
@@ -5736,6 +5763,7 @@ static unsigned char *readHexDb(ShellInState *psi, int *pnData){
   const char *zDbFilename = psi->pAuxDb->zDbFilename;
   /* Need next two objects only if redirecting input to get the hex. */
   InSource inRedir = INSOURCE_FILE_REDIR(0, zDbFilename, psi->pInSource);
+  AnyResourceHolder arh = { &psi->pInSource, (GenericFreer)finish_InSource };
   unsigned int x[16];
   char zLine[1000];
   if( zDbFilename ){
@@ -5745,6 +5773,8 @@ static unsigned char *readHexDb(ShellInState *psi, int *pnData){
       return 0;
     }
     psi->pInSource = &inRedir;
+    inRedir.closer.stream = fclose;
+    any_ref_holder(&arh);
   }else{
     /* Will read hex DB lines inline from present input, without redirect. */
     if( INSOURCE_IS_INTERACTIVE(psi->pInSource) ){
@@ -5762,7 +5792,7 @@ static unsigned char *readHexDb(ShellInState *psi, int *pnData){
   n = (n+pgsz-1)&~(pgsz-1);  /* Round n up to the next multiple of pgsz */
   a = sqlite3_malloc( n ? n : 1 );
   shell_check_ooms(a);
-  smem_holder(a); /* offset 0 */
+  smem_holder(a);
   memset(a, 0, n);
   if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
     utf8_printf(STD_ERR, "invalid pagesize\n");
@@ -5789,12 +5819,8 @@ static unsigned char *readHexDb(ShellInState *psi, int *pnData){
     }
   }
   *pnData = n; /* Record success and size. */
-  swap_held(mark, 0, 0);
+  pop_holder();
  readHexDb_cleanup:
-  if( psi->pInSource==&inRedir ){
-    fclose( inRedir.inFile );
-    psi->pInSource = inRedir.pFrom;
-  }
   holder_free(mark);
   return a;
 
@@ -9496,13 +9522,15 @@ DISPATCHABLE_COMMAND( binary 3 2 2 ){
 DISPATCHABLE_COMMAND( cd ? 2 2 ){
   int rc=0;
   if( ISS(p)->bSafeMode ) return DCR_AbortError;
+  else{
 #if defined(_WIN32) || defined(WIN32)
-  wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
-  rc = !SetCurrentDirectoryW(z);
-  sqlite3_free(z);
+    wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
+    rc = (z)? !SetCurrentDirectoryW(z) : 1;
+    sqlite3_free(z);
 #else
-  rc = chdir(azArg[1]);
+    rc = chdir(azArg[1]);
 #endif
+  }
   if( rc ){
     utf8_printf(STD_ERR, "Cannot change to directory \"%s\"\n", azArg[1]);
     rc = 1;
@@ -9589,14 +9617,14 @@ DISPATCHABLE_COMMAND( connection ? 1 4 ){
   }else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){
     int i = azArg[1][0] - '0';
     if( psi->pAuxDb != &psi->aAuxDb[i] && i>=0 && i<ArraySize(psi->aAuxDb) ){
-      psi->pAuxDb->db = DBI(psi);
+      psi->pAuxDb->db = DBX(p);
       psi->pAuxDb = &psi->aAuxDb[i];
 #if SHELL_DYNAMIC_EXTENSION
-      if( DBI(psi)!=0 ) notify_subscribers(psi, NK_DbUserVanishing, DBI(psi));
+      if( DBX(p)!=0 ) notify_subscribers(psi, NK_DbUserVanishing, DBX(p));
 #endif
-      globalDb = DBI(psi) = psi->pAuxDb->db;
+      globalDb = DBX(p) = psi->pAuxDb->db;
 #if SHELL_DYNAMIC_EXTENSION
-      if( DBI(psi)!=0 ) notify_subscribers(psi, NK_DbUserAppeared, DBI(psi));
+      if( DBX(p)!=0 ) notify_subscribers(psi, NK_DbUserAppeared, DBX(p));
 #endif
       psi->pAuxDb->db = 0;
     }
@@ -9639,6 +9667,7 @@ DISPATCHABLE_COMMAND( databases 2 1 0 ){
   open_db(p, 0);
   db = DBX(p);
   rc = sqlite3_prepare_v2(db, "PRAGMA database_list", -1, &pStmt, 0);
+  shell_check_nomem(rc);
   if( rc ){
     *pzErr = smprintf("%s\n", sqlite3_errmsg(db));
     rc = 1;
@@ -14283,9 +14312,11 @@ static DotCmdRC shellEvalText(char *zIn, const char *zName, ShellExState *psx){
   ShellInState *psi = ISS(psx);
   InSource inRedir
     = INSOURCE_STR_REDIR(zIn, zName, psi->pInSource);
+  AnyResourceHolder arh = { &psi->pInSource, (GenericFreer)finish_InSource };
   psi->pInSource = &inRedir;
+  any_ref_holder(&arh);
   rv = process_input(psi);
-  psi->pInSource = inRedir.pFrom;
+  release_holder();
   return rv;
 }
 
@@ -14316,9 +14347,10 @@ 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;
-  if( ISS(p)->bSafeMode ) return DCR_AbortError;
+  if( psi->bSafeMode ) return DCR_AbortError;
   if( azArg[1][0]=='|' ){
 #ifdef SQLITE_OMIT_POPEN
     *pzErr = smprintf("pipes are not supported in this OS\n");
@@ -14341,14 +14373,15 @@ DISPATCHABLE_COMMAND( read 3 2 2 ){
   }
   if( inUse!=0 ){
     InSource inSourceRedir
-      = INSOURCE_FILE_REDIR(inUse, azArg[1], ISS(p)->pInSource);
-    ISS(p)->pInSource = &inSourceRedir;
-    rc = process_input(ISS(p));
+      = INSOURCE_FILE_REDIR(inUse, azArg[1], psi->pInSource);
+    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. */
     if( rc==DCR_Error ) rc = DCR_CmdErred;
-    assert(fCloser!=0);
-    fCloser(inUse);
-    ISS(p)->pInSource = inSourceRedir.pFrom;
+    release_holder();
   }
   return rc;
 }
@@ -15086,7 +15119,9 @@ static DotCmdRC runDotCommand(DotCommand *pmc, char *azArg[], int nArg,
                                ShellExState *psx){
   char *zErr = 0;
   DotCmdRC dcr = pmc->pMethods->argsCheck(pmc, &zErr, nArg, azArg);
+  ResourceMark mark = holder_mark();
 
+  sstr_ptr_holder(&zErr);
   command_prep(ISS(psx));
   if( dcr==DCR_Ok ){
     dcr = pmc->pMethods->execute(pmc, psx, &zErr, nArg, azArg);
@@ -15094,7 +15129,7 @@ static DotCmdRC runDotCommand(DotCommand *pmc, char *azArg[], int nArg,
   if( dcr!=DCR_Ok ){
     dcr = dot_command_errors(zErr, azArg, nArg, dcr, psx);
   }
-  sqlite3_free(zErr);
+  holder_free(mark);
   command_post(ISS(psx));
   return dcr;
 }
@@ -15118,6 +15153,7 @@ static DotCmdRC do_dot_command(char *zLine, ShellExState *psx){
   int ncLineIn = strlen30(zLine);
   u8 bExpVars = SHEXT_VAREXP(ISS(psx));
 #endif
+  RipStackDest dotRipDest = RIP_STACK_DEST_INIT;
 
   /* Parse the input line into tokens which are 0-terminated and left in-place.
   */
@@ -15148,33 +15184,37 @@ static DotCmdRC do_dot_command(char *zLine, ShellExState *psx){
   /* Process the input line. If it was empty, do nothing and declare success.
    * Note that "empty" includes a leading '.' followed by nothing else.
    */
-  if( nArg>0 ){
-    int nFound;
-    DotCommand *pmc = findDotCommand(azArg[0], psx, &nFound);
-    if( pmc==0 || nFound>1 ){
-      if( nFound==0 ){
-        dcr = DCR_Unknown;
+  register_exit_ripper(&dotRipDest);
+  if( 0==RIP_TO_HERE(dotRipDest) ){
+    if( nArg>0 ){
+      int nFound;
+      DotCommand *pmc = findDotCommand(azArg[0], psx, &nFound);
+      if( pmc==0 || nFound>1 ){
+        if( nFound==0 ){
+          dcr = DCR_Unknown;
 #if SHELL_DYNAMIC_EXTENSION
-        pmc = ISS(psx)->pUnknown;
-        if( pmc ) dcr = runDotCommand(pmc, azArg, nArg, psx);
+          pmc = ISS(psx)->pUnknown;
+          if( pmc ) dcr = runDotCommand(pmc, azArg, nArg, psx);
 #endif
-      }else{
-        dcr = DCR_Ambiguous;
+        }else{
+          dcr = DCR_Ambiguous;
+        }
+        if( dcr > DCR_ArgIxMask ){
+          /* Issue error for unknown or inadequately specified dot command. */
+          dcr = dot_command_errors(0, azArg, nArg, dcr, psx);
+        }
       }
-      if( dcr > DCR_ArgIxMask ){
-        /* Issue error for unknown or inadequately specified dot command. */
-        dcr = dot_command_errors(0, azArg, nArg, dcr, psx);
+      else{
+        char *arg0 = azArg[0];
+        azArg[0] = (char *)(pmc->pMethods->name(pmc));
+        /* Run found command and issue or handle any errors it may report. */
+        dcr = runDotCommand(pmc, azArg, nArg, psx);
+        azArg[0] = arg0;
       }
     }
-    else{
-      char *arg0 = azArg[0];
-      azArg[0] = (char *)(pmc->pMethods->name(pmc));
-      /* Run found command and issue or handle any errors it may report. */
-      dcr = runDotCommand(pmc, azArg, nArg, psx);
-      azArg[0] = arg0;
-    }
+  }else{
+    dcr = DCR_Abort|DCR_Error;
   }
-
 #if SHELL_VARIABLE_EXPANSION
   if( bExpVars ){
     /* Free any arguments that are allocated rather than tokenized in place. */
@@ -16016,6 +16056,7 @@ static void process_sqliterc(
   const char *sqliterc = sqliterc_override;
   char *zBuf = 0;
   FILE *inUse;
+  ResourceMark mark = holder_mark();
 
   if( sqliterc == NULL ){
     sqliterc = find_xdg_config();
@@ -16030,27 +16071,31 @@ static void process_sqliterc(
     zBuf = smprintf("%s/.sqliterc",home_dir);
     shell_check_ooms(zBuf);
     sqliterc = zBuf;
+    sstr_holder(zBuf);
   }
   inUse = fopen(sqliterc,"rb");
   if( inUse!=0 ){
     InSource inSourceRedir
       = INSOURCE_FILE_REDIR(inUse, sqliterc, psi->pInSource);
+    AnyResourceHolder arh = { &psi->pInSource, (GenericFreer)finish_InSource };
     DotCmdRC rc;
     psi->pInSource = &inSourceRedir;
+    inSourceRedir.closer.stream = fclose;
     if( stdin_is_interactive ){
       utf8_printf(STD_ERR,"-- Loading resources from %s\n",sqliterc);
     }
+    any_ref_holder(&arh);
     rc = process_input(psi);
-    fclose(inUse);
-    psi->pInSource = inSourceRedir.pFrom;
     if( rc>DCR_Ok && bail_on_error ){
       XSS(psi)->shellAbruptExit = 0x100|((rc>>1)&0x3);
     }
   }else if( sqliterc_override!=0 ){
     utf8_printf(STD_ERR,"cannot open: \"%s\"\n", sqliterc);
-    if( bail_on_error ) exit(1); /* Future: Exit shell rather than process. */
+    if( bail_on_error ){
+      XSS(psi)->shellAbruptExit = 0x102;
+    }
   }
-  sqlite3_free(zBuf);
+  holder_free(mark);
 }
 
 /*