-C Complete\sshell.c\smaker's\smigration\sto\sTCL\sv.8.4
-D 2021-09-05T18:45:38.604
+C Commencing\sdynamic\sextensibility\stransition.\s(a\sWIP,\smay\snot\sbuild)
+D 2021-09-23T17:27:17.965
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F src/resolve.c 42b94d37a54200707a95566eff4f7e8a380e32d080016b699f23bd79a73a5028
F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
F src/select.c 63077c0243ded1432d97c90c1a4c3419b3a574b36634c674599a68bfe4c3bdc2
-F src/shell.c.in 729b233614ebf5e3a7f7ca1fcbefd0043439192962ae200e41822b2de88dcada
+F src/shell.c.in d9227a100a050eef557789d0fbe79f6b990171179633ff0213be966845702ff3
+F src/shext_linkage.h adf6b1c6a918b3c695a2181f1cec9cc452afd89fdbb9f476d4cd495e6a7c9aa7
F src/sqlite.h.in 43fcf0fe2af04081f420a906fc020bde1243851ba44b0aa567a27f94bf8c3145
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h e97f4e9b509408fea4c4e9bef5a41608dfac343b4d3c7a990dedde1e19af9510
F tool/mkopcodeh.tcl 130b88697da6ec5b89b41844d955d08fb62c2552e889dec8c7bcecb28d8f50bd
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
F tool/mkpragmatab.tcl 7f6db47d1995bc08247255622524567b2ab8962d98063f8aef97e35c3c54e3b8
-F tool/mkshellc.tcl 411eec479747ed1ab3083cfb1f6ad5adc6e0513dbb43457d1ecbb38f185fb0f7
+F tool/mkshellc.tcl 1f6105dc731a32eb49c76fc60672bb1de3f3e1f44d632094e5ee4249bf51b28d
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 c60f4f90c954eee6a2b644c50aca9a4ed7616d89177fd1c6acb997a362d9abff
-R f8fde1e6d9349d728c3d53f4be4fa502
+P e4b9b5b14bcb7b4c19ba47a7d5c74764b9122d36be82f52053cafcee144c581e
+R 66ca892bbdf4f099a713b813eb8e1d1a
U larrybr
-Z 465b5f34f9ce5fabad331f9659d67e22
+Z 079c8921973fd093a59246d0d2430319
-e4b9b5b14bcb7b4c19ba47a7d5c74764b9122d36be82f52053cafcee144c581e
\ No newline at end of file
+5ea71afe96ebe32641024aa8324b3ddd73da3ba35de204669130f8136cc1ba85
\ No newline at end of file
return rc;
}
+/* Configure help text generation to have coalesced secondary
+ * help lines with trailing newlines on all help lines.
+ */
+DISPATCH_CONFIG[
+ HELP_COALESCE=1
+];
+#define HELP_TEXT_FMT "%s"
+/* Above HELP_COALESCE config and HELP_TEXT_FMT PP vars must track.
+ * Alternative is 0 and "%s\n" .
+ */
+
/*
-** Output help text.
-**
-** zPattern describes the set of commands for which help text is provided.
-** If zPattern is NULL, then show all commands, but only give a one-line
-** description of each.
+** Output various subsets of help text. These 5 are defined:
+** 1. For all commands, primary help text only.
+** 2. For all commands, complete help text.
+** 3. For multiple commands matching a pattern, primary help text only.
+** 4. For a single matched command, complete help text.
+** 5. For commands whose help contains a pattern, complete help text.
+** These variations are indicated thusly:
+** 1. zPattern is NULL
+** 2. zPattern is ""
+** 3. zPattern is a prefix matching more than one command
+** 4. zPattern is a word or prefix matching just one command
+** 5. zPattern is neither case 3 or 4 but is found in complete help text
**
** Return the number of matches.
*/
static int showHelp(FILE *out, const char *zPattern){
- int n = 0;
- char *zPat;
+ int npm = 0;
+ char *zPat = sqlite3_mprintf(".%s*", zPattern? zPattern : "");
const char **pzHxtra;
const char **pzH;
- if( zPattern==0
- || zPattern[0]=='0'
- || strcmp(zPattern,"-a")==0
- || strcmp(zPattern,"-all")==0
- || strcmp(zPattern,"--all")==0
- ){
- /* Show all commands, but only one line per command */
- if( zPattern==0 ) zPattern = "";
- for(pzH = azHelp; *pzH != 0; ++pzH){
- if( *pzH[0]=='.' || zPattern[0] ){
- utf8_printf(out, "%s\n", *pzH);
- ++n;
- }
- }
- }else{
- /* Look for commands that for which zPattern is an exact prefix */
- zPat = sqlite3_mprintf(".%s*", zPattern);
- for(pzH = azHelp; *pzH != 0; ++pzH){
- if( sqlite3_strglob(zPat, *pzH)==0 ){
- utf8_printf(out, "%s\n", *pzH);
+ int nma = 0;
+ if( zPat==0 ) shell_out_of_memory();
+ for(pzH = azHelp; *pzH != 0; ++pzH){
+ /* Look for all commands or those for which zPattern is an exact prefix */
+ if( *pzH[0]=='.' ){
+ if ( sqlite3_strglob(zPat, *pzH)==0 ){
+ utf8_printf(out, HELP_TEXT_FMT, *pzH);
pzHxtra = pzH + 1;
- n++;
+ ++npm;
}
+ }else if( zPattern && *zPattern==0 ){
+ utf8_printf(out, HELP_TEXT_FMT, *pzH);
}
- sqlite3_free(zPat);
- if( n ){
- if( n==1 ){
- /* when zPattern is a prefix of exactly one command, then include the
- ** details of that command, which should begin at *pzHxtra */
- while( *pzHxtra !=0 && *pzHxtra[0]!='.' ){
- utf8_printf(out, "%s\n", *pzHxtra++);
- }
- }
- return n;
- }
- /* Look for commands that contain zPattern anywhere. Show the complete
- ** text of all commands that match. */
- zPat = sqlite3_mprintf("%%%s%%", zPattern);
- for(pzH = azHelp; *pzH != 0;){
- if( *pzH[0]=='.' ) pzHxtra = pzH;
- if( sqlite3_strlike(zPat, *pzHxtra, 0)==0 ){
- utf8_printf(out, "%s\n", *pzHxtra++);
- while( *pzHxtra != 0 && *pzHxtra[0]!='.' ){
- utf8_printf(out, "%s\n", *pzHxtra++);
- }
- pzH = pzHxtra;
- n++;
- }else{
- ++pzH;
- }
+ }
+ sqlite3_free(zPat);
+ if( npm==1 ){
+ /* When zPattern is a prefix of exactly one command, then include
+ ** the secondary help of that command, (beginning at *pzHxtra.) */
+ while( *pzHxtra !=0 && *pzHxtra[0]!='.' ){
+ utf8_printf(out, HELP_TEXT_FMT, *pzHxtra++);
}
- sqlite3_free(zPat);
}
- return n;
+ if( npm>0 )
+ return npm;
+
+ /* Having failed to match a command, look for commands whose help contains
+ * zPattern anywhere. Show the complete text of all such commands.
+ */
+ zPat = sqlite3_mprintf("%%%s%%", zPattern);
+ if( zPat==0 ) shell_out_of_memory();
+ for(pzH = azHelp; *pzH != 0;){
+ if( *pzH[0]=='.' ){
+ pzHxtra = pzH;
+ nma = 0;
+ }
+ if( sqlite3_strlike(zPat, *pzH, 0)==0 )
+ ++nma;
+ ++pzH;
+ if( nma>0 && (*pzH==0 || *pzH[0]=='.') ){
+ ++npm;
+ while( pzHxtra < pzH )
+ utf8_printf(out, HELP_TEXT_FMT, *pzHxtra++);
+ }
+ }
+ sqlite3_free(zPat);
+
+ return npm;
}
/* Forward reference */
return rc;
}
+#ifndef OBJECTIFY_COMMANDS
+# define OBJECTIFY_COMMANDS 1
+#endif
+
/* Meta-command implementation functions are defined in this section.
COMMENT Define meta-commands and provide for their dispatch and .help text.
COMMENT These should be kept in command name order for coding convenience
COMMENT executing tool/mkshellc.tcl --parameters (or --details or --help).
COMMENT Generally, this section defines dispatchable functions inline and
COMMENT causes collection of dispatch and help table entries, to be later
-COMMENT emitted by the EMIT_DISPATCH and EMIT_HELP_TEXT macros further on.
+COMMENT emitted by certain macros. (See EMIT_* further on.)
*/
DISPATCH_CONFIG[
RETURN_TYPE=int
DC_ARG_COUNT=7
];
-
CONDITION_COMMAND(seeargs defined(SQLITE_GIMME_SEEARGS));
/*****************
* The .seeargs command
* The .help command
*/
COLLECT_HELP_TEXT[
- ".help ?-all? ?PATTERN? Show help text, just for PATTERN if given",
+ ".help ?(PATTERN|-all)? Show help text for some or all command(s)",
+ " PATTERN Show help for matching command(s)",
+ " -all Show all help for all commands",
];
-DISPATCHABLE_COMMAND( help 3 0 0 ){
- if( nArg>=2 ){
- if( showHelp(p->out, azArg[1])==0 ){
- utf8_printf(p->out, "Nothing matches '%s'\n", azArg[1]);
+DISPATCHABLE_COMMAND( help 3 1 2 ){
+ const char *zPat = 0;
+ if( nArg>1 ){
+ char *z = azArg[1];
+ if( strcmp(z,"-a")==0
+ || strcmp(z,"-all")==0
+ || strcmp(z,"--all")==0 ){
+ zPat = "";
+ }else{
+ zPat = z;
}
- }else{
- showHelp(p->out, 0);
+ }
+ if( showHelp(p->out, zPat)==0 ){
+ utf8_printf(p->out, "Nothing matches '%s'\n", azArg[1]);
}
/* Help pleas never fail! */
return 0;
COLLECT_HELP_TEXT[
#ifndef LEGACY_TABLES_LISTING
- ".tables ?FLAG? ?TABLE? List names of tables/views matching LIKE pattern TABLE",
- " FLAG can be -t, -v or -s to list tables or views only, or system tables only",
+ ".tables ?FLAG? ?TVLIKE? List names of tables and/or views",
+ " FLAG may be -t, -v or -s to list only tables, views or system tables",
+ " TVLIKE may restrict the listing to names matching given LIKE pattern",
#else
- ".tables ?TABLE? List names of tables/views matching LIKE pattern TABLE",
+ ".tables ?TABLE? List names of tables matching LIKE pattern TABLE",
#endif
];
DISPATCHABLE_COMMAND( tables 2 1 3 ){
COLLECT_HELP_TEXT[
".testcase NAME Begin redirecting output to 'testcase-out.txt'",
".testctrl CMD ... Run various sqlite3_test_control() operations",
- " Run \".testctrl\" with no arguments for details",
+ " Run \".testctrl\" with no arguments for details",
".timeout MS Try opening locked tables for MS milliseconds",
".timer on|off Turn SQL timer on or off",
".trace ?OPTIONS? Output each SQL statement as it is run",
*/
INCLUDE( COMMAND_CUSTOMIZE );
+#if OBJECTIFY_COMMANDS
+INCLUDE shext_linkage.h
+#endif
+typedef struct MetaCommand MetaCommand;
+
/* Define and populate command dispatch table. */
-static struct DispatchEntry {
+static struct CommandInfo {
const char * cmdName;
int (*cmdDoer)(char *azArg[], int nArg, ShellState *);
unsigned char minLen, minArgs, maxArgs;
+#if OBJECTIFY_COMMANDS
+ const char *azHelp[2]; /* primary and secondary help text */
+ void * pCmdData;
+#endif
} command_table[] = {
COMMENT Emit the dispatch table entries generated and collected above.
EMIT_DISPATCH(2);
{ 0, 0, 0, -1, -1 }
};
-static unsigned numCommands = sizeof(command_table)/sizeof(struct DispatchEntry) - 1;
+static unsigned numCommands
+ = sizeof(command_table)/sizeof(struct CommandInfo) - 1;
COMMENT This help text is set seperately from meta-command definition section
COMMENT for the always-built-in, non-customizable commands with visible help.
{
const char *cmdName = azArg[0];
int cmdLen = strlen30(cmdName);
- struct DispatchEntry *pde = 0;
+ struct CommandInfo *pci = 0;
int ixb = 0, ixe = numCommands-1;
while( ixb <= ixe ){
int ixm = (ixb+ixe)/2;
if( command_table[ixm].minLen > cmdLen ){
return NO_SUCH_COMMAND;
}
- pde = &command_table[ixm];
+ pci = &command_table[ixm];
break;
}
}
- if( 0==pde ){
+ if( 0==pci ){
return NO_SUCH_COMMAND;
}
- if((pde->minArgs > 0 && pde->minArgs > nArg)||(pde->maxArgs > 0 && pde->maxArgs < nArg)){
+ if((pci->minArgs > 0 && pci->minArgs > nArg)||(pci->maxArgs > 0 && pci->maxArgs < nArg)){
return INVALID_ARGS;
}
- return (pde->cmdDoer)(azArg, nArg, pSS);
+ return (pci->cmdDoer)(azArg, nArg, pSS);
}
--- /dev/null
+#ifndef SQLITE3SHX_H
+#define SQLITE3SHX_H
+#include "sqlite3ext.h"
+
+typedef struct ShellState ShellState;
+
+/* This function pointer has the same signature as the sqlite3_X_init()
+ * function that is called as SQLite3 completes loading an extension.
+ */
+typedef int (*ExtensionId)
+ (sqlite3 *, char **, const struct sqlite3_api_routines *);
+
+/* An instance of below struct, possibly extended/subclassed, is registered
+ * with the shell to make new or altered meta-commands available to it.
+ */
+typedef struct MetaCommand {
+ struct MetaCommandVtable *pMCV;
+} MetaCommand;
+
+/* This vtable is for meta-command implementation and help linkage to shell.
+ */
+typedef struct MetaCommandVtable {
+ void (*destruct_free)(MetaCommand *);
+ const char * (*name)(MetaCommand *);
+ const char * (*help)(MetaCommand *, int more);
+ void (*argsRange)(MetaCommand *, int * pMinArgs, int * pMaxArgs);
+ int (*execute)
+ (MetaCommand *, ShellState *, char **pzErrMsg, char *azArgs[], int nArgs);
+} MetaCommandVtable;
+
+/* See "Shell Extensions, Programming" for purposes and usage of the following
+ * structs supporting extended meta-commands and import and output modes.
+ */
+
+/* Convey data to, from and/or between I/O handlers. */
+typedef struct {
+ char *zFieldSeparator;
+ char *zRecordSeparator;
+ char *zRecordLead;
+ char *zRecordTrail;
+ char *zNullValue;
+ char *zSQL;
+ int numWidths;
+ int *pWantWidths;
+ int *pHaveWidths;
+ void *pvHandlerData; /* Lifetime is from mid-openX() to mid-closeX(). */
+} FormatInfo;
+
+/* An instance of below struct, possibly extended/subclassed, is registered
+ * with the shell to make new or altered output modes available to it.
+ */
+typedef struct OutModeHandler {
+ struct OutModeHandlerVtable *pOMV;
+} OutModeHandler;
+
+typedef struct OutModeHandlerVtable {
+ void (*destruct_free)(OutModeHandler * pROS);
+ const char * (*name)(OutModeHandler *);
+ const char * (*help)(OutModeHandler *, int more);
+ int (*openResultsOutStream)
+ (OutModeHandler * pROS, FormatInfo *pFI, char **pzErr,
+ const char * zLocus, const char * zName);
+ int (*prependResultsOut)
+ (OutModeHandler * pROS, FormatInfo *pFI, char **pzErr,
+ sqlite3_stmt * pStmt);
+ int (*rowResultsOut)
+ (OutModeHandler * pROS, FormatInfo *pFI, char **pzErr,
+ sqlite3_stmt * pStmt);
+ int (*appendResultsOut)
+ (OutModeHandler * pROS, FormatInfo *pFI, char **pzErr,
+ sqlite3_stmt * pStmt);
+ int (*closeResultsOutStream)
+ (OutModeHandler * pROS, FormatInfo *pFI, char **pzErr);
+} OutModeHandlerVtable;
+
+/* An instance of below struct, possibly extended/subclassed, is registered
+ * with the shell to make new or altered data importers available to it.
+ */
+typedef struct ImportHandler {
+ struct ImportHandlerVtable *pIHV;
+} ImportHandler;
+
+typedef struct ImportHandlerVtable {
+ void (*destruct_free)(ImportHandler * pIH);
+ const char * (*name)(ImportHandler *);
+ const char * (*help)(ImportHandler *, int more);
+ int (*openDataInStream)
+ (ImportHandler *pIH, FormatInfo *pFI, char **pzErr,
+ const char * zLocus, const char * zName);
+ int (*prepareDataInput)
+ (ImportHandler *pIH, FormatInfo *pFI, char **pzErr, sqlite3_stmt * pStmt);
+ int (*rowDataInput)
+ (ImportHandler *pIH, FormatInfo *pFI, char **pzErr, sqlite3_stmt * pStmt);
+ int (*finishDataInput)
+ (ImportHandler *pIH, FormatInfo *pFI, char **pzErr, sqlite3_stmt * pStmt);
+ int (*closeDataInStream)
+ (ImportHandler *pIH, FormatInfo *pFI, char **pzErr);
+} ImportHandlerVtable;
+
+#define SHELLEXT_VALIDITY_MARK "ExtensibleShell"
+
+typedef struct ShellExtensionLink {
+ char validityMark[16]; /* Preset to contain "ExtensibleShell\x00" */
+ char *zErrMsg; /* Extension puts error message here if any. */
+ int sizeOfThis; /* sizeof(struct ShellExtensionLink) */
+ const char *shellVersion; /* Preset to "3.??.??\x00" or similar */
+
+ /* An init "out" parameter, used as the loaded extension ID. Unless
+ * this is set within sqlite3_X_init() prior to register*() calls,
+ * the extension cannot be unloaded.
+ */
+ 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.
+ */
+ void (*extensionDtor)(void *);
+
+ /* Various shell extension feature registration functions
+ */
+ union ShellExtensionAPI {
+ struct ShExtAPI {
+ /* Register a meta-command */
+ int (*registerMetaCommand)(ExtensionId eid, MetaCommand *pMC);
+ /* Register an output data display (or other disposition) mode */
+ int (*registerOutMode)(ExtensionId eid, OutModeHandler *pOMH);
+ /* Register an import variation from (various sources) for .import */
+ int (*registerImporter)(ExtensionId eid, ImportHandler *pIH);
+ /* Preset to 0 at extension load, a sentinel for expansion */
+ void (*pExtra)(void);
+ } *named;
+ void (*pFunctions[4])(); /* 0-terminated sequence of function pointers */
+ } api;
+} ShellExtensionLink;
+
+/* Test whether a char ** references a ShellExtensionLink instance's
+ * validityMark, and if so return the instance's address, else return 0.
+ * This macro may be used from a shell extension's sqlite3_X_init() function
+ * to obtain a pointer to the ShellExtensionLink struct, derived from the
+ * error message pointer (pzErrMsg) passed as the 2nd argument. This enables
+ * the extension to incorporate its features into a running shell process.
+ */
+#define EXTENSION_LINKAGE_PTR(pzem) ( \
+ pzem != 0 && *pzem != 0 && strcmp(*pzem, SHELLEXT_VALIDITY_MARK) == 0 \
+ && *pzem == (char *)pzem \
+ + offsetof(ShellExtensionLink, validityMark) \
+ - offsetof(ShellExtensionLink, zErrMsg) ) \
+ ? (ShellExtensionLink *) \
+ ((char *)pzem-offsetof(ShellExtensionLink,zErrMsg)) \
+ : 0
+
+/* String used with SQLite "Pointer Passing Interfaces" as a type marker.
+ * That API subset is used by the shell to pass its extension API to the
+ * sqlite3_X_init() function of extensions, via the DB parameter.
+ */
+#define SHELLEXT_API_POINTERS "shellext_api_pointers"
+
+/* Pre-write a function to retrieve a ShellExtensionLink pointer from the
+ * shell's DB. This is an alternative to use of the EXTENSION_LINKAGE_PTR
+ * macro above. It takes some more code, replicated across extensions.
+ */
+#define DEFINE_SHDB_TO_SHEXT_API(func_name) \
+ static ShellExtensionLink * func_name(sqlite3 * db){ \
+ ShellExtensionLink *rv; sqlite3_stmt *pStmt; \
+ if( SQLITE_OK!=sqlite3_prepare(db,"SELECT shext_pointer(0)",-1,&pStmt,0) \
+ || SQLITE_ROW != sqlite3_step(pStmt) ) return 0; \
+ rv = (ShellExtensionLink *)sqlite3_value_pointer \
+ (sqlite3_column_value(pStmt, 0), SHELLEXT_API_POINTERS); \
+ sqlite3_finalize(pStmt); return rv; \
+ }
+
+#endif /* !defined(SQLITE3SHX_H) */
#!/usr/bin/tclsh
#
-# Run this script to generate the "shell.c" source file from
-# constituent parts.
+# Run this script to generate the "shell.c" source file from its
+# constituent parts located normally within the SQLite source.
#
# No arguments are required. This script determines the location
# of its input files relative to the location of the script itself.
-# This script should be tool/mkshellc.tcl. If the directory holding
-# the script is $DIR, then the component parts are located in $DIR/../src
-# and $DIR/../ext/misc.
+# This script is assumed to be in <project root>/tool/mkshellc.tcl.
+# By default, shell.c's constituent parts, named in INCLUDE macros,
+# are located in <project root>/src and <project root>/ext/misc .
+# By default, the input src/shell.c.in is read and processed.
#
-set topdir [file dir [file dir [file normal $argv0]]]
+# To see other execution options, run this with a --help option.
+# This script may also be used for shell extensions, as described
+# at https://sqlite.org/shell_extend.html . ToDo
+#########1#########2#########3#########4#########5#########6#########7#########8
-set out stdout
+set ::help {
+ mkshellc.tcl <options>
+ <options> may be either --help, --details, --parameters or any sequence of:
+ <input_filename>
+ -ignored <signed_command_list>
+ -inc-type <inc_type>=<include_filename>
+ -source-tags <tags_degree>
+ -top-dir <project_root>
+ -tcl
+ If no input files are specified, <PROJECT_ROOT>/src/shell.c.in is read.
+ Input files are read and processed in order, producing output to sdout.
+
+ The -ignored option affects a list of commands which, during processing,
+ will be ignored and generate no output. The list starts empty.
+
+ The -inc-type option associates a filename with an <inc_type> word which
+ may be used during execution of INCLUDE(...) directives in the input.
-set headComment {/* DO NOT EDIT!
+ The -source-tags option sets the degree of #line directive emission via
+ the <tags_degree> value. 0 turns tagging off. 1, which is the default,
+ yields tagging only on non-macro code as it is scanned. 2 adds much more
+ tagging, (about 3x), on individual dispatch and help table entries, and
+ on conditional compilation preprocessor directives.
+
+ Input files may include macro lines or line sequences matching any of:
+ INCUDE <file_name>\
+}
+# MACRO_DOSTUFF ...
+set ::helpMore {
+ Use --details option for detailed effects of these macros.
+ Use --parameters option for CONFIGURE_DISPATCH parameter names and effects.
+}
+
+set ::headComment {/* DO NOT EDIT!
** 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
** then rerun the tool/mkshellc.tcl script.
*/}
-set customRun 0
-set lineDirectives 1
+set ::headCommentLines [expr 1+[regexp -all "\n" $::headComment]]
+
+set ::topdir [file dir [file dir [file normal $argv0]]]
+set runMode normal
+set ::lineDirectives 1
+set ::tclGenerate 0
+set ::verbosity 0
set infiles {}
-array set ::incTypes [list "*" {}]
+array set ::incTypes [list "*" "$::topdir/src/shell.c.in"]
+array set ::ignoringCommands [list]
while {[llength $argv] > 0} {
- set opt [lindex $argv 0]
- set argv [lreplace $argv 0 0]
+ set argv [lassign $argv opt]
if {[regexp {^-{1,2}((help)|(details)|(parameters))$} $opt ma ho]} {
- switch $ho {
- help {set customRun 2}
- details {set customRun 3}
- parameters {set customRun 4}
- }
+ set runMode $ho
} elseif {[regexp {^-it$} $opt]} {
- set nextOpt [lindex $argv 0]
- set argv [lreplace $argv 0 0]
+ set argv [lassign $argv nextOpt]
if {![regexp {^(\w+)=(.+)$} $nextOpt ma k v]} {
puts stderr "Get help with --help."
exit 1
}
set ::incTypes($k) $v
+ } elseif {$opt eq "-top-dir"} {
+ set argv [lassign $argv ::topdir]
+ if {::topdir eq ""} { set ::topdir . }
+ } elseif {$opt eq "-source-tags"} {
+ set argv [lassign $argv nextOpt]
+ if {![regexp {^\d$} $nextOpt ::lineDirectives]} {
+ puts stderr "Argument following -source-tags must be a digit."
+ }
} elseif {$opt eq "-tcl"} {
- puts stderr "Tcl extension not yet implemented." ; exit 1
- } elseif {[regexp {^--?no-line-directives$} $opt]} {
- set lineDirectives 0
+ puts stderr "Warning: Tcl extension not wholly implemented."
+ set ::tclGenerate 1
+ } elseif {$opt eq "-v"} {
+ incr ::verbosity
} elseif {[regexp {^[^-]} $opt]} {
lappend infiles $opt
- set customRun 1
} else {
puts stderr "Skipping unknown option: $opt"
}
}
-if {[llength $infiles] == 0} {
- set in [open $topdir/src/shell.c.in r]
-} else {
- set infile [lindex $infiles 0]
- set infiles [lreplace $infiles 0 0]
- set in [open $infile r]
+if {$runMode eq "normal"} {
+ if {[llength $infiles] == 0} {
+ lappend infiles $::incTypes(*)
+ }
+ fconfigure stdout -translation {auto lf}
+ set out stdout
}
fconfigure $in -translation auto
+if {$::lineDirectives >= 2} {
+ # These k/v stores hold {filename lineNum} lists keyed by meta-command,
+ # used to get #line directives on all dispatch and help table entries,
+ # and any conditionals affecting their compilation.
+ array set ::cmd_help_tags {}
+ array set ::cmd_dispatch_tags {}
+ array set ::cmd_conditional_tags {}
+}
+proc lineDirective {filename lineNum} {return "#line $lineNum \"${filename}\""}
+
array set ::cmd_help {}
array set ::cmd_dispatch {}
array set ::cmd_condition {}
(which is needed to permit them to be emitted in lexical order by name.)
DC_ARG_COUNT sets the effective argument count for DISPATCHABLE_COMMAND().
DC_ARG#_DEFAULT sets a default value, DISPATCHABLE_COMMAND() #'th argument.
+ HELP_COALESCE sets whether to coalesce secondary help text and add newlines.
Within values set for ARGS_SIGNATURE, DISPATCHEE_NAME, and DISPATCH_ENTRY
parameters, the variables $cmd and $arg# (where # is an integer) may appear,
to be replaced by the meta-command name or the #'th effective argument to
"{ \"\$cmd\", \${cmd}Command, \$arg1,\$arg2,\$arg3 }," \
DISPATCHEE_NAME {${cmd}Command} \
CMD_CAPTURE_RE "^\\s*$::lb\\s*\"(\\w+)\"" \
+ HELP_COALESCE 0 \
]
# Other config keys:
# DC_ARG_COUNT=<number of arguments to DISPATCHABLE_COMMAND()>
set ::cmd_condition($cmd) $pp_expr
}
-proc emit_conditionally {cmd lines ostrm {indent ""}} {
+proc emit_conditionally {cmd lines ostrm {indent ""} {cmdTagStore {}}} {
set wrapped [info exists ::cmd_condition($cmd)]
+ set iPut 0
if {$wrapped} {
+ if {$::lineDirectives >= 2} {
+ puts $ostrm [lineDirective $::cmd_conditional_tags($cmd)]
+ incr iPut
+ }
puts $ostrm $::cmd_condition($cmd)
+ incr iPut
+ }
+ if {$::lineDirectives >= 2} {
+
+ set fnln subst[[subst "\$$cmdTagStore(\$cmd)"]]
+ puts $ostrm [lineDirective {*}$fnln]
+ incr iPut
}
if {[regexp {^\s*(\d+)\s*$} $indent ma inum]} {
set lead [string repeat " " $inum]
} else {
puts $ostrm [join $lines "\n"]
}
+ incr iPut [llength $lines]
if {$wrapped} {
puts $ostrm "#endif"
+ incr iPut
}
+ return $iPut
+}
+
+# Coalesce secondary help text lines using C's string literal concatenation
+# and arrange that each command's help has one primary (leading '.') help
+# text line and one secondary help text line-set even if it is empty.
+proc coalesce_help {htin} {
+ set htrv {}
+ foreach hl $htin {
+ if {[regexp {^\s*"\.\w+} $hl]} { ;# "
+ lappend htrv [regsub {"\s*,\s*$} $hl {\n",}]
+ } elseif {[regexp {^\s*#\s*\w+} $hl]} {
+ lappend htrv $hl
+ } else {
+ lappend htrv [regsub {"\s*,\s*$} $hl {\n"}]
+ }
+ }
+ lappend htrv {"",}
}
# Convert list of help text lines into a key-value list.
incr ::iShuffleErrors
}
}
+ if {$::dispCfg(HELP_COALESCE)} {
+ foreach cmd_seen [array names rv] {
+ set rv($cmd_seen) [coalesce_help $rv($cmd_seen)]
+ }
+ }
return [array get rv]
}
DISPATCHABLE_COMMAND {^\(([\w\? ]+)\)(\S)\s*$} \
EMIT_DISPATCH {^\((\d*)\)} \
EMIT_HELP_TEXT {^\((\d*)\)} \
- INCLUDE {^\(\s*(\w+)\s*\)} \
+ INCLUDE {^(?:\(\s*(\w+)\s*\))|(?:\s+([\w./\\]+)\M)} \
+ IGNORE_COMMANDS {^\(\s*([-+\w ]*)\)\s*;\s*} \
]
+# Names of the subcaptures as formal parameter to macro procs.
+# COMMENT tailCapture_Commentary
+# CONDITION_COMMAND tailCapture_Cmd_Condition
+# CONFIGURE_DISPATCH tailCapture_Empty
+# COLLECT_DISPATCH tailCapture_Cmd
+# COLLECT_HELP_TEXT tailCapture_Empty
+# DISPATCHABLE_COMMAND tailCapture_ArgsGlom_TrailChar
+# EMIT_DISPATCH tailCapture_Indent
+# EMIT_HELP_TEXT tailCapture_Indent
+# IGNORED_COMMANDS tailCapture_SignedCmdGlom
+# INCLUDE tailCapture_IncType_Filename
+
array set ::macroUsages [list \
COLLECT_DISPATCH "\[\n <dispatch table entry lines>\n \];" \
COLLECT_HELP_TEXT "\[\n <help text lines>\n \];" \
COMMENT " <arbitrary characters to end of line>" \
CONDITION_COMMAND "( name pp_expr );" \
DISPATCH_CONFIG "\[\n <NAME=value lines>\n \];" \
- DISPATCHABLE_COMMAND "( name args... ){\n <implementation code lines>\n }" \
+ DISPATCHABLE_COMMAND \
+ "( name args... ){\n <implementation code lines>\n }" \
EMIT_DISPATCH "( indent );" \
EMIT_HELP_TEXT "( indent );" \
INCLUDE {( <inc_type> )} \
+ SKIP_COMMANDS "( <signed_names> );" \
]
# RE for early discard of non-macro lines, matching all above keywords
-set ::macroKeywordTailRE {^\s{0,8}((?:(?:CO)|(?:DI)|(?:EM)|(?:IN))[A-Z_]+)\M(.+)$}
+set ::macroKeywordTailRE \
+ {^\s{0,8}((?:(?:CO)|(?:DI)|(?:EM)|(?:IN)|(?:SK))[A-Z_]+)\M(.+)$}
-# All macro processor procs return the count of extra input lines consumed.
+# RE to recognize macros which may emit and probably will.
+set ::emitterMacrosRE {^[DEI]}
+# RE to recognize macros which certainly will not emit.
+set ::consumerMacrosRE {^[CS]}
+# RE to recognize macros which have gather/scatter operation, and will emit.
+set ::shufflerMacrosRE {^E}
+# Above 3 RE's are used to trigger needed #line emits and avoid useless ones.
-proc COLLECT_DISPATCH {hFile tailCapture ostrm} {
- # Collect dispatch table entries, along with ordering info.
+set ::splat15 [string repeat * 15]
+set ::sharp15 "//[string repeat # 13]"
+
+# Put marker and possibly a #line directive signifying end of an inclusion.
+# Return number of lines emitted.
+proc includeEnd {fromFile returnFile lineNum ostrm} {
+ if {$returnFile eq ""} {
+ } else {
+ set rsay ", resume $returnFile"
+ }
+ if {$::tclGenerate} {
+ puts $ostrm "$::sharp15 End $fromFile$rsay $::sharp15"
+ } else {
+ puts $ostrm "/$::splat15 End $fromFile$rsay ${::splat15}/"
+ }
+ # Skip #line directives if not doing them, at end of outer includer,
+ # or processing Tcl. (At end of outer includer, #line is pointless.)
+ if {$::lineDirectives && !$::tclGenerate && $returnFile ne ""} {
+ puts $ostrm "#line $lineNum \"${returnFile}\""
+ return 2
+ }
+ return 1
+}
+# Possibly put a #line directive within the middle of an includee's output,
+# whether during input scan or upon deferred output.
+# Return number of lines emitted.
+proc includeMiddle {withinFile lineNum ostrm} {
+ if {$::lineDirectives && !$::tclGenerate} {
+ puts "#line $lineNum \"${withinFile}\""
+ return 1
+ }
+ return 0
+}
+# Put marker and possibly a #line directive signifying top of an inclusion.
+# Return number of lines emitted.
+proc includeBegin {startFile ostrm} {
+ if {$::tclGenerate} {
+ puts $ostrm "$::sharp25 Begin $startFile $::sharp25"
+ } else {
+ puts $ostrm "/$::splat25 Begin $startFile ${::splat25}/"
+ }
+ if {$::lineDirectives && !$::tclGenerate} {
+ puts $ostrm "#line 1 \"${startFile}\""
+ return 2
+ }
+ return 1
+}
+
+proc IGNORED_COMMANDS {inSrc tcSignedCmdGlom ostrm} {
+ # Cause the listed commands to be ignored or allowed to generate, as set
+ # by a preceeding + or - respectively in the list. This may be useful
+ # when statically extending the shell to avoid duplicate implementation.
+ # Commands never mentioned within this macro are allowed to generate.
+ set sign ""
+ foreach {. o} [regexp -inline -all {\s*([\-\+]|[\w]+)\s*} $tcSignedCmdGlom] {
+ if {![regexp {[\+\-\?]} $o . sign]} {
+ if {$sign eq "+"} {
+ } else {
+ }
+ }
+ }
+ return [list 0 0]
+
+}
+
+proc COLLECT_DISPATCH {inSrc tailCaptureCmdOrStar ostrm} {
+ # Collect dispatch table entries, along with cmd(s) as ordering info.
+ foreach {infile istrm inLineNum} $inSrc {}
+ foreach {cmd} $tailCaptureCmdOrStar {}
set iAte 0
- set cmd [lindex $tailCapture 0]
- set lx [gets $hFile]
- while {![eof $hFile] && ![regexp {^\s*\];} $lx]} {
+ set lx [gets $istrm]
+ set disp_frag {}
+ while {![eof $istrm] && ![regexp {^\s*\];} $lx]} {
lappend disp_frag $lx
set grabCmd $::dispCfg(CMD_CAPTURE_RE)
if {![regexp $grabCmd $lx ma dcmd]} {
} else {
set ::cmd_dispatch($dcmd) [list $lx]
}
- set lx [gets $hFile]
+ set lx [gets $istrm]
incr iAte
}
incr iAte
- return $iAte
+ return [list $iAte 0]
}
proc COMMENT {hFile tailCaptureIgnore ostrm} {