]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
WIP, CLI resource manager worked in, barely used yet. (Leaking one object.)
authorlarrybr <larrybr@noemail.net>
Thu, 11 May 2023 03:11:20 +0000 (03:11 +0000)
committerlarrybr <larrybr@noemail.net>
Thu, 11 May 2023 03:11:20 +0000 (03:11 +0000)
FossilOrigin-Name: dbd00bcaa081ec73c2cb6eef0be5f82c890c8293aa90876942bc049e71f3c83a

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

index 26aba178579386e01246fb37b86d5da1f2b2ba17..6405bbca9c42205a033c3ec4a1d5d8f8b0b26654 100644 (file)
@@ -1162,6 +1162,8 @@ SHELL_SRC = \
   $(TOP)/ext/misc/memtrace.c \
   $(TOP)/src/shext_linkage.h \
   $(TOP)/src/obj_interfaces.h \
+  $(TOP)/src/resmanage.h \
+  $(TOP)/src/resmanage.c \
   $(TOP)/ext/recover/dbdata.c \
   $(TOP)/ext/misc/zipfile.c \
   $(TOP)/src/test_windirent.c
index 4095eff2c8395fa89d6a0b255fc1906400ab625d..91e77e814536646c5332a0285780a9669a2bffe2 100644 (file)
@@ -2244,25 +2244,27 @@ keywordhash.h:  $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
 
 # Source files that go into making shell.c
 SHELL_SRC = \
-       $(TOP)\src\shell.c.in \
-       $(TOP)\ext\misc\appendvfs.c \
-       $(TOP)\ext\misc\completion.c \
-        $(TOP)\ext\misc\base64.c \
-        $(TOP)\ext\misc\base85.c \
-       $(TOP)\ext\misc\decimal.c \
-       $(TOP)\ext\misc\fileio.c \
-       $(TOP)\ext\misc\ieee754.c \
-       $(TOP)\ext\misc\regexp.c \
-       $(TOP)\ext\misc\series.c \
-       $(TOP)\ext\misc\shathree.c \
-       $(TOP)\ext\misc\uint.c \
-       $(TOP)\ext\expert\sqlite3expert.c \
-       $(TOP)\ext\expert\sqlite3expert.h \
-       $(TOP)\ext\misc\memtrace.c \
-       $(TOP)/ext/recover/dbdata.c \
-       $(TOP)/ext/recover/sqlite3recover.c \
-       $(TOP)/ext/recover/sqlite3recover.h \
-       $(TOP)\src\test_windirent.c
+  $(TOP)\src\shell.c.in \
+  $(TOP)\ext\misc\appendvfs.c \
+  $(TOP)\ext\misc\completion.c \
+  $(TOP)\ext\misc\base64.c \
+  $(TOP)\ext\misc\base85.c \
+  $(TOP)\ext\misc\decimal.c \
+  $(TOP)\ext\misc\fileio.c \
+  $(TOP)\ext\misc\ieee754.c \
+  $(TOP)\ext\misc\regexp.c \
+  $(TOP)\ext\misc\series.c \
+  $(TOP)\ext\misc\shathree.c \
+  $(TOP)\ext\misc\uint.c \
+  $(TOP)\ext\expert\sqlite3expert.c \
+  $(TOP)\ext\expert\sqlite3expert.h \
+  $(TOP)\ext\misc\memtrace.c \
+  $(TOP)/ext/recover/dbdata.c \
+  $(TOP)/ext/recover/sqlite3recover.c \
+  $(TOP)/ext/recover/sqlite3recover.h \
+  $(TOP)\src\resmanage.h \
+  $(TOP)\src\resmanage.c \
+  $(TOP)\src\test_windirent.c
 
 # If use of zlib is enabled, add the "zipfile.c" source file.
 #
index 75d0dd1b33cafcc9333bd0d57317adf55091462f..2f1e3eae9b2f15741c2a8f6a679d712b4078d336 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,11 +1,11 @@
-C Begin\suse\sof\sa\sresource\smanager\sfor\sCLI.
-D 2023-05-10T09:51:51.040
+C WIP,\sCLI\sresource\smanager\sworked\sin,\sbarely\sused\syet.\s(Leaking\sone\sobject.)
+D 2023-05-11T03:11:20.248
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
-F Makefile.in 8e5d01f0df1584356ddb25fbdd3eb03d98a81f1c9f295a6943f8648464bc80f4
+F Makefile.in d97bb86f0fbd38e2b71b3bd8089158c89d6b03ef575b72183348c0d7322c90b7
 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
-F Makefile.msc 997ff8f34c6e8ec157d2b61e73cf4e1d2d5bcb9ffc6f9af068f4fd4deef0eebf
+F Makefile.msc ecdd1e9fd753ecc02cd52f0d256a1bd86323be6eab6a3b2a196005d54edea3d2
 F README.md e05bd8fcb45da04ab045c37f79a98654e8aa3b3b8f302cfbba80a0d510df75f7
 F VERSION 17f95ae2fdf21f0e9575eb0b0511ea63f15d71dfff431b21c2b4adbfa70cfbbf
 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@@ -637,12 +637,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 1af14f7ee7a1ae77f1cd21214661912d6cbd786e33e9c303e2aa272d6fd86de0
-F src/resmanage.h 63778a96198495727b40579291341fbe2a631bf8082e5be906f3a6b1e6b89f2f
+F src/resmanage.c 242d629812d391121f3a4f801f2802b17c89227985798a58f8ad1bdb25a8b356
+F src/resmanage.h 41e38ab8308611055582b031fdcbd179344025fce2f9814e3061b79f53efc34d
 F src/resolve.c 3e53e02ce87c9582bd7e7d22f13f4094a271678d9dc72820fa257a2abb5e4032
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c f9333ef8181192c22662f5cb8d257efc4a2880f9ee4853c6c4616f783d27e1b5
-F src/shell.c.in e84be25a7f2a4a9bfc3fb0755000cc35726e09e81c88af28a1a380eec5c56228
+F src/shell.c.in 154be768b7c1f1ba3de689f7567386647931e36030c50608d35e7e52d5d786c0
 F src/shext_linkage.h 27dcf7624df05b2a7a6d367834339a6db3636f3035157f641f7db2ec499f8f6d
 F src/sqlite.h.in 27ca1d4b2eda8feee468af5735182390e8fe4696522751eec0136d17323201ad
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
@@ -2079,8 +2079,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 05ecd8a59ec576d4afe7f8785bdcc052c0887b7a5933783a832895b8c40cdeb9
-R 87fb9edd1be1a9bd6d2d5e412cb26bac
+P 1527d429d66505d4f4bb8635c4d9d7ab926037b136554605d67835ee5d19d2de
+R 80825c1180825bb4f4ec0bb8962d01e6
 U larrybr
-Z 1047f910d7d5ef2b2fd5d09f9bd5a949
+Z a4f9d6999be83f832b2f293999c7ee60
 # Remove this line to create a well-formed Fossil manifest.
index fa3275e3f6850428afaa8c2eb5ae96361c90c08c..16c1536ecaccbe5f7274ba224e7424b19145fa40 100644 (file)
@@ -1 +1 @@
-1527d429d66505d4f4bb8635c4d9d7ab926037b136554605d67835ee5d19d2de
\ No newline at end of file
+dbd00bcaa081ec73c2cb6eef0be5f82c890c8293aa90876942bc049e71f3c83a
\ No newline at end of file
index f9aeda332795db25dfbb46c32c76ef73ae648454..99b30a0253dedf282d9fbd79313eac8dc37de6f3 100644 (file)
@@ -54,30 +54,32 @@ typedef struct ResourceHeld {
 
 /* The held-resource stack. This is for single-threaded use only. */
 static ResourceHeld *pResHold = 0;
-static unsigned short numResHold = 0;
-static unsigned short numResAlloc = 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 unsigned short numCustom = 0; /* number of the set */
-static unsigned short numCustomAlloc = 0; /* allocated space */
+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 */
 static ResourceMark exit_mark = 0;
 #ifndef SHELL_OMIT_LONGJMP
 static jmp_buf *p_exit_ripper = 0;
 #endif
 
-/* Implementation per header comment */
+/* Current position of the held-resource stack */
 ResourceMark holder_mark(){
   return numResHold;
 }
 
-/* Implementation per header comment */
+/* Strip resource stack then strip call stack (or exit.) */
 void quit_moan(const char *zMoan, int errCode){
   if( zMoan ){
     fprintf(stderr, "Quitting due to %s, freeing %d resources.\n",
@@ -129,58 +131,82 @@ static void free_rk( ResourceHeld *pRH ){
   pRH->held.p_any = 0;
 }
 
-void* take_held(ResourceMark mark, unsigned short offset){
-  unsigned short rix = mark + offset;
+/* Take back a held resource pointer, leaving held as NULL. (no-op) */
+void* take_held(ResourceMark mark, ResourceCount offset){
+  return swap_held(mark,offset,0);
+}
+
+/* Swap a held resource pointer for a new one. */
+void* swap_held(ResourceMark mark, ResourceCount offset, void *pNew){
+  ResourceCount rix = mark + offset;
+  assert(rix < numResHold && numResHold > 0);
   if( rix < numResHold && numResHold > 0 ){
     void *rv = pResHold[rix].held.p_any;
-    pResHold[rix].held.p_any = 0;
+    pResHold[rix].held.p_any = pNew;
     return rv;
   }else return 0;
 }
 
+/* Reserve room for at least count additional holders, with no chance
+** of an OOM abrupt exit. This is used internally only by this package.
+** The return is either 1 for success, or 0 indicating an OOM failure.
+ */
+static int more_holders_try(ResourceCount count){
+  ResourceHeld *prh;
+  ResourceCount newAlloc = numResHold + count;
+  if( newAlloc < numResHold ) return 0; /* Overflow, sorry. */
+  if( newAlloc <= numResAlloc ) return 1;
+  prh = (ResourceHeld*)realloc(pResHold, newAlloc*sizeof(ResourceHeld));
+  if( prh == 0 ) return 0;
+  pResHold = prh;
+  numResAlloc = newAlloc;
+  return 1;
+}
+
 /* Shared resource-stack pushing code */
 static void res_hold(void *pv, FreeableResourceKind frk){
   ResourceHeld rh = { pv, frk };
   if( numResHold == numResAlloc ){
-    size_t nrn = numResAlloc + (numResAlloc>>2) + 5;
-    ResourceHeld *prh;
-    prh = (ResourceHeld*)realloc(pResHold, nrn*sizeof(ResourceHeld));
-    if( prh!=0 ){
-      pResHold = prh;
-      numResAlloc = nrn;
-    }else{
-      quit_moan("Out of memory",1);
+    ResourceCount nrn = (ResourceCount)((numResAlloc>>2) + 5);
+    if( !more_holders_try(nrn) ){
+      free_rk(&rh);
+      quit_moan(resmanage_oom_message,1);
     }
   }
   pResHold[numResHold++] = rh;
 }
 
-/* Implementation per header comment */
+/* Hold a NULL (Assure swap_held() cannot fail.) */
+void more_holders(ResourceCount more){
+  if( !more_holders_try(more) ) quit_moan(resmanage_oom_message,1);
+}
+
+/* Hold anything in the malloc() heap */
 void* mmem_holder(void *pm){
   res_hold(pm, FRK_Malloc);
   return pm;
 }
-/* Implementation per header comment */
+/* Hold a C string in the malloc() heap */
 char* mstr_holder(char *z){
   if( z!=0 ) res_hold(z, FRK_Malloc);
   return z;
 }
-/* Implementation per header comment */
+/* Hold a C string in the SQLite heap */
 char* sstr_holder(char *z){
   if( z!=0 ) res_hold(z, FRK_DbMem);
   return z;
 }
-/* Implementation per header comment */
+/* Hold an open C runtime FILE */
 void file_holder(FILE *pf){
   if( pf!=0 ) res_hold(pf, FRK_File);
 }
 #if (!defined(_WIN32) && !defined(WIN32)) || !SQLITE_OS_WINRT
-/* Implementation per header comment */
+/* Hold an open C runtime pipe */
 void pipe_holder(FILE *pp){
   if( pp!=0 ) res_hold(pp, FRK_Pipe);
 }
 #endif
-/* Implementation per header comment */
+/* Hold anything together with arbitrary freeing function */
 void* any_holder(void *pm, void (*its_freer)(void*)){
   int i = 0;
   while( i < numCustom ){
@@ -196,27 +222,27 @@ void* any_holder(void *pm, void (*its_freer)(void*)){
       aCustomFreers = pcf;
       aCustomFreers[numCustom++] = its_freer;
     }else{
-      quit_moan("Out of memory",1);
+      quit_moan(resmanage_oom_message,1);
     }
   }
   res_hold(pm, i + FRK_CustomBase);
   return pm;
 }
-/* Implementation per header comment */
+/* Hold some SQLite-allocated memory */
 void* smem_holder(void *pm){
   res_hold(pm, FRK_DbMem);
   return pm;
 }
-/* Implementation per header comment */
+/* Hold a SQLite database "connection" */
 void conn_holder(sqlite3 *pdb){
   res_hold(pdb, FRK_DbConn);
 }
-/* Implementation per header comment */
+/* Hold a SQLite prepared statement */
 void stmt_holder(sqlite3_stmt *pstmt){
   res_hold(pstmt, FRK_DbStmt);
 }
 
-/* Implementation per header comment */
+/* Free all held resources in excess of given resource stack mark. */
 void holder_free(ResourceMark mark){
   while( numResHold > mark ){
     free_rk(&pResHold[--numResHold]);
@@ -237,12 +263,12 @@ void holder_free(ResourceMark mark){
 }
 
 #ifndef SHELL_OMIT_LONGJMP
-/* Implementation per header comment */
+/* Record a resource stack and call stack rip-to position */
 void register_exit_ripper(jmp_buf *pjb, ResourceMark rip_mark){
   exit_mark = rip_mark;
   p_exit_ripper = pjb;
 }
-/* Implementation per header comment */
+/* Undo register_exit_ripper effect, back to default state. */
 void forget_exit_ripper(jmp_buf *pjb){
   exit_mark = 0;
   assert(p_exit_ripper == pjb);
index a43bee0b972999b3d0a00c956e69e200601ac018..24b3dca803ddef68751639b34ef577928187d2f2 100644 (file)
@@ -46,6 +46,7 @@
 # define RIP_TO_HERE(jb) 0
 #endif
 #include <stdio.h>
+#include <assert.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -55,10 +56,15 @@ extern "C" {
 
 /* Type used for marking positions within a held-resource stack */
 typedef unsigned short ResourceMark;
+typedef unsigned short ResourceCount;
 
 /* Current position of the held-resource stack */
 extern ResourceMark holder_mark();
 
+/* Assure no allocation failure for some more xxx_holder() calls.
+** Note that this call may fail with an OOM abrupt exit. */
+extern void more_holders(ResourceCount more);
+
 /* Routines for holding resources on held-resource stack together
 ** with enough information for them to be freed by this package.
 */
@@ -84,7 +90,10 @@ extern void pipe_holder(FILE *);
 #endif
 
 /* Take back a held resource pointer, leaving held as NULL. (no-op) */
-extern void* take_held(ResourceMark mark, unsigned short offset);
+extern void* take_held(ResourceMark mark, ResourceCount offset);
+
+/* Swap a held resource pointer for a new one. */
+extern void* swap_held(ResourceMark mark, ResourceCount offset, void *pNew);
 
 /* Free all held resources in excess of given resource stack mark. */
 extern void holder_free(ResourceMark mark);
@@ -109,6 +118,9 @@ extern void forget_exit_ripper(jmp_buf *pjb);
 */
 extern void quit_moan(const char *zMoan, int errCode);
 
+/* What the complaint will be for OOM failures and abrupt exits. */
+extern const char *resmanage_oom_message;
+
 #ifdef __cplusplus
 }  /* End of the 'extern "C"' block */
 #endif
index d33a5a5d36730017f24f133cfbfeab96675bc122..0ead7dbf328c35d73f5d2bc6ac67ee2886f7ea3e 100644 (file)
@@ -242,6 +242,9 @@ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText);
 /* Get the shell extension interfaces and structs. */
 INCLUDE shext_linkage.h
 
+/* Get resource management package, supporting OOM and safe mode exits. */
+INCLUDE resmanage.c
+
 /* For an embedded shell, allow the 3 standard streams to be specified.
 ** If used, these names will have to refer to something globally reachable
 ** from the same thread which called the shell's main().
@@ -502,7 +505,7 @@ static sqlite3 *globalDb = 0;
 static sqlite3_mutex *pGlobalDbLock = 0;
 
 /*
-** True if an interrupt (Control-C) has been received.
+** Greater than 0 if an interrupt (Control-C) has been received.
 */
 static volatile int seenInterrupt = 0;
 
@@ -1905,6 +1908,8 @@ static void updateSafeMode(ShellInState *psi){
 #define ShellHasFlag(psx,X)    (((psx)->pSIS->shellFlgs & (X))!=0)
 #define ShellSetFlag(psx,X)    ((psx)->pSIS->shellFlgs|=(X))
 #define ShellClearFlag(psx,X)  ((psx)->pSIS->shellFlgs&=(~(X)))
+#define ShellSetFlagI(psi,X)    ((psi)->shellFlgs|=(X))
+#define ShellClearFlagI(psi,X)  ((psi)->shellFlgs&=(~(X)))
 
 /*
 ** These are the allowed modes, in search order (for abbreviation matches.)
@@ -15886,7 +15891,8 @@ static void verify_uninitialized(void){
 }
 
 /*
-** Initialize the state information in data and datax
+** Initialize the state information in data and datax.
+** Does no heap allocation.
 */
   static void main_init(ShellInState *pData, ShellExState *pDatax) {
   memset(pData, 0, sizeof(*pData));
@@ -15950,7 +15956,7 @@ static char *cmdline_option_value(int argc, char **argv, int i){
   if( i==argc ){
     utf8_printf(STD_ERR, "%s: Error: missing argument to %s\n",
             argv[0], argv[argc-1]);
-    exit(1);
+    quit_moan("invocation error", 1);
   }
   return argv[i];
 }
@@ -15959,6 +15965,373 @@ static void sayAbnormalExit(void){
   if( seenInterrupt ) fprintf(stderr, "Program interrupted.\n");
 }
 
+/* A vector of command strings collected from . */
+typedef struct CmdArgs {
+  /* Array is malloc'ed, but not its elements except by wmain()'s futzing. */
+  char **azCmd;  /* the strings */
+  int nCmd;      /* how many collected */
+  ResourceMark buffMark; /* where to grab held array pointer */
+} CmdArgs;
+/* Data collected during args scanning. */
+typedef struct ArgsData {
+  int readStdin;         /* whether stdin will be read */
+  int nOptsEnd;          /* where -- seen, else argc */
+  const char *zInitFile; /* specified init file */
+  const char *zVfs;      /* -vfs command-line option */
+  short bQuiet;          /* -quiet option */
+} ArgsData;
+/*
+** Perform CLI invocation argument processing.
+** This code is collected here for convenience, to declutter main()
+** and to make this processing a little simpler to understand.
+*/
+static int scanInvokeArgs(int argc, char **argv, int pass, ShellInState *psi,
+                          CmdArgs *pca, ArgsData *pad){
+  int rc = 0;
+  DotCmdRC drc;
+  int i;
+  if( pass==1 ){
+    for(i=1; i<argc && rc<2; i++){
+      char *z = argv[i];
+      if( z[0]!='-' || i>pad->nOptsEnd ){
+        if( psi->aAuxDb->zDbFilename==0 ){
+          psi->aAuxDb->zDbFilename = z;
+        }else{
+          void *vaz;
+          if( pca->nCmd == 0 ){
+            pca->buffMark = holder_mark();
+            more_holders(1);
+          }
+          vaz = realloc(pca->azCmd, sizeof(pca->azCmd[0])*(pca->nCmd+1));
+          shell_check_oom(vaz);
+          if( pca->nCmd == 0 ) mmem_holder(vaz);
+          else swap_held(pca->buffMark, 0, vaz);
+          pca->azCmd = (char**)vaz;
+          pca->azCmd[pca->nCmd++] = z;
+          /* Excesss arguments are interpreted as SQL (or dot-commands)
+          ** and mean that nothing is to be read from stdin. */
+          pad->readStdin = 0;
+        }
+        continue;
+      }
+      if( z[1]=='-' ) z++;
+      if( cli_strcmp(z, "-")==0 ){
+        pad->nOptsEnd = i;
+        continue;
+      }else if( cli_strcmp(z,"-separator")==0
+       || cli_strcmp(z,"-nullvalue")==0
+       || cli_strcmp(z,"-newline")==0
+       || cli_strcmp(z,"-cmd")==0
+      ){
+        (void)cmdline_option_value(argc, argv, ++i);
+        /* Will pickup value on next pass. */
+      }else if( cli_strcmp(z,"-init")==0 ){
+        pad->zInitFile = cmdline_option_value(argc, argv, ++i);
+      }else if( cli_strcmp(z,"-batch")==0 ){
+        /* Need to check for batch mode here to so we can avoid printing
+        ** informational messages (like from process_sqliterc) before
+        ** we do the actual processing of arguments later in a second pass.
+        */
+        stdin_is_interactive = 0;
+      }else if( cli_strcmp(z,"-heap")==0 ){
+  #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
+        const char *zSize;
+        sqlite3_int64 szHeap;
+
+        zSize = cmdline_option_value(argc, argv, ++i);
+        szHeap = integerValue(zSize);
+        if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000;
+        verify_uninitialized();
+        sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
+  #else
+        (void)cmdline_option_value(argc, argv, ++i);
+  #endif
+      }else if( cli_strcmp(z,"-pagecache")==0 ){
+        sqlite3_int64 n, sz;
+        void *pvCache = 0;
+        sz = integerValue(cmdline_option_value(argc,argv,++i));
+        if( sz>70000 ) sz = 70000;
+        if( sz<0 ) sz = 0;
+        n = integerValue(cmdline_option_value(argc,argv,++i));
+        if( sz>0 && n>0 && 0xffffffffffffLL/sz<n ){
+          n = 0xffffffffffffLL/sz;
+        }
+        verify_uninitialized();
+        if( n>0 && sz>0 ) pvCache = malloc(n*sz);
+        shell_check_oom(pvCache);
+        sqlite3_config(SQLITE_CONFIG_PAGECACHE, pvCache, sz, n);
+        psi->shellFlgs |= SHFLG_Pagecache;
+      }else if( cli_strcmp(z,"-lookaside")==0 ){
+        int n, sz;
+        sz = (int)integerValue(cmdline_option_value(argc,argv,++i));
+        if( sz<0 ) sz = 0;
+        n = (int)integerValue(cmdline_option_value(argc,argv,++i));
+        if( n<0 ) n = 0;
+        verify_uninitialized();
+        sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
+        if( sz*n==0 ) psi->shellFlgs &= ~SHFLG_Lookaside;
+      }else if( cli_strcmp(z,"-threadsafe")==0 ){
+        int n;
+        n = (int)integerValue(cmdline_option_value(argc,argv,++i));
+        verify_uninitialized();
+        switch( n ){
+           case 0:  sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);  break;
+           case 2:  sqlite3_config(SQLITE_CONFIG_MULTITHREAD);   break;
+           default: sqlite3_config(SQLITE_CONFIG_SERIALIZED);    break;
+        }
+  #ifdef SQLITE_ENABLE_VFSTRACE
+      }else if( cli_strcmp(z,"-vfstrace")==0 ){
+        extern int vfstrace_register(
+           const char *zTraceName,
+           const char *zOldVfsName,
+           int (*xOut)(const char*,void*),
+           void *pOutArg,
+           int makeDefault
+        );
+        vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,STD_ERR,1);
+  #endif
+  #ifdef SQLITE_ENABLE_MULTIPLEX
+      }else if( cli_strcmp(z,"-multiplex")==0 ){
+        extern int sqlite3_multiple_initialize(const char*,int);
+        sqlite3_multiplex_initialize(0, 1);
+  #endif
+      }else if( cli_strcmp(z,"-mmap")==0 ){
+        sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
+        verify_uninitialized();
+        sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz);
+  #ifdef SQLITE_ENABLE_SORTER_REFERENCES
+      }else if( cli_strcmp(z,"-sorterref")==0 ){
+        sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
+        verify_uninitialized();
+        sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, (int)sz);
+  #endif
+      }else if( cli_strcmp(z,"-vfs")==0 ){
+        pad->zVfs = cmdline_option_value(argc, argv, ++i);
+  #ifdef SQLITE_HAVE_ZLIB
+      }else if( cli_strcmp(z,"-zip")==0 ){
+        psi->openMode = SHELL_OPEN_ZIPFILE;
+  #endif
+      }else if( cli_strcmp(z,"-append")==0 ){
+        psi->openMode = SHELL_OPEN_APPENDVFS;
+  #ifndef SQLITE_OMIT_DESERIALIZE
+      }else if( cli_strcmp(z,"-deserialize")==0 ){
+        psi->openMode = SHELL_OPEN_DESERIALIZE;
+      }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
+        psi->szMax = integerValue(argv[++i]);
+  #endif
+      }else if( cli_strcmp(z,"-readonly")==0 ){
+        psi->openMode = SHELL_OPEN_READONLY;
+      }else if( cli_strcmp(z,"-nofollow")==0 ){
+        psi->openFlags = SQLITE_OPEN_NOFOLLOW;
+  #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
+      }else if( cli_strncmp(z, "-A",2)==0 ){
+        /* All remaining command-line arguments are passed to the ".archive"
+        ** command, so ignore them */
+        break;
+  #endif
+      }else if( cli_strcmp(z, "-memtrace")==0 ){
+        sqlite3MemTraceActivate(STD_ERR);
+      }else if( cli_strcmp(z,"-bail")==0 ){
+        bail_on_error = 1;
+  #if SHELL_EXTENSIONS
+      }else if( cli_strcmp(z,"-shxopts")==0 ){
+        psi->bExtendedDotCmds = (u8)integerValue(argv[++i]);
+  #endif
+      }else if( cli_strcmp(z,"-nonce")==0 ){
+        free(psi->zNonce);
+        psi->zNonce = strdup(argv[++i]);
+        shell_check_oom(psi->zNonce);
+      }else if( cli_strcmp(z,"-quiet")==0 ){
+        pad->bQuiet = (int)integerValue(cmdline_option_value(argc,argv,++i));
+      }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
+        psi->shellFlgs |= SHFLG_TestingMode;
+      }else if( cli_strcmp(z,"-safe")==0 ){
+        /* catch this on the second pass (Unsafe is fine on invocation.) */
+      }
+    }
+  }else if( pass==2 ){
+    for(i=1; i<argc && rc<2; i++){
+      char *z = argv[i];
+      char *zModeSet = 0;
+      if( z[0]!='-' || i>=pad->nOptsEnd ) continue;
+      if( z[1]=='-' ){ z++; }
+      if( cli_strcmp(z,"-init")==0 ){
+        i++;
+      }else if( cli_strcmp(z,"-html")==0 ){
+        zModeSet = z;
+      }else if( cli_strcmp(z,"-list")==0 ){
+        zModeSet = z;
+      }else if( cli_strcmp(z,"-quote")==0 ){
+        zModeSet = z;
+      }else if( cli_strcmp(z,"-line")==0 ){
+        zModeSet = z;
+      }else if( cli_strcmp(z,"-column")==0 ){
+        zModeSet = z;
+      }else if( cli_strcmp(z,"-json")==0 ){
+        zModeSet = z;
+      }else if( cli_strcmp(z,"-markdown")==0 ){
+        zModeSet = z;
+      }else if( cli_strcmp(z,"-table")==0 ){
+        zModeSet = z;
+      }else if( cli_strcmp(z,"-box")==0 ){
+        zModeSet = z;
+      }else if( cli_strcmp(z,"-csv")==0 ){
+        zModeSet = z;
+      }else if( cli_strcmp(z,"-ascii")==0 ){
+        zModeSet = z;
+      }else if( cli_strcmp(z,"-tabs")==0 ){
+        zModeSet = z;
+  #ifdef SQLITE_HAVE_ZLIB
+      }else if( cli_strcmp(z,"-zip")==0 ){
+        psi->openMode = SHELL_OPEN_ZIPFILE;
+  #endif
+      }else if( cli_strcmp(z,"-append")==0 ){
+        psi->openMode = SHELL_OPEN_APPENDVFS;
+  #ifndef SQLITE_OMIT_DESERIALIZE
+      }else if( cli_strcmp(z,"-deserialize")==0 ){
+        psi->openMode = SHELL_OPEN_DESERIALIZE;
+      }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
+        psi->szMax = integerValue(argv[++i]);
+  #endif
+      }else if( cli_strcmp(z,"-readonly")==0 ){
+        psi->openMode = SHELL_OPEN_READONLY;
+      }else if( cli_strcmp(z,"-nofollow")==0 ){
+        psi->openFlags |= SQLITE_OPEN_NOFOLLOW;
+      }else if( cli_strcmp(z,"-separator")==0 ){
+        sqlite3_snprintf(sizeof(psi->colSeparator), psi->colSeparator,
+                         "%s",cmdline_option_value(argc,argv,++i));
+      }else if( cli_strcmp(z,"-newline")==0 ){
+        sqlite3_snprintf(sizeof(psi->rowSeparator), psi->rowSeparator,
+                         "%s",cmdline_option_value(argc,argv,++i));
+      }else if( cli_strcmp(z,"-nullvalue")==0 ){
+        sqlite3_snprintf(sizeof(psi->nullValue), psi->nullValue,
+                         "%s",cmdline_option_value(argc,argv,++i));
+      }else if( cli_strcmp(z,"-header")==0 ){
+        psi->showHeader = 1;
+        ShellSetFlagI(psi, SHFLG_HeaderSet);
+      }else if( cli_strcmp(z,"-noheader")==0 ){
+        psi->showHeader = 0;
+        ShellSetFlagI(psi, SHFLG_HeaderSet);
+      }else if( cli_strcmp(z,"-echo")==0 ){
+        ShellSetFlagI(psi, SHFLG_Echo);
+      }else if( cli_strcmp(z,"-eqp")==0 ){
+        psi->autoEQP = AUTOEQP_on;
+      }else if( cli_strcmp(z,"-eqpfull")==0 ){
+        psi->autoEQP = AUTOEQP_full;
+      }else if( cli_strcmp(z,"-stats")==0 ){
+        psi->statsOn = 1;
+      }else if( cli_strcmp(z,"-scanstats")==0 ){
+        psi->scanstatsOn = 1;
+      }else if( cli_strcmp(z,"-backslash")==0 ){
+        /* Undocumented command-line option: -backslash
+        ** Causes C-style backslash escapes to be evaluated in SQL statements
+        ** prior to sending the SQL into SQLite.  Useful for injecting
+        ** crazy bytes in the middle of SQL statements for testing and debugging.
+        */
+        ShellSetFlagI(psi, SHFLG_Backslash);
+      }else if( cli_strcmp(z,"-bail")==0 ){
+        /* No-op.  The bail_on_error flag should already be set. */
+  #if SHELL_EXTENSIONS
+      }else if( cli_strcmp(z,"-shxopts")==0 ){
+        i++; /* Handled on first pass. */
+  #endif
+      }else if( cli_strcmp(z,"-version")==0 ){
+        fprintf(STD_OUT, "%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
+        rc = 2;
+      }else if( cli_strcmp(z,"-interactive")==0 ){
+        stdin_is_interactive = 1;
+      }else if( cli_strcmp(z,"-batch")==0 ){
+        stdin_is_interactive = 0;
+      }else if( cli_strcmp(z,"-utf8")==0 ){
+  #if SHELL_WIN_UTF8_OPT
+        console_utf8 = 1;
+  #endif /* SHELL_WIN_UTF8_OPT */
+      }else if( cli_strcmp(z,"-heap")==0 ){
+        i++;
+      }else if( cli_strcmp(z,"-pagecache")==0 ){
+        i+=2;
+      }else if( cli_strcmp(z,"-lookaside")==0 ){
+        i+=2;
+      }else if( cli_strcmp(z,"-threadsafe")==0 ){
+        i+=2;
+      }else if( cli_strcmp(z,"-nonce")==0 ){
+        i+=2;
+      }else if( cli_strcmp(z,"-mmap")==0 ){
+        i++;
+      }else if( cli_strcmp(z,"-memtrace")==0 ){
+        i++;
+  #ifdef SQLITE_ENABLE_SORTER_REFERENCES
+      }else if( cli_strcmp(z,"-sorterref")==0 ){
+        i++;
+  #endif
+      }else if( cli_strcmp(z,"-vfs")==0 ){
+        i++;
+  #ifdef SQLITE_ENABLE_VFSTRACE
+      }else if( cli_strcmp(z,"-vfstrace")==0 ){
+        i++;
+  #endif
+  #ifdef SQLITE_ENABLE_MULTIPLEX
+      }else if( cli_strcmp(z,"-multiplex")==0 ){
+        i++;
+  #endif
+      }else if( cli_strcmp(z,"-help")==0 ){
+        usage(1);
+      }else if( cli_strcmp(z,"-cmd")==0 ){
+        /* Run commands that follow -cmd first and separately from commands
+        ** that simply appear on the command-line.  This seems goofy.  It would
+        ** be better if all commands ran in the order that they appear.  But
+        ** we retain the goofy behavior for historical compatibility. */
+        if( i==argc-1 ) break; /* Pretend specified command is empty. */
+        z = cmdline_option_value(argc,argv,++i);
+        if( z[0]=='.' ){
+          drc = do_dot_command(z, psi->pSXS);
+          rc = (drc>2)? 2 : drc;
+        }else{
+          rc = run_single_query(psi->pSXS, z);
+        }
+        if( rc && bail_on_error ){
+          break;
+        }
+  #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
+      }else if( cli_strncmp(z, "-A", 2)==0 ){
+        if( pca->nCmd>0 ){
+          utf8_printf(STD_ERR, "Error: cannot mix regular SQL or dot-commands"
+                              " with \"%s\"\n", z);
+          rc = 1;
+          break;
+        }
+        open_db(XSS(psi), OPEN_DB_ZIPFILE);
+        if( z[2] ){
+          argv[i] = &z[2];
+          drc = arDotCommand(XSS(psi), 1, argv+(i-1), argc-(i-1));
+        }else{
+          drc = arDotCommand(XSS(psi), 1, argv+i, argc-i);
+        }
+        rc = (drc>2)? 2 : drc;
+        pad->readStdin = 0;
+        break;
+  #endif
+      }else if( cli_strcmp(z,"-safe")==0 ){
+        psi->bSafeMode = psi->bSafeModeFuture = 1;
+      }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
+        /* Acted upon in first pass. */
+      }else if( cli_strcmp(z,"-quiet")==0 ){
+        ++i;
+      }else{
+        utf8_printf(STD_ERR,"%s: Error: unknown option: %s\n", Argv0, z);
+        raw_printf(STD_ERR,"Use -help for a list of options.\n");
+        rc = 2;
+      }
+      if( zModeSet!=0 ){
+        char *azModeCmd[] = { ".mode", zModeSet+1 };
+        modeCommand(azModeCmd, 2, XSS(psi), 0);
+        psi->cMode = psi->mode;
+      }
+    }
+  }
+  return rc;
+}
+
 #ifndef SQLITE_SHELL_IS_UTF8
 #  if (defined(_WIN32) || defined(WIN32)) \
    && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
@@ -16000,21 +16373,23 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){
   BuiltInFFExporter ffExporter = BI_FF_EXPORTER_INIT( &data );
   BuiltInCMExporter cmExporter = BI_CM_EXPORTER_INIT( &data );
 #endif
+  RIP_STATE(exit_jb);
   const char *zInitFile = 0;
   int bQuiet = 0; /* for testing, to suppress banner and history actions */
   int i, aec;
   int rc = 0;
   DotCmdRC drc = DCR_Ok;
   int warnInmemoryDb = 0;
-  int readStdin = 1;
-  int nCmd = 0;
+  /* azCmd, nCmd, buffMark */
+  CmdArgs cmdArgs = {0,0,0};
+  /* readStdin, nOptsEnd, zInitFile, zVfs, bQuiet */
+  ArgsData argsData = { 1, argc, 0,0,0 };
   int nOptsEnd = argc;
-  char **azCmd = 0;
-  const char *zVfs = 0;           /* Value of -vfs command-line option */
 #if !SQLITE_SHELL_IS_UTF8
   char **argvToFree = 0;
   int argcToFree = 0;
 #endif
+  ResourceMark entry_mark = holder_mark();
 
   setvbuf(STD_ERR, 0, _IONBF, 0); /* Make sure stderr is unbuffered */
 #ifdef SQLITE_SHELL_FIDDLE
@@ -16076,541 +16451,235 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){
   data.pActiveExporter = (ExportHandler*)&ffExporter;
 #endif
 
-  /* On Windows, we must translate command-line arguments into UTF-8.
-  ** The SQLite memory allocator subsystem has to be enabled in order to
-  ** do this.  But we want to run an sqlite3_shutdown() afterwards so that
-  ** subsequent sqlite3_config() calls will work.  So copy all results into
-  ** memory that does not come from the SQLite memory allocator.
+  /* From here on, within the true clause of this next test, various
+  ** heap allocations are made which may fail, resulting in an abrupt
+  ** shell exit. Such an exit happens in 1 of 2 ways: A held resource
+  ** stack and the call stack are ripped back to this point; or just
+  ** the held resource stack is ripped back and a process exit occurs.
   */
-#if !SQLITE_SHELL_IS_UTF8
-  sqlite3_initialize();
-  argvToFree = malloc(sizeof(argv[0])*argc*2);
-  shell_check_oom(argvToFree);
-  argcToFree = argc;
-  argv = argvToFree + argc;
-  for(i=0; i<argc; i++){
-    char *z = sqlite3_win32_unicode_to_utf8(wargv[i]);
-    i64 n;
-    shell_check_oom(z);
-    n = strlen(z);
-    argv[i] = malloc( n+1 );
-    shell_check_oom(argv[i]);
-    memcpy(argv[i], z, n+1);
-    argvToFree[i] = argv[i];
-    sqlite3_free(z);
-  }
-  sqlite3_shutdown();
-#endif
-
-  assert( argc>=1 && argv && argv[0] );
-  Argv0 = argv[0];
-#if SHELL_DYNAMIC_EXTENSION
-  initStartupDir();
-  if( isExtendedBasename(Argv0) ) data.bExtendedDotCmds = SHELL_ALL_EXTENSIONS;
-#endif
+  register_exit_ripper(&exit_jb, entry_mark);
+  if( 0==RIP_TO_HERE(exit_jb) ){
+
+    /* On Windows, we must translate command-line arguments into UTF-8.
+    ** The SQLite memory allocator subsystem has to be enabled in order to
+    ** do this.  But we want to run an sqlite3_shutdown() afterwards so that
+    ** subsequent sqlite3_config() calls will work.  So copy all results into
+    ** memory that does not come from the SQLite memory allocator.
+    */
+  #if !SQLITE_SHELL_IS_UTF8
+    sqlite3_initialize();
+    argvToFree = malloc(sizeof(argv[0])*argc*2);
+    shell_check_oom(argvToFree);
+    argcToFree = argc;
+    argv = argvToFree + argc;
+    for(i=0; i<argc; i++){
+      char *z = sqlite3_win32_unicode_to_utf8(wargv[i]);
+      i64 n;
+      shell_check_oom(z);
+      n = strlen(z);
+      argv[i] = malloc( n+1 );
+      shell_check_oom(argv[i]);
+      memcpy(argv[i], z, n+1);
+      argvToFree[i] = argv[i];
+      sqlite3_free(z);
+    }
+    sqlite3_shutdown();
+  #endif
 
-#ifdef SQLITE_SHELL_DBNAME_PROC
-  {
-    /* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name
-    ** of a C-function that will provide the name of the database file.  Use
-    ** this compile-time option to embed this shell program in larger
-    ** applications. */
-    extern void SQLITE_SHELL_DBNAME_PROC(const char**);
-    SQLITE_SHELL_DBNAME_PROC(&data.pAuxDb->zDbFilename);
-    warnInmemoryDb = 0;
-  }
-#endif
+    assert( argc>=1 && argv && argv[0] );
+    Argv0 = argv[0];
+  #if SHELL_DYNAMIC_EXTENSION
+    initStartupDir();
+    if( isExtendedBasename(Argv0) ) data.bExtendedDotCmds = SHELL_ALL_EXTENSIONS;
+  #endif
 
-  /* Do an initial pass through the command-line argument to locate
-  ** the name of the database file, the name of the initialization file,
-  ** the size of the alternative malloc heap,
-  ** and the first command to execute.
-  */
-#ifndef SQLITE_SHELL_FIDDLE
-  verify_uninitialized();
-#endif
-  for(i=1; i<argc && rc<2; i++){
-    char *z;
-    z = argv[i];
-    if( z[0]!='-' || i>nOptsEnd ){
-      if( data.aAuxDb->zDbFilename==0 ){
-        data.aAuxDb->zDbFilename = z;
+  #ifdef SQLITE_SHELL_DBNAME_PROC
+    {
+      /* If the SQLITE_SHELL_DBNAME_PROC macro is defined, then it is the name
+      ** of a C-function that will provide the name of the database file.  Use
+      ** this compile-time option to embed this shell program in larger
+      ** applications. */
+      extern void SQLITE_SHELL_DBNAME_PROC(const char**);
+      SQLITE_SHELL_DBNAME_PROC(&data.pAuxDb->zDbFilename);
+      warnInmemoryDb = 0;
+    }
+  #endif
+
+    /* Do an initial pass through the command-line argument to locate
+    ** the name of the database file, the name of the initialization file,
+    ** the size of the alternative malloc heap,
+    ** and the first command to execute.
+    */
+  #ifndef SQLITE_SHELL_FIDDLE
+    verify_uninitialized();
+  #endif
+    i = scanInvokeArgs(argc, argv, 1, &data, &cmdArgs, &argsData);
+  #ifndef SQLITE_SHELL_FIDDLE
+    verify_uninitialized();
+  #endif
+
+  #ifdef SQLITE_SHELL_INIT_PROC
+    {
+      /* If the SQLITE_SHELL_INIT_PROC macro is defined, then it is the name
+      ** of a C-function that will perform initialization actions on SQLite that
+      ** occur just before or after sqlite3_initialize(). Use this compile-time
+      ** option to embed this shell program in larger applications. */
+      extern void SQLITE_SHELL_INIT_PROC(void);
+      SQLITE_SHELL_INIT_PROC();
+    }
+  #else
+    /* All the sqlite3_config() calls have now been made. So it is safe
+    ** to call sqlite3_initialize() and process any command line -vfs option. */
+    sqlite3_initialize();
+  #endif
+
+    /* Register the control-C (SIGINT) handler.
+    ** Make sure we have a valid signal handler early, before anything
+    ** is done that might take long. */
+    pGlobalDbLock = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+  #ifdef SIGINT
+    signal(SIGINT, interrupt_handler);
+  #elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE)
+    SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
+  #endif
+
+    if( argsData.zVfs ){
+      sqlite3_vfs *pVfs = sqlite3_vfs_find(argsData.zVfs);
+      if( pVfs ){
+        sqlite3_vfs_register(pVfs, 1);
       }else{
-        /* Excesss arguments are interpreted as SQL (or dot-commands) and
-        ** mean that nothing is read from stdin */
-        readStdin = 0;
-        nCmd++;
-        azCmd = realloc(azCmd, sizeof(azCmd[0])*nCmd);
-        shell_check_oom(azCmd);
-        azCmd[nCmd-1] = z;
-      }
-      continue;
-    }
-    if( z[1]=='-' ) z++;
-    if( cli_strcmp(z, "-")==0 ){
-      nOptsEnd = i;
-      continue;
-    }else if( cli_strcmp(z,"-separator")==0
-     || cli_strcmp(z,"-nullvalue")==0
-     || cli_strcmp(z,"-newline")==0
-     || cli_strcmp(z,"-cmd")==0
-    ){
-      (void)cmdline_option_value(argc, argv, ++i);
-    }else if( cli_strcmp(z,"-init")==0 ){
-      zInitFile = cmdline_option_value(argc, argv, ++i);
-    }else if( cli_strcmp(z,"-batch")==0 ){
-      /* Need to check for batch mode here to so we can avoid printing
-      ** informational messages (like from process_sqliterc) before
-      ** we do the actual processing of arguments later in a second pass.
-      */
-      stdin_is_interactive = 0;
-    }else if( cli_strcmp(z,"-heap")==0 ){
-#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
-      const char *zSize;
-      sqlite3_int64 szHeap;
-
-      zSize = cmdline_option_value(argc, argv, ++i);
-      szHeap = integerValue(zSize);
-      if( szHeap>0x7fff0000 ) szHeap = 0x7fff0000;
-      verify_uninitialized();
-      sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
-#else
-      (void)cmdline_option_value(argc, argv, ++i);
-#endif
-    }else if( cli_strcmp(z,"-pagecache")==0 ){
-      sqlite3_int64 n, sz;
-      sz = integerValue(cmdline_option_value(argc,argv,++i));
-      if( sz>70000 ) sz = 70000;
-      if( sz<0 ) sz = 0;
-      n = integerValue(cmdline_option_value(argc,argv,++i));
-      if( sz>0 && n>0 && 0xffffffffffffLL/sz<n ){
-        n = 0xffffffffffffLL/sz;
-      }
-      verify_uninitialized();
-      sqlite3_config(SQLITE_CONFIG_PAGECACHE,
-                    (n>0 && sz>0) ? malloc(n*sz) : 0, sz, n);
-      data.shellFlgs |= SHFLG_Pagecache;
-    }else if( cli_strcmp(z,"-lookaside")==0 ){
-      int n, sz;
-      sz = (int)integerValue(cmdline_option_value(argc,argv,++i));
-      if( sz<0 ) sz = 0;
-      n = (int)integerValue(cmdline_option_value(argc,argv,++i));
-      if( n<0 ) n = 0;
-      verify_uninitialized();
-      sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
-      if( sz*n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
-    }else if( cli_strcmp(z,"-threadsafe")==0 ){
-      int n;
-      n = (int)integerValue(cmdline_option_value(argc,argv,++i));
-      verify_uninitialized();
-      switch( n ){
-         case 0:  sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);  break;
-         case 2:  sqlite3_config(SQLITE_CONFIG_MULTITHREAD);   break;
-         default: sqlite3_config(SQLITE_CONFIG_SERIALIZED);    break;
+        utf8_printf(STD_ERR, "no such VFS: \"%s\"\n", argsData.zVfs);
+        rc = 1;
+        goto shell_bail;
       }
-#ifdef SQLITE_ENABLE_VFSTRACE
-    }else if( cli_strcmp(z,"-vfstrace")==0 ){
-      extern int vfstrace_register(
-         const char *zTraceName,
-         const char *zOldVfsName,
-         int (*xOut)(const char*,void*),
-         void *pOutArg,
-         int makeDefault
-      );
-      vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,STD_ERR,1);
-#endif
-#ifdef SQLITE_ENABLE_MULTIPLEX
-    }else if( cli_strcmp(z,"-multiplex")==0 ){
-      extern int sqlite3_multiple_initialize(const char*,int);
-      sqlite3_multiplex_initialize(0, 1);
-#endif
-    }else if( cli_strcmp(z,"-mmap")==0 ){
-      sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
-      verify_uninitialized();
-      sqlite3_config(SQLITE_CONFIG_MMAP_SIZE, sz, sz);
-#ifdef SQLITE_ENABLE_SORTER_REFERENCES
-    }else if( cli_strcmp(z,"-sorterref")==0 ){
-      sqlite3_int64 sz = integerValue(cmdline_option_value(argc,argv,++i));
-      verify_uninitialized();
-      sqlite3_config(SQLITE_CONFIG_SORTERREF_SIZE, (int)sz);
-#endif
-    }else if( cli_strcmp(z,"-vfs")==0 ){
-      zVfs = cmdline_option_value(argc, argv, ++i);
-#ifdef SQLITE_HAVE_ZLIB
-    }else if( cli_strcmp(z,"-zip")==0 ){
-      data.openMode = SHELL_OPEN_ZIPFILE;
-#endif
-    }else if( cli_strcmp(z,"-append")==0 ){
-      data.openMode = SHELL_OPEN_APPENDVFS;
-#ifndef SQLITE_OMIT_DESERIALIZE
-    }else if( cli_strcmp(z,"-deserialize")==0 ){
-      data.openMode = SHELL_OPEN_DESERIALIZE;
-    }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
-      data.szMax = integerValue(argv[++i]);
-#endif
-    }else if( cli_strcmp(z,"-readonly")==0 ){
-      data.openMode = SHELL_OPEN_READONLY;
-    }else if( cli_strcmp(z,"-nofollow")==0 ){
-      data.openFlags = SQLITE_OPEN_NOFOLLOW;
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
-    }else if( cli_strncmp(z, "-A",2)==0 ){
-      /* All remaining command-line arguments are passed to the ".archive"
-      ** command, so ignore them */
-      break;
-#endif
-    }else if( cli_strcmp(z, "-memtrace")==0 ){
-      sqlite3MemTraceActivate(STD_ERR);
-    }else if( cli_strcmp(z,"-bail")==0 ){
-      bail_on_error = 1;
-#if SHELL_EXTENSIONS
-    }else if( cli_strcmp(z,"-shxopts")==0 ){
-      data.bExtendedDotCmds = (u8)integerValue(argv[++i]);
-#endif
-    }else if( cli_strcmp(z,"-nonce")==0 ){
-      free(data.zNonce);
-      data.zNonce = strdup(argv[++i]);
-    }else if( cli_strcmp(z,"-quiet")==0 ){
-      bQuiet = (int)integerValue(cmdline_option_value(argc,argv,++i));
-    }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
-      data.shellFlgs |= SHFLG_TestingMode;
-    }else if( cli_strcmp(z,"-safe")==0 ){
-      /* catch this on the second pass (Unsafe is fine on invocation.) */
     }
-  }
-#ifndef SQLITE_SHELL_FIDDLE
-  verify_uninitialized();
-#endif
 
-#ifdef SQLITE_SHELL_INIT_PROC
-  {
-    /* If the SQLITE_SHELL_INIT_PROC macro is defined, then it is the name
-    ** of a C-function that will perform initialization actions on SQLite that
-    ** occur just before or after sqlite3_initialize(). Use this compile-time
-    ** option to embed this shell program in larger applications. */
-    extern void SQLITE_SHELL_INIT_PROC(void);
-    SQLITE_SHELL_INIT_PROC();
-  }
-#else
-  /* All the sqlite3_config() calls have now been made. So it is safe
-  ** to call sqlite3_initialize() and process any command line -vfs option. */
-  sqlite3_initialize();
-#endif
-
-  /* Register the control-C (SIGINT) handler.
-  ** Make sure we have a valid signal handler early, before anything
-  ** is done that might take long. */
-  pGlobalDbLock = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
-#ifdef SIGINT
-  signal(SIGINT, interrupt_handler);
-#elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE)
-  SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
-#endif
-
-  if( zVfs ){
-    sqlite3_vfs *pVfs = sqlite3_vfs_find(zVfs);
-    if( pVfs ){
-      sqlite3_vfs_register(pVfs, 1);
-    }else{
-      utf8_printf(STD_ERR, "no such VFS: \"%s\"\n", zVfs);
+    if( data.pAuxDb->zDbFilename==0 ){
+  #ifndef SQLITE_OMIT_MEMORYDB
+      data.pAuxDb->zDbFilename = ":memory:";
+      warnInmemoryDb = argc==1;
+  #else
+      utf8_printf(STD_ERR,"%s: Error: no database filename specified\n", Argv0);
       rc = 1;
       goto shell_bail;
+  #endif
     }
-  }
+    data.out = STD_OUT;
+  #ifndef SQLITE_SHELL_FIDDLE
+    sqlite3_appendvfs_init(0,0,0);
+  #endif
 
-  if( data.pAuxDb->zDbFilename==0 ){
-#ifndef SQLITE_OMIT_MEMORYDB
-    data.pAuxDb->zDbFilename = ":memory:";
-    warnInmemoryDb = argc==1;
-#else
-    utf8_printf(STD_ERR,"%s: Error: no database filename specified\n", Argv0);
-    rc = 1;
-    goto shell_bail;
-#endif
-  }
-  data.out = STD_OUT;
-#ifndef SQLITE_SHELL_FIDDLE
-  sqlite3_appendvfs_init(0,0,0);
-#endif
+    /* Go ahead and open the database file if it already exists.  If the
+    ** file does not exist, delay opening it.  This prevents empty database
+    ** files from being created if a user mistypes the database name argument
+    ** to the sqlite command-line tool.
+    */
+    if( access(data.pAuxDb->zDbFilename, 0)==0 ){
+      open_db(&datax, 0);
+    }
 
-  /* Go ahead and open the database file if it already exists.  If the
-  ** file does not exist, delay opening it.  This prevents empty database
-  ** files from being created if a user mistypes the database name argument
-  ** to the sqlite command-line tool.
-  */
-  if( access(data.pAuxDb->zDbFilename, 0)==0 ){
-    open_db(&datax, 0);
-  }
+    /* Process the initialization file if there is one.  If no -init option
+    ** is given on the command line, look for a file named ~/.sqliterc and
+    ** try to process it, without any quitting or bail-on-error.
+    */
+    process_sqliterc(&data,argsData.zInitFile);
 
-  /* Process the initialization file if there is one.  If no -init option
-  ** is given on the command line, look for a file named ~/.sqliterc and
-  ** try to process it, without any quitting or bail-on-error.
-  */
-  process_sqliterc(&data,zInitFile);
+    /* Make a second pass through the command-line argument and set
+    ** options.  This second pass is delayed until after the initialization
+    ** file is processed so that the command-line arguments will override
+    ** settings in the initialization file.
+    */
+    rc = scanInvokeArgs(argc, argv, 2, &data, &cmdArgs, &argsData);
+    if( (rc && bail_on_error) || rc>1 ){
+      if( rc==2 ) rc = 0;
+      goto shell_bail;
+    }
 
-  /* Make a second pass through the command-line argument and set
-  ** options.  This second pass is delayed until after the initialization
-  ** file is processed so that the command-line arguments will override
-  ** settings in the initialization file.
-  */
-  for(i=1; i<argc && rc<2; i++){
-    char *z = argv[i];
-    char *zModeSet = 0;
-    if( z[0]!='-' || i>=nOptsEnd ) continue;
-    if( z[1]=='-' ){ z++; }
-    if( cli_strcmp(z,"-init")==0 ){
-      i++;
-    }else if( cli_strcmp(z,"-html")==0 ){
-      zModeSet = z;
-    }else if( cli_strcmp(z,"-list")==0 ){
-      zModeSet = z;
-    }else if( cli_strcmp(z,"-quote")==0 ){
-      zModeSet = z;
-    }else if( cli_strcmp(z,"-line")==0 ){
-      zModeSet = z;
-    }else if( cli_strcmp(z,"-column")==0 ){
-      zModeSet = z;
-    }else if( cli_strcmp(z,"-json")==0 ){
-      zModeSet = z;
-    }else if( cli_strcmp(z,"-markdown")==0 ){
-      zModeSet = z;
-    }else if( cli_strcmp(z,"-table")==0 ){
-      zModeSet = z;
-    }else if( cli_strcmp(z,"-box")==0 ){
-      zModeSet = z;
-    }else if( cli_strcmp(z,"-csv")==0 ){
-      zModeSet = z;
-    }else if( cli_strcmp(z,"-ascii")==0 ){
-      zModeSet = z;
-    }else if( cli_strcmp(z,"-tabs")==0 ){
-      zModeSet = z;
-#ifdef SQLITE_HAVE_ZLIB
-    }else if( cli_strcmp(z,"-zip")==0 ){
-      data.openMode = SHELL_OPEN_ZIPFILE;
-#endif
-    }else if( cli_strcmp(z,"-append")==0 ){
-      data.openMode = SHELL_OPEN_APPENDVFS;
-#ifndef SQLITE_OMIT_DESERIALIZE
-    }else if( cli_strcmp(z,"-deserialize")==0 ){
-      data.openMode = SHELL_OPEN_DESERIALIZE;
-    }else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
-      data.szMax = integerValue(argv[++i]);
-#endif
-    }else if( cli_strcmp(z,"-readonly")==0 ){
-      data.openMode = SHELL_OPEN_READONLY;
-    }else if( cli_strcmp(z,"-nofollow")==0 ){
-      data.openFlags |= SQLITE_OPEN_NOFOLLOW;
-    }else if( cli_strcmp(z,"-separator")==0 ){
-      sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
-                       "%s",cmdline_option_value(argc,argv,++i));
-    }else if( cli_strcmp(z,"-newline")==0 ){
-      sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,
-                       "%s",cmdline_option_value(argc,argv,++i));
-    }else if( cli_strcmp(z,"-nullvalue")==0 ){
-      sqlite3_snprintf(sizeof(data.nullValue), data.nullValue,
-                       "%s",cmdline_option_value(argc,argv,++i));
-    }else if( cli_strcmp(z,"-header")==0 ){
-      data.showHeader = 1;
-      ShellSetFlag(&datax, SHFLG_HeaderSet);
-    }else if( cli_strcmp(z,"-noheader")==0 ){
-      data.showHeader = 0;
-      ShellSetFlag(&datax, SHFLG_HeaderSet);
-    }else if( cli_strcmp(z,"-echo")==0 ){
-      ShellSetFlag(&datax, SHFLG_Echo);
-    }else if( cli_strcmp(z,"-eqp")==0 ){
-      data.autoEQP = AUTOEQP_on;
-    }else if( cli_strcmp(z,"-eqpfull")==0 ){
-      data.autoEQP = AUTOEQP_full;
-    }else if( cli_strcmp(z,"-stats")==0 ){
-      data.statsOn = 1;
-    }else if( cli_strcmp(z,"-scanstats")==0 ){
-      data.scanstatsOn = 1;
-    }else if( cli_strcmp(z,"-backslash")==0 ){
-      /* Undocumented command-line option: -backslash
-      ** Causes C-style backslash escapes to be evaluated in SQL statements
-      ** prior to sending the SQL into SQLite.  Useful for injecting
-      ** crazy bytes in the middle of SQL statements for testing and debugging.
-      */
-      ShellSetFlag(&datax, SHFLG_Backslash);
-    }else if( cli_strcmp(z,"-bail")==0 ){
-      /* No-op.  The bail_on_error flag should already be set. */
-#if SHELL_EXTENSIONS
-    }else if( cli_strcmp(z,"-shxopts")==0 ){
-      i++; /* Handled on first pass. */
-#endif
-    }else if( cli_strcmp(z,"-version")==0 ){
-      fprintf(STD_OUT, "%s %s\n", sqlite3_libversion(), sqlite3_sourceid());
-      rc = 2;
-    }else if( cli_strcmp(z,"-interactive")==0 ){
-      stdin_is_interactive = 1;
-    }else if( cli_strcmp(z,"-batch")==0 ){
-      stdin_is_interactive = 0;
-    }else if( cli_strcmp(z,"-utf8")==0 ){
-#if SHELL_WIN_UTF8_OPT
-      console_utf8 = 1;
-#endif /* SHELL_WIN_UTF8_OPT */
-    }else if( cli_strcmp(z,"-heap")==0 ){
-      i++;
-    }else if( cli_strcmp(z,"-pagecache")==0 ){
-      i+=2;
-    }else if( cli_strcmp(z,"-lookaside")==0 ){
-      i+=2;
-    }else if( cli_strcmp(z,"-threadsafe")==0 ){
-      i+=2;
-    }else if( cli_strcmp(z,"-nonce")==0 ){
-      i += 2;
-    }else if( cli_strcmp(z,"-mmap")==0 ){
-      i++;
-    }else if( cli_strcmp(z,"-memtrace")==0 ){
-      i++;
-#ifdef SQLITE_ENABLE_SORTER_REFERENCES
-    }else if( cli_strcmp(z,"-sorterref")==0 ){
-      i++;
-#endif
-    }else if( cli_strcmp(z,"-vfs")==0 ){
-      i++;
-#ifdef SQLITE_ENABLE_VFSTRACE
-    }else if( cli_strcmp(z,"-vfstrace")==0 ){
-      i++;
-#endif
-#ifdef SQLITE_ENABLE_MULTIPLEX
-    }else if( cli_strcmp(z,"-multiplex")==0 ){
-      i++;
-#endif
-    }else if( cli_strcmp(z,"-help")==0 ){
-      usage(1);
-    }else if( cli_strcmp(z,"-cmd")==0 ){
-      /* Run commands that follow -cmd first and separately from commands
-      ** that simply appear on the command-line.  This seems goofy.  It would
-      ** be better if all commands ran in the order that they appear.  But
-      ** we retain the goofy behavior for historical compatibility. */
-      if( i==argc-1 ) break;
-      z = cmdline_option_value(argc,argv,++i);
-      if( z[0]=='.' ){
-        drc = do_dot_command(z, &datax);
-        rc = (drc>2)? 2 : drc;
-      }else{
-        rc = run_single_query(&datax, z);
-      }
-      if( rc && bail_on_error ){
-        if( rc==2 ) rc = 0;
-        goto shell_bail;
-      }
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
-    }else if( cli_strncmp(z, "-A", 2)==0 ){
-      if( nCmd>0 ){
-        utf8_printf(STD_ERR, "Error: cannot mix regular SQL or dot-commands"
-                            " with \"%s\"\n", z);
-        rc = 1;
-        goto shell_bail;
-      }
-      open_db(&datax, OPEN_DB_ZIPFILE);
-      if( z[2] ){
-        argv[i] = &z[2];
-        drc = arDotCommand(&datax, 1, argv+(i-1), argc-(i-1));
-      }else{
-        drc = arDotCommand(&datax, 1, argv+i, argc-i);
-      }
-      rc = (drc>2)? 2 : drc;
-      readStdin = 0;
-      break;
-#endif
-    }else if( cli_strcmp(z,"-safe")==0 ){
-      data.bSafeMode = data.bSafeModeFuture = 1;
-    }else if( cli_strcmp(z,"-unsafe-testing")==0 ){
-      /* Acted upon in first pass. */
-    }else if( cli_strcmp(z,"-quiet")==0 ){
-      ++i;
+  #if SHELL_WIN_UTF8_OPT
+    if( console_utf8 && stdin_is_interactive ){
+      console_prepare();
     }else{
-      utf8_printf(STD_ERR,"%s: Error: unknown option: %s\n", Argv0, z);
-      raw_printf(STD_ERR,"Use -help for a list of options.\n");
-      rc = 2;
-    }
-    if( zModeSet!=0 ){
-      char *azModeCmd[] = { ".mode", zModeSet+1 };
-      modeCommand(azModeCmd, 2, &datax, 0);
-      data.cMode = data.mode;
+      setBinaryMode(stdin, 0);
+      console_utf8 = 0;
     }
-  }
-#if SHELL_WIN_UTF8_OPT
-  if( console_utf8 && stdin_is_interactive ){
-    console_prepare();
-  }else{
-    setBinaryMode(stdin, 0);
-    console_utf8 = 0;
-  }
-#endif
+  #endif
 
-  if( !readStdin ){
-    /* Run all arguments that do not begin with '-' as if they were separate
-    ** command-line inputs, except for the argToSkip argument which contains
-    ** the database filename.
-    */
-    for(i=0; i<nCmd && rc<2; i++){
-      if( azCmd[i][0]=='.' ){
-        drc = do_dot_command(azCmd[i], &datax);
-        rc = (drc>2)? 2 : drc;
-      }else{
-        rc = run_single_query(&datax, azCmd[i]);
-      }
-      if( rc && bail_on_error ){
-        goto shell_bail;
-      }
-    }
-  }else{
-    /* Run commands received from standard input
-    */
-    if( stdin_is_interactive ){
-      char *zHome;
-      char *zHistory = 0;
-      if( bQuiet ){
-        /* bQuiet is almost like normal interactive, but quieter. */
-        mainPrompt[0] = 0;
-        continuePrompt[0] = 0;
-      }else{
-        fprintf(STD_OUT,
-          "SQLite version %s %.19s\n" /*extra-version-info*/
-          "Enter \".help\" for usage hints.\n",
-          sqlite3_libversion(), sqlite3_sourceid()
-        );
-        if( warnInmemoryDb ){
-          fprintf(STD_OUT, "Connected to a ");
-          printBold("transient in-memory database");
-          fprintf(STD_OUT, ".\nUse \".open FILENAME\" to reopen on a "
-                 "persistent database.\n");
+    if( !argsData.readStdin ){
+      /* Run all arguments that do not begin with '-' as if they were separate
+      ** command-line inputs, except for the argToSkip argument which contains
+      ** the database filename.
+      */
+      for(i=0; i<cmdArgs.nCmd && rc<2; i++){
+        if( cmdArgs.azCmd[i][0]=='.' ){
+          drc = do_dot_command(cmdArgs.azCmd[i], &datax);
+          rc = (drc>2)? 2 : drc;
+        }else{
+          rc = run_single_query(&datax, cmdArgs.azCmd[i]);
         }
-        zHistory = getenv("SQLITE_HISTORY");
-        if( zHistory ){
-          zHistory = strdup(zHistory);
-        }else if( (zHome = find_home_dir(0))!=0 ){
-          int nHistory = strlen30(zHome) + 20;
-          if( (zHistory = malloc(nHistory))!=0 ){
-            sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
-          }
+        if( rc && bail_on_error ){
+          goto shell_bail;
         }
-        if( zHistory ){ shell_read_history(zHistory); }
-#if HAVE_READLINE || HAVE_EDITLINE
-        rl_attempted_completion_function = readline_completion;
-#elif HAVE_LINENOISE
-        linenoiseSetCompletionCallback(linenoise_completion);
-#endif
       }
-      data.pInSource = &termInSource; /* read from stdin interactively */
-      drc = process_input(&data);
-      rc = (drc>2)? 2 : drc;
-      if( !bQuiet ){
-        if( zHistory ){
-          shell_stifle_history(2000);
-          shell_write_history(zHistory);
-          free(zHistory);
+    }else{
+      /* Run commands received from standard input
+      */
+      if( stdin_is_interactive ){
+        char *zHome;
+        char *zHistory = 0;
+        if( argsData.bQuiet ){
+          /* bQuiet is almost like normal interactive, but quieter
+          ** and avoids history keeping and line editor completions. */
+          mainPrompt[0] = 0;
+          continuePrompt[0] = 0;
+        }else{
+          fprintf(STD_OUT,
+            "SQLite version %s %.19s\n" /*extra-version-info*/
+            "Enter \".help\" for usage hints.\n",
+            sqlite3_libversion(), sqlite3_sourceid()
+          );
+          if( warnInmemoryDb ){
+            fprintf(STD_OUT, "Connected to a ");
+            printBold("transient in-memory database");
+            fprintf(STD_OUT, ".\nUse \".open FILENAME\" to reopen on a "
+                   "persistent database.\n");
+          }
+          zHistory = getenv("SQLITE_HISTORY");
+          if( zHistory ){
+            zHistory = strdup(zHistory);
+          }else if( (zHome = find_home_dir(0))!=0 ){
+            int nHistory = strlen30(zHome) + 20;
+            if( (zHistory = malloc(nHistory))!=0 ){
+              sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
+            }
+          }
+          if( zHistory ){ shell_read_history(zHistory); }
+  #if HAVE_READLINE || HAVE_EDITLINE
+          rl_attempted_completion_function = readline_completion;
+  #elif HAVE_LINENOISE
+          linenoiseSetCompletionCallback(linenoise_completion);
+  #endif
+        }
+        data.pInSource = &termInSource; /* read from stdin interactively */
+        drc = process_input(&data);
+        rc = (drc>2)? 2 : drc;
+        if( !bQuiet ){
+          if( zHistory ){
+            shell_stifle_history(2000);
+            shell_write_history(zHistory);
+            free(zHistory);
+          }
         }
+      }else{
+        data.pInSource = &stdInSource; /* read from stdin without prompts */
+        drc = process_input(&data);
+        rc = (drc>2)? 2 : drc;
       }
-    }else{
-      data.pInSource = &stdInSource; /* read from stdin without prompts */
-      drc = process_input(&data);
-      rc = (drc>2)? 2 : drc;
     }
+  }else{
+    /* An abrupt, stack-ripping exit arrives here. */
   }
  shell_bail:
+  holder_free(entry_mark);
 #ifndef SQLITE_SHELL_FIDDLE
   /* In WASM mode we have to leave the db state in place so that
   ** client code can "push" SQL into it after this call returns.
@@ -16663,7 +16732,6 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){
   free(datax.pSpecWidths);
   free(data.zNonce);
   for(i=0; i<data.nSavedModes; ++i) sqlite3_free(data.pModeStack[i]);
-  free(azCmd);
 # if SHELL_DATAIO_EXT
   cmExporter.pMethods->destruct((ExportHandler*)&cmExporter);
   ffExporter.pMethods->destruct((ExportHandler*)&ffExporter);