--- /dev/null
+/*
+** 2022 March 20
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains code to implement the "tclshext" shell extension
+** for use with the extensible "sqlite3" CLI shell. On *Nix, build thusly:
+ tool/mkshellc.tcl ext/misc/tclshext.c.in > tclshext.c
+ 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 two features to the host shell:
+** 1. The .tcl dot command is added.
+** 2. TCL scripting support is added.
+**
+** The .tcl command can be run with 0 or more arguments.
+** With no arguments, it does a REPL loop until the end of input is seen.
+** The end of input is either an EOF condition or a lone '.' on a line.
+** With more arguments, files they name are interpreted as TCL script.
+** In either case, the TCL command return code is tranlated to a DotCmdRC.
+**
+** 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, one
+** complete TCL command at a time.
+**
+** For any of these ways of providing TCL input, the same TCL interpreter
+** is used, with its state maintained from one input to the next. In this
+** way, .sqliterc or other preparatory shell scripts (or typing) can be
+** made to provide a useful set of user-defined shell enhancements.
+*/
+
+#include <limits.h>
+#include "shext_linkage.h"
+
+static struct ShExtAPI *pShExtApi = 0;
+static struct ExtHelpers *pExtHelpers = 0;
+
+SQLITE_EXTENSION_INIT1;
+
+/* This is not found in the API pointer table published for extensions: */
+#define sqlite3_enable_load_extension pExtHelpers->enable_load_extension
+
+/* Control how tclsqlite.c compiles. (REPL is effected here, not there.) */
+#undef SQLITE_AMALGAMATION
+#undef TCLSH
+#include <tclOO.h>
+INCLUDE tclsqlite.c
+
+#if defined(_WIN32) || defined(WIN32)
+# define getDir(cArray) _getwd(cArray)
+# define chdir(s) _chdir(s)
+#else
+# define getDir(cArray) getcwd(cArray, sizeof(cArray))
+#endif
+
+typedef struct TclCmd TclCmd;
+
+static void TclCmd_Takedown(TclCmd *ptc);
+
+/* These DERIVED_METHOD(...) macro calls' arguments were copied and
+ * pasted from the MetaCommand interface declaration in shext_linkage.h
+ */
+DERIVED_METHOD(void, destruct, MetaCommand,TclCmd, 0, ()){
+ TclCmd_Takedown((TclCmd *)pThis);
+}
+
+DERIVED_METHOD(const char *, name, MetaCommand,TclCmd, 0,()){
+ return "tcl";
+}
+
+DERIVED_METHOD(const char *, help, MetaCommand,TclCmd, 1,(int more)){
+ switch( more ){
+ case 0: return
+ ".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";
+ default: return 0;
+ }
+}
+
+DERIVED_METHOD(DotCmdRC, argsCheck, MetaCommand,TclCmd, 3,
+ (char **pzErrMsg, int nArgs, char *azArgs[])){
+ return DCR_Ok;
+}
+
+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));
+ }
+}
+
+DERIVED_METHOD(DotCmdRC, execute, MetaCommand,TclCmd, 4,
+ (ShellExState *psx, char **pzErrMsg, int nArgs, char *azArgs[])){
+ FILE *out = pExtHelpers->currentOutputFile(psx);
+ TclCmd *ptc = (TclCmd *)pThis;
+ DotCmdRC rv = DCR_Ok;
+ int rc = TCL_OK;
+ if( nArgs>1 ){
+ /* Read named files into the interpreter. */
+ int aix;
+ for( aix=0; aix<(nArgs-1) && rc==TCL_OK; ++aix ){
+ rc = Tcl_EvalFile(getInterp(ptc), azArgs[aix+1]);
+ }
+ }else{
+ /* Enter a REPL */
+ static const char * const zREPL =
+#ifdef REPL_STDIN_ONLY
+ "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"
+#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"
+ "} else {\n"
+ "puts -nonewline \"% \"\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"
+ "throw {NONE} {incomplete input at EOF}\n"
+ "}\n"
+ "set at_end 1\n"
+ "} else {\n"
+ "append line $li\n"
+ "if {[string trim $line] eq \"\"} {\n"
+ "set 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"
+ "}\n"
+ "set line {}\n"
+ "}\n"
+ "}\n"
+ "}\n"
+ "if {$prompting && $li ne \".\\n\"} {puts {}}\n"
+ "unset li line prompting at_end\n"
+ "read stdin 0\n"
+#endif
+ ;
+ rc = Tcl_Eval(getInterp(ptc), zREPL);
+ clearerr(stdin);
+ }
+ if( rc!=TCL_OK ){
+ copy_complaint(pzErrMsg, getInterp(ptc));
+ rv = DCR_Error;
+ }
+ return rv;
+}
+
+/* Define a MetaCommand v-table initialized to reference above methods. */
+MetaCommand_IMPLEMENT_VTABLE(TclCmd, tclcmd_methods);
+
+INSTANCE_BEGIN(TclCmd);
+ Tcl_Interp *interp;
+INSTANCE_END(TclCmd) tclcmd = {
+ &tclcmd_methods
+ , 0 /* interp pointer */
+};
+
+static Tcl_Interp *getInterp(TclCmd *ptc){
+ return ptc->interp;
+}
+
+static void TclCmd_Takedown(TclCmd *ptc){
+ Tcl_DeleteInterp(ptc->interp);
+ ptc->interp = 0;
+ Tcl_Finalize();
+}
+
+/* Say line is script lead-in iff its first dark is "..". */
+static int tclIsScriptLead(void *pvState, const char *zLineLead){
+ char c;
+ (void)(pvState);
+ while( (c=*zLineLead++) && (c==' '||c=='\t') ) {}
+ return (c=='.' && *zLineLead=='.');
+}
+
+static int tclIsComplete(void *pvState, const char *zScript){
+ (void)(pvState);
+ return Tcl_CommandComplete(zScript);
+}
+
+static DotCmdRC tclRunScript(void *pvState, const char *zScript,
+ ShellExState *p, char **pzErrMsg){
+ char c;
+ TclCmd *ptc = (TclCmd *)pvState;
+ while( (c=*zScript++) && (c==' '||c=='\t') ) {}
+ if( c=='.' && *zScript++=='.' ){
+ int rc, nc = strlen(zScript);
+ rc = Tcl_EvalEx(ptc->interp, zScript, nc, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL);
+ if( rc!=TCL_OK ) copy_complaint(pzErrMsg, getInterp(ptc));
+ return DCR_Ok|(rc!=TCL_OK);
+ }
+ return DCR_Error;
+}
+
+#define GETLINE_MAXLEN 1000
+
+/* C implementation of TCL proc, get_input_line */
+int getInputLine(void *pvSS, Tcl_Interp *interp,
+ int nArgs, const char *azArgs[]){
+ if( nArgs==1 ){
+ char buffer[GETLINE_MAXLEN+1];
+ ShellExState *psx = (ShellExState *)pvSS;
+ struct InSource *pis = pExtHelpers->currentInputSource(psx);
+ if( pExtHelpers->strLineGet(buffer, GETLINE_MAXLEN, pis) ){
+ Tcl_SetResult(interp, buffer, TCL_VOLATILE);
+ }else{
+ Tcl_SetResult(interp, 0, 0);
+ }
+ return TCL_OK;
+ }else{
+ Tcl_SetResult(interp, "too many arguments", TCL_STATIC);
+ return TCL_ERROR;
+ }
+}
+
+/* C implementation of TCL proc, now_interactive */
+int nowInteractive(void *pvSS, Tcl_Interp *interp,
+ int nArgs, const char *azArgs[]){
+ if( nArgs==1 ){
+ ShellExState *psx = (ShellExState *)pvSS;
+ struct InSource *pis = pExtHelpers->currentInputSource(psx);
+ static const char * zAns[2] = { "0","1" };
+ int iiix = (pExtHelpers->nowInteractive(psx) != 0);
+ Tcl_SetResult(interp, (char *)zAns[iiix], TCL_STATIC);
+ return TCL_OK;
+ }else{
+ Tcl_SetResult(interp, "too many arguments", TCL_STATIC);
+ return TCL_ERROR;
+ }
+}
+
+/* ToDo: C implementation of TCL ::unknown to reflect to dot commands */
+
+/* ToDo: ... TCL db_user command, like a (TCL) sqlite3 object except that
+ * it defers to shell's db and treats close subcommand as an error. */
+
+DEFINE_SHDB_TO_SHEXTLINK(shext_link);
+
+/*
+** Extension load function.
+*/
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_tclshext_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ ShellExtensionLink *pShExtLink;
+ SQLITE_EXTENSION_INIT2(pApi);
+ pShExtLink = shext_link(db);
+ if( pShExtLink && pShExtLink->pShellExtensionAPI->numRegistrars>=1 ){
+ ShellExState *psx = pShExtLink->pSXS;
+ MetaCommand *pmc = (MetaCommand *)&tclcmd;
+ const char *zShellName, *zShellDir;
+ int rc;
+
+ 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;
+ }
+ zShellName = pExtHelpers->shellInvokedAs();
+ zShellDir = pExtHelpers->shellStartupDir();
+ if( zShellDir!=0 ){
+ char cwd[FILENAME_MAX+1];
+ if( getDir(cwd) && 0==chdir(zShellDir) ){
+ Tcl_FindExecutable(zShellName);
+ rc = chdir(cwd); /* result ignored, kept only to silence gcc */
+ }
+ }
+ tclcmd.interp = Tcl_CreateInterp();
+ if( 0==Tcl_OOInitStubs(tclcmd.interp) ){
+ *pzErrMsg = sqlite3_mprintf("Tcl v8.6 or higher required.\n");
+ TclCmd_Takedown(&tclcmd);
+ return SQLITE_ERROR;
+ }
+ Tcl_SetSystemEncoding(tclcmd.interp, "utf-8");
+ Sqlite3_Init(tclcmd.interp);
+ rc = pShExtApi->registerMetaCommand(psx, sqlite3_tclshext_init, pmc);
+ if( rc==SQLITE_OK ){
+ ScriptHooks sh = { pmc, tclIsScriptLead, tclIsComplete, tclRunScript };
+ pShExtApi->hookScripting(psx, sqlite3_tclshext_init, &sh);
+ Tcl_CreateCommand(tclcmd.interp,
+ "get_input_line", getInputLine, psx, 0);
+ Tcl_CreateCommand(tclcmd.interp,
+ "now_interactive", nowInteractive, psx, 0);
+ pShExtLink->eid = sqlite3_tclshext_init;
+ }else{
+ TclCmd_Takedown(&tclcmd);
+ }
+ return rc;
+ }
+ else{
+ *pzErrMsg
+ = sqlite3_mprintf("Bad ShellExtensionLink or registration API.\n");
+ return SQLITE_ERROR;
+ }
+}
-C macro-ize\sextension\sboiler-plate,\simprove\sexit\sprocessing
-D 2022-03-19T14:27:57.463
+C Scripting\ssupport\sroughed\sin,\swith\sa\sdemo\sextension.\sMore\swork\sis\sneeded\sto\smake\sthis\struly\suseful.\sTests\sare\smissing.
+D 2022-03-23T21:03:48.356
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 9e8909361fb8e26488920acd6629753b5bf38541cd0c4e6a16f6532b7bf367c6
F ext/misc/templatevtab.c 8a16a91a5ceaccfcbd6aaaa56d46828806e460dd194965b3f77bf38f14b942c4
F ext/misc/totype.c fa4aedeb07f66169005dffa8de3b0a2b621779fd44f85c103228a42afa71853b
F ext/misc/uint.c 053fed3bce2e89583afcd4bf804d75d659879bbcedac74d0fa9ed548839a030b
F src/insert.c d9fd15b2bd030cb9bd3119b301dbdb2912f16fff76c6e3797296cfd1500faaf4
F src/json.c 24fcd7f5f9080b04b89722c343010d390f85e55b2ab560046cb567c9dd640f62
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
-F src/loadext.c aa919a6a7884f8b34d7b791841b24d14b1b0ab43f45b3940f4851043b2855c0c
+F src/loadext.c 2ecb1441f9b1c22e9e022ee0776e67d259facf34b56ba892b206f0a294ee6f8c
F src/main.c 89dfd569b4fbcab65281b3c6d636b887b2cb23cbaa16f8c6b67062862144c927
F src/malloc.c fec841aa0a0400a6f7d20706178a5d8e8219a6bf562b6fe712c17f6c26813266
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/resolve.c ea935b87d6fb36c78b70cdc7b28561dc8f33f2ef37048389549c7b5ef9b0ba5e
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c 4890a3cfee0bc60ff231c3a44db37968859ab0be156983dbcc0c096109832cdd
-F src/shell.c.in acb97f8ad071498a35748d31b86d5797745ba236e042d54ae31adb8c23a27581
-F src/shext_linkage.h 6c75ac9690965ae6968b5b246f83bde6bac9f13a248e5ac0f5775d2ad8a35496
+F src/shell.c.in 840a4a72dcc39fafbcc82275babc77f928c3531cb5fe7217cb0a1596ef0a4555
+F src/shext_linkage.h 71b3600ba0e20f696fb226547e99413c67cfb27c5532701df16935e2a45c152a
F src/sqlite.h.in 5845213799feca09cd69d18ff841a85fe0df31021f46aaa1797e703e80dc1d70
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
-F src/sqlite3ext.h a95cb9ed106e3d39e2118e4dcc15a14faec3fa50d0093425083d340d9dfd96e6
+F src/sqlite3ext.h f49e28c25bd941e79794db5415fdf7b202deb3bc072ed6f1ed273d578703684e
F src/sqliteInt.h 2ce7d868630ccd70ffd4b15d46b59ccf7daf89198993b62ed6e4a165d3511280
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
F tool/mkopcodeh.tcl 5dab48c49a25452257494e9601702ab63adaba6bd54a9b382615fa52661c8f8c
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl bd07bd59d45d0f3448e123d6937e9811195f9908a51e09d774609883055bfd3d
-F tool/mkshellc.tcl 8231651ce215baea7d003d7489d962d0dc286cbabc41a858790dd188fe889651
+F tool/mkshellc.tcl a8db284614942c48e35e58f2a3a2e762f3bae0262e81acd1dbfc4813fd304d85 x
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 898088008e2d15f89941db433f743594aea1351f353c12ce3932a39902dfb161
-R 129774c9c2e8cac58d8036d38701569e
+P 7996d3a359ab90ef9f42f501e0ddb2efa964a906739116836cb1eafe2a96a0ed
+Q +da874180d35aacdeb9c06f5b425e8909d833e2765179c4337854d56b8a624fd5
+R c463fc7c0074cded5ea87643f5ddeef3
U larrybr
-Z 76b79faefcf881b072c961370ce1a08d
+Z cf7f7bb9e5e070c2442801336636b4c2
# Remove this line to create a well-formed Fossil manifest.
-7996d3a359ab90ef9f42f501e0ddb2efa964a906739116836cb1eafe2a96a0ed
\ No newline at end of file
+abf0316b3f58646974ab8e4d3e68896c9fc03bdd338eb7dc7b2f5d4de7365298
\ No newline at end of file
sqlite3_vtab_distinct,
sqlite3_vtab_in,
sqlite3_vtab_in_first,
- sqlite3_vtab_in_next
+ sqlite3_vtab_in_next,
+ /* Version 3.39.0 and later */
+#ifndef SQLITE_OMIT_DESERIALIZE
+ sqlite3_deserialize,
+ sqlite3_serialize
+#else
+ 0,
+ 0
+#endif
};
/* True if x is the directory separator character
** in a number of other places, mostly for error messages.
*/
static char *Argv0;
+static char startupDir[PATH_MAX+1] = {0};
+#if defined(_WIN32) || defined(WIN32)
+# define initStartupDir() _getwd(startupDir)
+#else
+# define initStartupDir() getcwd(startupDir, sizeof(startupDir))
+#endif
/*
** Prompt strings. Initialized in main. Settable with
typedef struct ShExtInfo {
ExtensionId extId; /* The xInit function pointer */
void (*extDtor)(void *); /* Extension shutdown on exit or unload */
+ void *pvExtObj; /* Passed to extDtor(...) at shutdown */
/* Each shell extension library registers 0 or more of its extension
* implementations, interfaces to which are kept in below dynamic.
* arrays. The dbShell DB keeps indices into these arrays and into
#if SHELL_DYNAMIC_EXTENSION
int numExtLoaded; /* Number of extensions presently loaded or emulated */
ShExtInfo *pShxLoaded; /* Tracking and use info for loaded shell extensions */
+ int ixExtPending; /* Index of pending extension load operations if !0 */
+ ScriptHooks scripting; /* Hooks for scripting support from loaded extension */
+ ExtensionId scriptXid; /* Id of extension which has set scripting hooks */
#endif
+
ShellExState *pSXS; /* Pointer to companion, exposed shell state */
} ShellInState;
}
#if SHELL_DYNAMIC_EXTENSION
-/* Register a meta-command */
+/* Ensure there is room in loaded extension info list for one being loaded.
+ * On exit, psi->ixExtPending can be used to index into psi->pShxLoaded.
+ */
+static ShExtInfo *pending_ext_info(ShellInState *psi){
+ int ixpe = psi->ixExtPending;
+ assert(ixpe!=0);
+ if( ixpe >= psi->numExtLoaded ){
+ psi->pShxLoaded = sqlite3_realloc(psi->pShxLoaded,
+ (ixpe+1)*sizeof(ShExtInfo));
+ shell_check_oom(psi->pShxLoaded);
+ ++psi->numExtLoaded;
+ memset(psi->pShxLoaded+ixpe, 0, sizeof(ShExtInfo));
+ }
+ return &psi->pShxLoaded[ixpe];
+}
+
+/* Register a meta-command, to be called during extension load/init. */
static int register_meta_command(ShellExState *p,
ExtensionId eid, MetaCommand *pMC){
ShellInState *psi = ISS(p);
- ShExtInfo *psei = 0;
+ ShExtInfo *psei = pending_ext_info(psi);
const char *zSql
= "INSERT INTO ShellCommands (name, extIx, cmdIx) VALUES(?, ?, ?)";
- int nle = psi->numExtLoaded;
- int ie;
+ int ie = psi->ixExtPending;
+ assert(psi->pShxLoaded!=0 && p->dbShell!=0);
if( pMC==0 ) return SQLITE_ERROR;
- assert(psi->pShxLoaded!=0 && nle>0 && p->dbShell!=0);
- for( ie=0; ie<nle; ++ie ){
- if( psi->pShxLoaded[ie].extId==eid ){
- psei = &psi->pShxLoaded[ie];
- break;
- }
- }
- if( psei==0 ){
- ShExtInfo sei = {eid,0};
- psi->pShxLoaded = sqlite3_realloc(psi->pShxLoaded,
- (nle+1)*sizeof(ShExtInfo));
- shell_check_oom(psi->pShxLoaded);
- psei = &psi->pShxLoaded[psi->numExtLoaded];
- psi->pShxLoaded[psi->numExtLoaded++] = sei;
- ie = nle;
- }
- {
+ else{
const char *zName = pMC->pMethods->name(pMC);
sqlite3_stmt *pStmt;
- int rc = sqlite3_prepare_v2(p->dbShell, zSql, -1, &pStmt, 0);
int nc = psei->numMetaCommands;
- if( rc!=SQLITE_OK ) return SQLITE_ERROR;
+ int rc;
+ if( psei->extId!=0 && psei->extId!=eid ) return SQLITE_MISUSE;
+ psei->extId = eid;
+ rc = sqlite3_prepare_v2(p->dbShell, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ) return rc;
psei->ppMetaCommands
= sqlite3_realloc(psei->ppMetaCommands, (nc+1)*sizeof(MetaCommand *));
shell_check_oom(psei->ppMetaCommands);
return SQLITE_ERROR;
}
+static int hook_scripting(ShellExState *p, ExtensionId eid, ScriptHooks *pSH){
+ 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 ){
+ return SQLITE_MISUSE;
+ }
+ psi->scripting = *pSH;
+ psi->scriptXid = eid;
+ return SQLITE_OK;
+}
+
+
static FILE *currentOutputFile(ShellExState *p){
return ISS(p)->out;
}
return INSOURCE_IS_INTERACTIVE(ISS(p)->pInSource);
}
+static const char *shellInvokedAs(void){
+ return Argv0;
+}
+
+static const char *shellStartupDir(void){
+ return startupDir;
+}
+
static void setColumnWidths(ShellExState *p, char *azWidths[], int nWidths);
static MetaCommand * findMetaCommand(const char *, ShellExState *, int *);
static ExtensionHelpers extHelpers = {
- 7,
+ 10,
{
failIfSafeMode,
currentOutputFile,
findMetaCommand,
setColumnWidths,
nowInteractive,
+ shellInvokedAs,
+ shellStartupDir,
+ sqlite3_enable_load_extension,
0
}
};
static ShellExtensionAPI shellExtAPI = {
- &extHelpers, 3, {
+ &extHelpers, 4, {
register_meta_command,
register_out_mode,
register_importer,
+ hook_scripting,
0
}
};
return SQLITE_OK;
}
+/* Call one loaded extension's destructors, in reverse order
+ * of their objects' creation, then free the tracking dyna-arrays.
+ */
+static void free_one_shext_tracking(ShExtInfo *psei){
+ int j;
+ if( psei->ppMetaCommands!=0 ){
+ for( j=psei->numMetaCommands; j>0; --j ){
+ MetaCommand *pmc = psei->ppMetaCommands[j-1];
+ if( pmc->pMethods->destruct!=0 ) pmc->pMethods->destruct(pmc);
+ }
+ 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);
+ }
+ sqlite3_free(psei->ppOutModeHandlers);
+ }
+ if( psei->ppImportHandlers!=0 ){
+ for( j=psei->numImportHandlers; j>0; --j ){
+ ImportHandler *pih = psei->ppImportHandlers[j-1];
+ if( pih->pMethods->destruct!=0 ) pih->pMethods->destruct(pih);
+ }
+ sqlite3_free(psei->ppImportHandlers);
+ }
+ if( psei->extDtor!=0 ){
+ psei->extDtor(psei->pvExtObj);
+ }
+}
+
/* Call all existent loaded extension destructors, in reverse order
* of their objects' creation, then free the tracking dyna-arrays.
*/
-static void free_shext_tracking(ShellInState *psi){
- int i, j;
+static void free_all_shext_tracking(ShellInState *psi){
if( psi->pShxLoaded!=0 ){
- for( i=psi->numExtLoaded; i>0; --i ){
- ShExtInfo *psei = &psi->pShxLoaded[i-1];
- if( psei->ppMetaCommands!=0 ){
- for( j=psei->numMetaCommands; j>0; --j ){
- MetaCommand *pmc = psei->ppMetaCommands[j-1];
- if( pmc->pMethods->destruct!=0 ) pmc->pMethods->destruct(pmc);
- }
- 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);
- }
- sqlite3_free(psei->ppOutModeHandlers);
- }
- if( psei->ppImportHandlers!=0 ){
- for( j=psei->numImportHandlers; j>0; --j ){
- ImportHandler *pih = psei->ppImportHandlers[j-1];
- if( pih->pMethods->destruct!=0 ) pih->pMethods->destruct(pih);
- }
- sqlite3_free(psei->ppImportHandlers);
+ int i = psi->numExtLoaded;
+ while( i>1 ){
+ ShExtInfo *psei = &psi->pShxLoaded[--i];
+ free_one_shext_tracking(psei);
+ if( psi->scriptXid!=0 && psi->scriptXid==psei->extId ){
+ memset(&psi->scripting, 0, sizeof(ScriptHooks));
+ psi->scriptXid = 0;
}
}
sqlite3_free(psi->pShxLoaded);
psx, /* pSXS */
0, /* zErrMsg */
0, /* ExtensionId */
- 0 /* Extension destructor */
- };
+ 0, /* Extension destructor */
+ 0 /* Extension data ref */
+ }; //extDtor(pvExtObj)
+ ShellInState *psi = ISS(psx);
+ /* save script hooking state for possible fallback if load fails */
+ ScriptHooks shSave = psi->scripting;
+ ExtensionId siSave = psi->scriptXid;
int rc;
if( psx->dbShell==0 ){
rc = begin_db_dispatch(psx);
if( rc!=SQLITE_OK ) return rc;
assert(ISS(psx)->numExtLoaded==1 && psx->dbShell!=0);
}
+ psi->ixExtPending = psi->numExtLoaded;
sqlite3_create_function(psx->dbShell, "shext_pointer", 1,
SQLITE_DIRECTONLY|SQLITE_UTF8,
&shxLink, shell_linkage, 0, 0);
if( pzErr!=0 ) *pzErr = shxLink.zErrMsg;
if( rc==SQLITE_OK ){
/* Keep extension's id and destructor for later disposal. */
+ ShExtInfo *psei = pending_ext_info(psi);
+ if( psei->extId!=0 && psei->extId!=shxLink.eid ) rc = SQLITE_MISUSE;
+ psei->extId = shxLink.eid;
+ psei->extDtor = shxLink.extensionDestruct;
+ psei->pvExtObj = shxLink.pvExtensionObject;
+ }else{
+ /* Release all resources extension might have registered before failing. */
+ if( psi->ixExtPending < psi->numExtLoaded ){
+ 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;
}
+ psi->ixExtPending = 0;
return rc!=SQLITE_OK;
}
#endif
if( nArg==3 && strcmp(z, zHelpAll)==0 && strcmp(azArg[2], zHelpAll)==0 ){
/* Show the undocumented command help */
zPat = zHelpAll;
- }else if( strcmp(z,"-a")==0 || strcmp(z,"-all")==0 || strcmp(z,"--all")==0 ){
+ }else if( strcmp(z,"-a")==0 || optionMatch(z, "all") ){
zPat = "";
}else{
zPat = z;
/* An ordered enum to record kind of incoming line group. Its ordering
* means than a value greater than Comment implies something runnable.
*/
- enum { Tbd = 0, Eof, Comment, Sql, Cmd /*, Tcl */ } inKind = Tbd;
+ enum { Tbd = 0, Eof, Comment, Sql, Cmd
+#if SHELL_DYNAMIC_EXTENSION
+ , Script
+#endif
+ } inKind = Tbd;
/* An enum signifying the group disposition state */
enum {
Incoming, Runnable, Dumpable, Erroneous, Ignore
? skipWhite(zLineInput)-zLineInput
: 0; /* Disallow leading whitespace for . or # in legacy mode. */
#endif
- switch( zLineInput[ndcLeadWhite] ){
- case '.':
- inKind = Cmd;
- dot_command_scan(zLineInput+ndcLeadWhite, &dcScanState);
- break;
- case '#':
- inKind = Comment;
- break;
- default:
- /* Might be SQL, or a swallowable whole SQL comment. */
- sql_prescan(zLineInput, &sqScanState);
- if( SSS_PLAINWHITE(sqScanState) ){
- /* It's either all blank or a whole SQL comment. Swallowable. */
+#if SHELL_DYNAMIC_EXTENSION
+ if( psi->scripting.isScriptLeader!=0
+ && psi->scripting.isScriptLeader(psi->scripting.pvScriptingState,
+ zLineInput+ndcLeadWhite) ){
+ inKind = Script;
+ }else
+#endif
+ {
+ switch( zLineInput[ndcLeadWhite] ){
+ case '.':
+ inKind = Cmd;
+ dot_command_scan(zLineInput+ndcLeadWhite, &dcScanState);
+ break;
+ case '#':
inKind = Comment;
- }else{
- /* Something dark, not a # comment or dot-command. Must be SQL. */
- inKind = Sql;
+ break;
+ default:
+ /* Might be SQL, or a swallowable whole SQL comment. */
+ sql_prescan(zLineInput, &sqScanState);
+ if( SSS_PLAINWHITE(sqScanState) ){
+ /* It's either all blank or a whole SQL comment. Swallowable. */
+ inKind = Comment;
+ }else{
+ /* Something dark, not a # comment or dot-command. Must be SQL. */
+ inKind = Sql;
+ }
+ break;
}
- break;
- } /* end classification switch */
+ }
} /* end read/classify initial group input line */
/* Here, if not at end of input, the initial line of group is in, and
#endif
disposition = Runnable; /* Legacy, any dot-command line is ready. */
break;
+#if SHELL_DYNAMIC_EXTENSION
+ case Script:
+ if( psi->scripting.scriptIsComplete==0
+ || psi->scripting.scriptIsComplete(psi->scripting.pvScriptingState,
+ *pzLineUse+ndcLeadWhite) ){
+ disposition = Runnable;
+ }
+ break;
+#endif
case Sql:
/* Check to see if it is complete and ready to run. */
if( SSS_SEMITERM(sqScanState) && sqlite3_complete(*pzLineUse)){
if( dcr > termKind ) termKind = dcr;
break;
}
+#if SHELL_DYNAMIC_EXTENSION
+ case Script: {
+ if( psi->scripting.runScript!=0 ){
+ char *zErr = 0;
+ DotCmdRC dcr
+ = psi->scripting.runScript(psi->scripting.pvScriptingState,
+ *pzLineUse+ndcLeadWhite,
+ 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);
+ }
+ }
+ }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;
+ }
+#endif
default:
assert(inKind!=Tbd);
break;
assert( argc>=1 && argv && argv[0] );
Argv0 = argv[0];
+ initStartupDir();
#ifdef SQLITE_SHELL_DBNAME_PROC
{
clearTempFile(&data);
sqlite3_free(data.zEditor);
#if SHELL_DYNAMIC_EXTENSION
- free_shext_tracking(&data);
+ free_all_shext_tracking(&data);
sqlite3_close(datax.dbShell);
#endif
#if !SQLITE_SHELL_IS_UTF8
extern "C" {
#endif
+/*****************
+ * 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 (semi-ordered) */
+ 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) */
+ /* Above are in reverse-priority order for process_input() returns. */
+
+ /* 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_ArgWrong = 0x7000, /* non-specific argument error, nothing emitted */
+
+ /* This code indicates error and a usage message to be emitted to stderr. */
+ DCR_SayUsage = 0x7ffd, /* usage is at *pzErr or is to be generated */
+ /* This code indicates nothing more need be put to stderr (or stdout.) */
+ DCR_CmdErred = 0x7fff /* non-specific error for which complaint is done */
+} DotCmdRC;
+
/* Convey data to, from and/or between I/O handlers and meta-commands. */
typedef struct ShellExState {
/* A sizeof(*) to permit extensions to guard against too-old hosts */
struct ShellInState *pSIS; /* Offset of this member is NOT STABLE. */
} ShellExState;
-/*****************
- * 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 (semi-ordered) */
- 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) */
- /* Above are in reverse-priority order for process_input() returns. */
-
- /* 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_ArgWrong = 0x7000, /* non-specific argument error, nothing emitted */
-
- /* This code indicates error and a usage message to be emitted to stderr. */
- DCR_SayUsage = 0x7ffd, /* usage is at *pzErr or is to be generated */
- /* This code indicates nothing more need be put to stderr (or stdout.) */
- DCR_CmdErred = 0x7fff /* non-specific error for which complaint is done */
-} DotCmdRC;
-
/* An object implementing below interface is registered with the
* shell to make new or overriding meta-commands available to it.
*/
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
+ * one 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 ExtensionHelpers {
int helperCount; /* Helper count, not including sentinel */
union {
/* out */ int *pnFound);
void (*setColumnWidths)(ShellExState *p, char *azWidths[], int nWidths);
int (*nowInteractive)(ShellExState *p);
+ const char * (*shellInvokedAs)(void);
+ const char * (*shellStartupDir)(void);
+ int (*enable_load_extension)(sqlite3 *db, int onoff);
void (*sentinel)(void);
} named ;
- void (*nameless[5+1])(); /* Same as named but anonymous plus a sentinel. */
+ void (*nameless[10+1])(); /* Same as named but anonymous plus a sentinel. */
} helpers;
} ExtensionHelpers;
ExtensionHelpers * pExtHelpers;
/* Functions for extension to register its implementors with shell */
- const int numRegistrars; /* 3 for this version */
+ const int numRegistrars; /* 4 for this version */
union {
struct ShExtAPI {
/* Register a meta-command */
/* 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);
/* Preset to 0 at extension load, a sentinel for expansion */
void (*sentinel)(void);
} named;
*/
ExtensionId eid;
- /* Another init "out" parameter, a destructor for extension overall.
- * Set to 0 on input and may be left so if no destructor is needed.
+ /* Two more init "out" parameters, a destructor for extension overall.
+ * Set to 0 on input and left so if no destructor is needed. Otherwise,
+ * upon exit or unload, extensionDestruct(pvExtensionObject) is called.
*/
- void (*extensionDestruct)(void *);
+ void (*extensionDestruct)(void *pvExtObj);
+ void *pvExtensionObject;
} ShellExtensionLink;
int (*vtab_in)(sqlite3_index_info*,int,int);
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
+ /* Version 3.39.0 and later */
+ int (*deserialize)(sqlite3*,const char*,unsigned char*,
+ sqlite3_int64,sqlite3_int64,unsigned);
+ unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
+ unsigned int);
};
/*
#define sqlite3_vtab_in sqlite3_api->vtab_in
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
+#ifndef SQLITE_OMIT_DESERIALIZE
+#define sqlite3_deserialize sqlite3_api->deserialize
+#define sqlite3_serialize sqlite3_api->serialize
+#endif
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
** This file is automatically generated by the script in the canonical
** SQLite source tree at tool/mkshellc.tcl. That script combines and
** transforms code from various constituent source files of SQLite into
-** this single "shell.c" file to implement the SQLite command-line shell.
+** this single OUT_FILE file to implement TARGET_PROGRAM.
**
-** Most of the code found below comes from the "src/shell.c.in" file in
+** Much of the code found below comes from the SOURCE_FILE file in
** the canonical SQLite source tree. That main file contains "INCLUDE"
** lines that specify other files in the canonical source tree that are
** inserted and transformed, (via macro invocations explained by running
** "tool/mkshellc.tcl --help"), to generate this complete program source.
**
-** By means of this generation process, creating this single "shell.c"
-** file, building the command-line program is made simpler and easier.
+** By means of this generation process, creating this single OUT_FILE
+** file, building the program from it is simplified.
**
** To modify this program, get a copy of the canonical SQLite source tree,
-** edit file src/shell.c.in and/or some of the other files included by it,
+** edit file SOURCE_FILE and/or some of the other files it includes,
** then rerun the tool/mkshellc.tcl script.
*/}
set ::tclGenerate 0
set ::verbosity 0
set ::inFiles {}
-array set ::incTypes [list "*" "$::topDir/src/shell.c.in"]
+set ::topInfile "?"
+set ::presumedOutfile "?"
+set ::targetProgram "?"
+set ::defaultInfile "src/shell.c.in"
+array set ::incTypes [list "*" "$::topDir/$::defaultInfile"]
array set ::ignoringCommands [list]
+# Shift 0'th element out of named list and return it, modifying the named list.
+proc lshift {lvar} {
+ upvar $lvar l
+ set rv [lindex $l 0]
+ set l [lrange $l 1 end]
+ return $rv
+}
+
while {[llength $argv] > 0} {
- foreach {opt} $arv { set argv [lreplace $argv 1 end] ; break }
+ set opt [lshift argv]
if {[regexp {^-{1,2}((help)|(details)|(parameters))$} $opt ma ho]} {
set runMode $ho
} elseif {[regexp {^-it$} $opt]} {
- foreach {nextOpt} $arv { set argv [lreplace $argv 1 end] ; break }
+ set nextOpt [lshift argv]
if {![regexp {^(\w+)=(.+)$} $nextOpt ma k v]} {
puts stderr "Get help with --help."
exit 1
}
set ::incTypes($k) $v
} elseif {$opt eq "-top-dir"} {
- foreach {::topDir} $arv { set argv [lreplace $argv 1 end] ; break }
+ set ::topDir [lshift argv]
if {::topDir eq ""} { set ::topDir . }
} elseif {$opt eq "-source-tags"} {
- foreach {nextOpt} $arv { set argv [lreplace $argv 1 end] ; break }
+ set nextOpt [lshift argv]
if {![regexp {^\d$} $nextOpt ::lineTags]} {
puts stderr "Argument following -source-tags must be a digit."
}
} elseif {$opt eq "-tcl"} {
- puts stderr "Warning: Tcl extension not wholly implemented."
+ puts stderr "Warning: Tcl extension not implemented."
set ::tclGenerate 1
} elseif {$opt eq "-v"} {
incr ::verbosity
}
if {$runMode eq "normal"} {
if {[llength $::inFiles] == 0} {
- lappend ::inFiles $::incTypes(*)
+ set ::targetProgram "the SQLite command-line shell"
+ set ::topInfile $::defaultInfile
+ set ::presumedOutfile {"shell.c"}
+ lappend ::inFiles $::topInfile
+ } else {
+ set ::targetProgram "this SQLite shell extension"
+ set ::topInfile [lindex $::inFiles 0]
+ set ::presumedOutfile "source"
}
fconfigure stdout -translation {auto lf}
set ::outStrm stdout
}
proc DISPATCH_CONFIG {inSrc tailCaptureEmpty ostrm} {
- foreach { srcFile istrm srcPrecLines } $inSrc break
# Set parameters affecting generated dispatchable command function
# signatures and generated dispatch table entries.
+ foreach { srcFile istrm srcPrecLines } $inSrc break
set iAte 2
set def_disp {}
set lx [gets $istrm]
if {$runMode == "normal"} {
fconfigure $outStrm -translation {auto lf}
- emit_sync [list $::headComment] $outStrm $::headCommentLines
+ set infName [lindex $::inFiles 0]
+ set hc [regsub -all {OUT_FILE} $::headComment $::presumedOutfile]
+ set hc [regsub -all {SOURCE_FILE} $hc "\"$::topInfile\""]
+ set hc [regsub -all {TARGET_PROGRAM} $hc "$::targetProgram"]
+ emit_sync [list $hc] $outStrm $::headCommentLines
foreach {f} $::inFiles {
process_file $f $outStrm
}