TCL extension made to use shell's line inputter with prompting and line editing/history.
FossilOrigin-Name: fbf0eb0d12932513bba4c6a6ef31d9972d704ab38690c806098504a4cd67786d
This commit is contained in:
+190
-78
@@ -103,6 +103,132 @@ static void copy_complaint(char **pzErr, Tcl_Interp *pi){
|
||||
}
|
||||
}
|
||||
|
||||
/* The .tcl REPL script is one of the 3 following string literals,
|
||||
* selected at build time for these different purposes:
|
||||
* 1st: a simple input collection, reading only stdin, which may
|
||||
* be (handily) used as a fallback for debugging purposes.
|
||||
* 2nd: input collection which honors the shell's input switching
|
||||
* and otherwise has low dependency upon shell features, which
|
||||
* means that it has no input line editing or history recall.
|
||||
* 3rd: an input collection which fully leverages the shell's
|
||||
* input collection. It has higher shell dependency, and for
|
||||
* that it gains the shell's line editing and history recall,
|
||||
* in addition to working with the shell's input switching.
|
||||
*/
|
||||
#ifdef TCL_REPL_STDIN_ONLY
|
||||
# define TCL_REPL 1
|
||||
#elif defined(TCL_REPL_LOW_DEPENDENCY)
|
||||
# define TCL_REPL 2
|
||||
#else
|
||||
# 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"
|
||||
#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"
|
||||
#elif TCL_REPL==3
|
||||
/* using shell's input collection with line editing (if configured) */
|
||||
"namespace eval ::REPL {\n"
|
||||
"variable at_end 0\n"
|
||||
"variable interactive [now_interactive] 0\n"
|
||||
"}\n"
|
||||
"while {!$::REPL::at_end} {\n"
|
||||
"foreach {::REPL::group ::REPL::ready} [get_input_line_group] {}\n"
|
||||
"set ::REPL::trimmed [string trim $::REPL::group]\n"
|
||||
"if {$::REPL::group eq \"\" && !$::REPL::ready} break\n"
|
||||
"if {$::REPL::trimmed eq \"\"} continue\n"
|
||||
"if {!$::REPL::ready && $::REPL::trimmed ne \"\"} {\n"
|
||||
"throw {NONE} {incomplete input at EOF}\n"
|
||||
"}\n"
|
||||
"if {$::REPL::trimmed eq \".\"} break\n"
|
||||
"set ::REPL::rc [catch {uplevel #0 $::REPL::group} ::REPL::result]\n"
|
||||
"if {$::REPL::rc == 0} {\n"
|
||||
"if {$::REPL::result!=\"\" && $::REPL::interactive} {\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"
|
||||
"}\n"
|
||||
"if {$::REPL::interactive && $::REPL::trimmed ne \".\"} {puts {}}\n"
|
||||
"namespace delete ::REPL\n"
|
||||
"read stdin 0\n"
|
||||
#else
|
||||
"throw {NONE} {not built for REPL}\n"
|
||||
#endif
|
||||
; /* zREPL */
|
||||
|
||||
DERIVED_METHOD(DotCmdRC, execute, MetaCommand,TclCmd, 4,
|
||||
(ShellExState *psx, char **pzErrMsg, int nArgs, char *azArgs[])){
|
||||
FILE *out = pExtHelpers->currentOutputFile(psx);
|
||||
@@ -117,80 +243,6 @@ DERIVED_METHOD(DotCmdRC, execute, MetaCommand,TclCmd, 4,
|
||||
}
|
||||
}else{
|
||||
/* Enter a REPL */
|
||||
static const char * const zREPL =
|
||||
#ifdef TCL_REPL_STDIN_ONLY /* 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"
|
||||
#else
|
||||
"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"
|
||||
#endif
|
||||
; //... ToDo: Get line editing working here. Reuse shell's line entry.
|
||||
rc = Tcl_Eval(getInterp(ptc), zREPL);
|
||||
clearerr(stdin); /* Cure issue where stdin gets stuck after keyboard EOF. */
|
||||
}
|
||||
@@ -250,6 +302,7 @@ static DotCmdRC tclRunScript(void *pvState, const char *zScript,
|
||||
|
||||
#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[]){
|
||||
@@ -268,6 +321,61 @@ static int getInputLine(void *pvSS, Tcl_Interp *interp,
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TCL_REPL==3
|
||||
/* C implementation of TCL proc, get_input_line_group
|
||||
* This routine returns a 2 element list consisting of:
|
||||
* the collected input lines, joined with "\n", as a string
|
||||
* and
|
||||
* the line group status, as an integer.
|
||||
* The status is either 0, meaning input EOF was encountered,
|
||||
* or 1, meaning the input is a complete TCL line group.
|
||||
* There are only these return combinations:
|
||||
* { Empty 0 } => no input obtained and no more to be had
|
||||
* { Other 0 } => input collected, but is invalid TCL
|
||||
* { Other 1 } => input collected, may be valid TCL
|
||||
* By design, this combination is never returned:
|
||||
* { Empty 1 } => no input collected but valid TCL
|
||||
*/
|
||||
static int getInputLineGroup(void *pvSS, Tcl_Interp *interp,
|
||||
int objc, Tcl_Obj *const objv[]){
|
||||
if( objc==1 ){
|
||||
static Prompts cueTcl = { "tcl% ", " > " };
|
||||
ShellExState *psx = (ShellExState *)pvSS;
|
||||
struct InSource *pis = pExtHelpers->currentInputSource(psx);
|
||||
int isComplete = 0;
|
||||
char *zIn = 0;
|
||||
int isContinuation = 0;
|
||||
do {
|
||||
zIn = pExtHelpers->oneInputLine(pis, zIn, isContinuation, &cueTcl);
|
||||
if( isContinuation ){
|
||||
if( zIn ){
|
||||
Tcl_AppendResult(interp, "\n", zIn, (char*)0);
|
||||
isComplete = Tcl_CommandComplete(Tcl_GetStringResult(interp));
|
||||
}
|
||||
}else if( zIn ){
|
||||
isComplete = Tcl_CommandComplete(zIn);
|
||||
Tcl_SetResult(interp, zIn, TCL_VOLATILE);
|
||||
}
|
||||
isContinuation = 1;
|
||||
} while( zIn && !isComplete );
|
||||
if( zIn ) pExtHelpers->freeInputLine(zIn);
|
||||
{
|
||||
Tcl_Obj *const objv[] = {
|
||||
Tcl_NewStringObj(Tcl_GetStringResult(interp) , -1),
|
||||
Tcl_NewIntObj(isComplete)
|
||||
}; /* These unowned objects go directly into result, becoming owned. */
|
||||
Tcl_ResetResult(interp);
|
||||
Tcl_SetObjResult(interp, Tcl_NewListObj(2, objv));
|
||||
}
|
||||
return TCL_OK;
|
||||
}else{
|
||||
Tcl_SetResult(interp, "too many arguments", TCL_STATIC);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* C implementation of TCL proc, now_interactive */
|
||||
static int nowInteractive(void *pvSS, Tcl_Interp *interp,
|
||||
@@ -314,9 +422,7 @@ static int unknownDotDelegate(void *pvSS, Tcl_Interp *interp,
|
||||
}
|
||||
}else{
|
||||
/* Defer to the TCL-default ::unknown command, or fail here. */
|
||||
int haveUnkCmd = (0!=Tcl_FindCommand(interp, UNKNOWN_RENAME,
|
||||
0, TCL_GLOBAL_ONLY));
|
||||
if( haveUnkCmd ){
|
||||
if( 0!=Tcl_FindCommand(interp, UNKNOWN_RENAME, 0, TCL_GLOBAL_ONLY) ){
|
||||
Tcl_Obj **ppo = sqlite3_malloc((nArgs+1)*sizeof(Tcl_Obj*));
|
||||
if( ppo==0 ) return TCL_ERROR;
|
||||
ppo[0] = Tcl_NewStringObj(UNKNOWN_RENAME, -1);
|
||||
@@ -327,7 +433,7 @@ static int unknownDotDelegate(void *pvSS, Tcl_Interp *interp,
|
||||
}
|
||||
rc = Tcl_EvalObjv(interp, nArgs, ppo, TCL_EVAL_GLOBAL);
|
||||
for( ia=0; ia<nArgs; ++ia ) Tcl_DecrRefCount(ppo[ia]);
|
||||
return TCL_OK;
|
||||
return rc;
|
||||
}else{
|
||||
/* Fail now (instead of recursing back into this handler.) */
|
||||
Tcl_AppendResult(interp,
|
||||
@@ -606,8 +712,14 @@ int sqlite3_tclshext_init(
|
||||
pShExtLink->pvExtensionObject = pudb;
|
||||
}
|
||||
pShExtApi->hookScripting(psx, sqlite3_tclshext_init, &sh);
|
||||
#if TCL_REPL==2
|
||||
Tcl_CreateCommand(tclcmd.interp,
|
||||
"get_input_line", getInputLine, psx, 0);
|
||||
#endif
|
||||
#if TCL_REPL==3
|
||||
Tcl_CreateObjCommand(tclcmd.interp,
|
||||
"get_input_line_group", getInputLineGroup, psx, 0);
|
||||
#endif
|
||||
Tcl_CreateCommand(tclcmd.interp,
|
||||
"now_interactive", nowInteractive, psx, 0);
|
||||
Tcl_Eval(tclcmd.interp, "rename unknown "UNKNOWN_RENAME);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
C In\sTCL\sshell\sextension,\sadd\sTCL\scommand\s"udb".\sThis\sgets\sthe\sTCL\senvironment\sto\snear\sparity\swith\spre-extended\sutility,\sexcept\sfor\squery\sresult\sdisplay\s(TBD\ssoon).
|
||||
D 2022-03-27T03:08:02.170
|
||||
C TCL\sextension\smade\sto\suse\sshell's\sline\sinputter\swith\sprompting\sand\sline\sediting/history.
|
||||
D 2022-03-27T23:33:43.299
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@@ -329,7 +329,7 @@ F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
|
||||
F ext/misc/spellfix.c 94df9bbfa514a563c1484f684a2df3d128a2f7209a84ca3ca100c68a0163e29f
|
||||
F ext/misc/sqlar.c 0ace5d3c10fe736dc584bf1159a36b8e2e60fab309d310cd8a0eecd9036621b6
|
||||
F ext/misc/stmt.c 35063044a388ead95557e4b84b89c1b93accc2f1c6ddea3f9710e8486a7af94a
|
||||
F ext/misc/tclshext.c.in b5d5cd1b1ade04ec4ed6346ac72ddb19dc7ba1082386475ca2b140c9cb779f67
|
||||
F ext/misc/tclshext.c.in c5f99f74a5f39a216b0e7a4a7f344459a2d0615036e5e404560aab7b91d867cf
|
||||
F ext/misc/templatevtab.c 8a16a91a5ceaccfcbd6aaaa56d46828806e460dd194965b3f77bf38f14b942c4
|
||||
F ext/misc/totype.c fa4aedeb07f66169005dffa8de3b0a2b621779fd44f85c103228a42afa71853b
|
||||
F ext/misc/uint.c 053fed3bce2e89583afcd4bf804d75d659879bbcedac74d0fa9ed548839a030b
|
||||
@@ -556,8 +556,8 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
|
||||
F src/resolve.c ea935b87d6fb36c78b70cdc7b28561dc8f33f2ef37048389549c7b5ef9b0ba5e
|
||||
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
|
||||
F src/select.c c366c05e48e836ea04f8ecefb9c1225745dc250c3f01bdb39e9cbb0dc25e3610
|
||||
F src/shell.c.in de495287d860d9d26150c09fa3cb6d0de020046618df05b17e74bcc6089d27f6 x
|
||||
F src/shext_linkage.h 4fd85ce40389c576b66cc2d142cb3b4f4e7c2c8c7fa5a8b6fe7cc7973dbb6495
|
||||
F src/shell.c.in c95182f95dc2b1278214812e02fb5131a847851c93b95c548a8a825b87f47aa1 x
|
||||
F src/shext_linkage.h 68aca955c86a0ef7d1f007a4e776a7e9c17542cfde66195db1d7333695fcef7d
|
||||
F src/sqlite.h.in 2a35f62185eb5e7ecc64a2f68442b538ce9be74f80f28a00abc24837edcf1c17
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h f49e28c25bd941e79794db5415fdf7b202deb3bc072ed6f1ed273d578703684e
|
||||
@@ -1951,8 +1951,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 8402e5e78a83af3ba015bb6ab173350c1b73e04ce37ef933f9a847a7cf5e6157
|
||||
R 26d8ade198b7b866e1cc4ce801f7977d
|
||||
P c9aa76bf88401d193a536bc6576405aaad06681504996916b492962d890bc9e0
|
||||
R c4eefad0434fe8c4082bc59b2abace09
|
||||
U larrybr
|
||||
Z a49fe933efd58515fcb9ecde174d1264
|
||||
Z 5b99de2d0fe36315b33974a1d59f1b54
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
c9aa76bf88401d193a536bc6576405aaad06681504996916b492962d890bc9e0
|
||||
fbf0eb0d12932513bba4c6a6ef31d9972d704ab38690c806098504a4cd67786d
|
||||
+27
-8
@@ -469,6 +469,7 @@ static char startupDir[PATH_MAX+1] = {0};
|
||||
*/
|
||||
static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/
|
||||
static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */
|
||||
static Prompts shellPrompts = { mainPrompt, continuePrompt };
|
||||
|
||||
/*
|
||||
** Render output like fprintf(). Except, if the output is going to the
|
||||
@@ -798,8 +799,12 @@ static char *local_getline(char *zLine, InSource *pInSrc){
|
||||
** routine that can be reused.
|
||||
**
|
||||
** The result is stored in space obtained from malloc() and must either
|
||||
** be freed by the caller or else passed back into this routine via the
|
||||
** zPrior argument for reuse.
|
||||
** be freed by the caller, using the same allocator[a], or else passed
|
||||
** back into this routine via the zPrior argument for reuse.
|
||||
**
|
||||
** [a. This function is exposed for use by shell extensions which may
|
||||
** have no access to "the same allocator". This is why the function
|
||||
** following this one exists, also exposed to shell extensions. ]
|
||||
**
|
||||
** If this function is called until it returns NULL, and the prior return
|
||||
** has been passed in for resuse, then the caller need/must not free it.
|
||||
@@ -809,11 +814,15 @@ static char *local_getline(char *zLine, InSource *pInSrc){
|
||||
** The trailing newline (or its ilk), if any, is trimmed.
|
||||
** The input line number is adjusted (via delegation or directly.)
|
||||
*/
|
||||
static char *one_input_line(InSource *pInSrc, char *zPrior, int isContinuation){
|
||||
static char *one_input_line(InSource *pInSrc, char *zPrior,
|
||||
int isContinuation, Prompts *pCue){
|
||||
if( !INSOURCE_IS_INTERACTIVE(pInSrc) ){
|
||||
return local_getline(zPrior, pInSrc);
|
||||
}else{
|
||||
char *zPrompt = isContinuation ? continuePrompt : mainPrompt;
|
||||
static Prompts cueDefault = { "$ ","> " };
|
||||
const char *zPrompt;
|
||||
if( pCue==0 ) pCue = &cueDefault;
|
||||
zPrompt = isContinuation ? pCue->zContinue : pCue->zMain;
|
||||
#if SHELL_USE_LOCAL_GETLINE
|
||||
printf("%s", zPrompt);
|
||||
fflush(stdout);
|
||||
@@ -829,6 +838,11 @@ static char *one_input_line(InSource *pInSrc, char *zPrior, int isContinuation){
|
||||
}
|
||||
}
|
||||
|
||||
/* For use by shell extensions. See footnote [a] to above function. */
|
||||
void free_input_line(char *z){
|
||||
free(z);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the value of a hexadecimal digit. Return -1 if the input
|
||||
** is not a hex digit.
|
||||
@@ -7890,7 +7904,7 @@ static MetaCommand * findMetaCommand(const char *, ShellExState *, int *);
|
||||
static DotCmdRC runMetaCommand(MetaCommand*, char *[], int na, ShellExState*);
|
||||
|
||||
static ExtensionHelpers extHelpers = {
|
||||
11,
|
||||
13,
|
||||
{
|
||||
failIfSafeMode,
|
||||
currentOutputFile,
|
||||
@@ -7902,6 +7916,8 @@ static ExtensionHelpers extHelpers = {
|
||||
nowInteractive,
|
||||
shellInvokedAs,
|
||||
shellStartupDir,
|
||||
one_input_line,
|
||||
free_input_line,
|
||||
sqlite3_enable_load_extension,
|
||||
0
|
||||
}
|
||||
@@ -14001,7 +14017,8 @@ static DotCmdRC process_input(ShellInState *psi){
|
||||
int iStartline = 0; /* starting line number of group */
|
||||
|
||||
fflush(psi->out);
|
||||
zLineInput = one_input_line(psi->pInSource, zLineInput, nGroupLines>0);
|
||||
zLineInput = one_input_line(psi->pInSource, zLineInput,
|
||||
nGroupLines>0, &shellPrompts);
|
||||
if( zLineInput==0 ){
|
||||
bInputEnd = 1;
|
||||
inKind = Eof;
|
||||
@@ -14121,7 +14138,8 @@ static DotCmdRC process_input(ShellInState *psi){
|
||||
pncLineUse = &ncLineAcc;
|
||||
}
|
||||
/* Read in next line of group, (if available.) */
|
||||
zLineInput = one_input_line(psi->pInSource, zLineInput, nGroupLines>0);
|
||||
zLineInput = one_input_line(psi->pInSource, zLineInput,
|
||||
nGroupLines>0, &shellPrompts);
|
||||
if( zLineInput==0 ){
|
||||
bInputEnd = 1;
|
||||
inKind = Eof;
|
||||
@@ -15127,10 +15145,11 @@ int SQLITE_CDECL SHELL_MAIN(int argc, wchar_t **wargv){
|
||||
clearTempFile(&data);
|
||||
sqlite3_free(data.zEditor);
|
||||
#if SHELL_DYNAMIC_EXTENSION
|
||||
notify_subscribers(&data, NK_DbAboutToClose, datax.dbShell);
|
||||
sqlite3_close(datax.dbShell);
|
||||
notify_subscribers(&data, NK_ShutdownImminent, 0);
|
||||
free_all_shext_tracking(&data);
|
||||
subscribe_events(&datax, 0, &datax, NK_Unsubscribe, 0);
|
||||
sqlite3_close(datax.dbShell);
|
||||
#endif
|
||||
#if !SQLITE_SHELL_IS_UTF8
|
||||
for(i=0; i<argcToFree; i++) free(argvToFree[i]);
|
||||
|
||||
+11
-3
@@ -226,6 +226,11 @@ typedef struct ScriptHooks {
|
||||
ShellExState *, char **pzErrMsg);
|
||||
} ScriptHooks;
|
||||
|
||||
typedef struct Prompts {
|
||||
const char *zMain;
|
||||
const char *zContinue;
|
||||
} Prompts;
|
||||
|
||||
typedef struct ExtensionHelpers {
|
||||
int helperCount; /* Helper count, not including sentinel */
|
||||
union {
|
||||
@@ -242,17 +247,20 @@ typedef struct ExtensionHelpers {
|
||||
int (*nowInteractive)(ShellExState *p);
|
||||
const char * (*shellInvokedAs)(void);
|
||||
const char * (*shellStartupDir)(void);
|
||||
char * (*oneInputLine)(struct InSource *pInSrc, char *zPrior,
|
||||
int isContinuation, Prompts *pCue);
|
||||
void (*freeInputLine)(char *zLine);
|
||||
int (*enable_load_extension)(sqlite3 *db, int onoff);
|
||||
void (*sentinel)(void);
|
||||
} named ;
|
||||
void (*nameless[10+1])(); /* Same as named but anonymous plus a sentinel. */
|
||||
void (*nameless[13+1])(); /* Same as named but anonymous plus a sentinel. */
|
||||
} helpers;
|
||||
} ExtensionHelpers;
|
||||
|
||||
/* This enum is stable excepting that it grows at the end. Members will not
|
||||
* change value across successive shell versions, except for NK_CountOf. An
|
||||
* extension which is built to rely upon particular notifications can pass
|
||||
* an NK_CountOf value upon which it relies to subscribe(...) as nkMin,
|
||||
* an NK_CountOf value upon which it relies to subscribeEvents(...) as nkMin,
|
||||
* which call will fail if the hosting shell's NK_CountOf value is lower.
|
||||
*/
|
||||
typedef enum {
|
||||
@@ -269,7 +277,7 @@ typedef enum {
|
||||
* pvSubject is the newly set .dbUser value. */
|
||||
NK_DbUserVanishing, /* Current ShellExState .dbUser will soon vanish,
|
||||
* pvSubject is the vanishing .dbUser value. */
|
||||
NK_DbAboutToClose, /* A maybe-user-visible DB will soon be closed,
|
||||
NK_DbAboutToClose, /* A ShellExState-visible DB will soon be closed,
|
||||
* pvSubject is the sqlite3 pointer soon to close. */
|
||||
NK_CountOf /* Present count of preceding members (evolves) */
|
||||
} NoticeKind;
|
||||
|
||||
Reference in New Issue
Block a user