From 0746356f82e0348fa3a2b383e18488fa2980f174 Mon Sep 17 00:00:00 2001 From: larrybr Date: Tue, 15 Mar 2022 10:07:56 +0000 Subject: [PATCH] For CLI, begin centralizing argument checking/complaining. FossilOrigin-Name: 3457f87c5db7a28eb6a47223f7853cd066245edfe1882feabbdb8ce9555f872e --- manifest | 14 +- manifest.uuid | 2 +- src/shell.c.in | 613 ++++++++++++++++++++++++-------------------- src/shext_linkage.h | 50 +++- 4 files changed, 384 insertions(+), 295 deletions(-) diff --git a/manifest b/manifest index a589a60da3..40f6a930f6 100644 --- 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. diff --git a/manifest.uuid b/manifest.uuid index 930b491221..706134ee08 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9be5e0b503f63dbbf1375460ce74a0aad6f202c2e5b2a748bba1d3af82c7d2d8 \ No newline at end of file +3457f87c5db7a28eb6a47223f7853cd066245edfe1882feabbdb8ce9555f872e \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index f60a9ef626..5a4aafe85f 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -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; jout, "%s%s", azArg[rc], (rc==nArg-1)? "|\n" : "|"); - return 0; + int ia = 0; + for (ia=1; iaout, "%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; iout, " .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 = ""; @@ -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( aibSafeMode ) 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; iout, "%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; ipAuxDb->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; iizTestcase), 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_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; diff --git a/src/shext_linkage.h b/src/shext_linkage.h index 5a5fdb03c2..2ea55d723e 100644 --- a/src/shext_linkage.h +++ b/src/shext_linkage.h @@ -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 { -- 2.47.3