]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
TCL extension made to use shell's line inputter with prompting and line editing/history.
authorlarrybr <larrybr@noemail.net>
Sun, 27 Mar 2022 23:33:43 +0000 (23:33 +0000)
committerlarrybr <larrybr@noemail.net>
Sun, 27 Mar 2022 23:33:43 +0000 (23:33 +0000)
FossilOrigin-Name: fbf0eb0d12932513bba4c6a6ef31d9972d704ab38690c806098504a4cd67786d

ext/misc/tclshext.c.in
manifest
manifest.uuid
src/shell.c.in
src/shext_linkage.h

index a5a4127be5eb317b6ed51189ba4a52bd339fc3f1..108ae52766e31b26d9f079ac3076f099fc436040 100644 (file)
@@ -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);
index d79f156e619c65f4773e39c8ec5dad9b7b2d3586..1bb31b88f941760a8fbf73859e82cfdad6791ff3 100644 (file)
--- a/manifest
+++ b/manifest
@@ -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.
index 141070bbe444be1e17dc4fb5c56aae8fd3b70d0e..eb6d19d574625e301ad931710f224442bd4763ce 100644 (file)
@@ -1 +1 @@
-c9aa76bf88401d193a536bc6576405aaad06681504996916b492962d890bc9e0
\ No newline at end of file
+fbf0eb0d12932513bba4c6a6ef31d9972d704ab38690c806098504a4cd67786d
\ No newline at end of file
index b9f9b100885d8e084edb895f3b5de0af4eb16b77..961a380e70fae7f2bd5888444cb30e833fec0970 100755 (executable)
@@ -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]);
index ec72d5c9829b21b1ce7d602908471d8b7e9448f0..bc67988811aec58d67322c2424ece1262159d83c 100644 (file)
@@ -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;