]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Create infrastructure for dynamic shell extension.
authorlarrybr <larrybr@noemail.net>
Sat, 3 Jul 2021 19:20:48 +0000 (19:20 +0000)
committerlarrybr <larrybr@noemail.net>
Sat, 3 Jul 2021 19:20:48 +0000 (19:20 +0000)
FossilOrigin-Name: 5e7e0d4ef8665e924f499238b1469a5fc06d24f6cf96864b502e62734d92e7ee

manifest
manifest.uuid
src/shell.c.in
tool/mkshellc.tcl

index 40a5aa83a18373d02be144f43a309125381816d5..5821ef2b6a24169870660c9740b6b25ac0502e4e 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C In\sthe\sPRAGMA\sforeign_key_check,\sensure\sthat\ssufficient\sregisters\sare\nallocated\sfor\sthe\svirtual\smachine,\seven\sif\sone\sor\smore\sforeign\skeys\sreuses\nthe\ssame\scolumn\smultiple\stimes\sand\shas\smore\scolumns\sthan\sthe\stable\sit\sis\spart\nof.\s\s[forum:/forumpost/a6b0c05277|Forum\spost\sa6b0c05277].
-D 2021-07-03T02:55:47.684
+C Create\sinfrastructure\sfor\sdynamic\sshell\sextension.
+D 2021-07-03T19:20:48.689
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -545,7 +545,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
 F src/resolve.c b379c5ffe3b692e9c64fa37817cc0efa204b7c9468a818309dde85fd132d9d81
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c 4fa607bab6bcc580f12dbaf9c800b2250a1e408f10321a1d3bcb1dd30c447e62
-F src/shell.c.in 699910739eb7296fd47be19db71f6e5d15d0760f4352c62639d4d6cc7bd8d4cc
+F src/shell.c.in ab31fb9d92ab84e1abb02a2c40e910b24434e729fa7549acbb3fee9aed5973d8
 F src/sqlite.h.in ecf5aa981da30c33da3e9f353bf3ebf055d3c380c80d6a4f954e58d18ccd6df1
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h e97f4e9b509408fea4c4e9bef5a41608dfac343b4d3c7a990dedde1e19af9510
@@ -1853,7 +1853,7 @@ F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
 F tool/mkopcodeh.tcl 130b88697da6ec5b89b41844d955d08fb62c2552e889dec8c7bcecb28d8f50bd
 F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
 F tool/mkpragmatab.tcl ae5585ae76ca26e4d6ccd5ea9cdebaf5efefb318bf989497a0e846cd711d9ab1
-F tool/mkshellc.tcl 5fe7e518112b262e25726f248c0f33dd153192867453984b6af0a76a88e97cb2
+F tool/mkshellc.tcl 627d5f39b306cc335e5f29d8b24945c54a64a42e635fc8eb8c2558ba257c9844
 F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
 F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f
@@ -1919,7 +1919,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 55e2fbebb0a2c9991feef46f31e79b82a24de272aae836bf4d3c06ee06ef1a70
-R 6361a08fc6d48a70d5a532fc3e15d75a
-U drh
-Z e9d1bfeaf7de1edba9fbe2d83f57c7d8
+P 68db1ff9c44fa9c37690ce55ad304d4263ba6fac490063d9e08470de6c17cfe6
+R 347b1c92e54f1f6ec16ffdb8c5434cca
+T *branch * cli_extension
+T *sym-cli_extension *
+T -sym-trunk *
+U larrybr
+Z d788ab2ca1c9950dfd92c69b64866455
index ae2f2c4709a73b2600ac6a253d0887e8c6b69d0a..f649a6ef7e2eeb4d43e24246f09806389e9a6023 100644 (file)
@@ -1 +1 @@
-68db1ff9c44fa9c37690ce55ad304d4263ba6fac490063d9e08470de6c17cfe6
\ No newline at end of file
+5e7e0d4ef8665e924f499238b1469a5fc06d24f6cf96864b502e62734d92e7ee
\ No newline at end of file
index f90822e281824a5fba823c0e36319ccfd2282292..5fa089b49009c65857e4ba7943d12924c68e5686 100644 (file)
@@ -217,6 +217,7 @@ static void setTextMode(FILE *file, int isOutput){
 # define setTextMode(X,Y)
 #endif
 
+static const char *(azHelp[]);
 
 /* True if the timer is enabled */
 static int enableTimer = 0;
@@ -3849,258 +3850,6 @@ static int run_schema_dump_query(
   return rc;
 }
 
-/*
-** Text of help messages.
-**
-** The help text for each individual command begins with a line that starts
-** with ".".  Subsequent lines are supplimental information.
-**
-** There must be two or more spaces between the end of the command and the
-** start of the description of what that command does.
-*/
-static const char *(azHelp[]) = {
-#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE)
-  ".archive ...             Manage SQL archives",
-  "   Each command must have exactly one of the following options:",
-  "     -c, --create               Create a new archive",
-  "     -u, --update               Add or update files with changed mtime",
-  "     -i, --insert               Like -u but always add even if unchanged",
-  "     -t, --list                 List contents of archive",
-  "     -x, --extract              Extract files from archive",
-  "   Optional arguments:",
-  "     -v, --verbose              Print each filename as it is processed",
-  "     -f FILE, --file FILE       Use archive FILE (default is current db)",
-  "     -a FILE, --append FILE     Open FILE using the apndvfs VFS",
-  "     -C DIR, --directory DIR    Read/extract files from directory DIR",
-  "     -n, --dryrun               Show the SQL that would have occurred",
-  "   Examples:",
-  "     .ar -cf ARCHIVE foo bar  # Create ARCHIVE from files foo and bar",
-  "     .ar -tf ARCHIVE          # List members of ARCHIVE",
-  "     .ar -xvf ARCHIVE         # Verbosely extract files from ARCHIVE",
-  "   See also:",
-  "      http://sqlite.org/cli.html#sqlite_archive_support",
-#endif
-#ifndef SQLITE_OMIT_AUTHORIZATION
-  ".auth ON|OFF             Show authorizer callbacks",
-#endif
-  ".backup ?DB? FILE        Backup DB (default \"main\") to FILE",
-  "       --append            Use the appendvfs",
-  "       --async             Write to FILE without journal and fsync()",
-  ".bail on|off             Stop after hitting an error.  Default OFF",
-  ".binary on|off           Turn binary output on or off.  Default OFF",
-  ".cd DIRECTORY            Change the working directory to DIRECTORY",
-  ".changes on|off          Show number of rows changed by SQL",
-  ".check GLOB              Fail if output since .testcase does not match",
-  ".clone NEWDB             Clone data into NEWDB from the existing database",
-  ".databases               List names and files of attached databases",
-  ".dbconfig ?op? ?val?     List or change sqlite3_db_config() options",
-  ".dbinfo ?DB?             Show status information about the database",
-  ".dump ?OBJECTS?          Render database content as SQL",
-  "   Options:",
-  "     --data-only            Output only INSERT statements",
-  "     --newlines             Allow unescaped newline characters in output",
-  "     --nosys                Omit system tables (ex: \"sqlite_stat1\")",
-  "     --preserve-rowids      Include ROWID values in the output",
-  "   OBJECTS is a LIKE pattern for tables, indexes, triggers or views to dump",
-  "   Additional LIKE patterns can be given in subsequent arguments",
-  ".echo on|off             Turn command echo on or off",
-  ".eqp on|off|full|...     Enable or disable automatic EXPLAIN QUERY PLAN",
-  "   Other Modes:",
-#ifdef SQLITE_DEBUG
-  "      test                  Show raw EXPLAIN QUERY PLAN output",
-  "      trace                 Like \"full\" but enable \"PRAGMA vdbe_trace\"",
-#endif
-  "      trigger               Like \"full\" but also show trigger bytecode",
-  ".excel                   Display the output of next command in spreadsheet",
-  "   --bom                   Put a UTF8 byte-order mark on intermediate file",
-  ".exit ?CODE?             Exit this program with return-code CODE",
-  ".expert                  EXPERIMENTAL. Suggest indexes for queries",
-  ".explain ?on|off|auto?   Change the EXPLAIN formatting mode.  Default: auto",
-  ".filectrl CMD ...        Run various sqlite3_file_control() operations",
-  "   --schema SCHEMA         Use SCHEMA instead of \"main\"",
-  "   --help                  Show CMD details",
-  ".fullschema ?--indent?   Show schema and the content of sqlite_stat tables",
-  ".headers on|off          Turn display of headers on or off",
-  ".help ?-all? ?PATTERN?   Show help text for PATTERN",
-  ".import FILE TABLE       Import data from FILE into TABLE",
-  "   Options:",
-  "     --ascii               Use \\037 and \\036 as column and row separators",
-  "     --csv                 Use , and \\n as column and row separators",
-  "     --skip N              Skip the first N rows of input",
-  "     -v                    \"Verbose\" - increase auxiliary output",
-  "   Notes:",
-  "     *  If TABLE does not exist, it is created.  The first row of input",
-  "        determines the column names.",
-  "     *  If neither --csv or --ascii are used, the input mode is derived",
-  "        from the \".mode\" output mode",
-  "     *  If FILE begins with \"|\" then it is a command that generates the",
-  "        input text.",
-#ifndef SQLITE_OMIT_TEST_CONTROL
-  ".imposter INDEX TABLE    Create imposter table TABLE on index INDEX",
-#endif
-  ".indexes ?TABLE?         Show names of indexes",
-  "                           If TABLE is specified, only show indexes for",
-  "                           tables matching TABLE using the LIKE operator.",
-#ifdef SQLITE_ENABLE_IOTRACE
-  ".iotrace FILE            Enable I/O diagnostic logging to FILE",
-#endif
-  ".limit ?LIMIT? ?VAL?     Display or change the value of an SQLITE_LIMIT",
-  ".lint OPTIONS            Report potential schema issues.",
-  "     Options:",
-  "        fkey-indexes     Find missing foreign key indexes",
-#ifndef SQLITE_OMIT_LOAD_EXTENSION
-  ".load FILE ?ENTRY?       Load an extension library",
-#endif
-  ".log FILE|off            Turn logging on or off.  FILE can be stderr/stdout",
-  ".mode MODE ?TABLE?       Set output mode",
-  "   MODE is one of:",
-  "     ascii     Columns/rows delimited by 0x1F and 0x1E",
-  "     box       Tables using unicode box-drawing characters",
-  "     csv       Comma-separated values",
-  "     column    Output in columns.  (See .width)",
-  "     html      HTML <table> code",
-  "     insert    SQL insert statements for TABLE",
-  "     json      Results in a JSON array",
-  "     line      One value per line",
-  "     list      Values delimited by \"|\"",
-  "     markdown  Markdown table format",
-  "     quote     Escape answers as for SQL",
-  "     table     ASCII-art table",
-  "     tabs      Tab-separated values",
-  "     tcl       TCL list elements",
-  ".nullvalue STRING        Use STRING in place of NULL values",
-  ".once ?OPTIONS? ?FILE?   Output for the next SQL command only to FILE",
-  "     If FILE begins with '|' then open as a pipe",
-  "       --bom  Put a UTF8 byte-order mark at the beginning",
-  "       -e     Send output to the system text editor",
-  "       -x     Send output as CSV to a spreadsheet (same as \".excel\")",
-#ifdef SQLITE_DEBUG
-  ".oom ?--repeat M? ?N?    Simulate an OOM error on the N-th allocation",
-#endif 
-  ".open ?OPTIONS? ?FILE?   Close existing database and reopen FILE",
-  "     Options:",
-  "        --append        Use appendvfs to append database to the end of FILE",
-#ifndef SQLITE_OMIT_DESERIALIZE
-  "        --deserialize   Load into memory using sqlite3_deserialize()",
-  "        --hexdb         Load the output of \"dbtotxt\" as an in-memory db",
-  "        --maxsize N     Maximum size for --hexdb or --deserialized database",
-#endif
-  "        --new           Initialize FILE to an empty database",
-  "        --nofollow      Do not follow symbolic links",
-  "        --readonly      Open FILE readonly",
-  "        --zip           FILE is a ZIP archive",
-  ".output ?FILE?           Send output to FILE or stdout if FILE is omitted",
-  "   If FILE begins with '|' then open it as a pipe.",
-  "   Options:",
-  "     --bom                 Prefix output with a UTF8 byte-order mark",
-  "     -e                    Send output to the system text editor",
-  "     -x                    Send output as CSV to a spreadsheet",
-  ".parameter CMD ...       Manage SQL parameter bindings",
-  "   clear                   Erase all bindings",
-  "   init                    Initialize the TEMP table that holds bindings",
-  "   list                    List the current parameter bindings",
-  "   set PARAMETER VALUE     Given SQL parameter PARAMETER a value of VALUE",
-  "                           PARAMETER should start with one of: $ : @ ?",
-  "   unset PARAMETER         Remove PARAMETER from the binding table",
-  ".print STRING...         Print literal STRING",
-#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
-  ".progress N              Invoke progress handler after every N opcodes",
-  "   --limit N                 Interrupt after N progress callbacks",
-  "   --once                    Do no more than one progress interrupt",
-  "   --quiet|-q                No output except at interrupts",
-  "   --reset                   Reset the count for each input and interrupt",
-#endif
-  ".prompt MAIN CONTINUE    Replace the standard prompts",
-  ".quit                    Exit this program",
-  ".read FILE               Read input from FILE",
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
-  ".recover                 Recover as much data as possible from corrupt db.",
-  "   --freelist-corrupt       Assume the freelist is corrupt",
-  "   --recovery-db NAME       Store recovery metadata in database file NAME",
-  "   --lost-and-found TABLE   Alternative name for the lost-and-found table",
-  "   --no-rowids              Do not attempt to recover rowid values",
-  "                            that are not also INTEGER PRIMARY KEYs",
-#endif
-  ".restore ?DB? FILE       Restore content of DB (default \"main\") from FILE",
-  ".save FILE               Write in-memory database into FILE",
-  ".scanstats on|off        Turn sqlite3_stmt_scanstatus() metrics on or off",
-  ".schema ?PATTERN?        Show the CREATE statements matching PATTERN",
-  "   Options:",
-  "      --indent             Try to pretty-print the schema",
-  "      --nosys              Omit objects whose names start with \"sqlite_\"",
-  ".selftest ?OPTIONS?      Run tests defined in the SELFTEST table",
-  "    Options:",
-  "       --init               Create a new SELFTEST table",
-  "       -v                   Verbose output",
-  ".separator COL ?ROW?     Change the column and row separators",
-#if defined(SQLITE_ENABLE_SESSION)
-  ".session ?NAME? CMD ...  Create or control sessions",
-  "   Subcommands:",
-  "     attach TABLE             Attach TABLE",
-  "     changeset FILE           Write a changeset into FILE",
-  "     close                    Close one session",
-  "     enable ?BOOLEAN?         Set or query the enable bit",
-  "     filter GLOB...           Reject tables matching GLOBs",
-  "     indirect ?BOOLEAN?       Mark or query the indirect status",
-  "     isempty                  Query whether the session is empty",
-  "     list                     List currently open session names",
-  "     open DB NAME             Open a new session on DB",
-  "     patchset FILE            Write a patchset into FILE",
-  "   If ?NAME? is omitted, the first defined session is used.",
-#endif
-  ".sha3sum ...             Compute a SHA3 hash of database content",
-  "    Options:",
-  "      --schema              Also hash the sqlite_schema table",
-  "      --sha3-224            Use the sha3-224 algorithm",
-  "      --sha3-256            Use the sha3-256 algorithm (default)",
-  "      --sha3-384            Use the sha3-384 algorithm",
-  "      --sha3-512            Use the sha3-512 algorithm",
-  "    Any other argument is a LIKE pattern for tables to hash",
-#ifndef SQLITE_NOHAVE_SYSTEM
-  ".shell CMD ARGS...       Run CMD ARGS... in a system shell",
-#endif
-  ".show                    Show the current values for various settings",
-  ".stats ?ARG?             Show stats or turn stats on or off",
-  "   off                      Turn off automatic stat display",
-  "   on                       Turn on automatic stat display",
-  "   stmt                     Show statement stats",
-  "   vmstep                   Show the virtual machine step count only",
-#ifndef SQLITE_NOHAVE_SYSTEM
-  ".system CMD ARGS...      Run CMD ARGS... in a system shell",
-#endif
-  ".tables ?TABLE?          List names of tables matching LIKE pattern TABLE",
-  ".testcase NAME           Begin redirecting output to 'testcase-out.txt'",
-  ".testctrl CMD ...        Run various sqlite3_test_control() operations",
-  "                           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",
-#ifndef SQLITE_OMIT_TRACE
-  ".trace ?OPTIONS?         Output each SQL statement as it is run",
-  "    FILE                    Send output to FILE",
-  "    stdout                  Send output to stdout",
-  "    stderr                  Send output to stderr",
-  "    off                     Disable tracing",
-  "    --expanded              Expand query parameters",
-#ifdef SQLITE_ENABLE_NORMALIZE
-  "    --normalized            Normal the SQL statements",
-#endif
-  "    --plain                 Show SQL as it is input",
-  "    --stmt                  Trace statement execution (SQLITE_TRACE_STMT)",
-  "    --profile               Profile statements (SQLITE_TRACE_PROFILE)",
-  "    --row                   Trace each row (SQLITE_TRACE_ROW)",
-  "    --close                 Trace connection close (SQLITE_TRACE_CLOSE)",
-#endif /* SQLITE_OMIT_TRACE */
-#ifdef SQLITE_DEBUG
-  ".unmodule NAME ...       Unregister virtual table modules",
-  "    --allexcept             Unregister everything except those named",
-#endif
-  ".vfsinfo ?AUX?           Information about the top-level VFS",
-  ".vfslist                 List all available VFSes",
-  ".vfsname ?AUX?           Print the name of the VFS stack",
-  ".width NUM1 NUM2 ...     Set minimum column widths for columnar output",
-  "     Negative values right-justify",
-};
-
 /*
 ** Output help text.
 **
@@ -4111,10 +3860,10 @@ static const char *(azHelp[]) = {
 ** Return the number of matches.
 */
 static int showHelp(FILE *out, const char *zPattern){
-  int i = 0;
-  int j = 0;
   int n = 0;
   char *zPat;
+  const char **pzHxtra;
+  const char **pzH;
   if( zPattern==0
    || zPattern[0]=='0'
    || strcmp(zPattern,"-a")==0
@@ -4123,19 +3872,19 @@ static int showHelp(FILE *out, const char *zPattern){
   ){
     /* Show all commands, but only one line per command */
     if( zPattern==0 ) zPattern = "";
-    for(i=0; i<ArraySize(azHelp); i++){
-      if( azHelp[i][0]=='.' || zPattern[0] ){
-        utf8_printf(out, "%s\n", azHelp[i]);
-        n++;
+    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(i=0; i<ArraySize(azHelp); i++){
-      if( sqlite3_strglob(zPat, azHelp[i])==0 ){
-        utf8_printf(out, "%s\n", azHelp[i]);
-        j = i+1;
+    for(pzH = azHelp; *pzH != 0; ++pzH){
+      if( sqlite3_strglob(zPat, *pzH)==0 ){
+        utf8_printf(out, "%s\n", *pzH);
+        pzHxtra = pzH + 1;
         n++;
       }
     }
@@ -4143,10 +3892,9 @@ static int showHelp(FILE *out, const char *zPattern){
     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 offset j */
-        while( j<ArraySize(azHelp)-1 && azHelp[j][0]!='.' ){
-          utf8_printf(out, "%s\n", azHelp[j]);
-          j++;
+        ** details of that command, which should begin at *pzHxtra */
+        while( *pzHxtra !=0 && *pzHxtra[0]!='.' ){
+          utf8_printf(out, "%s\n", *pzHxtra++);
         }
       }
       return n;
@@ -4154,16 +3902,17 @@ static int showHelp(FILE *out, const char *zPattern){
     /* Look for commands that contain zPattern anywhere.  Show the complete
     ** text of all commands that match. */
     zPat = sqlite3_mprintf("%%%s%%", zPattern);
-    for(i=0; i<ArraySize(azHelp); i++){
-      if( azHelp[i][0]=='.' ) j = i;
-      if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
-        utf8_printf(out, "%s\n", azHelp[j]);
-        while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]!='.' ){
-          j++;
-          utf8_printf(out, "%s\n", azHelp[j]);
+    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++);
         }
-        i = j;
+        pzH = pzHxtra;
         n++;
+      } else {
+        ++pzH;
       }
     }
     sqlite3_free(zPat);
@@ -7381,7 +7130,61 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
 }
 #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */
 
-
+/*****************
+ * The .help command
+ */
+COLLECT_HELP_TEXT[
+  ".help ?-all? ?PATTERN?   Show help text for PATTERN",
+];
+COLLECT_DISPATCH( help )[
+  { "help", commandHelp, 3, 1, 0 },
+];
+static int commandHelp(char *azArg[], int nArg, ShellState *p)
+{
+  if( nArg>=2 ){
+    if( showHelp(p->out, azArg[1])==0 ){
+      utf8_printf(p->out, "Nothing matches '%s'\n", azArg[1]);
+    }
+  }else{
+    showHelp(p->out, 0);
+  }
+  /* Help pleas never fail! */
+  return 0;
+}
+
+/* Define and populate command dispatch table. */
+static struct DispatchEntry {
+  const char * cmdName;
+  int (*cmdDoer)(char *azArg[], int nArg, ShellState *);
+  unsigned char minLen, minArgs, maxArgs;
+} command_table[] = {
+  EMIT_DISPATCH();
+  { 0, 0, 0, -1, -1 }
+};
+
+/*****************
+ * Command dispatcher
+ */
+#define NO_SUCH_COMMAND -0x7fff
+#define INVALID_ARGS -0x7ffe
+
+int dispatchCommand(char *azArg[], int nArg, ShellState *pSS)
+{
+  const char *cmdName = azArg[0];
+  /* A temporary, simple-minded scan */
+  struct DispatchEntry *pde = command_table;
+  while (pde->cmdName != 0){
+    if (0==strncmp(cmdName, pde->cmdName, pde->minLen)){
+      if((pde->minArgs > 0 && pde->minArgs > nArg)||(pde->maxArgs > 0 && pde->maxArgs < nArg)){
+        return INVALID_ARGS;
+      }
+      return (pde->cmdDoer)(azArg, nArg, pSS);
+    } else { ++pde; }
+  }
+  return NO_SUCH_COMMAND;
+}
+
+
 /*
 ** If an input line begins with "." then invoke this routine to
 ** process that line.
@@ -8095,17 +7898,6 @@ static int do_meta_command(char *zLine, ShellState *p){
     }
   }else
 
-  if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
-    if( nArg>=2 ){
-      n = showHelp(p->out, azArg[1]);
-      if( n==0 ){
-        utf8_printf(p->out, "Nothing matches '%s'\n", azArg[1]);
-      }
-    }else{
-      showHelp(p->out, 0);
-    }
-  }else
-
   if( c=='i' && strncmp(azArg[0], "import", n)==0 ){
     char *zTable = 0;           /* Insert data into this table */
     char *zFile = 0;            /* Name of file to extra content from */
@@ -10403,11 +10195,14 @@ static int do_meta_command(char *zLine, ShellState *p){
       p->colWidth[j-1] = (int)integerValue(azArg[j]);
     }
   }else
-
+    /* The meta-command is not among the specially handled ones. Dispatch it. */
   {
-    utf8_printf(stderr, "Error: unknown command or invalid arguments: "
-      " \"%s\". Enter \".help\" for help\n", azArg[0]);
-    rc = 1;
+    int dispatchResult = dispatchCommand(azArg, nArg, p);
+    if( NO_SUCH_COMMAND==dispatchResult || INVALID_ARGS==dispatchResult ){
+      utf8_printf(stderr, "Error: unknown command or invalid arguments: "
+                  " \"%s\". Enter \".help\" for help\n", azArg[0]);
+      rc = 1;
+    }
   }
 
 meta_command_exit:
@@ -10736,6 +10531,308 @@ static void process_sqliterc(
   sqlite3_free(zBuf);
 }
 
+COLLECT_HELP_TEXT[
+#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE)
+  ".archive ...             Manage SQL archives",
+  "   Each command must have exactly one of the following options:",
+  "     -c, --create               Create a new archive",
+  "     -u, --update               Add or update files with changed mtime",
+  "     -i, --insert               Like -u but always add even if unchanged",
+  "     -t, --list                 List contents of archive",
+  "     -x, --extract              Extract files from archive",
+  "   Optional arguments:",
+  "     -v, --verbose              Print each filename as it is processed",
+  "     -f FILE, --file FILE       Use archive FILE (default is current db)",
+  "     -a FILE, --append FILE     Open FILE using the apndvfs VFS",
+  "     -C DIR, --directory DIR    Read/extract files from directory DIR",
+  "     -n, --dryrun               Show the SQL that would have occurred",
+  "   Examples:",
+  "     .ar -cf ARCHIVE foo bar  # Create ARCHIVE from files foo and bar",
+  "     .ar -tf ARCHIVE          # List members of ARCHIVE",
+  "     .ar -xvf ARCHIVE         # Verbosely extract files from ARCHIVE",
+  "   See also:",
+  "      http://sqlite.org/cli.html#sqlite_archive_support",
+#endif
+];
+COLLECT_HELP_TEXT[
+#ifndef SQLITE_OMIT_AUTHORIZATION
+  ".auth ON|OFF             Show authorizer callbacks",
+#endif
+];
+COLLECT_HELP_TEXT[
+  ".backup ?DB? FILE        Backup DB (default \"main\") to FILE",
+  "       --append            Use the appendvfs",
+  "       --async             Write to FILE without journal and fsync()",
+  ".bail on|off             Stop after hitting an error.  Default OFF",
+  ".binary on|off           Turn binary output on or off.  Default OFF",
+  ".cd DIRECTORY            Change the working directory to DIRECTORY",
+  ".changes on|off          Show number of rows changed by SQL",
+  ".check GLOB              Fail if output since .testcase does not match",
+  ".clone NEWDB             Clone data into NEWDB from the existing database",
+  ".databases               List names and files of attached databases",
+  ".dbconfig ?op? ?val?     List or change sqlite3_db_config() options",
+  ".dbinfo ?DB?             Show status information about the database",
+  ".dump ?OBJECTS?          Render database content as SQL",
+  "   Options:",
+  "     --data-only            Output only INSERT statements",
+  "     --newlines             Allow unescaped newline characters in output",
+  "     --nosys                Omit system tables (ex: \"sqlite_stat1\")",
+  "     --preserve-rowids      Include ROWID values in the output",
+  "   OBJECTS is a LIKE pattern for tables, indexes, triggers or views to dump",
+  "   Additional LIKE patterns can be given in subsequent arguments",
+  ".echo on|off             Turn command echo on or off",
+  ".eqp on|off|full|...     Enable or disable automatic EXPLAIN QUERY PLAN",
+  "   Other Modes:",
+#ifdef SQLITE_DEBUG
+  "      test                  Show raw EXPLAIN QUERY PLAN output",
+  "      trace                 Like \"full\" but enable \"PRAGMA vdbe_trace\"",
+#endif
+  "      trigger               Like \"full\" but also show trigger bytecode",
+  ".excel                   Display the output of next command in spreadsheet",
+  "   --bom                   Put a UTF8 byte-order mark on intermediate file",
+  ".exit ?CODE?             Exit this program with return-code CODE",
+  ".expert                  EXPERIMENTAL. Suggest indexes for queries",
+  ".explain ?on|off|auto?   Change the EXPLAIN formatting mode.  Default: auto",
+  ".filectrl CMD ...        Run various sqlite3_file_control() operations",
+  "   --schema SCHEMA         Use SCHEMA instead of \"main\"",
+  "   --help                  Show CMD details",
+  ".fullschema ?--indent?   Show schema and the content of sqlite_stat tables",
+  ".headers on|off          Turn display of headers on or off",
+  ".import FILE TABLE       Import data from FILE into TABLE",
+  "   Options:",
+  "     --ascii               Use \\037 and \\036 as column and row separators",
+  "     --csv                 Use , and \\n as column and row separators",
+  "     --skip N              Skip the first N rows of input",
+  "     -v                    \"Verbose\" - increase auxiliary output",
+  "   Notes:",
+  "     *  If TABLE does not exist, it is created.  The first row of input",
+  "        determines the column names.",
+  "     *  If neither --csv or --ascii are used, the input mode is derived",
+  "        from the \".mode\" output mode",
+  "     *  If FILE begins with \"|\" then it is a command that generates the",
+  "        input text.",
+];
+COLLECT_HELP_TEXT[
+#ifndef SQLITE_OMIT_TEST_CONTROL
+  ".imposter INDEX TABLE    Create imposter table TABLE on index INDEX",
+#endif
+];
+COLLECT_HELP_TEXT[
+  ".indexes ?TABLE?         Show names of indexes",
+  "                           If TABLE is specified, only show indexes for",
+  "                           tables matching TABLE using the LIKE operator.",
+];
+COLLECT_HELP_TEXT[
+#ifdef SQLITE_ENABLE_IOTRACE
+  ".iotrace FILE            Enable I/O diagnostic logging to FILE",
+#endif
+];
+COLLECT_HELP_TEXT[
+  ".limit ?LIMIT? ?VAL?     Display or change the value of an SQLITE_LIMIT",
+  ".lint OPTIONS            Report potential schema issues.",
+  "     Options:",
+  "        fkey-indexes     Find missing foreign key indexes",
+];
+COLLECT_HELP_TEXT[
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+  ".load FILE ?ENTRY?       Load an extension library",
+#endif
+];
+COLLECT_HELP_TEXT[
+  ".log FILE|off            Turn logging on or off.  FILE can be stderr/stdout",
+  ".mode MODE ?TABLE?       Set output mode",
+  "   MODE is one of:",
+  "     ascii     Columns/rows delimited by 0x1F and 0x1E",
+  "     box       Tables using unicode box-drawing characters",
+  "     csv       Comma-separated values",
+  "     column    Output in columns.  (See .width)",
+  "     html      HTML <table> code",
+  "     insert    SQL insert statements for TABLE",
+  "     json      Results in a JSON array",
+  "     line      One value per line",
+  "     list      Values delimited by \"|\"",
+  "     markdown  Markdown table format",
+  "     quote     Escape answers as for SQL",
+  "     table     ASCII-art table",
+  "     tabs      Tab-separated values",
+  "     tcl       TCL list elements",
+  ".nullvalue STRING        Use STRING in place of NULL values",
+  ".once ?OPTIONS? ?FILE?   Output for the next SQL command only to FILE",
+  "     If FILE begins with '|' then open as a pipe",
+  "       --bom  Put a UTF8 byte-order mark at the beginning",
+  "       -e     Send output to the system text editor",
+  "       -x     Send output as CSV to a spreadsheet (same as \".excel\")",
+];
+COLLECT_HELP_TEXT[
+#ifdef SQLITE_DEBUG
+  ".oom ?--repeat M? ?N?    Simulate an OOM error on the N-th allocation",
+#endif 
+];
+COLLECT_HELP_TEXT[
+  ".open ?OPTIONS? ?FILE?   Close existing database and reopen FILE",
+  "     Options:",
+  "        --append        Use appendvfs to append database to the end of FILE",
+#ifndef SQLITE_OMIT_DESERIALIZE
+  "        --deserialize   Load into memory using sqlite3_deserialize()",
+  "        --hexdb         Load the output of \"dbtotxt\" as an in-memory db",
+  "        --maxsize N     Maximum size for --hexdb or --deserialized database",
+#endif
+  "        --new           Initialize FILE to an empty database",
+  "        --nofollow      Do not follow symbolic links",
+  "        --readonly      Open FILE readonly",
+  "        --zip           FILE is a ZIP archive",
+  ".output ?FILE?           Send output to FILE or stdout if FILE is omitted",
+  "   If FILE begins with '|' then open it as a pipe.",
+  "   Options:",
+  "     --bom                 Prefix output with a UTF8 byte-order mark",
+  "     -e                    Send output to the system text editor",
+  "     -x                    Send output as CSV to a spreadsheet",
+  ".parameter CMD ...       Manage SQL parameter bindings",
+  "   clear                   Erase all bindings",
+  "   init                    Initialize the TEMP table that holds bindings",
+  "   list                    List the current parameter bindings",
+  "   set PARAMETER VALUE     Given SQL parameter PARAMETER a value of VALUE",
+  "                           PARAMETER should start with one of: $ : @ ?",
+  "   unset PARAMETER         Remove PARAMETER from the binding table",
+  ".print STRING...         Print literal STRING",
+];
+COLLECT_HELP_TEXT[
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+  ".progress N              Invoke progress handler after every N opcodes",
+  "   --limit N                 Interrupt after N progress callbacks",
+  "   --once                    Do no more than one progress interrupt",
+  "   --quiet|-q                No output except at interrupts",
+  "   --reset                   Reset the count for each input and interrupt",
+#endif
+];
+COLLECT_HELP_TEXT[
+  ".prompt MAIN CONTINUE    Replace the standard prompts",
+  ".quit                    Exit this program",
+  ".read FILE               Read input from FILE",
+];
+COLLECT_HELP_TEXT[
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
+  ".recover                 Recover as much data as possible from corrupt db.",
+  "   --freelist-corrupt       Assume the freelist is corrupt",
+  "   --recovery-db NAME       Store recovery metadata in database file NAME",
+  "   --lost-and-found TABLE   Alternative name for the lost-and-found table",
+  "   --no-rowids              Do not attempt to recover rowid values",
+  "                            that are not also INTEGER PRIMARY KEYs",
+#endif
+];
+COLLECT_HELP_TEXT[
+  ".restore ?DB? FILE       Restore content of DB (default \"main\") from FILE",
+  ".save FILE               Write in-memory database into FILE",
+  ".scanstats on|off        Turn sqlite3_stmt_scanstatus() metrics on or off",
+  ".schema ?PATTERN?        Show the CREATE statements matching PATTERN",
+  "   Options:",
+  "      --indent             Try to pretty-print the schema",
+  "      --nosys              Omit objects whose names start with \"sqlite_\"",
+  ".selftest ?OPTIONS?      Run tests defined in the SELFTEST table",
+  "    Options:",
+  "       --init               Create a new SELFTEST table",
+  "       -v                   Verbose output",
+  ".separator COL ?ROW?     Change the column and row separators",
+];
+COLLECT_HELP_TEXT[
+#if defined(SQLITE_ENABLE_SESSION)
+  ".session ?NAME? CMD ...  Create or control sessions",
+  "   Subcommands:",
+  "     attach TABLE             Attach TABLE",
+  "     changeset FILE           Write a changeset into FILE",
+  "     close                    Close one session",
+  "     enable ?BOOLEAN?         Set or query the enable bit",
+  "     filter GLOB...           Reject tables matching GLOBs",
+  "     indirect ?BOOLEAN?       Mark or query the indirect status",
+  "     isempty                  Query whether the session is empty",
+  "     list                     List currently open session names",
+  "     open DB NAME             Open a new session on DB",
+  "     patchset FILE            Write a patchset into FILE",
+  "   If ?NAME? is omitted, the first defined session is used.",
+#endif
+];
+COLLECT_HELP_TEXT[
+  ".sha3sum ...             Compute a SHA3 hash of database content",
+  "    Options:",
+  "      --schema              Also hash the sqlite_schema table",
+  "      --sha3-224            Use the sha3-224 algorithm",
+  "      --sha3-256            Use the sha3-256 algorithm (default)",
+  "      --sha3-384            Use the sha3-384 algorithm",
+  "      --sha3-512            Use the sha3-512 algorithm",
+  "    Any other argument is a LIKE pattern for tables to hash",
+];
+COLLECT_HELP_TEXT[
+#ifndef SQLITE_NOHAVE_SYSTEM
+  ".shell CMD ARGS...       Run CMD ARGS... in a system shell",
+#endif
+];
+COLLECT_HELP_TEXT[
+  ".show                    Show the current values for various settings",
+  ".stats ?ARG?             Show stats or turn stats on or off",
+  "   off                      Turn off automatic stat display",
+  "   on                       Turn on automatic stat display",
+  "   stmt                     Show statement stats",
+  "   vmstep                   Show the virtual machine step count only",
+];
+COLLECT_HELP_TEXT[
+#ifndef SQLITE_NOHAVE_SYSTEM
+  ".system CMD ARGS...      Run CMD ARGS... in a system shell",
+#endif
+];
+COLLECT_HELP_TEXT[
+  ".tables ?TABLE?          List names of tables matching LIKE pattern TABLE",
+  ".testcase NAME           Begin redirecting output to 'testcase-out.txt'",
+  ".testctrl CMD ...        Run various sqlite3_test_control() operations",
+  "                           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",
+];
+COLLECT_HELP_TEXT[
+#ifndef SQLITE_OMIT_TRACE
+  ".trace ?OPTIONS?         Output each SQL statement as it is run",
+  "    FILE                    Send output to FILE",
+  "    stdout                  Send output to stdout",
+  "    stderr                  Send output to stderr",
+  "    off                     Disable tracing",
+  "    --expanded              Expand query parameters",
+#ifdef SQLITE_ENABLE_NORMALIZE
+  "    --normalized            Normal the SQL statements",
+#endif
+  "    --plain                 Show SQL as it is input",
+  "    --stmt                  Trace statement execution (SQLITE_TRACE_STMT)",
+  "    --profile               Profile statements (SQLITE_TRACE_PROFILE)",
+  "    --row                   Trace each row (SQLITE_TRACE_ROW)",
+  "    --close                 Trace connection close (SQLITE_TRACE_CLOSE)",
+#endif /* SQLITE_OMIT_TRACE */
+];
+COLLECT_HELP_TEXT[
+#ifdef SQLITE_DEBUG
+  ".unmodule NAME ...       Unregister virtual table modules",
+  "    --allexcept             Unregister everything except those named",
+#endif
+];
+COLLECT_HELP_TEXT[
+  ".vfsinfo ?AUX?           Information about the top-level VFS",
+  ".vfslist                 List all available VFSes",
+  ".vfsname ?AUX?           Print the name of the VFS stack",
+  ".width NUM1 NUM2 ...     Set minimum column widths for columnar output",
+  "     Negative values right-justify",
+];
+
+/*
+** Text of help messages.
+**
+** The help text for each individual command begins with a line that starts
+** with ".".  Subsequent lines are supplimental information.
+**
+** There must be two or more spaces between the end of the command and the
+** start of the description of what that command does.
+*/
+static const char *(azHelp[]) = {
+  EMIT_HELP_TEXT();
+  0
+};
+
 /*
 ** Show available command line options
 */
index 46c2a528fef7c9da552d8d75b25513ccbcb9f560..71ab09e3025a87b7c4cb8632110d880f868eee66 100644 (file)
@@ -11,7 +11,7 @@
 #
 set topdir [file dir [file dir [file normal $argv0]]]
 set out stdout
-fconfigure stdout -translation binary
+fconfigure stdout -translation {auto lf}
 puts $out {/* DO NOT EDIT!
 ** This file is automatically generated by the script in the canonical
 ** SQLite source tree at tool/mkshellc.tcl.  That script combines source
@@ -30,20 +30,133 @@ puts $out {/* DO NOT EDIT!
 ** edit the src/shell.c.in" and/or some of the other files that are included
 ** by "src/shell.c.in", then rerun the tool/mkshellc.tcl script.
 */}
+
 set in [open $topdir/src/shell.c.in rb]
+
 proc omit_redundant_typedefs {line} {
+}
+
+set ::cmd_help [dict create]
+set ::cmd_dispatch [dict create]
+set ::iShuffleErrors 0
+
+# Convert list of help text lines into a dict.
+# Keys are the command names. Values are the help for the
+# commands as a list of lines, with .* logically first.
+# Any #if... #endif structures are maintained and do not
+# interact with "logically first" .* lines, except that
+# only one such line is seen within such a conditional.
+# (The effect of this is to defeat sorting by command if
+# help for multiple commands' is within one conditional.)
+proc chunkify_help {htin} {
+  set rv [dict create]
+  set if_depth 0
+  set cmd_seen ""
+  set chunk {}
+  foreach htx $htin {
+    if {[regexp {^\s*\"\.\w} $htx] && $cmd_seen ne "" && $if_depth == 0} {
+      # Flush accumulated chunk.
+      dict set rv $cmd_seen $chunk
+      set cmd_seen ""
+      set chunk {}
+    }
+    lappend chunk $htx
+    if {[regexp {^\s*#if} $htx]} {
+      incr if_depth
+    } elseif {[regexp {^\s*#endif} $htx]} {
+      incr if_depth -1
+    } else {
+      if {[regexp {^\s*\"\.(\w+)} $htx all cmd] && $cmd_seen eq ""} {
+        set cmd_seen $cmd
+      }
+    }
+  }
+  if {$if_depth != 0} {
+    puts stderr "Help chunk bad #conditional:"
+    puts stderr [join $htin "\n"]
+    puts stderr "Swallowed [join $chunk \n]"
+    incr ::iShuffleErrors
+  } else {
+    if {$cmd_seen ne "" && [llength $chunk] > 0} {
+      # Flush accumulated chunk.
+      dict set rv $cmd_seen $chunk
+    } elseif {$cmd_seen ne "" || [llength $chunk] > 0} {
+      puts stderr "Orphaned help: '$cmd_seen' [join $chunk \n]"
+      incr ::iShuffleErrors
+    }
+  }
+  return $rv
+}
+
+# Perform any input collection or deferred output emits.
+# This function may consume additional lines via hFile.
+# Return number of lines absorbed. A 0 return means the
+# input line lx had no meaning to the shuffle processing,
+# in which case it is emitted as-is.
+proc do_shuffle {hFile lx ostrm} {
+  set iAte 0
+  if {[regexp {^COLLECT_HELP_TEXT\[} $lx]} {
+    incr iAte
+    set help_frag {}
+    set lx [gets $hFile]
+    while {![eof $hFile] && ![regexp {^\s*\];} $lx]} {
+      lappend help_frag $lx
+      set lx [gets $hFile]
+      incr iAte
+    }
+    incr iAte
+    set ::cmd_help [dict merge $::cmd_help [chunkify_help $help_frag]]
+  } elseif {[regexp {^\s*EMIT_HELP_TEXT\(\)} $lx]} {
+    incr iAte
+    foreach htc [lsort [dict keys $::cmd_help]] {
+      puts $ostrm [join [dict get $::cmd_help $htc] "\n"]
+    }
+  } elseif {[regexp {^COLLECT_DISPATCH\(\s*(\w+)\s*\)\[} $lx ma cmd]} {
+    incr iAte
+    set disp_frag {}
+    set lx [gets $hFile]
+    while {![eof $hFile] && ![regexp {^\s*\];} $lx]} {
+      lappend disp_frag $lx
+      set lx [gets $hFile]
+      incr iAte
+    }
+    incr iAte
+    dict set ::cmd_dispatch $cmd $disp_frag
+  } elseif {[regexp {^\s*EMIT_DISPATCH\(\)} $lx]} {
+    incr iAte
+    foreach cmd [lsort [dict keys $::cmd_dispatch]] {
+      puts $ostrm [join [dict get $::cmd_dispatch $cmd] "\n"]
+    }
+  } else {
+    puts $ostrm $lx
+  }
+  return $iAte
+}
+
+# Filter redundant typedefs and certain includes and qualifiers.
+proc transform_line {line nesting} {
   global typedef_seen
   if {[regexp {^typedef .*;} $line]} {
     if {[info exists typedef_seen($line)]} {
       return "/* $line */"
     }
     set typedef_seen($line) 1
+    return $line
+  } elseif {$nesting == 0} {
+    return $line
+  }
+  if {[regexp {^#include "sqlite} $line]} {
+    return "/* $line */"
   }
-  return $line
+  if {[regexp {^# *include "test_windirent.h"} $line]} {
+    return "/* $line */"
+  }
+  return [string map [list __declspec(dllexport) {}] $line]
 }
+
 set iLine 0
 while {1} {
-  set lx [omit_redundant_typedefs [gets $in]]
+  set lx [transform_line [gets $in] 0]
   if {[eof $in]} break;
   incr iLine
   if {[regexp {^INCLUDE } $lx]} {
@@ -52,22 +165,20 @@ while {1} {
 #   puts $out "#line 1 \"$cfile\""
     set in2 [open $topdir/src/$cfile rb]
     while {![eof $in2]} {
-      set lx [omit_redundant_typedefs [gets $in2]]
-      if {[regexp {^#include "sqlite} $lx]} {
-        set lx "/* $lx */"
-      }
-      if {[regexp {^# *include "test_windirent.h"} $lx]} {
-        set lx "/* $lx */"
-      }
-      set lx [string map [list __declspec(dllexport) {}] $lx]
-      puts $out $lx
+      set lx [transform_line [gets $in2] 1]
+      do_shuffle $in2 $lx $out
     }
     close $in2
     puts $out "/************************* End $cfile ********************/"
 #   puts $out "#line [expr $iLine+1] \"shell.c.in\""
     continue
   }
-  puts $out $lx
+  set iAte [do_shuffle $in $lx $out]
+  if {$iAte > 0} {
+    incr iLine [expr {$iAte - 1}]
+  }
+
 }
 close $in
 close $out
+exit $::iShuffleErrors