gcc -shared -fPIC -O2 -I. -Isrc -I/usr/include/tcl8.6 tclshext.c \
-o tclshext.so -ltcl8.6
** Later TCL versions can be used if desired.
-**
-** This extension adds these features to the host shell:
-** 1. TCL scripting support is added.
-** 2. TCL commands added: udb shdb now_interactive get_tcl_group ..
-** 3. The .tcl and .unknown dot commands are added.
-**
-** TCL scripting support is added with a ShellExtensionAPI hookScripting()
-** call in the manner documented for it and the ScriptHooks struct. This
-** support lasts until the extension destructor is called. Until then,
-** shell input groups beginning with ".." are treated as TCL input, in
-** various ways, specifically: (1) When a bare ".." is entered, a TCL
-** REPL loop is run until the end of input is seen; (2) When "..X ..."
-** is entered, (where "X" is a dot-command name), the X dot command will
-** be run in its normal fashion, but its arguments will be collected
-** according to TCL parsing rules and will be expanded as is usual for
-** TCL commands; and (3) when ".. T ..." is entered, (where "T" is a TCL
-** command name), that TCL command and its arguments will be collected
-** and expanded according to TCL parsing rules, and the command will be
-** run in the TCL execution environment (in the root namespace), but the
-** shell execution environment remains in effect afterward. (Note that
-** cases 2 and 3 differ in whether space occurs after the leading "..".)
-**
-** The phrase "end of input" means either: end-of-file is seen on a file,
-** pipe or string stream input, or a lone "." on the first and only line
-** of an input line group is seen. This convention is useful in scripting
-** when it is expedient to switch execution environments from within the
-** same input stream. For example:
+ "TCL scripting support is added with a registerScripting() call in the\n"
+ "ShellExtensionAPI, as documented for ScriptingSupport interface. This\n"
+ "support lasts until the scripting object destructor is called. Until\n"
+*/
+static const char * const zTclHelp =
+ "This extension adds these features to the host shell:\n"
+ " 1. TCL scripting support is added.\n"
+ " 2. TCL commands are added: udb shdb now_interactive get_tcl_group ..\n"
+ " 3. The .tcl and .unknown dot commands are added.\n"
+ " 4. If built with Tk capability, a run_gui TCL command may be added if\n"
+ " the extension is loaded by the shell via .load ... -shext -tk . Any\n"
+ " other arguments beyond -shext are in TCL's argv variable.\n"
+ "Operation:\n"
+ " Shell input groups beginning with \"..\" are treated as TCL input, in\n"
+ " these ways: (1) When a bare \"..\" is entered, a TCL REPL loop is run\n"
+ " until the end of input is seen; (2) When \"..D ...\" is entered, (where\n"
+ " \"D\" is a dot-command name), the D dot command will be run in its normal\n"
+ " fashion, but its arguments will be collected according to TCL parsing\n"
+ " rules then expanded as usual for TCL commands; and (3) when \".. T ...\"\n"
+ " is entered, (where \"T\" is a TCL command name), that TCL command and its\n"
+ " arguments will be collected and expanded according to TCL parsing rules,\n"
+ " then run in the TCL execution environment (in its global namespace), but\n"
+ " the shell execution environment remains in effect afterward.\n"
+ "\n"
+ " Note that cases 2 and 3 differ in having space after the leading \"..\".\n"
+ "\n"
+ " The phrase \"end of input\" means either: end-of-file is seen on a file,\n"
+ " pipe or string stream input, or a lone \".\" on the first and only line\n"
+ " of an input line group is seen. This convention is useful in scripting\n"
+ " when it is expedient to switch execution environments from within the\n"
+ " same input stream. This could be input piped in from another process.\n"
+ "\n"
+ ;
+/*
+** For example:
# From shell, enter TCL REPL:
..
# Initialize some variables and insert into the DB:
**
** The added .tcl dot command may be used to enter a TCL REPL, or with
** arguments, it will read files as TCL. (This is somewhat extraneous,
-** as the same can be done with TCL commands. This is more easily done
+** as the same can be done with TCL commands, but it is more easily done
** from the shell invocation, and the .tcl command's integration into
** the .help facility provides a way for users to get help for "..".)
**
#undef SQLITE_AMALGAMATION
#undef TCLSH
#include <tclOO.h>
+#ifndef SHELL_OMIT_TK
+#include <tk.h> /* Only used if option -tk passed during load. */
+#endif
INCLUDE tclsqlite.c
#if defined(_WIN32) || defined(WIN32)
if( --interpKeep.nRefs==0 ){
if( interpKeep.pInterp ){
Tcl_DeleteInterp(interpKeep.pInterp);
+ Tcl_Release(interpKeep.pInterp);
interpKeep.pInterp = 0;
Tcl_Finalize();
}
}
}
-static int Tcl_BringUp(char **pzErrMsg){
+static int Tcl_BringUp(int *pWithTk, char **pzErrMsg){
if( ++interpKeep.nRefs==1 ){
const char *zShellName = pExtHelpers->shellInvokedAs();
const char *zShellDir = pExtHelpers->shellStartupDir();
interpKeep.pInterp = Tcl_CreateInterp();
Tcl_SetSystemEncoding(interpKeep.pInterp, "utf-8");
Sqlite3_Init(interpKeep.pInterp);
+ Tcl_Preserve(interpKeep.pInterp);
if( 0==Tcl_OOInitStubs(interpKeep.pInterp) ){
*pzErrMsg = sqlite3_mprintf("Tcl v8.6 or higher required.\n");
Tcl_TakeDown(&interpKeep);
*pzErrMsg = sqlite3_mprintf("Tcl interpreter startup failed.\n");
Tcl_TakeDown(&interpKeep);
return SQLITE_ERROR;
+ }else if( *pWithTk ){
+#ifndef SHELL_OMIT_TK
+ if( TCL_OK!=Tk_Init(interpKeep.pInterp) ){
+ fprintf(stderr, "Could not load/initialize Tk."
+ " (non-fatal, extension is loaded.)\n");
+ *pWithTk = 0;
+ }
+#else
+ fprintf(stderr, "This tclshext extension has no Tk support.\n");
+#endif
}
}
return (interpKeep.pInterp!=0)? SQLITE_OK : SQLITE_ERROR;
}
-/* These DERIVED_METHOD(...) macro calls' arguments were copied and
- * pasted from the MetaCommand interface declaration in shext_linkage.h
+static void copy_complaint(char **pzErr, Tcl_Interp *pi);
+static DotCmdRC runTclREPL(Tcl_Interp *interp, char **pzErrMsg);
+
+/* Following DERIVED_METHOD(...) macro calls' arguments were copied and
+ * pasted from the respective interface declarations in shext_linkage.h
*/
+
+/* This is in the interface for anouncing what was just provided. */
+DERIVED_METHOD(const char *, name, ScriptSupport,TclSS, 0,()){
+ (void)(pThis);
+ return "TclTk";
+}
+
+/* Provide help for users of this scripting implementation. */
+DERIVED_METHOD(const char *, help, ScriptSupport,TclSS, 1,( int more )){
+ (void)(pThis);
+ switch( more ){
+ case 0:
+ return "Provides TCL scripting support for SQLite extensible shell.\n";
+ case 1: return zTclHelp; /* ToDo: Rewrite this help. */
+ }
+ return 0;
+}
+
+/* Not doing this yet. */
+DERIVED_METHOD(int, configure, ScriptSupport,TclSS,
+ 4,( ShellExState *pSES, char **pzErr,
+ int numArgs, char *azArgs[] )){
+ (void)(pThis);
+ return 0;
+}
+
+/* Say line is script lead-in iff its first dark is "..".
+ * In combination with dot commands also being TCL commands and the
+ * special handling in the next three functions, this effects what is
+ * promised in this file's header text and by .tcl's help text.
+ */
+DERIVED_METHOD(int, isScriptLeader, ScriptSupport,TclSS,
+ 1,( const char *zScriptLeadingLine )){
+ char c;
+ (void)(pThis);
+ while( (c=*zScriptLeadingLine++) && (c==' '||c=='\t') ) {}
+ return (c=='.' && *zScriptLeadingLine=='.');
+}
+
+/* Say line group is complete if it passes muster as ready-to-go TCL. */
+DERIVED_METHOD(int, scriptIsComplete, ScriptSupport,TclSS,
+ 2,( const char *zScript, char **pzWhyNot )){
+ (void)(pThis);
+ (void)(pzWhyNot);
+ return Tcl_CommandComplete(zScript);
+}
+
+/* As we rely on Tcl_CommandComplete(), no resumable scanning is done. */
+DERIVED_METHOD(void, resetCompletionScan, ScriptSupport,TclSS, 0,()){
+ (void)(pThis);
+}
+
+/* Run as TCL after some jiggering with the leading dots. */
+DERIVED_METHOD(DotCmdRC, runScript, ScriptSupport,TclSS,
+ 3,( const char *zScript, ShellExState *psx, char **pzErrMsg )){
+ char c;
+ Tcl_Interp *interp = getInterp();
+ (void)(pThis);
+ (void)(psx);
+
+ if( interp==0 ) return DCR_Error;
+ while( (c=*zScript++) && (c==' '||c=='\t') ) {}
+ if( c=='.' && *zScript++=='.' ){
+ int rc, nc = strlen30(zScript);
+ /* At this point, *zScript should fall into one of these cases: */
+ switch( *zScript ){
+ case '.':
+ /* Three dots, assume user meant to run a dot command. */
+ one_shot_tcl:
+ rc = Tcl_EvalEx(interp, zScript, /* needs no adjustment */
+ nc, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL);
+ if( rc!=TCL_OK ) copy_complaint(pzErrMsg, getInterp());
+ break;
+ case ' ': case '\t':
+ /* Two dots then whitespace, it's a TCL one-shot command. */
+ while( (c = *zScript)!=0 && c==' ' || c=='\t' ) ++zScript, --nc;
+ if ( c!=0 ) goto one_shot_tcl;
+ /* It looks like "..", so run it that way via fall-thru. */
+ case 0:
+ /* Two lone dots, user wants to run TCL REPL. */
+ return runTclREPL(interp, pzErrMsg);
+ default:
+ /* Two dots then dark not dot, may be a dot command. */
+ if( *zScript>='a' && *zScript<='z' ){
+ --zScript, ++nc;
+ goto one_shot_tcl;
+ }
+ /* It cannot be a dot command; a user tip is apparently needed. */
+ if( pzErrMsg ){
+ *pzErrMsg = sqlite3_mprintf("Nothing valid begins with ..%c\n"
+ "Run .help tcl to see what is valid.\n",
+ *zScript);
+ return DCR_SayUsage;
+ }
+ }
+ return DCR_Ok|(rc!=TCL_OK);
+ }
+ return DCR_Error; /* Silent error because it should not happen. */
+}
+
DERIVED_METHOD(void, destruct, MetaCommand,TclCmd, 0, ()){
+ /* Nothing to do, instance data is static. */
(void)(pThis);
}
DERIVED_METHOD(void, destruct, MetaCommand,UnkCmd, 0, ()){
(void)(pThis);
}
+DERIVED_METHOD(void, destruct, ScriptSupport,TclSS, 0, ()){
+ (void)(pThis);
+}
DERIVED_METHOD(const char *, name, MetaCommand,TclCmd, 0,()){
return "tcl";
}
}
-/* Define MetaCommand v-tables initialized to reference above methods. */
+/* Define MetaCommand v-tables initialized to reference most above methods. */
MetaCommand_IMPLEMENT_VTABLE(TclCmd, tclcmd_methods);
MetaCommand_IMPLEMENT_VTABLE(UnkCmd, unkcmd_methods);
+/* Define ScriptSupport v-table initialized to reference the others. */
+ScriptSupport_IMPLEMENT_VTABLE(TclSS, tclss_methods);
/* Static instances are used because that suffices. */
INSTANCE_BEGIN(TclCmd);
INSTANCE_END(UnkCmd) unkcmd = {
&unkcmd_methods
};
-
-/* Say line is script lead-in iff its first dark is "..".
- * In combination with dot commands also being TCL commands and the
- * special handling in the next three functions, this effects what is
- * promised in this file's header text and by .tcl's help text.
- */
-static int tclIsScriptLead(void *pvState, const char *zLineLead){
- char c;
- (void)(pvState);
- while( (c=*zLineLead++) && (c==' '||c=='\t') ) {}
- return (c=='.' && *zLineLead=='.');
-}
-
-/* Say line group is complete if it passes muster as ready-to-go TCL. */
-static int tclIsComplete(void *pvState, const char *zScript){
- (void)(pvState);
- return Tcl_CommandComplete(zScript);
-}
-
-/* Run as TCL after some jiggering with the leading dots. */
-static DotCmdRC tclRunScript(void *pvState, const char *zScript,
- ShellExState *p, char **pzErrMsg){
- char c;
- struct InterpManage *pim = (struct InterpManage *)pvState;
- Tcl_Interp *interp = pim->pInterp;
- if( interp==0 ) return DCR_Error;
- while( (c=*zScript++) && (c==' '||c=='\t') ) {}
- if( c=='.' && *zScript++=='.' ){
- int rc, nc = strlen30(zScript);
- /* At this point, *zScript should fall into one of these cases: */
- switch( *zScript ){
- case '.':
- /* Three dots, assume user meant to run a dot command. */
- one_shot_tcl:
- rc = Tcl_EvalEx(interp, zScript, /* needs no adjustment */
- nc, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL);
- if( rc!=TCL_OK ) copy_complaint(pzErrMsg, getInterp());
- break;
- case ' ': case '\t':
- /* Two dots then whitespace, it's a TCL one-shot command. */
- while( (c = *zScript)!=0 && c==' ' || c=='\t' ) ++zScript, --nc;
- if ( c!=0 ) goto one_shot_tcl;
- /* It looks like "..", so run it that way via fall-thru. */
- case 0:
- /* Two lone dots, user wants to run TCL REPL. */
- return runTclREPL(interp, pzErrMsg);
- default:
- /* Two dots then dark not dot, may be a dot command. */
- if( *zScript>='a' && *zScript<='z' ){
- --zScript, ++nc;
- goto one_shot_tcl;
- }
- /* It cannot be a dot command; a user tip is apparently needed. */
- if( pzErrMsg ){
- *pzErrMsg = sqlite3_mprintf("Nothing valid begins with ..%c\n"
- "Run .help tcl to see what is valid.\n",
- *zScript);
- return DCR_SayUsage;
- }
- }
- return DCR_Ok|(rc!=TCL_OK);
- }
- return DCR_Error; /* Silent error because it should not happen. */
-}
+INSTANCE_BEGIN(TclSS);
+ /* no instance data */
+INSTANCE_END(TclSS) tclss = {
+ &tclss_methods
+};
#define GETLINE_MAXLEN 1000
}
}
+#ifndef SHELL_OMIT_TK
+static int runTkGUI(void *pvSS, Tcl_Interp *interp,
+ int nArgs, const char *azArgs[]){
+ ShellExState *psx = (ShellExState *)pvSS;
+ /* This runs without looking at stdin. So it cannot be a REPL, yet.
+ * Unless user has created something for it to do, it does nothing. */
+ Tk_MainLoop();
+}
+#endif
+
#define UNKNOWN_RENAME "::_original_unknown"
/* C implementation of TCL ::unknown to (maybe) delegate to dot commands */
ShellExtensionLink *pShExtLink;
SQLITE_EXTENSION_INIT2(pApi);
pShExtLink = shext_link(db);
- if( pShExtLink && pShExtLink->pShellExtensionAPI->numRegistrars>=1 ){
+ if( pShExtLink && pShExtLink->pShellExtensionAPI->numRegistrars>=5 ){
ShellExState *psx = pShExtLink->pSXS;
+ Tcl_Obj *targv = Tcl_NewListObj(0, NULL);
+ const char *zAppName = "tclshext";
+ int tnarg = 0;
+ int ldTk = 0;
int rc = 0;
pShExtApi = & pShExtLink->pShellExtensionAPI->api.named;
*pzErrMsg = sqlite3_mprintf("Shell version mismatch");
return SQLITE_ERROR;
}
+ if( pShExtLink->nLoadArgs>0 ){
+ int ila;
+ for( ila=0; ila<pShExtLink->nLoadArgs; ++ila ){
+ const char *zA = pShExtLink->azLoadArgs[ila];
+ if( strcmp(zA,"-tk")==0 ) ldTk = 1;
+ else {
+ /* Collect args not affecting init into the argv list. */
+ Tcl_ListObjAppendElement(NULL, targv, Tcl_NewStringObj(zA, -1));
+ ++tnarg;
+ }
+ }
+ }
rc = pShExtApi->registerMetaCommand(psx, sqlite3_tclshext_init,
(MetaCommand *)&unkcmd);
rc = pShExtApi->registerMetaCommand(psx, sqlite3_tclshext_init,
(MetaCommand *)&tclcmd);
- if( rc==SQLITE_OK && (rc = Tcl_BringUp(pzErrMsg))==SQLITE_OK ){
+ if( rc==SQLITE_OK && (rc = Tcl_BringUp(&ldTk, pzErrMsg))==SQLITE_OK ){
Tcl_Interp *interp = getInterp();
- ScriptHooks sh = {
- &interpKeep, tclIsScriptLead, tclIsComplete, tclRunScript
- };
if( TCL_OK==userDbInit(interp, psx) ){
UserDb *pudb = udbCreate(interp, psx);
pShExtLink->extensionDestruct = (void (*)(void*))udbCleanup;
pShExtLink->pvExtensionObject = pudb;
}
- pShExtApi->hookScripting(psx, sqlite3_tclshext_init, &sh);
+ pShExtApi->registerScripting(psx, sqlite3_tclshext_init,
+ (ScriptSupport *)&tclss);
#if TCL_REPL==1 || TCL_REPL==2
Tcl_CreateCommand(interp, "get_input_line", getInputLine, psx, 0);
#endif
/* Define this proc so that ".." either gets to the TCL REPL loop
* or does nothing (if already in it), as a user convenience. */
Tcl_Eval(interp, "proc .. {} {}");
+#ifndef SHELL_OMIT_TK
+ if( ldTk ){
+ /* Create a command which wraps Tk_MainLoop(). It runs a GUI event
+ * loop, so does not return until all of its Tk windows are closed. */
+ Tcl_CreateCommand(interp, "run_gui", runTkGUI, psx, 0);
+ zAppName = "tclshext_tk";
+ }
+#endif
+ Tcl_SetVar2Ex(interp, "argv0", NULL,
+ Tcl_NewStringObj(zAppName,-1), TCL_GLOBAL_ONLY);
+ Tcl_SetVar2Ex(interp, "argc", NULL,
+ Tcl_NewIntObj(tnarg), TCL_GLOBAL_ONLY);
+ Tcl_SetVar2Ex(interp, "argv", NULL, targv, TCL_GLOBAL_ONLY);
pShExtLink->eid = sqlite3_tclshext_init;
}
if( rc==SQLITE_OK ){
-C For\sTCL\sextension:\n\s\sCleanup\sTCL\slibrary\sinteraction\sand\sinterpreter\smanagement.\n\s\sGroup\scode\sinto\smore\spurpose\soriented\sfunctions.\n\s\sAdd\s.unknown\sdot\scommand,\sdelegating\sto\sTCL\scommands\swith\s"dot"\snames.\n\s\sDrop\splanned\s.eval\snew\sdot\scommand.\nFor\sshell:\n\s\sImplement\sundocumented\s.unknown\sdot\scommand,\sdoing\slittle.\n\s\sMake\sdispatcher\scall\s.unknown's\simplementation\sfor\sunknown\sdot\scommand.\n\s\sFix\sa\scommand\slookup\sbug\sexposed\sby\sabove\schange\sto\sunknown\shandling.\n\s\sAdd\s.eval\sdot\scommand.\n\s\sAdd\soptions\sto\s.x\scommand\saimed\sat\scontrol\sof\serrors\sin\scommand\ssequences.\s(a\sWIP)\n\s\sMake\sdispatcher\sreport\sambiguous\sdot\scommand\slookups,\seven\swith\sno\sextension.\n
-D 2022-03-29T21:11:30.607
+C For\sTCL\sextension:\sAdd\sTk\sand\smeans\sto\srun\sit,\soptionally.\sImplement\sScriptSupport\sinterface.\nFor\sshell:\sGet\sscripting\ssupport\sthru\san\sobject\sinterface\slike\sthe\sothers.\sRename\sOutMode*\sto\sExport*.\nFor\sboth:\sProvide\sa\sway\sto\spass\sarguments\sto\san\sextension\supon\sload\sand\sget\sthem\sinto\sTCL's\sargv.\n
+D 2022-03-31T03:45:52.869
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/misc/spellfix.c 94df9bbfa514a563c1484f684a2df3d128a2f7209a84ca3ca100c68a0163e29f
F ext/misc/sqlar.c 0ace5d3c10fe736dc584bf1159a36b8e2e60fab309d310cd8a0eecd9036621b6
F ext/misc/stmt.c 35063044a388ead95557e4b84b89c1b93accc2f1c6ddea3f9710e8486a7af94a
-F ext/misc/tclshext.c.in f3da6a8ff02e335a021ac16d0164e7dc4d4d38691811e37419ed07289e8b181a
+F ext/misc/tclshext.c.in 50eb7c99d8b9c4e7b5f1d066cb8d36400b7b250f18107ba1282f39314c590c14
F ext/misc/templatevtab.c 8a16a91a5ceaccfcbd6aaaa56d46828806e460dd194965b3f77bf38f14b942c4
F ext/misc/totype.c fa4aedeb07f66169005dffa8de3b0a2b621779fd44f85c103228a42afa71853b
F ext/misc/uint.c 053fed3bce2e89583afcd4bf804d75d659879bbcedac74d0fa9ed548839a030b
F src/resolve.c ea935b87d6fb36c78b70cdc7b28561dc8f33f2ef37048389549c7b5ef9b0ba5e
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c c366c05e48e836ea04f8ecefb9c1225745dc250c3f01bdb39e9cbb0dc25e3610
-F src/shell.c.in d0e2c2417ed895d40af402eea3a2a38d462c6b17a654dc8e33c417a9ff355d29 x
-F src/shext_linkage.h c70f95dd0738c2cd8452ab5c47c245d7fa3b99ec26cd160c67dfb4489d5dacf9
+F src/shell.c.in a0f9dc4a772ac1b9737936b69b5931d4087ccc3711bb031cc42742f8dea7332a x
+F src/shext_linkage.h ae26717af53bee705fcba3c0d755dc59f7a55aa18adbe8b89b9f166bc8e35e06
F src/sqlite.h.in 2a35f62185eb5e7ecc64a2f68442b538ce9be74f80f28a00abc24837edcf1c17
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h f49e28c25bd941e79794db5415fdf7b202deb3bc072ed6f1ed273d578703684e
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 4ee7df118033af368854992763d96f860f8bd218b28b9ad27dd7ad4291cabfd7
-R 9fd24b1d2a3d7020ba2ddb1e81bfc0a5
+P 7616a6f4abe20699e5aa66018233247aa94e3e87f3e6ebe1357f776a18115eb3
+R 51ec30730a18840f04b19c6edf2cb5f7
U larrybr
-Z 7b481df3c4b2b2c70f760eb720278652
+Z 604240d043bafbbe5712cfcf2c93e52d
# Remove this line to create a well-formed Fossil manifest.
-7616a6f4abe20699e5aa66018233247aa94e3e87f3e6ebe1357f776a18115eb3
\ No newline at end of file
+d2446e50805cc3a46f9bcafccf017da60b0cf9fcd27695c214d71d1c5141d03f
\ No newline at end of file
* of pointers to the implementations. */
int numMetaCommands;
MetaCommand **ppMetaCommands;
- int numOutModeHandlers;
- OutModeHandler **ppOutModeHandlers;
+ int numExportHandlers;
+ ExportHandler **ppExportHandlers;
int numImportHandlers;
ImportHandler **ppImportHandlers;
} ShExtInfo;
ShExtInfo *pShxLoaded; /* Tracking and use info for loaded shell extensions */
int ixExtPending; /* Index of pending extension load operations if !0 */
/* scripting integration */
- ScriptHooks scripting; /* Hooks for scripting support from loaded extension */
- ExtensionId scriptXid; /* Id of extension which has set scripting hooks */
+ ScriptSupport *script; /* Scripting support, if any, from loaded extension */
+ ExtensionId scriptXid; /* Id of extension which is supporting scripting */
/* shell event subscription list */
int numSubscriptions; /* Number of active entries in below list */
struct EventSubscription {
ExtensionId eid;
void *pvUserData;
ShellEventNotify eventHandler;
- } *pSubscriptions; /* The currently active shell event subscriptions */
+ } *pSubscriptions; /* The current shell event subscriptions */
#endif
ShellExState *pSXS; /* Pointer to companion, exposed shell state */
}
/* Register an output data display (or other disposition) mode */
-static int register_out_mode(ShellExState *p,
- ExtensionId eid, OutModeHandler *pOMH){
+static int register_exporter(ShellExState *p,
+ ExtensionId eid, ExportHandler *pEH){
return SQLITE_ERROR;
}
return SQLITE_ERROR;
}
-static int hook_scripting(ShellExState *p, ExtensionId eid, ScriptHooks *pSH){
+static int register_scripting(ShellExState *p, ExtensionId eid,
+ ScriptSupport *pSS){
ShellInState *psi = ISS(p);
if( psi->scriptXid!=0 ){
/* Scripting support already provided. Only one provider is allowed. */
return SQLITE_BUSY;
}
- if( eid==0 || pSH==0 || psi->ixExtPending==0
- || pSH->scriptIsComplete==0 || pSH->runScript==0 ){
+ if( eid==0 || pSS==0 || psi->ixExtPending==0 ){
return SQLITE_MISUSE;
}
- psi->scripting = *pSH;
+ psi->script = pSS;
psi->scriptXid = eid;
return SQLITE_OK;
}
static ShellExtensionAPI shellExtAPI = {
&extHelpers, 5, {
register_meta_command,
- register_out_mode,
+ register_exporter,
register_importer,
- hook_scripting,
+ register_scripting,
subscribe_events,
0
}
psi->pShxLoaded = (ShExtInfo *)sqlite3_malloc(2*sizeof(ShExtInfo));
sei.ppMetaCommands
= (MetaCommand **)sqlite3_malloc((numCommands+2)*sizeof(MetaCommand *));
- sei.ppOutModeHandlers
- = (OutModeHandler **)sqlite3_malloc(2*sizeof(OutModeHandler *));
+ sei.ppExportHandlers
+ = (ExportHandler **)sqlite3_malloc(2*sizeof(ExportHandler *));
sei.ppImportHandlers
= (ImportHandler **)sqlite3_malloc(2*sizeof(ImportHandler *));
- if( sei.ppMetaCommands==0||sei.ppOutModeHandlers==0||sei.ppImportHandlers==0
+ if( sei.ppMetaCommands==0||sei.ppExportHandlers==0||sei.ppImportHandlers==0
|| psi->pShxLoaded==0 ){
shell_out_of_memory();
}
- sei.numOutModeHandlers = 0;
+ sei.numExportHandlers = 0;
sei.numImportHandlers = 0;
for( ic=0; ic<numCommands; ++ic ){
sei.ppMetaCommands[ic] = builtInCommand(ic);
}
sqlite3_free(psei->ppMetaCommands);
}
- if( psei->ppOutModeHandlers!=0 ){
- for( j=psei->numOutModeHandlers; j>0; --j ){
- OutModeHandler *pomh = psei->ppOutModeHandlers[j-1];
- if( pomh->pMethods->destruct!=0 ) pomh->pMethods->destruct(pomh);
+ if( psei->ppExportHandlers!=0 ){
+ for( j=psei->numExportHandlers; j>0; --j ){
+ ExportHandler *peh = psei->ppExportHandlers[j-1];
+ if( peh->pMethods->destruct!=0 ) peh->pMethods->destruct(peh);
}
- sqlite3_free(psei->ppOutModeHandlers);
+ sqlite3_free(psei->ppExportHandlers);
}
if( psei->ppImportHandlers!=0 ){
for( j=psei->numImportHandlers; j>0; --j ){
}
}
-/* Call all existent loaded extension destructors, in reverse order
- * of their objects' creation, then free the tracking dyna-arrays.
+/* Call all existent loaded extension destructors, in reverse order of their
+ * objects' creation, except for scripting support which is done last,
+ * then free the tracking dyna-arrays.
*/
static void free_all_shext_tracking(ShellInState *psi){
if( psi->pShxLoaded!=0 ){
ShExtInfo *psei = &psi->pShxLoaded[--i];
free_one_shext_tracking(psei);
if( psi->scriptXid!=0 && psi->scriptXid==psei->extId ){
- memset(&psi->scripting, 0, sizeof(ScriptHooks));
+ assert(psi->script!=0);
+ if (psi->script->pMethods->destruct){
+ psi->script->pMethods->destruct(psi->script);
+ }
+ psi->script = 0;
psi->scriptXid = 0;
}
}
}
static int load_shell_extension(ShellExState *psx, const char *zFile,
- const char *zProc, char **pzErr){
+ const char *zProc, char **pzErr,
+ int nLoadArgs, char **azLoadArgs){
ShellExtensionLink shxLink = {
sizeof(ShellExtensionLink),
&shellExtAPI,
0, /* zErrMsg */
0, /* ExtensionId */
0, /* Extension destructor */
- 0 /* Extension data ref */
+ 0, /* Extension data ref */
+ nLoadArgs, azLoadArgs /* like-named members */
}; //extDtor(pvExtObj)
ShellInState *psi = ISS(psx);
- /* save script hooking state for possible fallback if load fails */
- ScriptHooks shSave = psi->scripting;
- ExtensionId siSave = psi->scriptXid;
+ /* save script support state for possible fallback if load fails */
+ ScriptSupport *pssSave = psi->script;
+ ExtensionId ssiSave = psi->scriptXid;
int rc;
if( pzErr ) *pzErr = 0;
free_one_shext_tracking(psi->pShxLoaded+psi->ixExtPending);
--psi->numExtLoaded;
}
- /* And unhook any scripting linkage it might have setup. */
- psi->scripting = shSave;
- psi->scriptXid = siSave;
+ /* And make it unwind any scripting linkage it might have setup. */
+ if( psi->script!=0 ) psi->script->pMethods->destruct(psi->script);
+ psi->script = pssSave;
+ psi->scriptXid = ssiSave;
}
psi->ixExtPending = 0;
if( rc!=SQLITE_OK ){
}
/*****************
- * The .imposter, .iotrace, limit, lint, .load and .log commands
+ * The .imposter, .iotrace, limit, lint and .log commands
*/
CONDITION_COMMAND( imposter !defined(SQLITE_OMIT_TEST_CONTROL) );
CONDITION_COMMAND( iotrace defined(SQLITE_ENABLE_IOTRACE) );
".lint OPTIONS Report potential schema issues.",
" Options:",
" fkey-indexes Find missing foreign key indexes",
- ".load FILE ?ENTRY? Load an extension library",
-#if SHELL_DYNAMIC_EXTENSION
- " Option -shellext will load the library as a shell extension.",
-#endif
".log FILE|off Turn logging on or off. FILE can be stderr/stdout",
];
DISPATCHABLE_COMMAND( imposter ? 3 3 ){
return DCR_Ok|(rc!=0);
}
-DISPATCHABLE_COMMAND( load ? 2 4 ){
- const char *zFile = 0, *zProc = 0;
- char *zErrMsg = 0;
- int ai = 1, rc;
-#if SHELL_DYNAMIC_EXTENSION
- u8 bLoadShellExt = 0;
-#endif
- if( ISS(p)->bSafeMode ) return DCR_AbortError;
- while( ai<nArg ){
- const char *zA = azArg[ai++];
-#if SHELL_DYNAMIC_EXTENSION
- if( optionMatch(zA, "shellext") ) bLoadShellExt = 1;
- else
-#endif
- if( zFile==0 ) zFile = zA;
- else if( zProc==0 ) zProc = zA;
- else return DCR_TooMany|ai;
- }
- open_db(p, 0);
-#if SHELL_DYNAMIC_EXTENSION
- if( bLoadShellExt ){
- rc = load_shell_extension(p, zFile, zProc, pzErr);
- }else
-#endif
- {
- rc = sqlite3_load_extension(DBX(p), zFile, zProc, pzErr);
- }
- return DCR_Ok|(rc!=SQLITE_OK);
-}
-
DISPATCHABLE_COMMAND( log ? 2 2 ){
const char *zFile = azArg[1];
if( ISS(p)->bSafeMode ) return DCR_AbortError;
psi->mode = modeNominal;
}
+/*****************
+ * The .load command
+ * This is out of order to alleviate code clarity concerns.
+ */
+#if SHELL_DYNAMIC_EXTENSION /* Avoid confusing users or mkshellc.tcl here. */
+# define LDEXT_OPTS "?ENTRY? ?..?"
+# define LDEXT_MAXARGS 0
+#else
+# define LDEXT_OPTS "?ENTRY? "
+# define LDEXT_MAXARGS 3
+#endif
+COLLECT_HELP_TEXT[
+ ".load FILE "LDEXT_OPTS" Load an extension library",
+#if SHELL_DYNAMIC_EXTENSION
+ " If option -shext follows the first 1 or 2 arguments, then the library",
+ " will be loaded as a shell extension, and any subsequent arguments will",
+ " be passed to the extension's init function. That function is named per",
+ " the docs for sqlite3_load_extension(), with ENTRY taking zProc's role.",
+#else
+ " If ENTRY is provided, the entry point \"sqlite_ENTRY_init\" is called.",
+ " Otherwise, the entry point name is derived from the FILE's name.",
+#endif
+];
+DISPATCHABLE_COMMAND( load ? 2 LDEXT_MAXARGS ){
+ const char *zFile = 0, *zProc = 0;
+ char *zErrMsg = 0;
+ int ai = 1, rc;
+#if SHELL_DYNAMIC_EXTENSION
+ char **pzShext = 0;
+#else
+ if( nArg>3 ) return DCR_TooMany|3;
+#endif
+ if( ISS(p)->bSafeMode ) return DCR_AbortError;
+ while( ai<nArg ){
+ const char *zA = azArg[ai++];
+#if SHELL_DYNAMIC_EXTENSION
+ if( optionMatch(zA, "shext") ){
+ if( zFile==0 ) return DCR_Missing;
+ pzShext = azArg + ai;
+ break;
+ }else if( zFile==0 ) zFile = zA;
+ else if( zProc==0 ) zProc = zA;
+#else
+ if( zFile==0 ) zFile = zA;
+ else if( zProc==0 ) zProc = zA;
+ else return DCR_TooMany|ai;
+#endif
+ }
+ open_db(p, 0);
+#if SHELL_DYNAMIC_EXTENSION
+ if( pzShext ){
+ rc = load_shell_extension(p, zFile, zProc, pzErr, nArg-ai, pzShext);
+ }else
+#endif
+ {
+ rc = sqlite3_load_extension(DBX(p), zFile, zProc, pzErr);
+ }
+ return DCR_Ok|(rc!=SQLITE_OK);
+}
+
/*****************
* The .mode command
*/
** This is broken out of process_input() mainly for readability.
** The return is TRUE for dot-command ready to run, else false.
*/
-static int line_join_ends(DCmd_ScanState dcss, char *zLine,
+static int line_join_done(DCmd_ScanState dcss, char *zLine,
int *pnLength, char *pcLE){
/* It is ready only if has no open argument or escaped newline. */
int bOpen = DCSS_IsOpen(dcss);
/* line-group processing loop (per SQL block, dot-command or comment) */
while( !bInputEnd && termKind==DCR_Ok && !bInterrupted ){
+ ScriptSupport *pSS = psi->script;
int nGroupLines = 0; /* count of lines belonging to this group */
int ncLineIn = 0; /* how many (non-zero) chars are in zLineInput */
int ncLineAcc = 0; /* how many (non-zero) chars are in zLineAccum */
SqlScanState sqScanState = SSS_Start; /* for SQL scan */
#if SHELL_EXTENDED_PARSING
DCmd_ScanState dcScanState = DCSS_Start; /* for dot-command scan */
- int ndcLeadWhite = 0; /* skips over initial whitespace to . or # */
+ int nLeadWhite = 0; /* skips over initial whitespace to . or # */
char cLineEnd = '\n'; /* May be swallowed or replaced with space. */
#else
-# define ndcLeadWhite 0 /* For legacy parsing, no white before . or # . */
+# define nLeadWhite 0 /* For legacy parsing, no white before . or # . */
# define cLineEnd '\n' /* For legacy parsing, this always joins lines. */
#endif
/* An ordered enum to record kind of incoming line group. Its ordering
}
/* Classify and check for single-line dispositions, prep for more. */
#if SHELL_EXTENDED_PARSING
- ndcLeadWhite = (SHEXT_PARSING(psi))
+ nLeadWhite = (SHEXT_PARSING(psi))
? skipWhite(zLineInput)-zLineInput
: 0; /* Disallow leading whitespace for . or # in legacy mode. */
#endif
#if SHELL_DYNAMIC_EXTENSION
- if( psi->scripting.isScriptLeader!=0
- && psi->scripting.isScriptLeader(psi->scripting.pvScriptingState,
- zLineInput+ndcLeadWhite) ){
+ if( pSS && pSS->pMethods->isScriptLeader(pSS, zLineInput+nLeadWhite) ){
inKind = Script;
}else
#endif
{
- switch( zLineInput[ndcLeadWhite] ){
+ switch( zLineInput[nLeadWhite] ){
case '.':
inKind = Cmd;
- dot_command_scan(zLineInput+ndcLeadWhite, &dcScanState);
+ dot_command_scan(zLineInput+nLeadWhite, &dcScanState);
break;
case '#':
inKind = Comment;
case Cmd:
#if SHELL_EXTENDED_PARSING
if( SHEXT_PARSING(psi) ){
- if( line_join_ends(dcScanState, *pzLineUse, pncLineUse, &cLineEnd) ){
+ if( line_join_done(dcScanState, *pzLineUse, pncLineUse, &cLineEnd) ){
disposition = Runnable;
}
}else
break;
#if SHELL_DYNAMIC_EXTENSION
case Script:
- if( psi->scripting.scriptIsComplete==0
- || psi->scripting.scriptIsComplete(psi->scripting.pvScriptingState,
- *pzLineUse+ndcLeadWhite) ){
+ if( pSS==0
+ || pSS->pMethods->scriptIsComplete(pSS, *pzLineUse+nLeadWhite, 0) ){
disposition = Runnable;
}
break;
switch( disposition ){
case Dumpable:
echo_group_input(psi, *pzLineUse);
+#if SHELL_DYNAMIC_EXTENSION
+ if( inKind==Script && pSS!=0 ) pSS->pMethods->restartCompletionScan(pSS);
+#endif
break;
case Runnable:
switch( inKind ){
case Cmd: {
DotCmdRC dcr;
echo_group_input(psi, *pzLineUse);
- dcr = do_meta_command(*pzLineUse+ndcLeadWhite, XSS(psi));
+ dcr = do_meta_command(*pzLineUse+nLeadWhite, XSS(psi));
nErrors += (dcr & DCR_Error);
dcr &= ~DCR_Error;
if( dcr > termKind ) termKind = dcr;
}
#if SHELL_DYNAMIC_EXTENSION
case Script: {
- if( psi->scripting.runScript!=0 ){
- char *zErr = 0;
- DotCmdRC dcr
- = psi->scripting.runScript(psi->scripting.pvScriptingState,
- *pzLineUse+ndcLeadWhite,
+ char *zErr = 0;
+ DotCmdRC dcr;
+ assert(pSS!=0);
+ pSS->pMethods->restartCompletionScan(pSS);
+ dcr = pSS->pMethods->runScript(pSS, *pzLineUse+nLeadWhite,
XSS(psi), &zErr);
- if( dcr!=DCR_Ok || zErr!=0 ){
- /* ToDo: Handle errors more informatively and like dot commands. */
- nErrors += (dcr!=DCR_Ok);
- if( zErr!=0 ){
- utf8_printf(STD_ERR, "Error: %s\n", zErr);
- sqlite3_free(zErr);
- }
+ if( dcr!=DCR_Ok || zErr!=0 ){
+ /* ToDo: Handle errors more informatively and like dot commands. */
+ nErrors += (dcr!=DCR_Ok);
+ if( zErr!=0 ){
+ utf8_printf(STD_ERR, "Error: %s\n", zErr);
+ sqlite3_free(zErr);
}
- }else{
- utf8_printf(STD_ERR, "Error: No script support;"
- " ignoring group at line %d of \"%s\"\n",
- psi->pInSource->lineno, psi->pInSource->zSourceSay);
- ++nErrors;
}
break;
}
case Erroneous:
utf8_printf(STD_ERR, "Error: Input incomplete at line %d of \"%s\"\n",
psi->pInSource->lineno, psi->pInSource->zSourceSay);
+#if SHELL_DYNAMIC_EXTENSION
+ if( inKind==Script && pSS!=0 ) pSS->pMethods->restartCompletionScan(pSS);
+#endif
++nErrors;
break;
case Ignore:
/* An object implementing below interface is registered with the
* shell to make new or overriding output modes available to it.
*/
-INTERFACE_BEGIN( OutModeHandler );
-PURE_VMETHOD(const char *, name, OutModeHandler, 0,());
-PURE_VMETHOD(const char *, help, OutModeHandler, 1,(int more));
-PURE_VMETHOD(int, openResultsOutStream, OutModeHandler,
+INTERFACE_BEGIN( ExportHandler );
+PURE_VMETHOD(const char *, name, ExportHandler, 0,());
+PURE_VMETHOD(const char *, help, ExportHandler, 1,(int more));
+PURE_VMETHOD(int, openResultsOutStream, ExportHandler,
5,( ShellExState *pSES, char **pzErr,
int numArgs, char *azArgs[], const char * zName ));
-PURE_VMETHOD(int, prependResultsOut, OutModeHandler,
+PURE_VMETHOD(int, prependResultsOut, ExportHandler,
3,( ShellExState *pSES, char **pzErr, sqlite3_stmt *pStmt ));
-PURE_VMETHOD(int, rowResultsOut, OutModeHandler,
+PURE_VMETHOD(int, rowResultsOut, ExportHandler,
3,( ShellExState *pSES, char **pzErr, sqlite3_stmt *pStmt ));
-PURE_VMETHOD(int, appendResultsOut, OutModeHandler,
+PURE_VMETHOD(int, appendResultsOut, ExportHandler,
3,( ShellExState *pSES, char **pzErr, sqlite3_stmt *pStmt ));
-PURE_VMETHOD(void, closeResultsOutStream, OutModeHandler,
+PURE_VMETHOD(void, closeResultsOutStream, ExportHandler,
2,( ShellExState *pSES, char **pzErr ));
-INTERFACE_END( OutModeHandlerVtable );
+INTERFACE_END( ExportHandler );
/* An object implementing below interface is registered with the
* shell to make new or overriding data importers available to it.
3,( ShellExState *pSES, char **pzErr, sqlite3_stmt *pStmt ));
PURE_VMETHOD(void, closeDataInStream, ImportHandler,
2,( ShellExState *pSES, char **pzErr ));
-INTERFACE_END( ImportHandlerVtable );
+INTERFACE_END( ImportHandler );
+/* An object implementing this next interface is registered with the shell
+ * to make scripting support available to it. Only one at a time can be used.
+ *
+ * If registerScripting() has been called to register an extension's support
+ * for scripting, then its methods are called, and must respond, as follows:
+ *
+ * When the initial line of an "execution group" is collected by the shell,
+ * it calls isScriptLeader(pObj, zLineLead) to determine whether the group
+ * should be considered as (eventually) being one for the script handler
+ * to execute. This does not indicate whether it is good input or runnable;
+ * it is only for classification (so that different parsing/collection rules
+ * may be applied for different categories of shell input.) The method should
+ * return true iff the group should be parsed and run by this handler. If it
+ * returns false, something else will be done with the group.
+ *
+ * As one or more lines of an "execution group" are collected by the shell,
+ * scriptIsComplete(pObj, zLineGroup, pzWhyNot) is called with the group as
+ * so far accumulated. If out parameter pzWhyNot is non-zero, the method may
+ * output a message indicating in what way the input is incomplete, which is
+ * then the shell's responsibility to pass to sqlite3_free(). The method must
+ * return true if the group is ready to be executed, otherwise false. This is
+ * not the time at which to execute the accumulated group.
+ *
+ * After the scriptIsComplete() method returns true, or whenever the script is
+ * being ignored (due to end-of-stream or interrupt), the resetCompletionScan()
+ * method is called. This may be used to reset the scanning state held across
+ * calls to scriptIsComplete() so that it need not scan the whole script text
+ * at each call. It might do other things; it is always called after a call to
+ * isScriptLeader() has returned true and scriptIsComplete() has been called.
+ *
+ * If a script group is complete (as above-determined), then runScript() may
+ * be called to execute that script group. (Or, it may not.) It must either
+ * execute it successfully and return DCR_Ok, suffer an ordinary failure and
+ * return DCR_Error, or return one of the codes DCR_{Return,Exit,Abort} or'ed
+ * with DCR_Error or not, to indicate extraordinary post-execute actions.
+ * DCR_Return is to indicate the present execution context should be left.
+ * DCR_Exit is for shell exit requests. DCR_Abort means exit with prejudice.
+ *
+ * An extension which has called registerScripting() should arrange to
+ * free associated resources upon exit or when its destructor runs.
+ */
+INTERFACE_BEGIN( ScriptSupport );
+PURE_VMETHOD(const char *, name, ScriptSupport, 0,());
+PURE_VMETHOD(const char *, help, ScriptSupport, 1,( int more ));
+PURE_VMETHOD(int, configure, ScriptSupport,
+ 4,( ShellExState *pSES, char **pzErr,
+ int numArgs, char *azArgs[] ));
+PURE_VMETHOD(int, isScriptLeader, ScriptSupport,
+ 1,( const char *zScript ));
+PURE_VMETHOD(int, scriptIsComplete, ScriptSupport,
+ 2,( const char *zScript, char **pzWhyNot ));
+PURE_VMETHOD(void, resetCompletionScan, ScriptSupport, 0,());
+
+PURE_VMETHOD(DotCmdRC, runScript, ScriptSupport,
+ 3,( const char *zScript, ShellExState *pSES, char **pzErr ));
+INTERFACE_END( ScriptSupport );
+
+/* Define a v-table implementation for ScriptSupport interface. */
+#define ScriptSupport_IMPLEMENT_VTABLE(Derived, vtname) \
+CONCRETE_BEGIN(ScriptSupport, Derived); \
+CONCRETE_METHOD(const char *, name, ScriptSupport, 0,()); \
+CONCRETE_METHOD(const char *, help, ScriptSupport, 1,( int more )); \
+CONCRETE_METHOD(int, configure, ScriptSupport, \
+ 4,( ShellExState *pSES, char **pzErr, int numArgs, char *azArgs[] )); \
+CONCRETE_METHOD(int, isScriptLeader, ScriptSupport, \
+ 1,( const char *zScript )); \
+CONCRETE_METHOD(int, scriptIsComplete, ScriptSupport, \
+ 2,( const char *zScript, char **pzWhyNot )); \
+CONCRETE_METHOD(void, resetCompletionScan, ScriptSupport, 0,()); \
+CONCRETE_METHOD(DotCmdRC, runScript, ScriptSupport, \
+ 3,( const char *zScript, ShellExState *pSES, char **pzErr )); \
+CONCRETE_END(Derived) vtname = { \
+ DECORATE_METHOD(Derived,destruct), \
+ DECORATE_METHOD(Derived,name), \
+ DECORATE_METHOD(Derived,help), \
+ DECORATE_METHOD(Derived,configure), \
+ DECORATE_METHOD(Derived,isScriptLeader), \
+ DECORATE_METHOD(Derived,scriptIsComplete), \
+ DECORATE_METHOD(Derived,resetCompletionScan), \
+ DECORATE_METHOD(Derived,runScript) \
+}
/* Define an implementation's v-table matching the MetaCommand interface.
* Method signatures are copied and pasted from above interface declaration.
*/
typedef int (*ExtensionId)
(sqlite3 *, char **, const struct sqlite3_api_routines *);
-/* Hooks for scripting language integration.
- *
- * If hookScripting(...) has been called to register an extension's
- * scripting support, and isScriptLeader(pvSS, zLineLead) returns true,
- * (where zLineLead is an input group's leading line), then the shell
- * will collect input lines until scriptIsComplete(pvSS, zLineGroup)
- * returns non-zero, whereupon, the same group is submitted to be run
- * via runScript(pvSS, zLineGroup, ...). The default behaviors (when
- * any of the function pointers is 0) are: return false; return true;
- * and return DCR_Error after doing nothing.
- *
- * An extension which has called hookScripting() should arrange to
- * free associated resources upon exit or when its destructor runs.
- *
- * The 1st member, pvScriptingState, is an arbitrary, opaque pointer.
- */
-typedef struct ScriptHooks {
- void *pvScriptingState; /* passed into below functions as pvSS */
- int (*isScriptLeader)(void *pvSS, const char *zScript);
- int (*scriptIsComplete)(void *pvSS, const char *zScript);
- DotCmdRC (*runScript)(void *pvSS, const char *zScript,
- ShellExState *, char **pzErrMsg);
-} ScriptHooks;
-
typedef struct Prompts {
const char *zMain;
const char *zContinue;
int (*registerMetaCommand)(ShellExState *p,
ExtensionId eid, MetaCommand *pMC);
/* Register query result data display (or other disposition) mode */
- int (*registerOutMode)(ShellExState *p,
- ExtensionId eid, OutModeHandler *pOMH);
+ int (*registerExporter)(ShellExState *p,
+ ExtensionId eid, ExportHandler *pOMH);
/* Register an import variation from (various sources) for .import */
int (*registerImporter)(ShellExState *p,
ExtensionId eid, ImportHandler *pIH);
- /* Provide scripting support to host shell. (See ScriptHooks above.) */
- int (*hookScripting)(ShellExState *p,
- ExtensionId eid, ScriptHooks *pSH);
+ /* Provide scripting support to host shell. (See ScriptSupport above.) */
+ int (*registerScripting)(ShellExState *p,
+ ExtensionId eid, ScriptSupport *pSS);
/* Subscribe to (or unsubscribe from) messages about various changes.
* See above NoticeKind enum and ShellEventNotify callback typedef. */
int (*subscribeEvents)(ShellExState *p, ExtensionId eid, void *pvUserData,
void (*extensionDestruct)(void *pvExtObj);
void *pvExtensionObject;
+ /* If extra arguments were provided to the .load command, they are
+ * available through these two members. Only azLoadArgs[0] through
+ * azLoadArgs[nLoadArgs-1] may be referenced. (That may be none.)
+ * If an extension keeps the argument values, copies must be made
+ * as the pointers in azLoadArgs[] become invalid after loading.
+ */
+ int nLoadArgs;
+ char **azLoadArgs;
} ShellExtensionLink;
/* String used with SQLite "Pointer Passing Interfaces" as a type marker.