#include "shext_linkage.h"
-static struct ShExtAPI *pShExtApi = 0;
-static struct ExtHelpers *pExtHelpers = 0;
-
+/* Extension boiler-plate to dynamically link into host's SQLite library */
SQLITE_EXTENSION_INIT1;
+/* Extension boiler-plate for a function to get ShellExtensionLink pointer
+ * from db passed to extension init() and define a pair of static API refs.
+ */
+SHELL_EXTENSION_INIT1(pShExtApi, pExtHelpers, shextLinkFetcher);
+
/* This is not found in the API pointer table published for extensions: */
#define sqlite3_enable_load_extension pExtHelpers->enable_load_extension
# define TCL_REPL 3
#endif
-static const char * const zREPL =
+
#if TCL_REPL==1 /* a fallback for debug */
- "set line {}\n"
- "while {![eof stdin]} {\n"
- "if {$line!=\"\"} {\n"
- "puts -nonewline \"> \"\n"
- "} else {\n"
- "puts -nonewline \"% \"\n"
- "}\n"
- "flush stdout\n"
- "append line [gets stdin]\n"
- "if {$line eq \".\"} break\n"
- "if {[info complete $line]} {\n"
- "if {[catch {uplevel #0 $line} result]} {\n"
- "puts stderr \"Error: $result\"\n"
- "} elseif {$result!=\"\"} {\n"
- "puts $result\n"
- "}\n"
- "set line {}\n"
- "} else {\n"
- "append line \\n\n"
- "}\n"
- "}\n"
- "if {$line ne \".\"} {puts {}}\n"
- "read stdin 0\n"
+TCL_CSTR_LITERAL(static const char * const zREPL = ){
+ set line {}
+ while {![eof stdin]} {
+ if {$line!=""} {
+ puts -nonewline "> "
+ } else {
+ puts -nonewline "% "
+ }
+ flush stdout
+ append line [gets stdin]
+ if {$line eq "."} break
+ if {[info complete $line]} {
+ if {[catch {uplevel #0 $line} result]} {
+ puts stderr "Error: $result"
+ } elseif {$result!=""} {
+ puts $result
+ }
+ set line {}
+ } else {
+ append line \n
+ }
+ }
+ if {$line ne "."} {puts {}}
+ read stdin 0
+};
#elif TCL_REPL==2 /* minimal use of shell's read */
- "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 \"tcl% \"\n"
- "}\n"
- "}\n"
- "flush stdout\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 ::REPL::at_end 1\n"
- "} else {\n"
- "append ::REPL::line $::REPL::li\n"
- "if {[string trim $::REPL::line] eq \"\"} {\n"
- "set ::REPL::line \"\"\n"
- "continue\n"
- "}\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 ::REPL::line {}\n"
- "}\n"
- "}\n"
- "}\n"
- "if {$::REPL::prompting && $::REPL::li ne \".\\n\"} {puts {}}\n"
- "namespace delete ::REPL\n"
- "read stdin 0\n"
+TCL_CSTR_LITERAL(static const char * const zREPL = ){
+ namespace eval ::REPL {
+ variable line {}
+ variable at_end 0
+ variable prompting [now_interactive]
+ }
+ while {!$::REPL::at_end} {
+ if {$::REPL::prompting} {
+ if {$::REPL::line!=""} {
+ puts -nonewline "...> "
+ } else {
+ puts -nonewline "tcl% "
+ }
+ }
+ flush stdout
+ set ::REPL::li [get_input_line]
+ if {$::REPL::li eq ""} {
+ set ::REPL::at_end 1
+ } elseif {[string trimright $::REPL::li] eq "."} {
+ if {$::REPL::line ne ""} {
+ throw {NONE} {incomplete input at EOF}
+ }
+ set ::REPL::at_end 1
+ } else {
+ append ::REPL::line $::REPL::li
+ if {[string trim $::REPL::line] eq ""} {
+ set ::REPL::line ""
+ continue
+ }
+ if {[info complete $::REPL::line]} {
+ set ::REPL::rc [catch {uplevel #0 $::REPL::line} ::REPL::result]
+ if {$::REPL::rc == 0} {
+ if {$::REPL::result!="" && $::REPL::prompting} {
+ puts $::REPL::result
+ }
+ } elseif {$::REPL::rc == 1} {
+ puts stderr "Error: $::REPL::result"
+ } elseif {$::REPL::rc == 2} {
+ set ::REPL::at_end 1
+ }
+ set ::REPL::line {}
+ }
+ }
+ }
+ if {$::REPL::prompting && $::REPL::li ne ".\n"} {puts {}}
+ namespace delete ::REPL
+ read stdin 0
+};
#elif TCL_REPL==3
-# define SHELL_REPL_CMDNAME "sqlite_shell_REPL"
- /* using shell's input collection with line editing (if configured) */
- SHELL_REPL_CMDNAME
+/* using shell's input collection with line editing (if configured) */
+static const char * const zREPL = "sqlite_shell_REPL";
+
+TCL_CSTR_LITERAL(static const char * const zDefineREPL = ){
+ proc sqlite_shell_REPL {} {
+ set interactive [now_interactive]
+ while {1} {
+ foreach {group ready} [get_tcl_group] {}
+ set trimmed [string trim $group]
+ if {$group eq "" && !$ready} break
+ if {$trimmed eq ""} continue
+ if {!$ready && $trimmed ne ""} {
+ throw {NONE} {incomplete input at EOF}
+ }
+ if {$trimmed eq "."} break
+ set rc [catch {uplevel #0 $group} result]
+ if {$rc == 0} {
+ if {$result != "" && $interactive} {
+ puts $result
+ }
+ } elseif {$rc == 1} {
+ puts stderr "Error: $result"
+ } elseif {$rc == 2} {
+ return -code 2
+ }
+ }
+ if {$interactive && $trimmed ne "."} {puts {}}
+ read stdin 0
+ }
+};
#else
"throw {NONE} {not built for REPL}\n"
#endif
- ; /* zREPL */
-
-static const char * const zDefineREPL =
- "proc "SHELL_REPL_CMDNAME" {} {\n"
- "set interactive [now_interactive]\n"
- "while {1} {\n"
- "foreach {group ready} [get_tcl_group] {}\n"
- "set trimmed [string trim $group]\n"
- "if {$group eq \"\" && !$ready} break\n"
- "if {$trimmed eq \"\"} continue\n"
- "if {!$ready && $trimmed ne \"\"} {\n"
- "throw {NONE} {incomplete input at EOF}\n"
- "}\n"
- "if {$trimmed eq \".\"} break\n"
- "set rc [catch {uplevel #0 $group} result]\n"
- "if {$rc == 0} {\n"
- "if {$result != \"\" && $interactive} {\n"
- "puts $result\n"
- "}\n"
- "} elseif {$rc == 1} {\n"
- "puts stderr \"Error: $result\"\n"
- "} elseif {$rc == 2} {\n"
- "return -code 2\n"
- "}\n"
- "}\n"
- "if {$interactive && $trimmed ne \".\"} {puts {}}\n"
- "read stdin 0\n"
- "}\n"
- ;
/* Enter the preferred REPL */
static DotCmdRC runTclREPL(Tcl_Interp *interp, char **pzErrMsg){
return runTclREPL(getInterp(), pzErrMsg);
}
}
+
DERIVED_METHOD(DotCmdRC, execute, MetaCommand,UnkCmd, 4,
(ShellExState *psx, char **pzErrMsg, int nArgs, char *azArgs[])){
Tcl_Interp *interp = getInterp();
&tclss_methods
};
+#if TCL_REPL==2
#define GETLINE_MAXLEN 1000
-#if TCL_REPL==2
/* C implementation of TCL proc, get_input_line */
static int getInputLine(void *pvSS, Tcl_Interp *interp,
int nArgs, const char *azArgs[]){
static int UserDbObjCmd(void *cd, Tcl_Interp *interp,
int objc, Tcl_Obj * const * objv){
UserDb *pudb = (UserDb*)cd;
- static const char *azDoHere[] = { "close", ".eval", 0 };
- enum DbDoWhat { DDW_Close, DDW_ShellEval };
+ static const char *azDoHere[] = { "close", 0 };
+ enum DbDoWhat { DDW_Close };
int doWhat;
int whichDb = -1;
const char *zMoan;
case DDW_Close:
zMoan = " close is disallowd. It is a wrapped DB.\n";
goto complain_fail;
- case DDW_ShellEval:
- Tcl_AppendResult(interp, "Faking .eval ...\n", 0);
- //... ToDo: Implement this.
- return TCL_OK;
}
}
if( pudb->numSdb==0 || whichDb<0 ){
return TCL_ERROR;
}
-/* Extension boiler-plate to grab ShellExtensionLink pointer from db. */
-DEFINE_SHDB_TO_SHEXTLINK(shext_link);
-
/*
** Extension load function.
*/
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
- ShellExtensionLink *pShExtLink;
SQLITE_EXTENSION_INIT2(pApi);
- pShExtLink = shext_link(db);
- if( pShExtLink && pShExtLink->pShellExtensionAPI->numRegistrars>=5 ){
+ SHELL_EXTENSION_INIT2(pShExtLink, shextLinkFetcher, db);
+
+ SHELL_EXTENSION_INIT3(pShExtApi, pExtHelpers, pShExtLink);
+ if( SHELL_EXTENSION_LOADFAIL(pShExtLink, 5, 10) ){
+ *pzErrMsg
+ = sqlite3_mprintf("No ShellExtensionLink or shell API is too old.\n"
+ "Use '.load tclshext -shext' or update the shell.\n");
+ return SQLITE_ERROR;
+ }else{
ShellExState *psx = pShExtLink->pSXS;
Tcl_Obj *targv = Tcl_NewListObj(0, NULL);
const char *zAppName = "tclshext";
int ldTk = 0;
int rc = 0;
- pShExtApi = & pShExtLink->pShellExtensionAPI->api.named;
- pExtHelpers = & pShExtLink->pShellExtensionAPI->pExtHelpers->helpers.named;
- if( pShExtLink->pShellExtensionAPI->pExtHelpers->helperCount < 10 ){
- *pzErrMsg = sqlite3_mprintf("Shell version mismatch");
- return SQLITE_ERROR;
- }
if( pShExtLink->nLoadArgs>0 ){
int ila;
for( ila=0; ila<pShExtLink->nLoadArgs; ++ila ){
}
return rc;
}
- else{
- *pzErrMsg
- = sqlite3_mprintf("Bad ShellExtensionLink or registration API.\n");
- return SQLITE_ERROR;
- }
}
-C For\sTCL\sextension:\sAdjust\sprovided\s"gui"\scommand,\sand\sdocument\sit.\nFor\sshell:\sSeparate\sshell\svariables\sfrom\sbinding\sparameters,\smainly\sso\sthey\slive\slonger,\sin\sthe\sshell\sDB.\nAdd\s.vars\sdot\scommand\sto\sreflect\sthis\sseparation,\sand\sspecialized\sfor\sshell\svariables.\nMuch\scode\sshuffling\sto\sshare\scode\sbetween\s.parameters\sand\s.vars\scommands.\n
-D 2022-04-04T06:33:43.022
+C For\sshell\sextension\swriters,\sreduce\sboilerplate\s(mimicing\sSQLITE_EXTENSION_INIT#\smacros)
+D 2022-04-04T17:27:59.089
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 05768ea49bd04d3b1ca6287b0769ca63fa7453544db6c3c58d28ce7469ba2c4b
+F ext/misc/tclshext.c.in b09d1d38698f0d89fdfaae91231bf1ef017a56335b153f1bfb6d44e09d8d6674
F ext/misc/templatevtab.c 8a16a91a5ceaccfcbd6aaaa56d46828806e460dd194965b3f77bf38f14b942c4
F ext/misc/totype.c fa4aedeb07f66169005dffa8de3b0a2b621779fd44f85c103228a42afa71853b
F ext/misc/uint.c 053fed3bce2e89583afcd4bf804d75d659879bbcedac74d0fa9ed548839a030b
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c c366c05e48e836ea04f8ecefb9c1225745dc250c3f01bdb39e9cbb0dc25e3610
F src/shell.c.in 37740ab1661dcd54ec9577f632afc968692aa86001a7ce21f839a6077ec3c36c x
-F src/shext_linkage.h a07c4eaaeff29386a2be06773b779ff4a2a299fda966fd432b7258f191666fdc
+F src/shext_linkage.h 307e241b9fdc42ca02387303b0abdffd5afd04a5a8540807a5061a97fb2c26cd
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 4f11e639f02e6ebe5ec9268cf066b83e6992599c049f497ebed704614b870706
-R de816fd352883164601c9ffe394f900e
+P fa492ff57ca9d89ac623734e8ed0411e29ed6a926c2f534f2a91e41fad994b46
+R 79ea17b79c197cddd70007ce0ce4c0bc
U larrybr
-Z c0273a0e4655d42ef0fee3956260570a
+Z 9520f9c90493a0493ae4b54df22d1805
# Remove this line to create a well-formed Fossil manifest.
ExtensionId eid, MetaCommand *pMC);
/* Register query result data display (or other disposition) mode */
int (*registerExporter)(ShellExState *p,
- ExtensionId eid, ExportHandler *pOMH);
+ ExtensionId eid, ExportHandler *pEH);
/* Register an import variation from (various sources) for .import */
int (*registerImporter)(ShellExState *p,
ExtensionId eid, ImportHandler *pIH);
* pointer to a ShellExtensionLink instance during an extension's *init*()
* call (during shell extension load) or 0 (during SQLite extension load.)
*/
-#define DEFINE_SHDB_TO_SHEXTLINK(func_name) \
- static ShellExtensionLink * func_name(sqlite3 * db){ \
+#define DEFINE_SHDB_TO_SHEXTLINK(link_func_name) \
+ static ShellExtensionLink * link_func_name(sqlite3 * db){ \
ShellExtensionLink *rv = 0; sqlite3_stmt *pStmt = 0; \
if( SQLITE_OK==sqlite3_prepare_v2(db,"SELECT shext_pointer(0)",-1,&pStmt,0) \
&& SQLITE_ROW == sqlite3_step(pStmt) ) \
sqlite3_finalize(pStmt); return rv; \
}
+/*
+ * Define boilerplate macros analogous to SQLITE_EXTENSION_INIT#
+ */
+/* Place at file scope prior to usage of the arguments by extension code. */
+#define SHELL_EXTENSION_INIT1( shell_api_ptr, ext_helpers_ptr, link_func ) \
+ static struct ShExtAPI *shell_api_ptr = 0; \
+ static struct ExtHelpers *ext_helpers_ptr = 0; \
+ DEFINE_SHDB_TO_SHEXTLINK(link_func)
+
+/* Place within sqlite3_x_init() among its local variable declarations. */
+#define SHELL_EXTENSION_INIT2( link_ptr, link_func, db_ptr ) \
+ ShellExtensionLink * link_ptr = link_func(db_ptr)
+
+/* Place within sqlite3_x_init() code prior to usage of the *_ptr arguments. */
+#define SHELL_EXTENSION_INIT3( shell_api_ptr, ext_helpers_ptr, link_ptr ) \
+ if( (link_ptr)!=0 ){ \
+ shell_api_ptr = &link_ptr->pShellExtensionAPI->api.named; \
+ ext_helpers_ptr = &link_ptr->pShellExtensionAPI->pExtHelpers->helpers.named; \
+ }
+
+/* This test may be used within sqlite3_x_init() after SHELL_EXTENSION_INIT3 */
+#define SHELL_EXTENSION_LINKED(link_ptr) ((link_ptr)!=0)
+/* These *_COUNT() macros help determine version compatibility.
+ * They should only be used when the above test yields true.
+ */
+#define SHELL_API_COUNT(link_ptr) \
+ (link_ptr->pShellExtensionAPI->numRegistrars)
+#define SHELL_HELPER_COUNT(link_ptr) \
+ (link_ptr->pShellExtensionAPI->pExtHelpers->helperCount)
+
+/* Combining the above, safely, to provide a single test for extensions to
+ * use for assurance that: (1) the load was as a shell extension (with the
+ * -shext flag rather than bare .load); and (2) the loading host provides
+ * stated minimum extension API and helper counts.
+ */
+#define SHELL_EXTENSION_LOADFAIL(link_ptr, minNumApi, minNumHelpers) \
+ (!SHELL_EXTENSION_LINKED(link_ptr) \
+ || SHELL_API_COUNT(link_ptr)<(minNumApi) \
+ || SHELL_HELPER_COUNT(link_ptr)<(minNumHelpers) \
+ )
+
#ifdef __cplusplus
} // extern "C"
#endif