From: larrybr Date: Fri, 25 Mar 2022 06:46:20 +0000 (+0000) Subject: Get shell TCL extension more smoothly integrated. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b58d987b4299a8cbddf752ec5b66cb39b001eef3;p=thirdparty%2Fsqlite.git Get shell TCL extension more smoothly integrated. FossilOrigin-Name: 29fd246fb2605a2731dd6327a354daedd8182225a97234939c79fc66e1e9e83e --- diff --git a/ext/misc/tclshext.c.in b/ext/misc/tclshext.c.in index 2ab9653684..2362e6bb9a 100644 --- a/ext/misc/tclshext.c.in +++ b/ext/misc/tclshext.c.in @@ -83,8 +83,8 @@ DERIVED_METHOD(const char *, help, MetaCommand,TclCmd, 1,(int more)){ ".tcl ?FILES? Run a TCL REPL or interpret files as TCL\n"; case 1: return " If FILES are provided, they name files to be read in as TCL.\n" - " Otherwise, a read/evaluate/print loop is run until a lone \".\" is\n" - " entered on an input line or end-of-stream is encountered.\n"; + " Otherwise, a read/evaluate/print loop is run until a lone \".\"\n" + " is entered on an input line or end-of-stream is encountered.\n"; default: return 0; } } @@ -99,7 +99,7 @@ static Tcl_Interp *getInterp(TclCmd *ptc); static void copy_complaint(char **pzErr, Tcl_Interp *pi){ if( pzErr ){ Tcl_Obj *po = Tcl_GetObjResult(pi); - *pzErr = sqlite3_mprintf("%s", Tcl_GetStringFromObj(po,0)); + *pzErr = sqlite3_mprintf("%s\n", Tcl_GetStringFromObj(po,0)); } } @@ -143,44 +143,51 @@ DERIVED_METHOD(DotCmdRC, execute, MetaCommand,TclCmd, 4, "if {$line ne \".\"} {puts {}}\n" "read stdin 0\n" #else - "set line {}\n" - "set at_end 0\n" - "set prompting [now_interactive]\n" - "while {!$at_end} {\n" - "if {$prompting} {\n" - "if {$line!=\"\"} {\n" - "puts -nonewline \"> \"\n" + "namespace eval ::REPL {\n" + "variable line {}\n" + "variable at_end 0\n" + "variable prompting [now_interactive]\n" + "}\n" + "while {!$::REPL::at_end} {\n" + "if {$::REPL::prompting} {\n" + "if {$::REPL::line!=\"\"} {\n" + "puts -nonewline \"...> \"\n" "} else {\n" - "puts -nonewline \"% \"\n" + "puts -nonewline \"tcl% \"\n" "}\n" "}\n" "flush stdout\n" - "set li [get_input_line]\n" - "if {$li eq \"\"} {\n" - "set at_end 1\n" - "} elseif {[string trimright $li] eq \".\"} {\n" - "if {$line ne \"\"} {\n" + "set ::REPL::li [get_input_line]\n" + "if {$::REPL::li eq \"\"} {\n" + "set ::REPL::at_end 1\n" + "} elseif {[string trimright $::REPL::li] eq \".\"} {\n" + "if {$::REPL::line ne \"\"} {\n" "throw {NONE} {incomplete input at EOF}\n" "}\n" - "set at_end 1\n" + "set ::REPL::at_end 1\n" "} else {\n" - "append line $li\n" - "if {[string trim $line] eq \"\"} {\n" - "set line \"\"\n" + "append ::REPL::line $::REPL::li\n" + "if {[string trim $::REPL::line] eq \"\"} {\n" + "set ::REPL::line \"\"\n" "continue\n" "}\n" - "if {[info complete $line]} {\n" - "if {[catch {uplevel #0 $line} result]} {\n" - "puts stderr \"Error: $result\"\n" - "} elseif {$result!=\"\" && $prompting} {\n" - "puts $result\n" + "if {[info complete $::REPL::line]} {\n" + "set ::REPL::rc [catch {uplevel #0 $::REPL::line} ::REPL::result]\n" + "if {$::REPL::rc == 0} {\n" + "if {$::REPL::result!=\"\" && $::REPL::prompting} {\n" + "puts $::REPL::result\n" + "}\n" + "} elseif {$::REPL::rc == 1} {\n" + "puts stderr \"Error: $::REPL::result\"\n" + "} elseif {$::REPL::rc == 2} {\n" + "set ::REPL::at_end 1\n" "}\n" - "set line {}\n" + "set ::REPL::line {}\n" "}\n" "}\n" "}\n" - "if {$prompting && $li ne \".\\n\"} {puts {}}\n" - "unset li line prompting at_end\n" + "if {$::REPL::prompting && $::REPL::li ne \".\\n\"} {puts {}}\n" + "namespace delete ::REPL\n" "read stdin 0\n" #endif ; @@ -281,7 +288,7 @@ static int nowInteractive(void *pvSS, Tcl_Interp *interp, #define UNKNOWN_RENAME "::_original_unknown" /* C implementation of TCL ::unknown to delegate to dot commands */ -static int unknown_dot_delegate(void *pvSS, Tcl_Interp *interp, +static int unknownDotDelegate(void *pvSS, Tcl_Interp *interp, int nArgs, const char *azArgs[]){ const char *name = (nArgs>1 && *azArgs[1]=='.')? azArgs[1]+1 : 0; ShellExState *psx = (ShellExState *)pvSS; @@ -290,31 +297,27 @@ static int unknown_dot_delegate(void *pvSS, Tcl_Interp *interp, int ia, rc; if( name ) pmc = pExtHelpers->findMetaCommand(name, psx, &nFound); - if( pmc &&nFound==1 ){ + if( pmc==(MetaCommand*)&tclcmd && nArgs==2 ){ + /* Will not do a nested REPL, just silently semi-fake it. */ + return TCL_OK; + } + if( pmc && nFound==1 ){ /* Run the dot command and interpret its returns. */ - char *zErr = 0; - DotCmdRC drc = pmc->pMethods->argsCheck(pmc, &zErr, nArgs-1, - (char **)azArgs+1); - if( drc==DCR_Ok ){ - drc = pmc->pMethods->execute(pmc, psx, &zErr, nArgs-1, - (char **)azArgs+1); - } - assert(!(drc==DCR_Ok && zErr!=0)); + DotCmdRC drc = pExtHelpers->runMetaCommand(pmc, (char **)azArgs+1, + nArgs-1, psx); if( drc==DCR_Ok ) return TCL_OK; - else{ - /* ToDo: Try to indicate what went wrong as part of result. - * This is deferred until some shell functionality helping - * with this is factored out and exposed for extensions. */ - sqlite3_free(zErr); + else if( drc==DCR_Return ){ + return TCL_RETURN; + }else{ + Tcl_AppendResult(interp, "Execution of .", name, " failed.", 0); return TCL_ERROR; } }else{ /* Defer to the TCL-default ::unknown command, or fail here. */ int haveUnkCmd = (0!=Tcl_FindCommand(interp, UNKNOWN_RENAME, 0, TCL_GLOBAL_ONLY)); - Tcl_Obj **ppo; if( haveUnkCmd ){ - ppo = sqlite3_malloc((nArgs+1)*sizeof(Tcl_Obj*)); + Tcl_Obj **ppo = sqlite3_malloc((nArgs+1)*sizeof(Tcl_Obj*)); if( ppo==0 ) return TCL_ERROR; ppo[0] = Tcl_NewStringObj(UNKNOWN_RENAME, -1); Tcl_IncrRefCount(ppo[0]); @@ -394,7 +397,7 @@ int sqlite3_tclshext_init( "now_interactive", nowInteractive, psx, 0); Tcl_Eval(tclcmd.interp, "rename unknown "UNKNOWN_RENAME); Tcl_CreateCommand(tclcmd.interp, - "unknown", unknown_dot_delegate, psx, 0); + "unknown", unknownDotDelegate, psx, 0); pShExtLink->eid = sqlite3_tclshext_init; }else{ TclCmd_Takedown(&tclcmd); diff --git a/manifest b/manifest index 6f7b09a6f6..b1f4be46ec 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C TCL\sshell\sextension\sdelegates\sto\sdot\scommands -D 2022-03-24T12:49:05.311 +C Get\sshell\sTCL\sextension\smore\ssmoothly\sintegrated. +D 2022-03-25T06:46:20.823 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -329,7 +329,7 @@ F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52 F ext/misc/spellfix.c 94df9bbfa514a563c1484f684a2df3d128a2f7209a84ca3ca100c68a0163e29f F ext/misc/sqlar.c 0ace5d3c10fe736dc584bf1159a36b8e2e60fab309d310cd8a0eecd9036621b6 F ext/misc/stmt.c 35063044a388ead95557e4b84b89c1b93accc2f1c6ddea3f9710e8486a7af94a -F ext/misc/tclshext.c.in 4c9e9c36877ea3a9bde62e4d4706e848fe0d56984e7ad36d5952de7f9d9cf308 +F ext/misc/tclshext.c.in d8f1a894edb7670341b8eb319686f4833f0d5545405c093d4d95b53966bbf5ff F ext/misc/templatevtab.c 8a16a91a5ceaccfcbd6aaaa56d46828806e460dd194965b3f77bf38f14b942c4 F ext/misc/totype.c fa4aedeb07f66169005dffa8de3b0a2b621779fd44f85c103228a42afa71853b F ext/misc/uint.c 053fed3bce2e89583afcd4bf804d75d659879bbcedac74d0fa9ed548839a030b @@ -556,8 +556,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 840a4a72dcc39fafbcc82275babc77f928c3531cb5fe7217cb0a1596ef0a4555 -F src/shext_linkage.h 71b3600ba0e20f696fb226547e99413c67cfb27c5532701df16935e2a45c152a +F src/shell.c.in 06593f6e93bbbcb5e58e29f1100ff1fa9e211642dc946d28ef1748be06383854 +F src/shext_linkage.h 511a218406b45240b3ad8a849a0898fac52ea64f7ad133548a9126d0f7d637cf F src/sqlite.h.in 5845213799feca09cd69d18ff841a85fe0df31021f46aaa1797e703e80dc1d70 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h f49e28c25bd941e79794db5415fdf7b202deb3bc072ed6f1ed273d578703684e @@ -606,7 +606,7 @@ F src/test_quota.h 2a8ad1952d1d2ca9af0ce0465e56e6c023b5e15d F src/test_rtree.c 671f3fae50ff116ef2e32a3bf1fe21b5615b4b7b F src/test_schema.c f5d6067dfc2f2845c4dd56df63e66ee826fb23877855c785f75cc2ca83fd0c1b F src/test_server.c a2615049954cbb9cfb4a62e18e2f0616e4dc38fe -F src/test_shellext.c 0d07a78ac1487532f39ac93eafff0240e861f8f3430205af62b937136b6dbd49 +F src/test_shellext.c 6cbc7cbc4c1c55a747fcc8367fb9b45fe3d2d253e1b4235c8d4c4e6bd667b5ce F src/test_sqllog.c 540feaea7280cd5f926168aee9deb1065ae136d0bbbe7361e2ef3541783e187a F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939 @@ -1950,8 +1950,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 abf0316b3f58646974ab8e4d3e68896c9fc03bdd338eb7dc7b2f5d4de7365298 -R ce874ae1862d105669b12cd18d25e5bc +P 353943108b36cabb3b9c745aa7f4d9a42cd100b7d0444373184b45cc1715837e +R b9914f8d3b5384fdfccd2318410fb67e U larrybr -Z 4469eea4a17023c287722633d65da7c8 +Z 0021f50f48c3d55a3facb3cd03bb67af # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 85e7c76977..6feb5de2bb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -353943108b36cabb3b9c745aa7f4d9a42cd100b7d0444373184b45cc1715837e \ No newline at end of file +29fd246fb2605a2731dd6327a354daedd8182225a97234939c79fc66e1e9e83e \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 566bace22b..e0be6627c6 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -5206,7 +5206,7 @@ static void linenoise_completion(const char *zLine, linenoiseCompletions *lc){ #endif /* -** Do C-language style dequoting. +** Do C-language style escape sequence translation. ** ** \a -> alarm ** \b -> backspace @@ -7802,15 +7802,17 @@ static const char *shellStartupDir(void){ static void setColumnWidths(ShellExState *p, char *azWidths[], int nWidths); static MetaCommand * findMetaCommand(const char *, ShellExState *, int *); +static DotCmdRC runMetaCommand(MetaCommand*, char *[], int na, ShellExState*); static ExtensionHelpers extHelpers = { - 10, + 11, { failIfSafeMode, currentOutputFile, currentInputSource, strLineGet, findMetaCommand, + runMetaCommand, setColumnWidths, nowInteractive, shellInvokedAs, @@ -7833,8 +7835,9 @@ static ShellExtensionAPI shellExtAPI = { /* This SQL function provides a way for a just-loaded shell extension to * obtain a ShellExtensionLink pointer from the shell core while using * the same sqlite3_load_extension API used for SQLite extensions. - * This serves as an alternative to deriving the same pointer from - * the pzErr argument passed into that API. + * + * (It is also useful for debugging a shell extension, as a breakpoint + * on it will be hit soon after loading and before real work is done.) */ static void shell_linkage( sqlite3_context *context, @@ -7842,17 +7845,25 @@ static void shell_linkage( sqlite3_value **argv ){ int linkKind = 0; + void *pv; if( argc>0 ){ linkKind = sqlite3_value_int(argv[0]); } switch (linkKind){ case 0: - sqlite3_result_pointer(context, sqlite3_user_data(context), - SHELLEXT_API_POINTERS, 0); + pv = sqlite3_user_data(context); + break; + case 1: + pv = &extHelpers; + break; + case 2: + pv = &shellExtAPI; break; default: - sqlite3_result_null(context); + pv = 0; } + if( pv==0 ) sqlite3_result_null(context); + else sqlite3_result_pointer(context, pv, SHELLEXT_API_POINTERS, 0); } #ifndef SHELL_DB_FILE @@ -8010,6 +8021,8 @@ static int load_shell_extension(ShellExState *psx, const char *zFile, ScriptHooks shSave = psi->scripting; ExtensionId siSave = psi->scriptXid; int rc; + + if( pzErr ) *pzErr = 0; if( psx->dbShell==0 ){ rc = begin_db_dispatch(psx); if( rc!=SQLITE_OK ) return rc; @@ -8042,7 +8055,13 @@ static int load_shell_extension(ShellExState *psx, const char *zFile, psi->scriptXid = siSave; } psi->ixExtPending = 0; - return rc!=SQLITE_OK; + if( rc!=SQLITE_OK ){ + if( rc==SQLITE_MISUSE && pzErr!=0 ){ + *pzErr = sqlite3_mprintf("extension id mismatch %z\n", *pzErr); + } + rc = SQLITE_ERROR; + } + return rc; } #endif @@ -13129,28 +13148,6 @@ MetaCommand *findMetaCommand(const char *cmdName, ShellExState *psx, } } -/***************** -** Command dispatcher -** After successful command lookup and (simple) argument checking, -** invoke the found MetaCommand with the input arguments (except -** that azArg[0] is replaced with the properly spelled command name.) -** The return is either a dispatch error or whatever the dispatched -** MetaCommand returns. -*/ -static DotCmdRC dispatchCommand(char *azArg[], int nArg, - ShellExState *psx, char **pzErr){ - const char *cmdName = azArg[0]; - int nFound = 0; - DotCmdRC argsCheck; - MetaCommand *pMC = findMetaCommand(cmdName, psx, &nFound); - if( 0==pMC || nFound>1 ) return (nFound>1)? DCR_Ambiguous: DCR_Unknown; - argsCheck = pMC->pMethods->argsCheck(pMC, pzErr, nArg, azArg); - 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); -} - /* ** Output primary (single-line) help for a known command. */ @@ -13243,84 +13240,51 @@ static int showHelp(FILE *out, const char *zPattern, ShellExState *psx){ return npm; } -/* -** If an input line or line group begins with "." then invoke this routine -** to process that line. -** -** 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 DotCmdRC do_meta_command(char *zLine, ShellExState *psx){ - int h = 1; - int nArg = 0; - char *azArg[52]; - char *zErr = 0; - DotCmdRC dispatchResult; -#if SHELL_VARIABLE_EXPANSION - int ncLineIn = strlen30(zLine); - u8 bExpVars = SHEXT_VAREXP(ISS(psx)); -#endif - +/* Perform preparation needed prior to actually running any dot command. */ +static void command_prep(ShellInState *psi){ + clearTempFile(psi); #ifndef SQLITE_OMIT_VIRTUALTABLE - if( ISS(psx)->expert.pExpert ){ - expertFinish(ISS(psx), 1, 0); + if( psi->expert.pExpert ){ + expertFinish(psi, 1, 0); } #endif +} - /* Parse the input line into tokens. - */ - while( zLine[h] && nArgoutCount ){ + psi->outCount--; + if( psi->outCount==0 ) output_reset(psi); } - azArg[nArg] = 0; - - /* Process the input line. - */ - if( nArg==0 ) return DCR_Ok; /* no tokens, no error, done */ - - clearTempFile(ISS(psx)); + updateSafeMode(psi); +} - dispatchResult = dispatchCommand(azArg, nArg, psx, &zErr); +/* Issue errors per returned DotCmdRC and error message, handle certain + * exceptional returns, and translate return to the sanitized first 8. + */ +static DotCmdRC meta_command_errors(char *zErr, char *azArg[], int nArg, + DotCmdRC dcr, ShellExState *psx){ if( psx->shellAbruptExit!=0 ){ - if( psx->shellAbruptExit>0x1ff ) dispatchResult = DCR_AbortError; - else dispatchResult = DCR_Exit | (dispatchResult&DCR_Error); + if( psx->shellAbruptExit>0x1ff ) dcr = DCR_AbortError; + else dcr = DCR_Exit | (dcr & DCR_Error); } - if( dispatchResult==DCR_CmdErred ){ + if( dcr==DCR_CmdErred ){ /* Error message(s) already emitted. Just translate to execute error. */ - dispatchResult = DCR_Error; - }else if( dispatchResult==DCR_SayUsage ){ + dcr = DCR_Error; + }else if( dcr==DCR_SayUsage ){ if( zErr ){ utf8_printf(STD_ERR, "%s", zErr); }else{ utf8_printf(STD_ERR, "Usage:\n"); showPrimaryHelp(STD_ERR, azArg[0], psx); } - dispatchResult = DCR_Error; - }else if( dispatchResult > DCR_AbortError ){ + dcr = DCR_Error; + }else if( dcr > DCR_AbortError ){ /* Handle invocation errors. */ - int ia = dispatchResult & DCR_ArgIxMask; + int ia = dcr & DCR_ArgIxMask; const char *pArg1st = (ia>=nArg)? "" : azArg[ia]; const char *pArg2nd = 0; - int ec = dispatchResult & ~DCR_ArgIxMask; + int ec = dcr & ~DCR_ArgIxMask; int unknownCmd = 0; const char *z = 0; switch( ec ){ @@ -13366,10 +13330,11 @@ static DotCmdRC do_meta_command(char *zLine, ShellExState *psx){ z = "?"; break; } - if( zErr==0 ){ + if( !zErr ){ if( z!=0 ){ - zErr = smprintf("Error: %z\n", smprintf(z, pArg1st, pArg2nd)); - utf8_printf(STD_ERR, "%s", zErr); + char *ze = smprintf("Error: %z\n", smprintf(z, pArg1st, pArg2nd)); + utf8_printf(STD_ERR, "%s", ze); + sqlite3_free(ze); } }else{ const char *fmt @@ -13384,17 +13349,17 @@ static DotCmdRC do_meta_command(char *zLine, ShellExState *psx){ showPrimaryHelp(STD_ERR, azArg[0], psx); } } - /* If the shell DB is messed up, at least .quit will be doable. */ + /* If the shell DB becomes messed up, at least .quit will be doable. */ if( unknownCmd && psx->dbShell!=0 && sqlite3_strnicmp(azArg[0],"quit",strlen30(azArg[0]))==0 ){ - dispatchResult = DCR_Return; + dcr = DCR_Return; }else{ - dispatchResult = DCR_Error; + dcr = DCR_Error; } }else{ /* Handle execution errors. */ - if( dispatchResult==DCR_AbortError ){ - if( zErr!=0 ){ + if( dcr==DCR_AbortError ){ + if( zErr ){ utf8_printf(STD_ERR, "Error: \".%s\" may not %s in -safe mode\n", azArg[0], zErr); }else { @@ -13403,10 +13368,10 @@ static DotCmdRC do_meta_command(char *zLine, ShellExState *psx){ } psx->shellAbruptExit = 0x203; }else{ - int error = dispatchResult & DCR_Error; - int action = dispatchResult & ~DCR_Error; + int error = dcr & DCR_Error; + int action = dcr & ~DCR_Error; if( error ){ - if( zErr!=0 ){ + if( zErr ){ const char *fmt = (sqlite3_strnicmp(zErr, "Error", 5)==0)? "%s" : "Error: %s"; utf8_printf(STD_ERR, fmt, zErr); @@ -13415,12 +13380,104 @@ static DotCmdRC do_meta_command(char *zLine, ShellExState *psx){ } } } - if( zErr ) sqlite3_free(zErr); - if( ISS(psx)->outCount ){ - ISS(psx)->outCount--; - if( ISS(psx)->outCount==0 ) output_reset(ISS(psx)); + return dcr; +} + +/* Argument-check and execute a found MetaCommand, wrapping execution + * with command_{prep,post}(...), and issue errors as made evident. + * Return one of the "Post-execute action and success/error status" + * codes from the DotCmdRC enum. + * + * Note that this function is exposed for shell extensions to use. + * + * This should be called for "top-level" dot command execution only. + * Should an extension wrap or use a MetaCommand object to effect its + * own functionality, that object's execute() method should be called + * directly, without going through this function. + */ +static DotCmdRC runMetaCommand(MetaCommand *pmc, char *azArg[], int nArg, + ShellExState *psx){ + char *arg0 = azArg[0]; + char *zErr = 0; + DotCmdRC dcr = pmc->pMethods->argsCheck(pmc, &zErr, nArg, azArg); + + command_prep(ISS(psx)); + azArg[0] = (char *)(pmc->pMethods->name(pmc)); + if( dcr==DCR_Ok ){ + dcr = pmc->pMethods->execute(pmc, psx, &zErr, nArg, azArg); + } + if( dcr!=DCR_Ok ){ + dcr = meta_command_errors(zErr, azArg, nArg, dcr, psx); + } + azArg[0] = arg0; + sqlite3_free(zErr); + command_post(ISS(psx)); + return dcr; +} + +/* +** If an input line or line group begins with "." then invoke this routine +** to process that line. +** +** Returns 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. +** +** Any applicable error messages are issued along with output messages. +*/ +static DotCmdRC do_meta_command(char *zLine, ShellExState *psx){ + int h = 1; /* Passing over leading '.' */ + int nArg = 0; + char *azArg[52]; + DotCmdRC dcr = DCR_Ok; +#if SHELL_VARIABLE_EXPANSION + int ncLineIn = strlen30(zLine); + u8 bExpVars = SHEXT_VAREXP(ISS(psx)); +#endif + + /* Parse the input line into tokens which are 0-terminated and left in-place. + */ + while( zLine[h] && nArg0 ){ + int nFound; + MetaCommand *pmc = findMetaCommand(azArg[0], psx, &nFound); + if( pmc==0 || nFound>1 ){ + dcr = (nFound>1)? DCR_Ambiguous: DCR_Unknown; + /* Issue error for an unknown or inadequately specified dot command. */ + dcr = meta_command_errors(0, azArg, nArg, dcr, psx); + } + else{ + /* Run found command and issue or handle any errors it may report. */ + dcr = runMetaCommand(pmc, azArg, nArg, psx); + } } - updateSafeMode(ISS(psx)); + #if SHELL_VARIABLE_EXPANSION if( bExpVars ){ /* Free any arguments that are allocated rather than tokenized in place. */ @@ -13431,7 +13488,7 @@ static DotCmdRC do_meta_command(char *zLine, ShellExState *psx){ } } #endif - return dispatchResult; + return dcr; } /* Line scan result and intermediate states (supporting scan resumption) diff --git a/src/shext_linkage.h b/src/shext_linkage.h index eaa8054b7d..7f4c24b94f 100644 --- a/src/shext_linkage.h +++ b/src/shext_linkage.h @@ -230,6 +230,8 @@ typedef struct ExtensionHelpers { char * (*strLineGet)(char *zBuf, int ncMax, struct InSource *pInSrc); MetaCommand * (*findMetaCommand)(const char *cmdName, ShellExState *p, /* out */ int *pnFound); + DotCmdRC (*runMetaCommand)(MetaCommand *pmc, char *azArg[], int nArg, + ShellExState *psx); void (*setColumnWidths)(ShellExState *p, char *azWidths[], int nWidths); int (*nowInteractive)(ShellExState *p); const char * (*shellInvokedAs)(void); diff --git a/src/test_shellext.c b/src/test_shellext.c index 981e865120..5be936720e 100644 --- a/src/test_shellext.c +++ b/src/test_shellext.c @@ -121,6 +121,7 @@ int sqlite3_testshellext_init( batty.pPrint = pExtHelpers->findMetaCommand("print", psx, &rc); rc = pShExtApi->registerMetaCommand(psx, sqlite3_testshellext_init,pmc); if( rc!=0 ) ++nErr; + pShExtLink->eid = sqlite3_testshellext_init; } else{ printf("No ShellExtensionLink pointer or registration API.\n");