]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
For CLI, begin centralizing argument checking/complaining.
authorlarrybr <larrybr@noemail.net>
Tue, 15 Mar 2022 10:07:56 +0000 (10:07 +0000)
committerlarrybr <larrybr@noemail.net>
Tue, 15 Mar 2022 10:07:56 +0000 (10:07 +0000)
FossilOrigin-Name: 3457f87c5db7a28eb6a47223f7853cd066245edfe1882feabbdb8ce9555f872e

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

index a589a60da3ea94ba05083fdcd23f680072240d11..40f6a930f65cfa6313478c0e43b6547e8f7b9d0b 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C CLI\sextension's\sregisterMetaCommand\sdone,\sand\sused\sby\stest_shellext.c\s(demo\scode)
-D 2022-03-11T22:59:24.198
+C For\sCLI,\sbegin\scentralizing\sargument\schecking/complaining.
+D 2022-03-15T10:07:56.877
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -555,8 +555,8 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
 F src/resolve.c ea935b87d6fb36c78b70cdc7b28561dc8f33f2ef37048389549c7b5ef9b0ba5e
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c 4890a3cfee0bc60ff231c3a44db37968859ab0be156983dbcc0c096109832cdd
-F src/shell.c.in 8ecf1834819828c7ba2c375722c01a3db346ce5fd8d1313839c14d89db517259
-F src/shext_linkage.h 6a9830f48061677ead5c05b537af3452b655f088db3a1298d1bbdd71279d8fe1
+F src/shell.c.in af1a255482cd315901eb75134838dc9403fc04f8d755b7e75e2a85ee47df9a87
+F src/shext_linkage.h aba0d02f92fdea48904bf0b0805df9140a6b5dc20e9e76dad866e0568abaf458
 F src/sqlite.h.in e82ac380b307659d0892f502b742f825504e78729f4edaadce946003b9c00816
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6
@@ -1949,8 +1949,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 19f2a747b8f4d84d8695beb9b23a455f1dead24c94aa90466510555a796027d7
-R f5ab49aa4d163198ec36e8804abd988c
+P 9be5e0b503f63dbbf1375460ce74a0aad6f202c2e5b2a748bba1d3af82c7d2d8
+R 859f2736e1cb4ffb3cf8130c1ca2c5f2
 U larrybr
-Z 7b79332717703ce0f5d855b6c23450ad
+Z 1a5305d5e77e7119519f6839e7b8c711
 # Remove this line to create a well-formed Fossil manifest.
index 930b491221761db23733df364b08b1b011511b67..706134ee08ceb465ddd53196ddd411eb261af7a5 100644 (file)
@@ -1 +1 @@
-9be5e0b503f63dbbf1375460ce74a0aad6f202c2e5b2a748bba1d3af82c7d2d8
\ No newline at end of file
+3457f87c5db7a28eb6a47223f7853cd066245edfe1882feabbdb8ce9555f872e
\ No newline at end of file
index f60a9ef626bc6943f4e0d9be008466a890304723..5a4aafe85f7b9b3a3203b953713e9a0d8e9539c8 100644 (file)
@@ -1555,7 +1555,8 @@ static void shellPutsFunc(
 ** the caller of the shell's main, "do shell things" entry point.
 **
 ** It is an error, (perhaps with only minor effect such as memory leak),
-** for a meta-command to call this function while it holds resources.
+** for a meta-command to call this function while it holds resources in
+** need of freeing. Instead, it should be called before acquiring them.
 **
 ** The return is true if failing, 0 otherwise.
 */
@@ -6461,11 +6462,11 @@ static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){
 
 /*
 ** Parse the command line for an ".ar" command. The results are written into
-** structure (*pAr). SQLITE_OK is returned if the command line is parsed
-** successfully, otherwise an error message is written to stderr and
-** SQLITE_ERROR returned.
+** structure (*pAr). DCR_Ok is returned if the command line is parsed
+** successfully, otherwise an error message is written to stderr and an
+** appropriate DCR_* argument error is returned.
 */
-static int arParseCommand(
+static DotCmdRC arParseCommand(
   char **azArg,                   /* Array of arguments passed to dot command */
   int nArg,                       /* Number of entries in azArg[] */
   ArCommand *pAr                  /* Populate this object */
@@ -6492,13 +6493,15 @@ static int arParseCommand(
   };
   int nSwitch = sizeof(aSwitch) / sizeof(struct ArSwitch);
   struct ArSwitch *pEnd = &aSwitch[nSwitch];
+  DotCmdRC rv = DCR_Ok;
 
   if( nArg<=1 ){
     utf8_printf(STD_ERR, "Error: Wrong number of arguments to \"%s\".\n",
                 azArg[0]);
     if( stdin_is_interactive ){
       utf8_printf(STD_ERR, "Usage:\n");
-      return arUsage(STD_ERR, pAr);
+      arUsage(STD_ERR, pAr);
+      return DCR_TooFew;
     }
   }else{
     char *z = azArg[1];
@@ -6513,15 +6516,18 @@ static int arParseCommand(
           if( z[i]==pOpt->cShort ) break;
         }
         if( pOpt==pEnd ){
-          return arErrorMsg(pAr, "unrecognized option: %c", z[i]);
+          arErrorMsg(pAr, "unrecognized option: %c", z[i]);
+          return DCR_Unknown|i;
         }
         if( pOpt->bArg ){
           if( iArg>=nArg ){
-            return arErrorMsg(pAr, "option requires an argument: %c",z[i]);
+            arErrorMsg(pAr, "option requires an argument: %c",z[i]);
+            return DCR_Unpaired|i;
           }
           zArg = azArg[iArg++];
         }
-        if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR;
+        rv = arProcessSwitch(pAr, pOpt->eSwitch, zArg);
+        if( rv!=DCR_Ok ) return rv;
       }
       pAr->nArg = nArg-iArg;
       if( pAr->nArg>0 ){
@@ -6551,7 +6557,8 @@ static int arParseCommand(
               if( z[i]==pOpt->cShort ) break;
             }
             if( pOpt==pEnd ){
-              return arErrorMsg(pAr, "unrecognized option: %c", z[i]);
+              arErrorMsg(pAr, "unrecognized option: %c", z[i]);
+              return DCR_Unknown|iArg;
             }
             if( pOpt->bArg ){
               if( i<(n-1) ){
@@ -6559,13 +6566,14 @@ static int arParseCommand(
                 i = n;
               }else{
                 if( iArg>=(nArg-1) ){
-                  return arErrorMsg(pAr, "option requires an argument: %c",
-                                    z[i]);
+                  arErrorMsg(pAr, "option requires an argument: %c", z[i]);
+                  return DCR_Unpaired|iArg;
                 }
                 zArg = azArg[++iArg];
               }
             }
-            if( arProcessSwitch(pAr, pOpt->eSwitch, zArg) ) return SQLITE_ERROR;
+            rv = arProcessSwitch(pAr, pOpt->eSwitch, zArg);
+            if( rv!=DCR_Ok ) return rv;
           }
         }else if( z[2]=='\0' ){
           /* A -- option, indicating that all remaining command line words
@@ -6582,7 +6590,8 @@ static int arParseCommand(
             const char *zLong = pOpt->zLong;
             if( (n-2)<=strlen30(zLong) && 0==memcmp(&z[2], zLong, n-2) ){
               if( pMatch ){
-                return arErrorMsg(pAr, "ambiguous option: %s",z);
+                arErrorMsg(pAr, "ambiguous option: %s",z);
+                return DCR_Ambiguous|iArg;
               }else{
                 pMatch = pOpt;
               }
@@ -6590,11 +6599,13 @@ static int arParseCommand(
           }
 
           if( pMatch==0 ){
-            return arErrorMsg(pAr, "unrecognized option: %s", z);
+            arErrorMsg(pAr, "unrecognized option: %s", z);
+            return DCR_Unknown|iArg;
           }
           if( pMatch->bArg ){
             if( iArg>=(nArg-1) ){
-              return arErrorMsg(pAr, "option requires an argument: %s", z);
+              arErrorMsg(pAr, "option requires an argument: %s", z);
+              return DCR_Unpaired|iArg;
             }
             zArg = azArg[++iArg];
           }
@@ -6993,19 +7004,20 @@ end_ar_transaction:
 /*
 ** Implementation of ".ar" dot command.
 */
-static int arDotCommand(
+static DotCmdRC arDotCommand(
   ShellExState *pState,        /* Current shell tool state */
   int fromCmdLine,             /* True if -A command-line option, not .ar cmd */
   char **azArg,                /* Array of arguments passed to dot command */
   int nArg                     /* Number of entries in azArg[] */
 ){
   ArCommand cmd;
+  DotCmdRC rv;
   int rc;
   memset(&cmd, 0, sizeof(cmd));
   cmd.fromCmdLine = fromCmdLine;
-  rc = arParseCommand(azArg, nArg, &cmd);
+  rv = arParseCommand(azArg, nArg, &cmd);
   cmd.out = currentOutputFile(pState);
-  if( rc==SQLITE_OK ){
+  if( rv==DCR_Ok ){
     int eDbType = SHELL_OPEN_UNSPEC;
     cmd.p = pState;
     cmd.db = DBX(pState);
@@ -7098,7 +7110,7 @@ end_ar_command:
   }
   sqlite3_free(cmd.zSrcTable);
 
-  return rc;
+  return (rv!=DCR_Ok)? rv : (DCR_Ok|(rc!=0));
 }
 /* End of the ".archive" or ".ar" command logic
 *******************************************************************************/
@@ -7465,7 +7477,8 @@ static RecoverTable *recoverOrphanTable(
 }
 #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */
 
-static int writeDb( char *azArg[], int nArg, ShellExState *psx, char **pzErr ){
+static DotCmdRC
+writeDb( char *azArg[], int nArg, ShellExState *psx, char **pzErr ){
   int rc = 0;
   const char *zDestFile = 0;
   const char *zDb = 0;
@@ -7474,7 +7487,7 @@ static int writeDb( char *azArg[], int nArg, ShellExState *psx, char **pzErr ){
   int j;
   int bAsync = 0;
   const char *zVfs = 0;
-  if( ISS(psx)->bSafeMode ) return SHELL_FORBIDDEN_OP;
+  if( ISS(psx)->bSafeMode ) return DCR_AbortError;
   for(j=1; j<nArg; j++){
     const char *z = azArg[j];
     if( z[0]=='-' ){
@@ -7487,7 +7500,7 @@ static int writeDb( char *azArg[], int nArg, ShellExState *psx, char **pzErr ){
         }else
           {
             utf8_printf(STD_ERR, "unknown option: %s\n", azArg[j]);
-            return SHELL_INVALID_ARGS;
+            return DCR_Unknown|j;
           }
     }else if( zDestFile==0 ){
       zDestFile = azArg[j];
@@ -7495,11 +7508,11 @@ static int writeDb( char *azArg[], int nArg, ShellExState *psx, char **pzErr ){
       zDb = zDestFile;
       zDestFile = azArg[j];
     }else{
-      return SHELL_INVALID_ARGS;
+      return DCR_TooMany|j;
     }
   }
   if( zDestFile==0 ){
-    return SHELL_INVALID_ARGS;
+    return DCR_Missing;
   }
   if( zDb==0 ) zDb = "main";
   rc = sqlite3_open_v2(zDestFile, &pDest,
@@ -7507,7 +7520,7 @@ static int writeDb( char *azArg[], int nArg, ShellExState *psx, char **pzErr ){
   if( rc!=SQLITE_OK ){
     utf8_printf(STD_ERR, "Error: cannot open \"%s\"\n", zDestFile);
     close_db(pDest);
-    return 1;
+    return DCR_Error;
   }
   if( bAsync ){
     sqlite3_exec(pDest, "PRAGMA synchronous=OFF; PRAGMA journal_mode=OFF;",
@@ -7529,7 +7542,7 @@ static int writeDb( char *azArg[], int nArg, ShellExState *psx, char **pzErr ){
     rc = 1;
   }
   close_db(pDest);
-  return rc;
+  return DCR_Ok|rc;
 }
 
 /*
@@ -8042,7 +8055,7 @@ COMMENT  emitted by a mkshellc macro. (See EMIT_METACMD_INIT further on.)
 static int someCommand(char *azArg[], int nArg, ShellExState *p, char **pzErr);
 */
 DISPATCH_CONFIG[
-  RETURN_TYPE=int
+  RETURN_TYPE=DotCmdRC
   STORAGE_CLASS=static
   ARGS_SIGNATURE=char *$arg4\[\], int $arg5, ShellExState *$arg6, char **$arg7
   DISPATCH_ENTRY={ "$cmd", ${cmd}Command, $arg1, $arg2, $arg3 },
@@ -8067,10 +8080,10 @@ COLLECT_HELP_TEXT[
   ",seeargs                 Echo arguments suffixed with |",
 ];
 DISPATCHABLE_COMMAND( seeargs ? 0 0 azArg nArg p ){
-  int rc = 0;
-  for (rc=1; rc<nArg; ++rc)
-    raw_printf(ISS(p)->out, "%s%s", azArg[rc], (rc==nArg-1)? "|\n" : "|");
-  return 0;
+  int ia = 0;
+  for (ia=1; ia<nArg; ++ia)
+    raw_printf(ISS(p)->out, "%s%s", azArg[ia], (ia==nArg-1)? "|\n" : "|");
+  return DCR_Ok;
 }
 
 CONDITION_COMMAND(archive !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB));
@@ -8102,7 +8115,7 @@ COLLECT_HELP_TEXT[
 ];
 DISPATCHABLE_COMMAND( archive ? 0 0 azArg nArg p ){
   open_db(p, 0);
-  if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP;
+  if( ISS(p)->bSafeMode ) return DCR_AbortError;
   return arDotCommand(p, 0, azArg, nArg);
 }
 
@@ -8114,7 +8127,6 @@ COLLECT_HELP_TEXT[
   ".auth ON|OFF             Show authorizer callbacks",
 ];
 DISPATCHABLE_COMMAND( auth 3 2 2 azArg nArg p ){
-  int rc = 0;
   open_db(p, 0);
   if( booleanValue(azArg[1]) ){
     sqlite3_set_authorizer(DBX(p), shellAuth, p);
@@ -8123,7 +8135,7 @@ DISPATCHABLE_COMMAND( auth 3 2 2 azArg nArg p ){
   }else{
     sqlite3_set_authorizer(DBX(p), 0, 0);
   }
-  return rc;
+  return DCR_Ok;
 }
 
 /*****************
@@ -8155,7 +8167,7 @@ COLLECT_HELP_TEXT[
 ];
 DISPATCHABLE_COMMAND( bail 3 2 2 ){
   bail_on_error = booleanValue(azArg[1]);
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -8171,12 +8183,12 @@ DISPATCHABLE_COMMAND( binary 3 2 2 ){
   }else{
     setTextMode(ISS(p)->out, 1);
   }
-  return 0;
+  return DCR_Ok;
 }
 
 DISPATCHABLE_COMMAND( cd ? 2 2 ){
   int rc=0;
-  if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP;
+  if( ISS(p)->bSafeMode ) return DCR_AbortError;
 #if defined(_WIN32) || defined(WIN32)
   wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
   rc = !SetCurrentDirectoryW(z);
@@ -8188,7 +8200,7 @@ DISPATCHABLE_COMMAND( cd ? 2 2 ){
     utf8_printf(STD_ERR, "Cannot change to directory \"%s\"\n", azArg[1]);
     rc = 1;
   }
-  return rc;
+  return DCR_Ok|rc;
 }
 
 /* The undocumented ".breakpoint" command causes a call
@@ -8196,7 +8208,7 @@ DISPATCHABLE_COMMAND( cd ? 2 2 ){
 */
 DISPATCHABLE_COMMAND( breakpoint 3 1 1 ){
   test_breakpoint();
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -8210,7 +8222,7 @@ COLLECT_HELP_TEXT[
 ];
 DISPATCHABLE_COMMAND( changes 3 2 2 ){
   setOrClearFlag(p, SHFLG_CountChanges, azArg[1]);
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( check 3 0 0 ){
   /* Cancel output redirection, if it is currently set (by .testcase)
@@ -8218,30 +8230,31 @@ DISPATCHABLE_COMMAND( check 3 0 0 ){
   ** azArg[1].  If there are differences, report an error and exit.
   */
   char *zRes = 0;
-  int rc=0;
+  int rc = 0;
+  DotCmdRC rv = DCR_Ok;
   output_reset(ISS(p));
   if( nArg!=2 ){
-    return SHELL_INVALID_ARGS;
+    return DCR_ArgError;
   }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){
     *pzErr = shellMPrintf(&rc, "Error: cannot read 'testcase-out.txt'");
-    rc = 2;
+    rv = DCR_Return;
   }else if( testcase_glob(azArg[1],zRes)==0 ){
     *pzErr =
       shellMPrintf(&rc,
                    "testcase-%s FAILED\n Expected: [%s]\n      Got: [%s]\n",
                    ISS(p)->zTestcase, azArg[1], zRes);
-    rc = 1;
+    rv = DCR_Error;
   }else{
     utf8_printf(STD_OUT, "testcase-%s ok\n", ISS(p)->zTestcase);
     ISS(p)->nCheck++;
   }
   sqlite3_free(zRes);
-  return rc;
+  return (rc==SQLITE_NOMEM)? DCR_Abort : rv;
 }
 DISPATCHABLE_COMMAND( clone ? 2 2 ){
-  if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP;
+  if( ISS(p)->bSafeMode ) return DCR_AbortError;
   tryToClone(p, azArg[1]);
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( connection ? 1 4 ){
   ShellInState *psi = ISS(p);
@@ -8278,16 +8291,16 @@ DISPATCHABLE_COMMAND( connection ? 1 4 ){
       /* No-op */
     }else if( psi->pAuxDb == &psi->aAuxDb[i] ){
       raw_printf(STD_ERR, "cannot close the active database connection\n");
-      return 1;
+      return DCR_Error;
     }else if( psi->aAuxDb[i].db ){
       session_close_all(psi, i);
       close_db(psi->aAuxDb[i].db);
       psi->aAuxDb[i].db = 0;
     }
   }else{
-    return SHELL_INVALID_ARGS;
+    return DCR_ArgError;
   }
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -8341,7 +8354,7 @@ DISPATCHABLE_COMMAND( databases 2 1 0 ){
     free(azName[i*2+1]);
   }
   sqlite3_free(azName);
-  return rc;
+  return DCR_Ok|(rc!=0);
 }
 DISPATCHABLE_COMMAND( dbconfig 3 1 3 ){
   static const struct DbConfigChoices {
@@ -8382,9 +8395,9 @@ DISPATCHABLE_COMMAND( dbconfig 3 1 3 ){
       ("Error: unknown dbconfig \"%s\"\n"
        "Enter \".dbconfig\" with no arguments for a list\n",
        azArg[1]);
-    return 1;
+    return DCR_ArgError;
   }
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( dbinfo 3 1 2 ){
   return shell_dbinfo_command(p, nArg, azArg);
@@ -8433,7 +8446,7 @@ DISPATCHABLE_COMMAND( dump ? 1 2 ){
           ("The --preserve-rowids option is not compatible"
                    " with SQLITE_OMIT_VIRTUALTABLE\n");
         sqlite3_free(zLike);
-        return 1;
+        return DCR_ArgError;
 #else
         ShellSetFlag(p, SHFLG_PreserveRowid);
 #endif
@@ -8450,7 +8463,7 @@ DISPATCHABLE_COMMAND( dump ? 1 2 ){
           *pzErr = sqlite3_mprintf
             ("Unknown option \"%s\" on \".dump\"\n", azArg[i]);
           sqlite3_free(zLike);
-          return 1;
+          return DCR_ArgError;
         }
       }
     }else{
@@ -8525,11 +8538,11 @@ DISPATCHABLE_COMMAND( dump ? 1 2 ){
   psi->showHeader = savedShowHeader;
   psi->shellFlgs = savedShellFlags;
 
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( echo ? 2 2 ){
   setOrClearFlag(p, SHFLG_Echo, azArg[1]);
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( eqp ? 0 0 ){
   ShellInState *psi = ISS(p);
@@ -8558,8 +8571,9 @@ DISPATCHABLE_COMMAND( eqp ? 0 0 ){
       psi->autoEQP = (u8)booleanValue(azArg[1]);
     }
   }else{
-    return SHELL_INVALID_ARGS;
+    return DCR_ArgError;
   }
+  return DCR_Ok;
 }
 
 /*****************
@@ -8574,10 +8588,10 @@ DISPATCHABLE_COMMAND( exit 3 1 0 ){
   int rc;
   if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 )
     p->shellAbruptExit = rc;
-  return 2;
+  return DCR_Return;
 }
 DISPATCHABLE_COMMAND( quit 1 1 0 ){
-  return 2;
+  return DCR_Return;
 }
 
 /*****************
@@ -8591,7 +8605,7 @@ COLLECT_HELP_TEXT[
 DISPATCHABLE_COMMAND( expert ? 1 1 ){
   open_db(p, 0);
   expertDotCommand(ISS(p), azArg, nArg);
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( explain ? 1 2 ){
   /* The ".explain" command is automatic now.  It is largely
@@ -8616,7 +8630,7 @@ DISPATCHABLE_COMMAND( explain ? 1 2 ){
     if( psi->mode==MODE_Explain ) psi->mode = psi->normalMode;
     psi->autoExplain = 1;
   }
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -8639,20 +8653,10 @@ COLLECT_HELP_TEXT[
   "     -e                    Send output to the system text editor",
   "     -x       Send output as CSV to a spreadsheet (same as \".excel\")",
 ];
-static int outputRedirs(char *[], int, ShellInState *,
-                        char **pzErr, int bOnce, int eMode);
-DISPATCHABLE_COMMAND( excel ? 1 2 ){
-  return outputRedirs(azArg, nArg, ISS(p), pzErr, 2, 'x');
-}
-DISPATCHABLE_COMMAND( once ? 1 6 ){
-  return outputRedirs(azArg, nArg, ISS(p), pzErr, 1, 0);
-}
-DISPATCHABLE_COMMAND( output ? 1 6 ){
-  return outputRedirs(azArg, nArg, ISS(p), pzErr, 0, 0);
-}
-
-static int outputRedirs(char *azArg[], int nArg, ShellInState *psi,
-                        char **pzErr, int bOnce, int eMode){
+/* Shared implementation of .excel, .once and .output */
+static DotCmdRC outputRedirs(char *azArg[], int nArg,
+                                      ShellInState *psi, char **pzErr,
+                                      int bOnce, int eMode){
   /* bOnce => 0: .output, 1: .once, 2: .excel */
   /* eMode => 'x' for excel, else 0 */
   int rc = 0;
@@ -8660,7 +8664,7 @@ static int outputRedirs(char *azArg[], int nArg, ShellInState *psi,
   int bTxtMode = 0;
   int i;
   int bBOM = 0;
-  if( psi->bSafeMode ) return SHELL_FORBIDDEN_OP;
+  if( psi->bSafeMode ) return DCR_AbortError;
   for(i=1; i<nArg; i++){
     char *z = azArg[i];
     if( z[0]=='-' ){
@@ -8673,7 +8677,7 @@ static int outputRedirs(char *azArg[], int nArg, ShellInState *psi,
         eMode = 'e';  /* text editor */
       }else{
         *pzErr = shellMPrintf(0,"       unknown option: \"%s\"\n",azArg[i]);
-        return SHELL_INVALID_ARGS;
+        return DCR_Unknown|i;
       }
     }else if( zFile==0 && eMode!='e' && eMode!='x' ){
       zFile = sqlite3_mprintf("%s", z);
@@ -8688,7 +8692,7 @@ static int outputRedirs(char *azArg[], int nArg, ShellInState *psi,
     }else{
       *pzErr = shellMPrintf(0,"       excess argument: \"%s\"\n", azArg[i]);
       sqlite3_free(zFile);
-      return SHELL_INVALID_ARGS;
+      return DCR_TooMany|i;
     }
   }
   if( zFile==0 ){
@@ -8753,7 +8757,16 @@ static int outputRedirs(char *azArg[], int nArg, ShellInState *psi,
     }
   }
   sqlite3_free(zFile);
-  return rc;
+  return DCR_Ok|rc;
+}
+DISPATCHABLE_COMMAND( excel ? 1 2 ){
+  return outputRedirs(azArg, nArg, ISS(p), pzErr, 2, 'x');
+}
+DISPATCHABLE_COMMAND( once ? 1 6 ){
+  return outputRedirs(azArg, nArg, ISS(p), pzErr, 1, 0);
+}
+DISPATCHABLE_COMMAND( output ? 1 6 ){
+  return outputRedirs(azArg, nArg, ISS(p), pzErr, 0, 0);
 }
 
 
@@ -8819,7 +8832,7 @@ DISPATCHABLE_COMMAND( filectrl ? 2 0 ){
       utf8_printf(psi->out, "  .filectrl %s %s\n",
                   aCtrl[i].zCtrlName, aCtrl[i].zUsage);
     }
-    return 1;
+    return DCR_ArgError;
   }
 
   /* Convert filectrl text option to value. Allow any
@@ -8833,7 +8846,7 @@ DISPATCHABLE_COMMAND( filectrl ? 2 0 ){
       }else{
         utf8_printf(STD_ERR, "Error: ambiguous file-control: \"%s\"\n"
                     "Use \".filectrl --help\" for help\n", zCmd);
-        return 1;
+        return DCR_ArgError;
       }
     }
   }
@@ -8904,13 +8917,13 @@ DISPATCHABLE_COMMAND( filectrl ? 2 0 ){
   }
   if( isOk==0 && iCtrl>=0 ){
     utf8_printf(psi->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
-    return 1;
+    return DCR_ArgError;
   }else if( isOk==1 ){
     char zBuf[100];
     sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes);
     raw_printf(psi->out, "%s\n", zBuf);
   }
-  return 0;
+  return DCR_Ok;
 }
 
 DISPATCHABLE_COMMAND( fullschema ? 1 2 ){
@@ -8930,7 +8943,7 @@ DISPATCHABLE_COMMAND( fullschema ? 1 2 ){
     nArg = 1;
   }
   if( nArg!=1 ){
-    return SHELL_INVALID_ARGS;
+    return DCR_TooMany|1;
   }
   open_db(p, 0);
   rc = sqlite3_exec(datax.dbUser,
@@ -8974,7 +8987,7 @@ COLLECT_HELP_TEXT[
 DISPATCHABLE_COMMAND( headers 6 2 2 ){
   ISS(p)->showHeader = booleanValue(azArg[1]);
   ISS(p)->shellFlgs |= SHFLG_HeaderSet;
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -9006,7 +9019,7 @@ DISPATCHABLE_COMMAND( help 3 1 3 ){
     utf8_printf(out, "Nothing matches '%s'\n", azArg[1]);
   }
   /* Help pleas never fail! */
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -9049,7 +9062,7 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
   ShellInState *psi = ISS(p);
   int rc = 0;
 
-  if(psi->bSafeMode) return SHELL_FORBIDDEN_OP;
+  if(psi->bSafeMode) return DCR_AbortError;
   memset(&sCtx, 0, sizeof(sCtx));
   if( 0==(sCtx.z = sqlite3_malloc64(120)) ){
     shell_out_of_memory();
@@ -9070,7 +9083,7 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
         zTable = z;
       }else{
         *pzErr = shellMPrintf(0,"       surplus argument: \"%s\"\n", z);
-        return SHELL_INVALID_ARGS;
+        return DCR_TooMany|i;
       }
     }else if( strcmp(z,"-v")==0 ){
       eVerbose++;
@@ -9090,13 +9103,13 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
       useOutputMode = 0;
     }else{
       *pzErr = shellMPrintf(0,"       unknown option: \"%s\"", z);
-      return SHELL_INVALID_ARGS;
+      return DCR_Unknown|i;
     }
   }
   if( zTable==0 ){
     *pzErr = shellMPrintf(0,"       missing %s argument.\n",
                 zFile==0 ? "FILE" : "TABLE");
-    return SHELL_INVALID_ARGS;
+    return DCR_Missing;
   }
   seenInterrupt = 0;
   open_db(p, 0);
@@ -9118,7 +9131,7 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
     }
     if( zYap!=0 ){
       *pzErr = shellMPrintf(0,"%s\n", zYap);
-      return 1;
+      return DCR_Error;
     }
     if( nSep==2 && psi->mode==MODE_Csv
         && strcmp(psi->rowSeparator,SEP_CrLf)==0 ){
@@ -9132,7 +9145,7 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
     if( nSep>1 ){
       *pzErr = sqlite3_mprintf
         ("Error: multi-character row separators not allowed for import\n");
-      return 1;
+      return DCR_Error;
     }
     sCtx.cColSep = psi->colSeparator[0];
     sCtx.cRowSep = psi->rowSeparator[0];
@@ -9142,7 +9155,7 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
   if( sCtx.zFile[0]=='|' ){
 #ifdef SQLITE_OMIT_POPEN
     *pzErr = shellMPrintf(0,"Error: pipes are not supported in this OS\n");
-    return 1;
+    return DCR_Error;
 #else
     sCtx.in = popen(sCtx.zFile+1, "r");
     sCtx.zFile = "<pipe>";
@@ -9155,7 +9168,7 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
   if( sCtx.in==0 ){
         *pzErr = shellMPrintf(0,"Error: cannot open \"%s\"\n", zFile);
     import_cleanup(&sCtx);
-    return 1;
+    return DCR_Error;
   }
   /* Here and below, resources must be freed before exit. */
   if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){
@@ -9209,7 +9222,7 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
       sqlite3_free(zSql);
       sqlite3_free(zFullTabName);
       import_cleanup(&sCtx);
-      return 1;
+      return DCR_Error;
     }
     zCreate = sqlite3_mprintf("%z%z\n", zCreate, zColDefs);
     if( eVerbose>=1 ){
@@ -9232,7 +9245,7 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
   nCol = sqlite3_column_count(pStmt);
   sqlite3_finalize(pStmt);
   pStmt = 0;
-  if( nCol==0 ) return 0; /* no columns, no error */
+  if( nCol==0 ) return DCR_Ok; /* no columns, no error */
   zSql = sqlite3_malloc64( nByte*2 + 20 + nCol*2 );
   if( zSql==0 ){
     import_cleanup(&sCtx);
@@ -9313,7 +9326,7 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
       "Added %d rows with %d errors using %d lines of input\n",
       sCtx.nRow, sCtx.nErr, sCtx.nLine-1);
   }
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -9353,7 +9366,7 @@ DISPATCHABLE_COMMAND( keyword ? 1 2 ){
     utf8_printf(out, "%s is%s a keyword\n",
                 azArg[1], (isKeyword)? "" : " not");
   }
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -9395,13 +9408,13 @@ DISPATCHABLE_COMMAND( imposter ? 3 3 ){
     ** where TABLE is a WITHOUT ROWID table.  In that case, the
     ** imposter is another WITHOUT ROWID table with the columns in
     ** storage order. */
-    return 1;
+    return DCR_ArgError;
   }
   open_db(p, 0);
   db = DBX(p);
   if( nArg==2 ){
     sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, "main", 0, 1);
-    return 0;
+    return DCR_Ok;
   }
   zSql = sqlite3_mprintf(
                          "SELECT rootpage, 0 FROM sqlite_schema"
@@ -9448,7 +9461,7 @@ DISPATCHABLE_COMMAND( imposter ? 3 3 ){
   if( i==0 || tnum==0 ){
     *pzErr = shellMPrintf(0,"no such index: \"%s\"\n", azArg[1]);
     sqlite3_free(zCollist);
-    return 1;
+    return DCR_Error;
   }
   if( lenPK==0 ) lenPK = 100000;
   zSql = sqlite3_mprintf("CREATE TABLE \"%w\"(%s,PRIMARY KEY(%.*s))"
@@ -9488,12 +9501,12 @@ DISPATCHABLE_COMMAND( iotrace ? 2 2 ){
     if( iotrace==0 ){
       *pzErr = shellMPrintf(0,"Error: cannot open \"%s\"\n", azArg[1]);
       sqlite3IoTrace = 0;
-      return 1;
+      return DCR_Error;
     }else{
       sqlite3IoTrace = iotracePrintf;
     }
   }
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( limits 5 1 3 ){
   static const struct {
@@ -9521,7 +9534,7 @@ DISPATCHABLE_COMMAND( limits 5 1 3 ){
              sqlite3_limit(DBX(p), aLimit[i].limitCode, -1));
     }
   }else if( nArg>3 ){
-    return SHELL_INVALID_ARGS;
+    return DCR_TooMany;
   }else{
     int iLimit = -1;
     n2 = strlen30(azArg[1]);
@@ -9531,7 +9544,7 @@ DISPATCHABLE_COMMAND( limits 5 1 3 ){
           iLimit = i;
         }else{
           *pzErr = shellMPrintf(0,"ambiguous limit: \"%s\"\n", azArg[1]);
-          return 1;
+          return DCR_Error;
         }
       }
     }
@@ -9540,7 +9553,7 @@ DISPATCHABLE_COMMAND( limits 5 1 3 ){
         ("unknown limit: \"%s\"\n"
          "enter \".limits\" with no arguments for a list.\n",
          azArg[1]);
-      return 1;
+      return DCR_ArgError;
     }
     if( nArg==3 ){
       sqlite3_limit(DBX(p), aLimit[iLimit].limitCode,
@@ -9549,7 +9562,7 @@ DISPATCHABLE_COMMAND( limits 5 1 3 ){
     fprintf(STD_OUT, "%20s %d\n", aLimit[iLimit].zLimitName,
            sqlite3_limit(DBX(p), aLimit[iLimit].limitCode, -1));
   }
-  return 0;
+  return DCR_Ok;
 }
 
 DISPATCHABLE_COMMAND( lint 3 1 0 ){
@@ -9568,7 +9581,7 @@ DISPATCHABLE_COMMAND( lint 3 1 0 ){
       ("Usage %s sub-command ?switches...?\n"
        "Where sub-commands are:\n"
        "    fkey-indexes\n", azArg[0]);
-    return 1;
+    return DCR_ArgError;
   }
   open_db(p, 0);
   db = DBX(p);
@@ -9736,7 +9749,7 @@ DISPATCHABLE_COMMAND( load ? 2 4 ){
 #if SHELL_DYNAMIC_EXTENSION
   u8 bLoadExt = 0;
 #endif
-  if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP;
+  if( ISS(p)->bSafeMode ) return DCR_AbortError;
   while( ai<nArg ){
     const char *zA = azArg[ai++];
 #if SHELL_DYNAMIC_EXTENSION
@@ -9745,26 +9758,26 @@ DISPATCHABLE_COMMAND( load ? 2 4 ){
 #endif
     if( zFile==0 ) zFile = zA;
     else if( zProc==0 ) zProc = zA;
-    else return SHELL_INVALID_ARGS;
+    else return DCR_TooMany;
   }
+  open_db(p, 0);
 #if SHELL_DYNAMIC_EXTENSION
   if( bLoadExt ){
     rc = load_shell_extension(p, zFile, zProc, pzErr);
   }else
 #endif
   {
-    open_db(p, 0);
     rc = sqlite3_load_extension(DBX(p), zFile, zProc, pzErr);
   }
-  return rc!=SQLITE_OK;
+  return DCR_Ok|(rc!=SQLITE_OK);
 }
 
 DISPATCHABLE_COMMAND( log ? 2 2 ){
   const char *zFile = azArg[1];
-  if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP;
+  if( ISS(p)->bSafeMode ) return DCR_AbortError;
   output_file_close(ISS(p)->pLog);
   ISS(p)->pLog = output_file_open(zFile, 0);
-  return 0;
+  return DCR_Ok;
 }
 
 static void effectMode(ShellInState *psi, u8 modeRequest, u8 modeNominal){
@@ -9932,7 +9945,7 @@ DISPATCHABLE_COMMAND( mode ? 1 0 ){
     }
   }
   psi->cMode = psi->mode;
-  return 0;
+  return DCR_Ok;
  flag_unknown:
   utf8_printf(STD_ERR, "Error: Unknown .mode option: %s\nValid options:\n%s",
               zArg,
@@ -9941,15 +9954,15 @@ DISPATCHABLE_COMMAND( mode ? 1 0 ){
               "  --wordwrap on/off\n"
               "  --wrap N\n"
               "  --ww\n");
-  return 1;
+  return DCR_ArgError;
  mode_unknown:
   raw_printf(STD_ERR, "Error: Mode should be one of: "
              "ascii box column csv html insert json line list markdown "
              "qbox quote table tabs tcl\n");
-  return 1;
+  return DCR_ArgError;
  mode_badarg:
   utf8_printf(STD_ERR, "Error: Invalid .mode argument: %s\n", zArg);
-  return 1;
+  return DCR_ArgError;
 }
 
 /*****************
@@ -10006,10 +10019,10 @@ DISPATCHABLE_COMMAND( open 3 1 0 ){
 #endif /* SQLITE_OMIT_DESERIALIZE */
     }else if( z[0]=='-' ){
       *pzErr = shellMPrintf(0,"unknown option: %s\n", z);
-      return SHELL_INVALID_ARGS;
+      return DCR_Unknown|iName;
     }else if( zFN ){
       *pzErr = shellMPrintf(0,"extra argument: \"%s\"\n", z);
-      return SHELL_INVALID_ARGS;
+      return DCR_TooMany;
     }else{
       zFN = z;
     }
@@ -10035,7 +10048,7 @@ DISPATCHABLE_COMMAND( open 3 1 0 ){
         && strcmp(zFN,":memory:")!=0
         ){
       *pzErr = shellMPrintf(0,"cannot open database files in safe mode");
-      return SHELL_FORBIDDEN_OP;
+      return DCR_AbortError;
     }
     if( zFN ){
       zNewFilename = sqlite3_mprintf("%s", zFN);
@@ -10072,13 +10085,13 @@ DISPATCHABLE_COMMAND( nonce ? 2 2 ){
   }
   /* Suspend safe mode for 1 meta-command after this. */
   psi->bSafeModeFuture = 2;
-  return 0;
+  return DCR_Ok;
 }
 
 DISPATCHABLE_COMMAND( nullvalue ? 2 2 ){
   sqlite3_snprintf(sizeof(ISS(p)->nullValue), ISS(p)->nullValue, "%.*s",
                    (int)ArraySize(ISS(p)->nullValue)-1, azArg[1]);
-  return 0;
+  return DCR_Ok;
 }
 
 /* Helper functions for .parameter command
@@ -10232,7 +10245,7 @@ static int parameters_load(sqlite3 *db, const char *azArg[], int nArg){
   const char *zStore = params_store_path((nArg>1)? azArg[1] : 0);
   if( zStore==0 ){
     utf8_printf(STD_ERR, "Cannot form parameter load path. Nothing loaded.\n");
-    return 1;
+    return DCR_Error;
   }else{
     const char **pzFirst = (nArg>2)? azArg+2 : 0;
     int nNames = (nArg>2)? nArg-2 : 0;
@@ -10247,7 +10260,7 @@ static int parameters_save(sqlite3 *db, const char *azArg[], int nArg){
   const char *zStore = params_store_path((nArg>1)? azArg[1] : 0);
   if( zStore==0 ){
     utf8_printf(STD_ERR, "Cannot form parameter save path. Nothing saved.\n");
-    return 1;
+    return DCR_Error;
   }else{
     const char **pzFirst = (nArg>2)? azArg+2 : 0;
     int nNames = (nArg>2)? nArg-2 : 0;
@@ -10616,7 +10629,7 @@ DISPATCHABLE_COMMAND( parameter 2 2 0 ){
     if( !INSOURCE_IS_INTERACTIVE(psi->pInSource) ){
       utf8_printf(STD_ERR, "Error: "
                   ".parameter edit can only be used interactively.\n");
-      return 1;
+      return DCR_Error;
     }
     param_table_init(db);
     if( psi->zEditor==0 ){
@@ -10638,7 +10651,7 @@ DISPATCHABLE_COMMAND( parameter 2 2 0 ){
                   " editor and restart, or rerun\n "
                   ".parameter edit with an initial "
                   "edit option, --editor=EDITOR_COMMAND .\n");
-      return 1;
+      return DCR_Error;
     }
     /* Future: Allow an option whereby new value can be evaluated
      * the way that .parameter set ... does.
@@ -10654,14 +10667,14 @@ DISPATCHABLE_COMMAND( parameter 2 2 0 ){
         default:
           utf8_printf(STD_ERR, "Error: bad .parameter name: %s\n",
                       azArg[--ia]);
-          return 1;
+          return DCR_Error;
         }
       }
       ptu = classify_param_name(azArg[ia]);
       if( ptu==PTU_Nil ){
         utf8_printf(STD_ERR, "Error: %s cannot be a binding or executable"
                     " parameter name.\n", azArg[ia]);
-        return 1;
+        return DCR_Error;
       }
       rc = edit_one_param(db, azArg[ia], eval, ptu, psi->zEditor);
       ++ia;
@@ -10730,7 +10743,7 @@ DISPATCHABLE_COMMAND( parameter 2 2 0 ){
 
   {  /* If no command name and arg count matches, show a syntax error */
     showHelp(ISS(p)->out, "parameter", p);
-    return 1;
+    return DCR_ArgError;
   }
 
   return rc;
@@ -10754,7 +10767,7 @@ DISPATCHABLE_COMMAND( print 3 1 0 ){
   for(i=1; i<nArg; i++){
     utf8_printf(ISS(p)->out, "%s%s", azArg[i], (i==nArg-1)? "\n" : " ");
   }
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( progress 3 2 0 ){
   ShellInState *psi = ISS(p);
@@ -10783,21 +10796,21 @@ DISPATCHABLE_COMMAND( progress 3 2 0 ){
       if( strcmp(z,"limit")==0 ){
         if( i+1>=nArg ){
           *pzErr = shellMPrintf(0,"Error: missing argument on --limit\n");
-          return SHELL_INVALID_ARGS;
+          return DCR_Unpaired|i;
         }else{
           psi->mxProgress = (int)integerValue(azArg[++i]);
         }
         continue;
       }
       *pzErr = shellMPrintf(0, "Error: unknown option: \"%s\"\n", azArg[i]);
-      return SHELL_INVALID_ARGS;
+      return DCR_Unknown|i;
     }else{
       nn = (int)integerValue(z);
     }
   }
   open_db(p, 0);
   sqlite3_progress_handler(DBX(p), nn, progress_handler, psi);
-  return 0;
+  return DCR_Ok;
 }
 /* Allow too few arguments by tradition, (a form of no-op.) */
 DISPATCHABLE_COMMAND( prompt ? 1 3 ){
@@ -10807,7 +10820,7 @@ DISPATCHABLE_COMMAND( prompt ? 1 3 ){
   if( nArg >= 3) {
     strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1);
   }
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -10829,7 +10842,7 @@ DISPATCHABLE_COMMAND( read 3 2 2 ){
   int rc = 0;
   FILE *inUse = 0;
   int (*fCloser)(FILE *) = 0;
-  if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP;
+  if( ISS(p)->bSafeMode ) return DCR_AbortError;
   if( azArg[1][0]=='|' ){
 #ifdef SQLITE_OMIT_POPEN
     *pzErr = shellMPrintf(0,"Error: pipes are not supported in this OS\n");
@@ -10907,7 +10920,7 @@ DISPATCHABLE_COMMAND( recover ? 1 7 ){
     else{
       *pzErr = shellMPrintf(0,"unexpected option: %s\n", azArg[i]);
       showHelp(out, azArg[0], p);
-      return 1;
+      return DCR_ArgError;
     }
   }
 
@@ -11192,7 +11205,7 @@ DISPATCHABLE_COMMAND( restore ? 2 3 ){
   sqlite3_backup *pBackup;
   int nTimeout = 0;
 
-  if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP;
+  if( ISS(p)->bSafeMode ) return DCR_AbortError;
   if( nArg==2 ){
     zSrcFile = azArg[1];
     zDb = "main";
@@ -11200,20 +11213,20 @@ DISPATCHABLE_COMMAND( restore ? 2 3 ){
     zSrcFile = azArg[2];
     zDb = azArg[1];
   }else{
-    return SHELL_INVALID_ARGS;
+    return DCR_TooMany;
   }
   rc = sqlite3_open(zSrcFile, &pSrc);
   if( rc!=SQLITE_OK ){
     *pzErr = shellMPrintf(0,"Error: cannot open \"%s\"\n", zSrcFile);
     close_db(pSrc);
-    return 1;
+    return DCR_Error;
   }
   open_db(p, 0);
   pBackup = sqlite3_backup_init(DBX(p), zDb, pSrc, "main");
   if( pBackup==0 ){
     *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(DBX(p)));
     close_db(pSrc);
-    return 1;
+    return DCR_Error;
   }
   while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK
          || rc==SQLITE_BUSY  ){
@@ -11251,7 +11264,7 @@ DISPATCHABLE_COMMAND( scanstats ? 2 2 ){
 #ifndef SQLITE_ENABLE_STMT_SCANSTATUS
   raw_printf(STD_ERR, "Warning: .scanstats not available in this build.\n");
 #endif
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( schema ? 1 2 ){
   int rc;
@@ -11285,11 +11298,11 @@ DISPATCHABLE_COMMAND( schema ? 1 2 ){
       bNoSystemTabs = 1;
     }else if( azArg[ii][0]=='-' ){
       *pzErr = shellMPrintf(0,"Unknown option: \"%s\"\n", azArg[ii]);
-      return SHELL_INVALID_ARGS;
+      return DCR_Unknown|ii;
     }else if( zName==0 ){
       zName = azArg[ii];
     }else{
-      return SHELL_INVALID_ARGS;
+      return DCR_TooMany;
     }
   }
   if( zName!=0 ){
@@ -11323,7 +11336,7 @@ DISPATCHABLE_COMMAND( schema ? 1 2 ){
     if( rc ){
       *pzErr = shellMPrintf(0,"Error: %s\n", sqlite3_errmsg(datax.dbUser));
       sqlite3_finalize(pStmt);
-      return 1;
+      return DCR_Error;
     }
     appendText(&sSelect, "SELECT sql FROM", 0);
     iSchema = 0;
@@ -11391,12 +11404,12 @@ DISPATCHABLE_COMMAND( schema ? 1 2 ){
   }
   if( zErrMsg ){
     *pzErr = zErrMsg;
-    return 1;
+    return DCR_Error;
   }else if( rc != SQLITE_OK ){
     *pzErr = shellMPrintf(0,"Error: querying schema information\n");
-    return 1;
+    return DCR_Error;
   }else{
-    return 0;
+    return DCR_Ok;
   }
 }
 
@@ -11431,7 +11444,7 @@ COLLECT_HELP_TEXT[
 DISPATCHABLE_COMMAND( selecttrace ? 1 0 ){
   unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff;
   sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 1, &x);
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( separator ? 2 3 ){
   if( nArg>=2 ){
@@ -11442,7 +11455,7 @@ DISPATCHABLE_COMMAND( separator ? 2 3 ){
     sqlite3_snprintf(sizeof(ISS(p)->rowSeparator), ISS(p)->rowSeparator,
                      "%.*s", (int)ArraySize(ISS(p)->rowSeparator)-1, azArg[2]);
   }
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( session 3 2 0 ){
   int rc = 0;
@@ -11494,7 +11507,7 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){
     FILE *cs_out = 0;
     if( failIfSafeMode
         (p, "cannot run \".session %s\" in safe mode", azCmd[0]) ){
-      rc = SHELL_FORBIDDEN_OP;
+      rc = DCR_AbortError;
     }else{
       if( nCmd!=2 ) goto session_syntax_error;
       if( pSession->p==0 ) goto session_not_open;
@@ -11646,7 +11659,7 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){
   /* If no command name matches, show a syntax error */
   session_syntax_error:
     showHelp(out, "session", p);
-    return 1;
+    return DCR_ArgError;
   }
   return rc;
 }
@@ -11682,10 +11695,10 @@ DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){
             {
               *pzErr = sqlite3_mprintf
                 ("Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]);
-              return SHELL_INVALID_ARGS;
+              return DCR_Unknown|i;
             }
     }else if( zLike ){
-      return SHELL_INVALID_ARGS;
+      return DCR_TooMany;
     }else{
       zLike = z;
       bSeparate = 1;
@@ -11760,7 +11773,7 @@ DISPATCHABLE_COMMAND( sha3sum 4 1 1 ){
     shell_exec(p, zSql, 0);
   }
   sqlite3_free(zSql);
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -11796,7 +11809,7 @@ DISPATCHABLE_COMMAND( selftest_bool 10 0 0 ){
     v = booleanValue(azArg[i]);
     utf8_printf(ISS(p)->out, "%s: %d 0x%x\n", azArg[i], v, v);
   }
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( selftest_int 10 0 0 ){
   int i; sqlite3_int64 v;
@@ -11806,7 +11819,7 @@ DISPATCHABLE_COMMAND( selftest_int 10 0 0 ){
     sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v);
     utf8_printf(ISS(p)->out, "%s", zBuf);
   }
-  return 0;
+  return DCR_Ok;
 }
 
 DISPATCHABLE_COMMAND( selftest 4 0 0 ){
@@ -11834,7 +11847,7 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){
           *pzErr = sqlite3_mprintf
             ("Unknown option \"%s\" on \"%s\"\n"
              "Should be one of: --init -v\n", azArg[i], azArg[0]);
-          return 1;
+          return DCR_ArgError;
         }
   }
   open_db(p,0);
@@ -11864,7 +11877,7 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){
     if( rc ){
       *pzErr = shellMPrintf(0,"Error querying the selftest table\n");
       sqlite3_finalize(pStmt);
-      return 1;
+      return DCR_Error;
     }
     for(i=1; sqlite3_step(pStmt)==SQLITE_ROW; i++){
       int tno = sqlite3_column_int(pStmt, 0);
@@ -11917,7 +11930,7 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){
 DISPATCHABLE_COMMAND( shell ? 2 0 ){
   char *zCmd;
   int i, x;
-  if( ISS(p)->bSafeMode ) return SHELL_FORBIDDEN_OP;
+  if( ISS(p)->bSafeMode ) return DCR_AbortError;
   zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]);
   shell_check_oom(zCmd);
   for(i=2; i<nArg; i++){
@@ -11928,7 +11941,7 @@ DISPATCHABLE_COMMAND( shell ? 2 0 ){
   x = system(zCmd);
   sqlite3_free(zCmd);
   if( x ) raw_printf(STD_ERR, "%s command returns %d\n", azArg[0], x);
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( shxopts 3 0 0 ){
   static struct { const char *name; u8 mask; } shopts[] = {
@@ -11979,10 +11992,10 @@ DISPATCHABLE_COMMAND( shxopts 3 0 0 ){
                  shopts[io].name,  v, m);
     }
   }
-  return 0;
+  return DCR_Ok;
  moan_error:
   raw_printf(STD_ERR, "Error: %s %s\n", zAbout, zMoan);
-  return 1;
+  return DCR_ArgError;
 }
 DISPATCHABLE_COMMAND( system ? 2 0 ){
   return shellCommand(azArg, nArg, p, pzErr);
@@ -12036,7 +12049,7 @@ DISPATCHABLE_COMMAND( show ? 1 1 ){
   raw_printf(out, "\n");
   utf8_printf(out, "%12.12s: %s\n", "filename",
               psi->pAuxDb->zDbFilename ? psi->pAuxDb->zDbFilename : "");
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( stats ? 0 0 ){
   ShellInState *psi = ISS(p);
@@ -12052,9 +12065,9 @@ DISPATCHABLE_COMMAND( stats ? 0 0 ){
     display_stats(DBX(p), psi, 0);
   }else{
     *pzErr = shellMPrintf(0,"Usage: .stats ?on|off|stmt|vmstep?\n");
-    return 1;
+    return DCR_ArgError;
   }
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -12088,7 +12101,7 @@ static int showTableLike(char *azArg[], int nArg, ShellExState *p,
     ** command does not. */
     *pzErr = shellMPrintf(0,"Usage: .indexes ?LIKE-PATTERN?\n");
     sqlite3_finalize(pStmt);
-    return 1;
+    return DCR_ArgError;
   }
   for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
     const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
@@ -12189,7 +12202,7 @@ static int showTableLike(char *azArg[], int nArg, ShellExState *p,
 
   for(ii=0; ii<nRow; ii++) sqlite3_free(azResult[ii]);
   sqlite3_free(azResult);
-  return 0;
+  return DCR_Ok;
 }
 
 COLLECT_HELP_TEXT[
@@ -12215,7 +12228,7 @@ DISPATCHABLE_COMMAND( tables 2 1 3 ){
       --nArg;
       break;
     default:
-      return SHELL_INVALID_ARGS;
+      return DCR_Unknown|1;
     }
   }
 #endif
@@ -12268,7 +12281,7 @@ DISPATCHABLE_COMMAND( testcase ? 0 0 ){
   }else{
     sqlite3_snprintf(sizeof(psi->zTestcase), psi->zTestcase, "?");
   }
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( testctrl ? 0 0 ){
   FILE *out = ISS(p)->out;
@@ -12324,7 +12337,7 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){
       utf8_printf(out, "  .testctrl %s %s\n",
                   aCtrl[i].zCtrlName, aCtrl[i].zUsage);
     }
-    return 1;
+    return DCR_ArgError;
   }
 
   /* convert testctrl text option to value. allow any unique prefix
@@ -12339,7 +12352,7 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){
         *pzErr = sqlite3_mprintf
           ("Error: ambiguous test-control: \"%s\"\n"
            "Use \".testctrl --help\" for help\n", zCmd);
-        return 1;
+        return DCR_ArgError;
       }
     }
   }
@@ -12487,18 +12500,18 @@ DISPATCHABLE_COMMAND( testctrl ? 0 0 ){
   }
   if( isOk==0 && iCtrl>=0 ){
     utf8_printf(out, "Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
-    return 1;
+    return DCR_ArgError;
   }else if( isOk==1 ){
     raw_printf(out, "%d\n", rc2);
   }else if( isOk==2 ){
     raw_printf(out, "0x%08x\n", rc2);
   }
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( timeout 4 1 2 ){
   open_db(p, 0);
   sqlite3_busy_timeout(DBX(p), nArg>=2 ? (int)integerValue(azArg[1]) : 0);
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( timer ? 2 2 ){
   enableTimer = booleanValue(azArg[1]);
@@ -12506,7 +12519,7 @@ DISPATCHABLE_COMMAND( timer ? 2 2 ){
     raw_printf(STD_ERR, "Error: timer not available on this system.\n");
     enableTimer = 0;
   }
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( trace ? 0 0 ){
   ShellInState *psi = ISS(p);
@@ -12541,7 +12554,7 @@ DISPATCHABLE_COMMAND( trace ? 0 0 ){
       }
       else {
         *pzErr = shellMPrintf(0,"Unknown option \"%s\" on \".trace\"\n", z);
-        return 1;
+        return DCR_ArgError;
       }
     }else{
       output_file_close(psi->traceOut);
@@ -12554,7 +12567,7 @@ DISPATCHABLE_COMMAND( trace ? 0 0 ){
     if( mType==0 ) mType = SQLITE_TRACE_STMT;
     sqlite3_trace_v2(DBX(p), mType, sql_trace_callback, psi);
   }
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -12581,7 +12594,7 @@ DISPATCHABLE_COMMAND( unmodule ? 2 0 ){
       sqlite3_create_module(DBX(p), azArg[ii], 0, 0);
     }
   }
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -12602,7 +12615,7 @@ DISPATCHABLE_COMMAND( user ? 0 0 ){
   if( nArg<2 ){
   teach_fail:
     *pzErr = shellMPrintf(0,usage);
-    return 1;
+    return DCR_ArgError;
   }
   open_db(p, 0);
   if( strcmp(azArg[1],"login")==0 ){
@@ -12613,7 +12626,7 @@ DISPATCHABLE_COMMAND( user ? 0 0 ){
                                    strlen30(azArg[3]));
     if( rc ){
       *pzErr = shellMPrintf(0,"Authentication failed for user %s\n", azArg[2]);
-      return 1;
+      return DCR_Error;
     }
   }else if( strcmp(azArg[1],"add")==0 ){
     if( nArg!=5 ){
@@ -12623,7 +12636,7 @@ DISPATCHABLE_COMMAND( user ? 0 0 ){
                           booleanValue(azArg[4]));
     if( rc ){
       *pzErr = shellMPrintf(0,"User-Add failed: %d\n", rc);
-      return 1;
+      return DCR_Error;
     }
   }else if( strcmp(azArg[1],"edit")==0 ){
     if( nArg!=5 ){
@@ -12633,7 +12646,7 @@ DISPATCHABLE_COMMAND( user ? 0 0 ){
                              booleanValue(azArg[4]));
     if( rc ){
       *pzErr = shellMPrintf(0,"User-Edit failed: %d\n", rc);
-      return 1;
+      return DCR_Error;
     }
   }else if( strcmp(azArg[1],"delete")==0 ){
     if( nArg!=3 ){
@@ -12642,12 +12655,12 @@ DISPATCHABLE_COMMAND( user ? 0 0 ){
     rc = sqlite3_user_delete(DBX(p), azArg[2]);
     if( rc ){
       *pzErr = shellMPrintf(0,"User-Delete failed: %d\n", rc);
-      return 1;
+      return DCR_Error;
     }
   }else{
     goto teach_fail;
   }
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -12677,7 +12690,7 @@ DISPATCHABLE_COMMAND( version ? 1 1 ){
 #elif defined(__GNUC__) && defined(__VERSION__)
   utf8_printf(out, "gcc-" __VERSION__ "\n");
 #endif
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( vfsinfo ? 1 2 ){
   const char *zDbName = nArg==2 ? azArg[1] : "main";
@@ -12692,7 +12705,7 @@ DISPATCHABLE_COMMAND( vfsinfo ? 1 2 ){
       raw_printf(out, "vfs.mxPathname = %d\n", pVfs->mxPathname);
     }
   }
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( vfslist ? 1 1 ){
   sqlite3_vfs *pVfs;
@@ -12711,7 +12724,7 @@ DISPATCHABLE_COMMAND( vfslist ? 1 1 ){
       raw_printf(out, "-----------------------------------\n");
     }
   }
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( vfsname ? 0 0 ){
   const char *zDbName = nArg==2 ? azArg[1] : "main";
@@ -12723,7 +12736,7 @@ DISPATCHABLE_COMMAND( vfsname ? 0 0 ){
       sqlite3_free(zVfsName);
     }
   }
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -12749,12 +12762,12 @@ COLLECT_HELP_TEXT[
 ];
 DISPATCHABLE_COMMAND( width ? 1 0 ){
   setColumnWidths(p, azArg+1, nArg-1);
-  return 0;
+  return DCR_Ok;
 }
 DISPATCHABLE_COMMAND( wheretrace ? 1 2 ){
   unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff;
   sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &x);
-  return 0;
+  return DCR_Ok;
 }
 
 /*****************
@@ -12768,24 +12781,25 @@ DISPATCHABLE_COMMAND( x ? 1 0 ){
   int ia, rc, nErrors = 0;
   sqlite3_stmt *pStmt = 0;
   sqlite3 *db;
+  DotCmdRC rv = DCR_Ok;
 
   open_db(p, 0);
   db = DBX(p);
   if( db==0 ){
     utf8_printf(STD_ERR, ".x can only be done with a database open.\n");
-    return 1;
+    return DCR_Error;
   }
   if( sqlite3_table_column_metadata(db, PARAM_TABLE_SCHEMA,
                                     PARAM_TABLE_NAME,
                                     "key", 0, 0, 0, 0, 0)!=SQLITE_OK ){
     utf8_printf(STD_ERR, "No "PARAM_TABLE_SNAME" table exists.\n");
-    return 1;
+    return DCR_Error;
   }
   rc = sqlite3_prepare_v2(db, "SELECT value FROM "PARAM_TABLE_SNAME
                           " WHERE key=$1 AND uses=1", -1, &pStmt, 0);
   if( rc!=SQLITE_OK ){
     utf8_printf(STD_ERR, PARAM_TABLE_SNAME" is wrongly created.\n");
-    return 1;
+    return DCR_Error;
   }
   for( ia=1; ia < nArg; ++ia ){
     if( isalpha(azArg[ia][0]) ){
@@ -12808,9 +12822,14 @@ DISPATCHABLE_COMMAND( x ? 1 0 ){
           ShellInState *psi = ISS(p);
           InSource inRedir
             = INSOURCE_STR_REDIR(zSubmit, azArg[ia], psi->pInSource);
+          sqlite3_reset(pStmt); /* End the parameter read to unlock DB. */
           shell_check_oom(zSubmit);
           psi->pInSource = &inRedir;
-          rc = process_input(psi);
+          rv = process_input(psi);
+          if( rv<DCR_ArgIxMask ){
+            nErrors += (rv & DCR_Error);
+            if( rv>=DCR_Exit ) break;
+          }
           sqlite3_free(zSubmit);
           psi->pInSource = inRedir.pFrom;
         }else{
@@ -12829,7 +12848,7 @@ DISPATCHABLE_COMMAND( x ? 1 0 ){
     }
   }
   sqlite3_finalize(pStmt);
-  return (rc==2)? 2 : nErrors>0;
+  return (rv==DCR_Return)? DCR_Return : DCR_Ok|(nErrors>0);
 }
 
 /* End of published, standard meta-command implementation functions
@@ -12850,9 +12869,10 @@ COLLECT_HELP_TEXT[
 static void MetaCommand_dtor(MetaCommand *);
 static const char * MetaCommand_name(MetaCommand *);
 static const char * MetaCommand_help(MetaCommand *, int);
-static int MetaCommand_argsCheck(MetaCommand *, char **, int, char *azArgs[]);
-static int MetaCommand_execute(MetaCommand *, ShellExState *,
-                               char **, int, char *azArgs[]);
+static DotCmdRC
+  MetaCommand_argsCheck(MetaCommand *, char **, int nArgs, char *azArgs[]);
+static DotCmdRC
+  MetaCommand_execute(MetaCommand *, ShellExState *, char **, int, char *[]);
 
 static VTABLE_NAME(MetaCommand) meta_cmd_VtabBuiltIn = {
   MetaCommand_dtor,
@@ -12866,7 +12886,8 @@ static VTABLE_NAME(MetaCommand) meta_cmd_VtabBuiltIn = {
 static struct CommandInfo {
   VTABLE_NAME(MetaCommand) *mcVtabBuiltIn;
   const char * cmdName;
-  int (*cmdDoer)(char *azArg[], int nArg, ShellExState *, char **pzErr);
+  DotCmdRC (*cmdDoer)(char *azArg[], int nArg,
+                               ShellExState *, char **pzErr);
   unsigned char minLen, minArgs, maxArgs;
   const char *azHelp[2]; /* primary and secondary help text */
   void * pCmdData;
@@ -12901,19 +12922,28 @@ static const char * MetaCommand_help(MetaCommand *pMe, int more){
   else return 0;
 }
 
-static int MetaCommand_argsCheck(MetaCommand *pMe,
-                                 char **pzErrMsg, int nArgs, char *azArgs[]){
+static DotCmdRC
+  MetaCommand_argsCheck(MetaCommand *pMe,
+                        char **pzErrMsg, int nArgs, char *azArgs[]){
   struct CommandInfo *pci = (struct CommandInfo *)pMe;
-  UNUSED_PARAMETER(pzErrMsg); /* Future: Make this more informative. */
-  UNUSED_PARAMETER(nArgs);
   UNUSED_PARAMETER(azArgs);
-  if( pci->minArgs > nArgs||(pci->maxArgs > 0 && pci->maxArgs < nArgs) ){
-    return SHELL_INVALID_ARGS;
-  }else return 0;
+  if( pci->minArgs > nArgs ){
+    if( pzErrMsg ){
+      *pzErrMsg = sqlite3_mprintf("Too few arguments, need %d\n", pci->minArgs);
+    }
+    return DCR_TooFew;
+  }else if( pci->maxArgs > 0 && pci->maxArgs < nArgs ){
+    if( pzErrMsg ){
+      *pzErrMsg = sqlite3_mprintf("Too many arguments, over %d\n",
+                                  pci->maxArgs);
+    }
+    return DCR_TooMany;
+  }else return DCR_Ok;
 }
 
-static int MetaCommand_execute(MetaCommand *pMe, ShellExState *pssx,
-                               char **pzErrMsg, int nArgs, char *azArgs[]){
+static DotCmdRC
+  MetaCommand_execute(MetaCommand *pMe, ShellExState *pssx,
+                      char **pzErrMsg, int nArgs, char *azArgs[]){
   return (((struct CommandInfo *)pMe)->cmdDoer)(azArgs, nArgs, pssx, pzErrMsg);
 }
 
@@ -13046,9 +13076,6 @@ MetaCommand *findMetaCommand(const char *cmdName, ShellExState *psx,
   }
 }
 
-#define NO_SUCH_COMMAND SQLITE_NOTFOUND
-/* SHELL_INVALID_ARGS defined as SQLITE_MISUSE in shext_linkage.h */
-
 /*****************
 ** Command dispatcher
 ** After successful command lookup and (simple) argument checking,
@@ -13057,14 +13084,15 @@ MetaCommand *findMetaCommand(const char *cmdName, ShellExState *psx,
 ** The return is either a dispatch error or whatever the dispatched
 ** MetaCommand returns.
 */
-int dispatchCommand(char *azArg[], int nArg, ShellExState *psx, char **pzErr){
+static DotCmdRC dispatchCommand(char *azArg[], int nArg,
+                                ShellExState *psx, char **pzErr){
   const char *cmdName = azArg[0];
-  int nFound = 0, argsCheck;
+  int nFound = 0;
+  DotCmdRC argsCheck;
   MetaCommand *pMC = findMetaCommand(cmdName, psx, &nFound);
-  if( 0==pMC ) return NO_SUCH_COMMAND;
-  /* Future: Distinguish not found from ambiguous (due to too-short name.) */
+  if( 0==pMC || nFound>1 ) return (nFound>1)? DCR_Ambiguous: DCR_Unknown;
   argsCheck = pMC->pMethods->argsCheck(pMC, pzErr, nArg, azArg);
-  if( argsCheck!=0 ) return SHELL_INVALID_ARGS;
+  if( argsCheck!=DCR_Ok ) return argsCheck;
   /* Replace any user-shortened command name with its whole name. */
   azArg[0] = (char *)(pMC->pMethods->name(pMC));
   return pMC->pMethods->execute(pMC, psx, pzErr, nArg, azArg);
@@ -13163,19 +13191,20 @@ static int showHelp(FILE *out, const char *zPattern, ShellExState *psx){
 }
 
 /*
-** If an input line begins with "." then invoke this routine to
-** process that line.
+** If an input line or line group begins with "." then invoke this routine
+** to process that line.
 **
-** Return 1 on error, 2 to exit, and 0 otherwise.
+** Return sanitized DotCmdRC values, with invocation error codes
+** translated to DCR_Error, so that only these 8 returns are possible:
+** DCR_Ok, DCR_Return, DCR_Exit or DCR_Abort possibly or'ed with DCR_Error.
 */
-static int do_meta_command(char *zLine, ShellExState *psx){
+static DotCmdRC do_meta_command(char *zLine, ShellExState *psx){
   int h = 1;
   int nArg = 0;
   int n, c;
-  int rc = 0;
   char *azArg[52];
   char *zErr = 0;
-  int dispatchResult;
+  DotCmdRC dispatchResult;
 #if SHELL_VARIABLE_EXPANSION
   int ncLineIn = strlen30(zLine);
   u8 bExpVars = SHEXT_VAREXP(ISS(psx));
@@ -13214,58 +13243,96 @@ static int do_meta_command(char *zLine, ShellExState *psx){
 
   /* Process the input line.
   */
-  if( nArg==0 ) return 0; /* no tokens, no error */
-  n = strlen30(azArg[0]);
-  c = azArg[0][0];
+  if( nArg==0 ) return DCR_Ok; /* no tokens, no error, done */
+
   clearTempFile(ISS(psx));
 
   dispatchResult = dispatchCommand(azArg, nArg, psx, &zErr);
   if( psx->shellAbruptExit!=0 ){
-    dispatchResult = SHELL_FORBIDDEN_OP;
-  }
-  switch( dispatchResult ){
-  case NO_SUCH_COMMAND:
-    utf8_printf(STD_ERR, "Error: unknown command: \"%s\"\n", azArg[0]);
-    if( stdin_is_interactive )
-      utf8_printf(STD_ERR, "  Enter \".help\" for a list of commands.\n");
-    if( psx->dbShell!=0 && sqlite3_strnicmp(azArg[0],"quit",n)==0 ) rc = 2;
-    else rc = 1;
-    break;
-  case SHELL_INVALID_ARGS:
-    utf8_printf(STD_ERR, "Error: invalid arguments for \".%s\"\n", azArg[0]);
-    if( stdin_is_interactive ){
-      if( zErr!=0 ){
-        utf8_printf(STD_ERR, "  %s\n", zErr);
+    dispatchResult = DCR_AbortError;
+  }
+  if( dispatchResult > DCR_AbortError ){
+    /* Handle invocation errors. */
+    int ia = dispatchResult & DCR_ArgIxMask;
+    int ec = dispatchResult & ~DCR_ArgIxMask;
+    int unknownCmd = 0;
+    const char *z = 0;
+    switch( ec ){
+    case DCR_Unknown:
+      unknownCmd = ia==0;
+      z = unknownCmd
+        ? "unknown dot command: \"%s\""
+        : "unknown option or subcommand: \"%s\"";
+      break;
+    case DCR_Ambiguous:
+      z = ia>0? "\"%s\" is ambiguous; it matches more than one subcommand"
+        : "\"%s\" is ambiguous; it matches multiple dot commands";
+      break;
+    case DCR_Unpaired:
+      z = "option %s must be paired with a value argument";
+      break;
+    case DCR_TooMany:
+      z = "excess arguments provided; try .help %s";
+      ia = 0;
+      break;
+    case DCR_TooFew:
+      z = "insufficient arguments; try .help %s";
+      ia = 0;
+      break;
+    case DCR_Missing:
+      z = "required arguments missing; try .help %s";
+      ia = 0;
+      break;
+    case DCR_ArgError:
+      z = "argument(s) are not right; try .help %s";
+      ia = 0;
+      break;
+    default:
+      assert(0);
+      z = "?";
+      break;
+    }
+    if( zErr==0 ){
+      zErr = sqlite3_mprintf(z, azArg[ia]);
+      zErr = sqlite3_mprintf("Error: %z\n", zErr);
+    }
+    utf8_printf(STD_ERR, "%s", zErr);
+    if( INSOURCE_IS_INTERACTIVE(ISS(psx)->pInSource) ){
+      if( unknownCmd ){
+        utf8_printf(STD_ERR, "Enter \".help\" for a list of commands.\n");
       }else{
-        utf8_printf(STD_ERR, "Usage: ");
+        utf8_printf(STD_ERR, "Usage:\n");
         showPrimaryHelp(STD_ERR, azArg[0], psx);
       }
     }
-    rc = 1;
-    break;
-  case SHELL_FORBIDDEN_OP:
-    if( zErr!=0 ){
-      utf8_printf
-        (STD_ERR,
-         "Error: \".%s\" may not %s in --safe mode\n", azArg[0], zErr);
-      sqlite3_free(zErr);
-    }else {
-      utf8_printf(STD_ERR,
-                  "Error: \".%s\" forbidden in --safe mode\n", azArg[0]);
+    /* If the shell DB is messed up, at least .quit will be doable. */
+    if( unknownCmd && psx->dbShell!=0
+        && sqlite3_strnicmp(azArg[0],"quit",n)==0 ){
+      dispatchResult = DCR_Return;
+    }else{
+      dispatchResult = DCR_Error;
     }
-    psx->shellAbruptExit = 3;
-    /* fall thru */
-  case 2:
-    rc = 2;
-    /* fall thru */
-  default:
-    if( dispatchResult>2 ) rc = 1;
-    if( zErr!=0 ){
-      utf8_printf(STD_ERR, "%s", zErr);
-      sqlite3_free(zErr);
+  }else{
+    /* Handle execution errors. */
+    if( dispatchResult==DCR_AbortError ){
+      if( zErr!=0 ){
+        utf8_printf(STD_ERR, "Error: \".%s\" may not %s in --safe mode\n",
+                    azArg[0], zErr);
+      }else {
+        utf8_printf(STD_ERR, "Error: \".%s\" forbidden in --safe mode\n",
+                    azArg[0]);
+      }
+      psx->shellAbruptExit = 3;
+    }else{
+      int error = dispatchResult & DCR_Error;
+      int action = dispatchResult & ~DCR_Error;
+      if( error ){
+        if( zErr!=0 ) utf8_printf(STD_ERR, "%s", zErr);
+        else utf8_printf(STD_ERR, "%s failed", azArg[0]);
+      }
     }
   }
-
+  if( zErr ) sqlite3_free(zErr);
   if( ISS(psx)->outCount ){
     ISS(psx)->outCount--;
     if( ISS(psx)->outCount==0 ) output_reset(ISS(psx));
@@ -13281,7 +13348,7 @@ static int do_meta_command(char *zLine, ShellExState *psx){
     }
   }
 #endif
-  return rc;
+  return dispatchResult;
 }
 
 /* Line scan result and intermediate states (supporting scan resumption)
@@ -13839,14 +13906,14 @@ static int process_input(ShellInState *psi){
                                  iStartline);
         if( bail_on_error && nErrors>0 ) bErrorBail = 1;
         break;
-      case Cmd:
+      case Cmd: {
+        DotCmdRC dcr;
         echo_group_input(psi, *pzLineUse);
-        switch( do_meta_command(*pzLineUse+ndcLeadWhite, XSS(psi)) ){
-        default: ++nErrors; /* fall thru */
-        case 0: break;
-        case 2: bExitDemand = 1; break;
-        }
+        dcr = do_meta_command(*pzLineUse+ndcLeadWhite, XSS(psi));
+        nErrors += (dcr & DCR_Error);
+        bExitDemand |= (dcr & ~DCR_Error)!= DCR_Ok;
         break;
+      }
       default:
         assert(inKind!=Tbd);
         break;
index 5a5fdb03c2e215a1e81cf0aec20252fca704d520..2ea55d723e5dd67215cdad5ef99fbb296215626f 100644 (file)
@@ -80,35 +80,51 @@ typedef struct ShellExState {
   struct ShellInState *pSIS; /* Offset of this member is NOT STABLE. */
 } ShellExState;
 
-/* This function pointer has the same signature as the sqlite3_X_init()
- * function that is called as SQLite3 completes loading an extension.
- */
-typedef int (*ExtensionId)
-  (sqlite3 *, char **, const struct sqlite3_api_routines *);
-
 /*****************
  * See "Shell Extensions, Programming" for purposes and usage of the following
  * interfaces supporting extended meta-commands and import and output modes.
  */
 
+/* Define status codes returned by a meta-command, either during its argument
+ * checking or during its execution (to which checking may be deferred.) The
+ * code has 1 or 2 parts. The low-valued codes, below MCR_ArgIxMask, have an
+ * action part and an error flag. Higher-valued codes are bitwise-or'ed with
+ * a small integer and indicate problems with the meta-command itself.
+ */
+typedef enum DotCmdRC {
+  /* Post-execute action and success/error status */
+  DCR_Ok          = 0,    /* ordinary success and continue */
+  DCR_Error       = 1,    /* or'ed with low-valued codes upon error */
+  DCR_Return      = 2,    /* return from present input source/script */
+  DCR_ReturnError = 3,    /* return with error */
+  DCR_Exit        = 4,    /* exit shell ( process or pseudo-main() ) */
+  DCR_ExitError   = 5,    /* exit with error */
+  DCR_Abort       = 6,    /* abort for unrecoverable cause (OOM) */
+  DCR_AbortError  = 7,    /* abort with error (blocked unsafe) */
+  /* Dispatch and argument errors */
+  DCR_ArgIxMask = 0xfff,  /* mask to retain/exclude argument index */
+  /* Below codes may be or'ed with the offending argument index */
+  DCR_Unknown   = 0x1000, /* unknown command, subcommand or option */
+  DCR_Ambiguous = 0x2000, /* ambiguous (sub)command (too abreviated) */
+  DCR_Unpaired  = 0x3000, /* option value indicated but missing */
+  DCR_TooMany   = 0x4000, /* excess arguments were provided */
+  DCR_TooFew    = 0x5000, /* insufficient arguments provided */
+  DCR_Missing   = 0x6000, /* required argument(s) missing */
+  DCR_ArgError  = 0x7000  /* non-specific argument error */
+} DotCmdRC;
+
 /* An object implementing below interface is registered with the
  * shell to make new or overriding meta-commands available to it.
  */
 INTERFACE_BEGIN( MetaCommand );
 PURE_VMETHOD(const char *, name, MetaCommand, 0,());
 PURE_VMETHOD(const char *, help, MetaCommand, 1,(int more));
-PURE_VMETHOD(int, argsCheck, MetaCommand,
+PURE_VMETHOD(DotCmdRC, argsCheck, MetaCommand,
              3, (char **pzErrMsg, int nArgs, char *azArgs[]));
-PURE_VMETHOD(int, execute, MetaCommand,
+PURE_VMETHOD(DotCmdRC, execute, MetaCommand,
              4,(ShellExState *, char **pzErrMsg, int nArgs, char *azArgs[]));
 INTERFACE_END( MetaCommand );
 
-/* Define error codes to be returned either by a meta-command during
- * its own checking or by the dispatcher for bad argument counts.
- */
-#define SHELL_INVALID_ARGS SQLITE_MISUSE
-#define SHELL_FORBIDDEN_OP 0x7ffe /* Action disallowed under --safe.*/
-
 /* An object implementing below interface is registered with the
  * shell to make new or overriding output modes available to it.
  */
@@ -147,6 +163,12 @@ PURE_VMETHOD(void, closeDataInStream, ImportHandler,
              2,( ShellExState *pSES, char **pzErr ));
 INTERFACE_END( ImportHandlerVtable );
 
+/* This function pointer has the same signature as the sqlite3_X_init()
+ * function that is called as SQLite3 completes loading an extension.
+ */
+typedef int (*ExtensionId)
+  (sqlite3 *, char **, const struct sqlite3_api_routines *);
+
 typedef struct ExtensionHelpers {
   int helperCount; /* Helper count, not including sentinel */
   union {