SQLite

Check-in Differences
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Difference From fa492ff57ca9d89a To d2446e50805cc3a4

2022-04-04
17:27
For shell extension writers, reduce boilerplate (mimicing SQLITE_EXTENSION_INIT# macros) (check-in: 761208132d user: larrybr tags: cli_extension)
06:33
For TCL extension: Adjust provided "gui" command, and document it. For shell: Separate shell variables from binding parameters, mainly so they live longer, in the shell DB. Add .vars dot command to reflect this separation, and specialized for shell variables. Much code shuffling to share code between .parameters and .vars commands. (check-in: fa492ff57c user: larrybr tags: cli_extension)
2022-03-31
04:05
Make CLI build after an incomplete rename completed. (check-in: 4f11e639f0 user: larrybr tags: cli_extension)
03:45
For TCL extension: Add Tk and means to run it, optionally. Implement ScriptSupport interface. For shell: Get scripting support thru an object interface like the others. Rename OutMode* to Export*. For both: Provide a way to pass arguments to an extension upon load and get them into TCL's argv. (check-in: d2446e5080 user: larrybr tags: cli_extension)
2022-03-29
21:11
For TCL extension: Cleanup TCL library interaction and interpreter management. Group code into more purpose oriented functions. Add .unknown dot command, delegating to TCL commands with "dot" names. Drop planned .eval new dot command. For shell: Implement undocumented .unknown dot command, doing little. Make dispatcher call .unknown's implementation for unknown dot command. Fix a command lookup bug exposed by above change to unknown handling. Add .eval dot command. Add options to .x command aimed at control of errors in command sequences. (a WIP) Make dispatcher report ambiguous dot command lookups, even with no extension. (check-in: 7616a6f4ab user: larrybr tags: cli_extension)

Changes to ext/misc/tclshext.c.in.
12
13
14
15
16
17
18
19
20


21
22
23
24
25
26
27
28
29



30
31
32
33
34
35
36
12
13
14
15
16
17
18


19
20
21
22
23
24
25
26



27
28
29
30
31
32
33
34
35
36







-
-
+
+






-
-
-
+
+
+







** This file contains code to implement the "tclshext" shell extension
** for use with the extensible "sqlite3" CLI shell. On *Nix, build thusly:
     tool/mkshellc.tcl ext/misc/tclshext.c.in > tclshext.c
     gcc -shared -fPIC -O2 -I. -Isrc -I/usr/include/tcl8.6 tclshext.c \
       -o tclshext.so -ltcl8.6
** Later TCL versions can be used if desired.
  "TCL scripting support is added with a registerScripting() call in the\n"
  "ShellExtensionAPI, per ScriptingSupport interface requirements. This\n"
  "support lasts until the scripting object destructor is called.\n"
  "ShellExtensionAPI, as documented for ScriptingSupport interface. This\n"
  "support lasts until the scripting object destructor is called. Until\n"
*/
static const char * const zTclHelp =
  "This extension adds these features to the host shell:\n"
  " 1. TCL scripting support is added.\n"
  " 2. TCL commands are added: udb shdb now_interactive get_tcl_group ..\n"
  " 3. The .tcl and .unknown dot commands are added.\n"
  " 4. If built with Tk capability, the gui TCL command will be added if\n"
  "  this extension was loaded using the shell via: .load ... -shext -tk .\n"
  "  Any other arguments beyond -shext are in TCL's argv variable.\n"
  " 4. If built with Tk capability, a run_gui TCL command may be added if\n"
  "  the extension is loaded by the shell via .load ... -shext -tk . Any\n"
  "  other arguments beyond -shext are in TCL's argv variable.\n"
  "Operation:\n"
  " Shell input groups beginning with \"..\" are treated as TCL input, in\n"
  " these ways: (1) When a bare \"..\" is entered, a TCL REPL loop is run\n"
  " until the end of input is seen; (2) When \"..D ...\" is entered, (where\n"
  " \"D\" is a dot-command name), the D dot command will be run in its normal\n"
  " fashion, but its arguments will be collected according to TCL parsing\n"
  " rules then expanded as usual for TCL commands; and (3) when \".. T ...\"\n"
204
205
206
207
208
209
210
211

212
213
214
215
216
217
218
204
205
206
207
208
209
210

211
212
213
214
215
216
217
218







-
+








/* Provide help for users of this scripting implementation. */
DERIVED_METHOD(const char *, help, ScriptSupport,TclSS, 1,( int more )){
  (void)(pThis);
  switch( more ){
  case 0:
    return "Provides TCL scripting support for SQLite extensible shell.\n";
  case 1: return zTclHelp;
  case 1: return zTclHelp; /* ToDo: Rewrite this help. */
  }
  return 0;
}

/* Not doing this yet. */
DERIVED_METHOD(int,  configure, ScriptSupport,TclSS,
               4,( ShellExState *pSES, char **pzErr,
683
684
685
686
687
688
689
690
691
692
693
694


695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714

715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745

746
747
748
749
750
751
752
753
683
684
685
686
687
688
689





690
691




















692



693
694


























695

696
697
698
699
700
701
702







-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-


-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-







  }else{
    Tcl_SetResult(interp, "too many arguments", TCL_STATIC);
    return TCL_ERROR;
  }
}

#ifndef SHELL_OMIT_TK
static int numEventLoops = 0;
static int inOuterLoop = 0;

static int exitThisTkGUI(void *pvSS, Tcl_Interp *interp,
                         int nArgs, const char *azArgs[]){
static int runTkGUI(void *pvSS, Tcl_Interp *interp,
                    int nArgs, const char *azArgs[]){
  if( numEventLoops==0 && !inOuterLoop ){
    int ec = 0;
    if( nArgs>=2 ){
      if( azArgs[1] && sscanf(azArgs[1], "%d", &ec)!=1 ){
        ec = 1;
        fprintf(stderr, "Exit: %d\n", ec);
      }else{
        const char *zA = (azArgs[1])? azArgs[1] : "null";
        fprintf(stderr, "Exit: \"%s\"\n", azArgs[1]);
      }
    }else{
      fprintf(stderr, "Exit without argument\n");
    }
    fprintf(stderr, "Exit: \"%s\"\n", azArgs[1]);
    // exit(ec);
  }
  --numEventLoops;
  return TCL_BREAK;
}

  ShellExState *psx = (ShellExState *)pvSS;
static void runTclEventLoop(void){
  int inOuter = inOuterLoop;
  int nmw = Tk_GetNumMainWindows();
  /* This runs without looking at stdin. So it cannot be a REPL, yet.
   * Unless user has created something for it to do, it does nothing. */
  /* Tk_MapWindow(Tk_MainWindow(interpKeep.pInterp)); */
  ++numEventLoops;
  inOuterLoop = 1;
  while( nmw  > 0 ) {
    Tcl_DoOneEvent(0);
    nmw = Tk_GetNumMainWindows();
    /* if( nmw==1 ){ */
    /*   Tk_UnmapWindow(Tk_MainWindow(interpKeep.pInterp)); */
    /*   nmw = Tk_GetNumMainWindows(); */
    /*   break; */
    /* } */
  }
  if( nmw==0 ){
    fprintf(stderr,
            "Tk application and its root window destroyed. Restarting Tk.\n");
    Tk_Init(interpKeep.pInterp);
  }
  --numEventLoops;
  inOuterLoop = inOuter;
}

static int runTkGUI(void *pvSS, Tcl_Interp *interp,
                    int nArgs, const char *azArgs[]){
  (void)(pvSS); /* ShellExState *psx = (ShellExState *)pvSS; */
  Tcl_SetMainLoop(runTclEventLoop);
  runTclEventLoop();
  Tk_MainLoop();
  return TCL_OK;
}
#endif

#define UNKNOWN_RENAME "::_original_unknown"

/* C implementation of TCL ::unknown to (maybe) delegate to dot commands */
static int unknownDotDelegate(void *pvSS, Tcl_Interp *interp,
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192


1193
1194
1195

1196
1197
1198
1199
1200
1201
1202
1203

1204
1205

1206
1207
1208
1209
1210
1211

1212
1213
1214
1215
1216
1217
1218
1219
1057
1058
1059
1060
1061
1062
1063














































































1064
1065



1066




1067
1068
1069

1070
1071

1072
1073





1074

1075
1076
1077
1078
1079
1080
1081







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
+
-
-
-
-



-
+

-
+

-
-
-
-
-
+
-







      Tcl_Eval(interp, "rename unknown "UNKNOWN_RENAME);
      Tcl_CreateCommand(interp, "unknown", unknownDotDelegate, psx, 0);
      /* Define this proc so that ".." either gets to the TCL REPL loop
       * or does nothing (if already in it), as a user convenience. */
      Tcl_Eval(interp, "proc .. {} {}");
#ifndef SHELL_OMIT_TK
      if( ldTk ){
        /* Create a proc to launch GUI programs, in faint mimicry of wish. 
         *
         * Its first argument, pgmName is the name to be given to the GUI
         * program that may be launched, used for error reporting and to
         * become the value of the ::argv0 that it sees.
         *
         * Its second argument, pgmSetup, will be executed as a list (of
         * a command and its arguments) to setup the GUI program. It may
         * do anything necessary to prepare for the GUI program to be
         * run by running a Tk event loop. It may be an empty list, in
         * which case pgmName must name a list serving the same purpose.
         *
         * Subsequent arguments to this proc will be passed to the GUI
         * program in the ::argv/::argc variable pair it sees.
         *
         * If only two empty arguments are provided to this proc, (whether
         * as defaulted or explictly passed), the GUI event loop will be
         * run with whatever conditions have been setup prior to the call.
         * (This is perfectly legitimate; this "gui" proc provides a way
         * to package GUI preparation and separate it from GUI run.)
         *
         * It is the responsibility of whatever setup code is run, if any,
         * to leave Tk objects and variables set so that when a GUI event
         * loop is run, some useful GUI program runs and can terminate.
         *
         * Before running the setup code, a variable, ::isHost, is set 
         * true to possibly inform the setup code that it should avoid
         * exit and exec calls. Setup code which is designed for either
         * hosted or standalone use, when run with $::isHost!=0, may opt
         * to leave variables ::exitCode and ::resultValue set which are
         * taken to indicate pseudo-exit status and a string result to
         * be used for error reporting or possibly other purposes.
         *
         * If the above responsibilities cannot be met, setup code should
         * fail in some way so that its execution produces a TCL error or
         * follows the ::exitCode and ::resultValue convention. Otherwise,
         * annoying sqlite3 shell hangs or abrupt exits may result.
         */
        TCL_CSTR_LITERAL(const char * const zGui =){
          proc gui {{pgmName ""} {pgmSetup {}} args} {
            unset -nocomplain ::exitCode
            set ::tcl_interactive [now_interactive]
            set saveArgs [list $::argv0 $::argc $::argv]
            if {"$pgmName" ne ""} {
              set ::argv0 $pgmName
            } else {set ::argv0 "?"}
            set ::argv $args
            set ::argc [llength $args]
            if {[llength $pgmSetup] == 0 && $pgmName ne ""} {
              if { [catch {set ::programSetup [subst "\$$pgmName"]}] } {
                foreach {::argv0 ::argc ::argv} $saveArgs {}
                return -code 1 "Error: pgmSetup empty, and pgmName does not\
                   name a list that might be\n executed in\
                   its place. Consult tclshext doc on using the gui command."
              }
            } elseif {[llength $pgmSetup] == 0 && $pgmName eq ""} {
              unset -nocomplain ::programSetup
            } else {
              set ::programSetup $pgmSetup
            }
            if {[info exists ::programSetup] && [llength $::programSetup] > 0} {
              set rc [catch {uplevel #0 {
                {*}$::programSetup
              }} result options]
              if {$rc==1} {
                puts stderr "gui setup failed: $result"
                puts stderr [dict get $options -errorinfo]
              } elseif {[info exists ::exitCode] && $::exitCode!=0} {
                puts stderr "gui setup failed: $::resultValue"
              } else { run_gui_event_loop }
            } else {
              run_gui_event_loop
            }
            foreach {::argv0 ::argc ::argv} $saveArgs {}
          }
        };
        /* Create a command which nearly emuluates Tk_MainLoop(). It runs a
         * GUI event loop, so does not return until either: all Tk top level
        /* Create a command which wraps Tk_MainLoop(). It runs a GUI event
         * loop, so does not return until all of its Tk windows are closed. */
         * windows are destroyed, which causes and error return, or the Tk
         * app has called the replacement exit routine described next. */
        Tcl_CreateCommand(interp, "run_gui_event_loop", runTkGUI, psx, 0);
        Tcl_CreateCommand(interp, "run_gui", runTkGUI, psx, 0);
        Tcl_Eval(interp, "rename exit process_exit");
        Tcl_CreateCommand(interp, "exit", exitThisTkGUI, psx, 0);
        Tcl_Eval(interp, zGui);
        Tcl_SetMainLoop(runTclEventLoop);
        zAppName = "tclshext_tk";
      }
#endif
      Tcl_SetVar2Ex(interp, "::argv0", NULL,
      Tcl_SetVar2Ex(interp, "argv0", NULL,
                    Tcl_NewStringObj(zAppName,-1), TCL_GLOBAL_ONLY);
      Tcl_SetVar2Ex(interp, "::argc", NULL,
      Tcl_SetVar2Ex(interp, "argc", NULL,
                    Tcl_NewIntObj(tnarg), TCL_GLOBAL_ONLY);
      Tcl_SetVar2Ex(interp, "::argv", NULL, targv, TCL_GLOBAL_ONLY);
      Tcl_SetVar2Ex(interp, "::tcl_interactive", NULL,
                    Tcl_NewIntObj(pExtHelpers->nowInteractive(psx)),
                    TCL_GLOBAL_ONLY);
      Tcl_SetVar2Ex(interp, "::isHosted", NULL,
      Tcl_SetVar2Ex(interp, "argv", NULL, targv, TCL_GLOBAL_ONLY);
                    Tcl_NewIntObj(1), TCL_GLOBAL_ONLY);
      pShExtLink->eid = sqlite3_tclshext_init;
    }
    if( rc==SQLITE_OK ){
      pShExtLink->extensionDestruct = Tcl_TakeDown;
      pShExtLink->pvExtensionObject = &interpKeep;
    }else{
      Tcl_TakeDown(&interpKeep);
Changes to src/shell.c.in.
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418


1419
1420
1421
1422
1423
1424
1425
1396
1397
1398
1399
1400
1401
1402

1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415


1416
1417
1418
1419
1420
1421
1422
1423
1424







-













-
-
+
+







  /* shell event subscription list */
  int numSubscriptions;  /* Number of active entries in below list */
  struct EventSubscription {
    ExtensionId eid;
    void *pvUserData;
    ShellEventNotify eventHandler;
  } *pSubscriptions;     /* The current shell event subscriptions */
  u8 bDbDispatch;        /* Cache fact of dbShell dispatch table */
#endif

  ShellExState *pSXS;    /* Pointer to companion, exposed shell state */
} ShellInState;

/*
** Limit input nesting via .read or any other input redirect.
** It's not too expensive, so a generous allowance can be made.
*/
#define MAX_INPUT_NESTING 25

/*
** This procedure updates the bSafeMode flag after completion of any
** operation (meta-command, SQL submission, or script execution) that
** counts as one for which safe mode might be suspended.
** operation (meta-command or SQL submission) that counts as one for
** which safe mode might be suspended.
** bSafeModeFuture has 3 states salient here:
** equal 0 => Safe mode is and will remain inactive.
** equal 1 => Safe mode is and will remain active.
** N > 1 => Safe mode is suspended for N-1 operations.
*/
static void updateSafeMode(ShellInState *psi){
  switch( psi->bSafeModeFuture ){
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2409
2410
2411
2412
2413
2414
2415




































































2416
2417
2418
2419
2420
2421
2422







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







  if( (psi->flgProgress & SHELL_PROGRESS_QUIET)==0 ){
    raw_printf(psi->out, "Progress %u\n", psi->nProgress);
  }
  return 0;
}
#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */

/* For debugging or experimentation, the shell DB can be made file-based. */
#ifndef SHELL_DB_FILE
# define SHELL_DB_STORE ":memory:"
#else
# define SHELL_DB_STORE SHELL_STRINGIFY(SHELL_DB_FILE)
#endif

#define SHELL_DISP_SCHEMA "main"
#define SHELL_DISP_TAB "ShellCommands"
#define SHELL_DISP_VIEW "ShellActiveCmds"

/*
** Ensure dbShell exists and return SQLITE_OK,
** or complain and return SQLITE_ERROR.
*/
static int ensure_shell_db(ShellExState *psx){
  if( psx->dbShell!=0 ) return SQLITE_OK;
  else{
    int rc = sqlite3_open(SHELL_DB_STORE, &psx->dbShell);
    if( rc!=SQLITE_OK ){
      utf8_printf(STD_ERR, "Shell DB open failure: %s\n", sqlite3_errstr(rc));
      return SQLITE_ERROR;
    }
#ifndef SQLITE_NOHAVE_SYSTEM
    sqlite3_create_function(psx->dbShell, "edit", 1,
                            SQLITE_UTF8, 0, editFunc, 0, 0);
    sqlite3_create_function(psx->dbShell, "edit", 2,
                            SQLITE_UTF8, 0, editFunc, 0, 0);
#endif
    return rc;
  }
}

/* Tell whether the above-created table exists, return true iff exists. */
static int dispatch_table_exists(sqlite3 *dbs){
  return sqlite3_table_column_metadata
    (dbs, SHELL_DISP_SCHEMA, SHELL_DISP_TAB, 0, 0, 0, 0, 0, 0)==SQLITE_OK;
}

static int ensure_dispatch_table(ShellExState *psx){
  int rc = ensure_shell_db(psx);
  if( rc==SQLITE_OK ){
    char *zErr = 0;
    int rc1, rc2;
    if( dispatch_table_exists(psx->dbShell) ) return rc;
    /* Create the dispatch table and view on it. */
#ifdef SHELL_DB_FILE
    sqlite3_exec(psx->dbShell, "DROP TABLE IF EXISTS "SHELL_DISP_TAB, 0,0,0);
    sqlite3_exec(psx->dbShell, "DROP VIEW IF EXISTS "SHELL_DISP_VIEW, 0,0,0);
#endif
    rc1 = sqlite3_exec(psx->dbShell, "CREATE TABLE "SHELL_DISP_TAB"("
                       "name TEXT, extIx INT, cmdIx INT,"
                       "PRIMARY KEY(extIx,cmdIx)) WITHOUT ROWID", 0, 0, &zErr);
    rc2 = sqlite3_exec(psx->dbShell,
                       "CREATE VIEW "SHELL_DISP_VIEW
                       " AS SELECT s.name AS name,"
                       " max(s.extIx) AS extIx, s.cmdIx AS cmdIx"
                       " FROM "SHELL_DISP_TAB" s GROUP BY name",
                       0, 0, &zErr);
    if( rc1!=SQLITE_OK || rc2!=SQLITE_OK || zErr!=0 ){
      utf8_printf(STD_ERR, "Shell DB init failure, %s\n", zErr? zErr : "?");
      rc = SQLITE_ERROR;
    }else rc = SQLITE_OK;
    sqlite3_free(zErr);
  }
  return rc;
}

/*
** Skip over whitespace, returning remainder.
*/
static const char *skipWhite( const char *z ){
  while( IsSpace(*z) ) ++z;
  return z;
}
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614

3615
3616

3617
3618

3619
3620

3621
3622

3623
3624

3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3489
3490
3491
3492
3493
3494
3495








































3496
3497
3498
3499






3500
3501

3502
3503

3504
3505

3506
3507

3508
3509

3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528


























3529
3530
3531
3532
3533
3534
3535







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-




-
-
-
-
-
-
+

-
+

-
+

-
+

-
+

-
+


















-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







  char c = *zName;
  switch( c ){
  case '$': case ':': case '@': case '?': return PTU_Binding;
  default: return isalpha(c)? PTU_Script : PTU_Nil;
  }
}

#ifndef SQLITE_NOHAVE_SYSTEM
/* Possibly using a -editor=X argument and env-var VISUAL, attempt
 * to get the zEditor shell state member set iff not already set.
 * If there is no such argument, the env-var is retrieved if set.
 * If the argument is -editor=X or --editor=X, use that and leave
 * the zEditor member set accordingly. Returns are:
 * 0 => editor set, zEd was not the -editor option
 * 1 => editor set, zEd consumed as -editor option
 * -1 => editor not set, and error/advice message issued.
 *
 * This implements an undocumented fall-back for the .vars and
 * .parameters edit subcommands, so that users need not restart
 * a shell session to get an editor specified upon need for it. */
int attempt_editor_set(ShellInState *psi, char *zDot, const char *zEd){
  if( psi->zEditor==0 ){
    const char *zE = getenv("VISUAL");
    if( zE!=0 ) psi->zEditor = smprintf("%s", zE);
  }
  if( zEd && zEd[0]=='-' ){
    zEd += 1 + (zEd[1]=='-');
    if( strncmp(zEd,"editor=",7)==0 ){
      sqlite3_free(psi->zEditor);
      /* Accept an initial -editor=? option. */
      psi->zEditor = smprintf("%s", zEd+7);
      return 1;
    }
  }
  if( psi->zEditor==0 ){
    utf8_printf(STD_ERR,
                "Either set env-var VISUAL to name an"
                " editor and restart, or rerun\n "
                ".%s edit with an initial edit option,"
                " --editor=EDITOR_COMMAND .\n", zDot);
    return -1;
  }
  return 0;
}
#endif

/* The table kept for user DBs if .parameter command is used usefully. */
#define PARAM_TABLE_NAME "sqlite_parameters"
#define PARAM_TABLE_SCHEMA "temp"
#define PARAM_TABLE_SNAME PARAM_TABLE_SCHEMA"."PARAM_TABLE_NAME

/* The table kept for the shell DB if .vars command is used usefully. */
#define SHVAR_TABLE_NAME "sqlite_variables"
#define SHVAR_TABLE_SCHEMA "main"
#define SHVAR_TABLE_SNAME SHVAR_TABLE_SCHEMA"."SHVAR_TABLE_NAME

#ifndef SH_KV_STORE_NAME
#ifndef PARAM_STORE_NAME
/* Name for table keeping user's saved parameters */
# define SH_KV_STORE_NAME "SQLiteShell_KeyValuePairs"
# define PARAM_STORE_NAME "SQLiteShellParameters"
#endif
#ifndef SH_KV_STORE_SCHEMA
#ifndef PARAM_STORE_SCHEMA
/* Schema name used to attach saved parameters DB during load/save */
# define SH_KV_STORE_SCHEMA "SQLiteShell"
# define PARAM_STORE_SCHEMA "SQLiteShell"
#endif
#define SH_KV_STORE_SNAME SH_KV_STORE_SCHEMA"."SH_KV_STORE_NAME
#define PARAM_STORE_SNAME PARAM_STORE_SCHEMA"."PARAM_STORE_NAME

/* Create the TEMP table used to store parameter bindings */
/* Create the TEMP table used to store parameter bindings and SQL statements */
static void param_table_init(sqlite3 *db){
  DbProtectState dps = allow_sys_schema_change(db);
  sqlite3_exec(db,
    "CREATE TABLE IF NOT EXISTS "PARAM_TABLE_SNAME"(\n"
    "  key TEXT PRIMARY KEY,\n"
    "  value,\n"
    "  uses INT DEFAULT (0)" /* aka PTU_Binding */
    ") WITHOUT ROWID;",
    0, 0, 0);
  restore_sys_schema_protection( db, &dps );
}

/* Tell whether the above-created table exists, return true iff exists. */
static int param_table_exists( sqlite3 *db ){
  return sqlite3_table_column_metadata
    (db, PARAM_TABLE_SCHEMA, PARAM_TABLE_NAME, 0, 0, 0, 0, 0, 0)==SQLITE_OK;
}

/* Create the shell DB table used to store shell variables or scripts */
static int shvars_table_init(sqlite3 *db){
  DbProtectState dps = allow_sys_schema_change(db);
  int rc = sqlite3_exec(db,
    "CREATE TABLE IF NOT EXISTS "SHVAR_TABLE_SNAME"(\n"
    "  key TEXT PRIMARY KEY,\n"
    "  value,\n"
    "  uses INT DEFAULT (0)" /* aka PTU_Binding */
    ") WITHOUT ROWID;",
    0, 0, 0);
  restore_sys_schema_protection( db, &dps );
  return rc!=SQLITE_OK;
}

/* Tell whether the above-created table exists, return true iff exists. */
static int shvars_table_exists( sqlite3 *db ){
  return sqlite3_table_column_metadata
    (db, SHVAR_TABLE_SCHEMA, SHVAR_TABLE_NAME, 0, 0, 0, 0, 0, 0)==SQLITE_OK;
}

/* Make shell vars table exist. */
static int ensure_shvars_table(sqlite3 *dbs){
  if( shvars_table_exists(dbs) ) return SQLITE_OK;
  else return shvars_table_init(dbs);
}

/*
** Bind parameters on a prepared statement.
**
** Parameter bindings are taken from a TEMP table of the form:
**
**    CREATE TEMP TABLE
**    sqlite_parameters(key TEXT PRIMARY KEY, value, uses INT)
7897
7898
7899
7900
7901
7902
7903
7904

7905
7906
7907
7908
7909
7910
7911
7757
7758
7759
7760
7761
7762
7763

7764
7765
7766
7767
7768
7769
7770
7771







-
+








/* Register a meta-command, to be called during extension load/init. */
static int register_meta_command(ShellExState *p,
                                 ExtensionId eid, MetaCommand *pMC){
  ShellInState *psi = ISS(p);
  ShExtInfo *psei = pending_ext_info(psi);
  const char *zSql
    = "INSERT INTO "SHELL_DISP_TAB"(name, extIx, cmdIx) VALUES(?, ?, ?)";
    = "INSERT INTO ShellCommands (name, extIx, cmdIx) VALUES(?, ?, ?)";
  int ie = psi->ixExtPending;
  assert(psi->pShxLoaded!=0 && p->dbShell!=0);
  if( pMC==0 ) return SQLITE_ERROR;
  else{
    const char *zName = pMC->pMethods->name(pMC);
    sqlite3_stmt *pStmt;
    int nc = psei->numMetaCommands;
7945
7946
7947
7948
7949
7950
7951
7952

7953
7954
7955
7956
7957
7958
7959
7960
7961
7962
7963
7964
7805
7806
7807
7808
7809
7810
7811

7812
7813
7814
7815
7816

7817
7818
7819
7820
7821
7822
7823







-
+




-







                             ExtensionId eid, ImportHandler *pIH){
  return SQLITE_ERROR;
}

static int register_scripting(ShellExState *p, ExtensionId eid,
                              ScriptSupport *pSS){
  ShellInState *psi = ISS(p);
  if( psi->scriptXid!=0 || psi->script!=0 ){
  if( psi->scriptXid!=0 ){
    /* Scripting support already provided. Only one provider is allowed. */
    return SQLITE_BUSY;
  }
  if( eid==0 || pSS==0 || psi->ixExtPending==0 ){
    /* Scripting addition allowed only when sqlite3_*_init() runs. */
    return SQLITE_MISUSE;
  }
  psi->script = pSS;
  psi->scriptXid = eid;
  return SQLITE_OK;
}

8125
8126
8127
8128
8129
8130
8131






8132
8133
8134
8135
8136
8137
8138
8139

8140
8141
8142
8143
8144
8145

8146
8147
8148
8149
8150
8151
8152
8153
8154
8155
8156
8157
8158
8159
8160
8161
8162
8163
8164
8165
8166
8167
8168
8169
8170


















8171

8172
8173
8174
8175
8176
8177
8178
8179
8180
8181
8182
8183
8184
8185
8186
8187
8188
8189
8190
8191
8192
8193
8194
8195
8196
8197
8198
8199
8200
8201
7984
7985
7986
7987
7988
7989
7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
8001
8002
8003

8004

8005
8006
8007
8008

8009







8010
8011
8012
8013
8014
8015
8016
8017
8018
8019
8020
8021
8022
8023
8024
8025
8026
8027
8028
8029
8030
8031
8032
8033
8034
8035
8036
8037
8038
8039
8040
8041
8042
8043
8044
8045

8046
8047
8048
8049
8050
8051
8052
8053
8054
8055
8056
8057
8058
8059
8060
8061
8062
8063
8064
8065
8066



8067
8068
8069
8070
8071
8072
8073







+
+
+
+
+
+







-
+
-




-
+
-
-
-
-
-
-
-


















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+




















-
-
-







    break;
  default:
    pv = 0;
  }
  if( pv==0 ) sqlite3_result_null(context);
  else sqlite3_result_pointer(context, pv, SHELLEXT_API_POINTERS, 0);
}

#ifndef SHELL_DB_FILE
# define SHELL_DB_STORE ":memory:"
#else
# define SHELL_DB_STORE SHELL_STRINGIFY(SHELL_DB_FILE)
#endif

/* Do the initialization needed for use of dbShell for command lookup
 * and dispatch and for I/O handler lookup and dispatch.
 */
static int begin_db_dispatch(ShellExState *psx){
  ShellInState *psi = ISS(psx);
  sqlite3_stmt *pStmt = 0;
  int ic, rc1, rc2;
  int ic, rc, rc1, rc2;
  int rc = 0;
  char *zErr = 0;
  const char *zSql;
  ShExtInfo sei = {0};
  /* Consider: Store these dynamic arrays in the DB as indexed-into blobs. */
  assert(psx->dbShell==0 || (psi->numExtLoaded==0 && psi->pShxLoaded==0));
  assert(psx->dbShell==0 && psi->numExtLoaded==0 && psi->pShxLoaded==0);
  rc = ensure_shell_db(psx);
  if( rc!=SQLITE_OK ){
    utf8_printf(STD_ERR, "Error: Shell DB uncreatable. Best to .quit soon.\n");
    return SQLITE_ERROR;
  }
  if( ensure_dispatch_table(psx)!=SQLITE_OK ) return 1;

  psi->pShxLoaded = (ShExtInfo *)sqlite3_malloc(2*sizeof(ShExtInfo));
  sei.ppMetaCommands
    = (MetaCommand **)sqlite3_malloc((numCommands+2)*sizeof(MetaCommand *));
  sei.ppExportHandlers
    = (ExportHandler **)sqlite3_malloc(2*sizeof(ExportHandler *));
  sei.ppImportHandlers
    = (ImportHandler **)sqlite3_malloc(2*sizeof(ImportHandler *));
  if( sei.ppMetaCommands==0||sei.ppExportHandlers==0||sei.ppImportHandlers==0
      || psi->pShxLoaded==0 ){
    shell_out_of_memory();
  }
  sei.numExportHandlers = 0;
  sei.numImportHandlers = 0;
  for( ic=0; ic<numCommands; ++ic ){
    sei.ppMetaCommands[ic] = builtInCommand(ic);
  }
  sei.numMetaCommands = ic;
  psi->pShxLoaded[psi->numExtLoaded++] = sei;
  rc = sqlite3_open(SHELL_DB_STORE, &psx->dbShell);
  if( rc!=SQLITE_OK ) return 1;
#ifdef SHELL_DB_FILE
  sqlite3_exec(psx->dbShell, "DROP TABLE IF EXISTS ShellCommands", 0,0,0);
  sqlite3_exec(psx->dbShell, "DROP VIEW IF EXISTS ActiveCommands", 0,0,0);
#endif
  rc1 = sqlite3_exec(psx->dbShell, "CREATE TABLE ShellCommands("
                     "name TEXT, extIx INT, cmdIx INT,"
                     "PRIMARY KEY(extIx,cmdIx)) WITHOUT ROWID",
                     0, 0, &zErr);
  rc2 = sqlite3_exec(psx->dbShell, "CREATE VIEW ActiveCommands AS SELECT "
                     "s.name AS name, max(s.extIx) AS extIx, s.cmdIx AS cmdIx "
                     "FROM ShellCommands s GROUP BY name",
                     0, 0, &zErr);
  if( rc1!=SQLITE_OK || rc2!=SQLITE_OK || zErr!=0 ){
    utf8_printf(STD_ERR, "Shell DB init failure, %s\n", zErr? zErr : "");
    return 1;
  }
  zSql = "INSERT INTO "SHELL_DISP_TAB"(name, extIx, cmdIx) VALUES(?, 0, ?)";
  zSql = "INSERT INTO ShellCommands (name, extIx, cmdIx) VALUES(?, 0, ?)";
  rc1 = sqlite3_prepare_v2(psx->dbShell, zSql, -1, &pStmt, 0);
  rc2 = sqlite3_exec(psx->dbShell, "BEGIN TRANSACTION", 0, 0, &zErr);
  if( rc1!=SQLITE_OK || rc2!=SQLITE_OK ) return 1;
  assert(sei.numMetaCommands>0);
  for( ic=0; ic<sei.numMetaCommands; ++ic ){
    MetaCommand *pmc = sei.ppMetaCommands[ic];
    const char *zName = pmc->pMethods->name(pmc);
    sqlite3_reset(pStmt);
    sqlite3_bind_text(pStmt, 1, zName, -1, 0);
    sqlite3_bind_int(pStmt, 2, ic);
    rc = sqlite3_step(pStmt);
    if( rc!=SQLITE_DONE ){
      sqlite3_exec(psx->dbShell, "ABORT", 0, 0, 0);
      break;
    }
  }
  sqlite3_finalize(pStmt);
  if( rc!=SQLITE_DONE ) return 1;
  rc = sqlite3_exec(psx->dbShell, "COMMIT", 0, 0, &zErr);
  sqlite3_enable_load_extension(psx->dbShell, 1);
  sqlite3_free(zErr);
  psi->bDbDispatch = 1;

  return SQLITE_OK;
}

/* Call one loaded extension's destructors, in reverse order
 * of their objects' creation, then free the tracking dyna-arrays.
 */
static void free_one_shext_tracking(ShExtInfo *psei){
8277
8278
8279
8280
8281
8282
8283
8284

8285
8286
8287
8288
8289
8290
8291
8149
8150
8151
8152
8153
8154
8155

8156
8157
8158
8159
8160
8161
8162
8163







-
+







  ShellInState *psi = ISS(psx);
  /* save script support state for possible fallback if load fails */
  ScriptSupport *pssSave = psi->script;
  ExtensionId ssiSave = psi->scriptXid;
  int rc;

  if( pzErr ) *pzErr = 0;
  if( psx->dbShell==0 || ISS(psx)->numExtLoaded==0 ){
  if( psx->dbShell==0 ){
    rc = begin_db_dispatch(psx);
    if( rc!=SQLITE_OK ) return rc;
    assert(ISS(psx)->numExtLoaded==1 && psx->dbShell!=0);
  }
  psi->ixExtPending = psi->numExtLoaded;
  sqlite3_create_function(psx->dbShell, "shext_pointer", 1,
                          SQLITE_DIRECTONLY|SQLITE_UTF8,
10460
10461
10462
10463
10464
10465
10466
10467

10468
10469
10470

10471
10472

10473
10474
10475

10476
10477
10478
10479
10480
10481
10482
10332
10333
10334
10335
10336
10337
10338

10339
10340
10341

10342
10343

10344
10345
10346

10347
10348
10349
10350
10351
10352
10353
10354







-
+


-
+

-
+


-
+








DISPATCHABLE_COMMAND( nullvalue ? 2 2 ){
  sqlite3_snprintf(sizeof(ISS(p)->nullValue), ISS(p)->nullValue, "%.*s",
                   (int)ArraySize(ISS(p)->nullValue)-1, azArg[1]);
  return DCR_Ok;
}

/* Helper functions for .parameter and .vars commands
/* Helper functions for .parameter command
 */

struct keyval_row { char * value; int uses; int hits; };
struct param_row { char * value; int uses; int hits; };

static int kv_find_callback(void *pData, int nc, char **pV, char **pC){
static int param_find_callback(void *pData, int nc, char **pV, char **pC){
  assert(nc>=1);
  assert(strcmp(pC[0],"value")==0);
  struct keyval_row *pParam = (struct keyval_row *)pData;
  struct param_row *pParam = (struct param_row *)pData;
  assert(pParam->value==0); /* key values are supposedly unique. */
  if( pParam->value!=0 ) sqlite3_free( pParam->value );
  pParam->value = smprintf("%s", pV[0]); /* source owned by statement */
  if( nc>1 ) pParam->uses = (int)integerValue(pV[1]);
  ++pParam->hits;
  return 0;
}
10521
10522
10523
10524
10525
10526
10527
10528

10529
10530

10531
10532
10533
10534
10535
10536
10537




10538
10539
10540
10541
10542
10543
10544
10545
10546
10547
10548
10549
10550
10551
10552
10553

10554
10555
10556
10557

10558
10559
10560

10561
10562

10563
10564
10565
10566
10567
10568

10569
10570
10571
10572
10573
10574
10575
10576
10577
10578
10579
10580
10581
10582

10583
10584
10585
10586
10587
10588
10589
10590
10591
10592
10593
10594
10595
10596
10597
10598

10599
10600
10601
10602

10603
10604
10605
10606
10607
10608
10609
10610
10611
10612
10613

10614
10615
10616
10617
10618
10619
10620
10621

10622
10623
10624
10625
10626
10627
10628

10629
10630
10631


10632
10633
10634
10635
10636
10637
10638

10639
10640
10641
10642
10643
10644
10645
10646
10647


10648
10649
10650
10651
10652
10653
10654

10655
10656
10657
10658
10659
10660
10661
10662
10663
10664
10665
10666





10667
10668
10669
10670



10671
10672
10673
10674

10675
10676
10677
10678
10679
10680
10681

10682
10683
10684

10685
10686
10687


10688
10689

10690
10691
10692



10693
10694
10695
10696
10697



10698
10699
10700



10701
10702
10703
10704

10705

10706

10707
10708
10709


10710
10711

10712
10713
10714
10715




10716
10717
10718


10719
10720
10721
10722
10723
10724
10725
10726
10727
10728
10729
10730
10731
10732
10733
10734
10735
10736























10737
10738
10739
10740
10741
10742
10743
10393
10394
10395
10396
10397
10398
10399

10400


10401
10402
10403
10404




10405
10406
10407
10408
10409
10410
10411
10412
10413







10414
10415
10416

10417
10418
10419
10420

10421
10422
10423

10424
10425

10426
10427
10428
10429
10430
10431

10432
10433
10434
10435
10436
10437
10438
10439
10440
10441
10442
10443
10444
10445

10446
10447
10448
10449
10450
10451
10452
10453
10454
10455
10456
10457
10458
10459
10460
10461

10462
10463
10464
10465

10466
10467

10468
10469
10470
10471
10472
10473
10474
10475

10476
10477







10478
10479
10480
10481
10482
10483
10484

10485



10486
10487
10488
10489
10490
10491
10492
10493

10494
10495
10496
10497
10498
10499
10500



10501
10502
10503
10504
10505
10506
10507
10508

10509
10510
10511
10512
10513
10514
10515
10516





10517
10518
10519
10520
10521
10522



10523
10524
10525
10526
10527


10528







10529

10530

10531
10532


10533
10534
10535

10536
10537


10538
10539
10540

10541
10542


10543
10544
10545
10546


10547
10548
10549
10550
10551
10552

10553
10554
10555

10556
10557
10558

10559
10560
10561

10562
10563



10564
10565
10566
10567
10568
10569
10570
10571
10572
10573
10574
10575
10576
10577
10578
10579
10580
10581
10582
10583
10584
10585
10586
10587
10588
10589
10590
10591
10592
10593
10594
10595
10596
10597
10598
10599
10600
10601
10602
10603
10604
10605
10606
10607
10608
10609
10610
10611
10612
10613
10614
10615
10616
10617
10618
10619
10620







-
+
-
-
+



-
-
-
-
+
+
+
+





-
-
-
-
-
-
-



-
+



-
+


-
+

-
+





-
+













-
+















-
+



-
+

-








-
+

-
-
-
-
-
-
-
+






-
+
-
-
-
+
+






-
+






-
-
-
+
+






-
+







-
-
-
-
-
+
+
+
+
+

-
-
-
+
+
+


-
-
+
-
-
-
-
-
-
-
+
-

-
+

-
-
+
+

-
+

-
-
+
+
+
-


-
-
+
+
+

-
-
+
+
+



-
+

+
-
+


-
+
+

-
+

-
-
-
+
+
+
+



+
+


















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







 * If it is an empty list, all parameters will be saved or loaded.
 * Otherwise, only the named parameters are transferred, if they exist.
 * It is not an error to specify a name that cannot be transferred
 * because it does not exist in the source table.
 *
 * Returns are SQLITE_OK for success, or other codes for failure.
 */
static int kv_xfr_table(sqlite3 *db, const char *zStoreDbName,
static int param_xfr_table(sqlite3 *db, const char *zStoreDbName,
                        int bSaveNotLoad, ParamTableUse ptu,
                        const char *azNames[], int nNames){
                           int bSaveNotLoad, const char *azNames[], int nNames){
  int rc = 0;
  char *zSql = 0; /* to be sqlite3_free()'ed */
  sqlite3_str *sbCopy = 0;
  const char *zHere = 0;
  const char *zThere = SH_KV_STORE_SNAME;
  const char *zTo;
  const char *zFrom;
  const char *zHere = PARAM_TABLE_SNAME;
  const char *zThere = PARAM_STORE_SNAME;
  const char *zTo = (bSaveNotLoad)? zThere : zHere;
  const char *zFrom = (bSaveNotLoad)? zHere : zThere;
  sqlite3 *dbStore = 0;
  int openFlags = (bSaveNotLoad)
    ? SQLITE_OPEN_URI|SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE
    : SQLITE_OPEN_READONLY;

  switch( ptu ){
  case PTU_Binding: zHere = PARAM_TABLE_SNAME; break;
  case PTU_Script:  zHere = SHVAR_TABLE_SNAME; break;
  default: assert(0); return 1;
  }
  zTo = (bSaveNotLoad)? zThere : zHere;
  zFrom = (bSaveNotLoad)? zHere : zThere;
  /* Ensure store DB can be opened and/or created appropriately. */
  rc = sqlite3_open_v2(zStoreDbName, &dbStore, openFlags, 0);
  if( rc!=SQLITE_OK ){
    utf8_printf(STD_ERR, "Error: Cannot %s key/value store DB %s\n",
    utf8_printf(STD_ERR, "Error: Cannot %s parameter store DB %s\n",
                bSaveNotLoad? "open/create" : "read", zStoreDbName);
    return rc;
  }
  /* Ensure it has the kv store table, or handle its absence. */
  /* Ensure it has the parameter store table, or handle its absence. */
  assert(dbStore!=0);
  if( sqlite3_table_column_metadata
      (dbStore, "main", SH_KV_STORE_NAME, 0, 0, 0, 0, 0, 0)!=SQLITE_OK ){
      (dbStore, "main", PARAM_STORE_NAME, 0, 0, 0, 0, 0, 0)!=SQLITE_OK ){
    if( !bSaveNotLoad ){
      utf8_printf(STD_ERR, "Error: No key/value pairs ever stored in DB %s\n",
      utf8_printf(STD_ERR, "Error: No parameters ever stored in DB %s\n",
                  zStoreDbName);
      rc = 1;
    }else{
      /* The saved parameters table is not there yet; create it. */
      const char *zCT =
        "CREATE TABLE IF NOT EXISTS "SH_KV_STORE_NAME"(\n"
        "CREATE TABLE IF NOT EXISTS "PARAM_STORE_NAME"(\n"
        "  key TEXT PRIMARY KEY,\n"
        "  value,\n"
        "  uses INT\n"
        ") WITHOUT ROWID;";
      rc = sqlite3_exec(dbStore, zCT, 0, 0, 0);
      if( rc!=SQLITE_OK ){
        utf8_printf(STD_ERR, "Cannot create table %s. Nothing saved.", zThere);
      }
    }
  }
  sqlite3_close(dbStore);
  if( rc!=0 ) return rc;

  zSql = smprintf("ATTACH %Q AS %s;", zStoreDbName, SH_KV_STORE_SCHEMA);
  zSql = smprintf("ATTACH %Q AS %s;", zStoreDbName, PARAM_STORE_SCHEMA);
  shell_check_oom(zSql);
  rc = sqlite3_exec(db, zSql, 0, 0, 0);
  sqlite3_free(zSql);
  if( rc!=SQLITE_OK ) return rc;

  sbCopy = sqlite3_str_new(db);
  sqlite3_str_appendf
      (sbCopy, "INSERT OR REPLACE INTO %s(key,value,uses)"
       "SELECT key, value, uses FROM %s WHERE key ", zTo, zFrom);
  append_in_clause(sbCopy, azNames, azNames+nNames);
  zSql = sqlite3_str_finish(sbCopy);
  shell_check_oom(zSql);
  rc = sqlite3_exec(db, zSql, 0, 0, 0);
  sqlite3_free(zSql);

  sqlite3_exec(db, "DETACH "SH_KV_STORE_SCHEMA";", 0, 0, 0);
  sqlite3_exec(db, "DETACH "PARAM_STORE_SCHEMA";", 0, 0, 0);
  return rc;
}

/* Default locations of kv store DBs for .parameters and .vars save/load. */
/* Default location of parameters store DB for .parameters save/load. */
static const char *zDefaultParamStore = "~/sqlite_params.sdb";
static const char *zDefaultVarStore = "~/sqlite_vars.sdb";

/* Possibly generate a derived path from input spec, with defaulting
 * and conversion of leading (or only) tilde as home directory.
 * The above-set default is used for zSpec NULL, "" or "~".
 * When return is 0, there is an error; what needs doing cannnot be done.
 * If the return is exactly the input, it must not be sqlite3_free()'ed.
 * If the return differs from the input, it must be sqlite3_free()'ed.
 */
  static const char *kv_store_path(const char *zSpec, ParamTableUse ptu){
static const char *params_store_path(const char *zSpec){
  if( zSpec==0 || zSpec[0]==0 || strcmp(zSpec,"~")==0 ){
    const char *zDef;
    switch( ptu ){
    case PTU_Binding: zDef = zDefaultParamStore; break;
    case PTU_Script:  zDef = zDefaultVarStore; break;
    default: return 0;
    }
    return home_based_path(zDef);
    return home_based_path(zDefaultParamStore);
  }else if ( zSpec[0]=='~' ){
    return home_based_path(zSpec);
  }
  return zSpec;
}

/* Load some or all kv pairs. Arguments are "load FILE ?NAMES?". */
/* Load some or all parameters. Arguments are "load FILE ?NAMES?". */
static int kv_pairs_load(sqlite3 *db, ParamTableUse ptu,
                         const char *azArg[], int nArg){
  const char *zStore = kv_store_path((nArg>1)? azArg[1] : 0, ptu);
static int parameters_load(sqlite3 *db, const char *azArg[], int nArg){
  const char *zStore = params_store_path((nArg>1)? azArg[1] : 0);
  if( zStore==0 ){
    utf8_printf(STD_ERR, "Cannot form parameter load path. Nothing loaded.\n");
    return DCR_Error;
  }else{
    const char **pzFirst = (nArg>2)? azArg+2 : 0;
    int nNames = (nArg>2)? nArg-2 : 0;
    int rc = kv_xfr_table(db, zStore, 0, ptu, pzFirst, nNames);
    int rc = param_xfr_table(db, zStore, 0, pzFirst, nNames);
    if( nArg>1 && zStore!=azArg[1] ) sqlite3_free((void*)zStore);
    return rc;
  }
}

/* Save some or all parameters. Arguments are "save FILE ?NAMES?". */
static int kv_pairs_save(sqlite3 *db, ParamTableUse ptu,
                         const char *azArg[], int nArg){
  const char *zStore = kv_store_path((nArg>1)? azArg[1] : 0, ptu);
static int parameters_save(sqlite3 *db, const char *azArg[], int nArg){
  const char *zStore = params_store_path((nArg>1)? azArg[1] : 0);
  if( zStore==0 ){
    utf8_printf(STD_ERR, "Cannot form parameter save path. Nothing saved.\n");
    return DCR_Error;
  }else{
    const char **pzFirst = (nArg>2)? azArg+2 : 0;
    int nNames = (nArg>2)? nArg-2 : 0;
    int rc = kv_xfr_table(db, zStore, 1, ptu, pzFirst, nNames);
    int rc = param_xfr_table(db, zStore, 1, pzFirst, nNames);
    if( nArg>1 && zStore!=azArg[1] ) sqlite3_free((void*)zStore);
    return rc;
  }
}

#ifndef SQLITE_NOHAVE_SYSTEM
/*
 * Edit one named value in the parameters or shell variables table.
 * If it does not yet exist, create it. If eval is true, the value
 * is treated as a bare expression, otherwise it is a text value.
 * The "uses" argument sets the 3rd column in the selected table,
 * and serves to select which of the two tables is modified.
 * Edit one named parameter in the parameters table. If it does not
 * yet exist, create it. If eval is true, the value is treated as a
 * bare expression, otherwise it is a text value. The uses argument
 * sets the 3rd column in the parameters table, and may also serve
 * to partition the key namespace. (This is not done now.)
 */
static int edit_one_kvalue(sqlite3 *db, char *name, int eval,
                           ParamTableUse uses, const char * zEditor){
  struct keyval_row kvRow = {0,0,0};
static int edit_one_param(sqlite3 *db, char *name, int eval,
                          ParamTableUse uses, const char * zEditor){
  struct param_row paramVU = {0,0,0};
  int rc;
  char * zVal = 0;
  const char *zTab = 0;
  char * zSql = 0;
  char * zSql = smprintf

  switch( uses ){
  case PTU_Script:  zTab = SHVAR_TABLE_SNAME; break;
  case PTU_Binding: zTab = PARAM_TABLE_SNAME; break;
  default: assert(0);
  }
  zSql = smprintf("SELECT value, uses FROM %s "
    ("SELECT value, uses FROM " PARAM_TABLE_SNAME " WHERE key=%Q", name);
                  "WHERE key=%Q AND uses=%d", zTab, name, uses);
  shell_check_oom(zSql);
  sqlite3_exec(db, zSql, kv_find_callback, &kvRow, 0);
  sqlite3_exec(db, zSql, param_find_callback, &paramVU, 0);
  sqlite3_free(zSql);
  assert(kvRow.hits<2);
  if( kvRow.hits==1 && kvRow.uses==uses){
  assert(paramVU.hits<2);
  if( paramVU.hits==1 && paramVU.uses==uses){
    /* Editing an existing value of same kind. */
    sqlite3_free(kvRow.value);
    sqlite3_free(paramVU.value);
    if( eval!=0 ){
      zSql = smprintf("SELECT edit(value, %Q) FROM %s "
                      "WHERE key=%Q AND uses=%d", zEditor, zTab, name, uses);
      zSql = smprintf
        ("SELECT edit(value, %Q) FROM " PARAM_TABLE_SNAME
         " WHERE key=%Q AND uses=%d", zEditor, name, uses);
      shell_check_oom(zSql);
      zVal = db_text(db, zSql, 1);
      sqlite3_free(zSql);
      zSql = smprintf("UPDATE %s SET value=(SELECT %s) "
                      "WHERE key=%Q AND uses=%d", zTab, zVal, name, uses);
      zSql = smprintf
        ("UPDATE "PARAM_TABLE_SNAME" SET value=(SELECT %s) WHERE"
         " key=%Q AND uses=%d", zVal, name, uses);
    }else{
      zSql = smprintf("UPDATE %s SET value=edit(value, %Q) WHERE"
                      " key=%Q AND uses=%d", zTab, zEditor, name, uses);
      zSql = smprintf
        ("UPDATE "PARAM_TABLE_SNAME" SET value=edit(value, %Q) WHERE"
         " key=%Q AND uses=%d", zEditor, name, uses);
    }
  }else{
    /* Editing a new value of same kind. */
    assert(kvRow.value==0 || kvRow.uses!=uses);
    assert(paramVU.value==0 || paramVU.uses!=uses);
    if( eval!=0 ){
      zSql = smprintf
      zSql = smprintf("SELECT edit('-- %q%s', %Q)", name, "\n", zEditor);
        ("SELECT edit('-- %q%s', %Q)", name, "\n", zEditor);
      zVal = db_text(db, zSql, 1);
      sqlite3_free(zSql);
      zSql = smprintf("INSERT INTO %s(key,value,uses)"
      zSql = smprintf
        ("INSERT INTO "PARAM_TABLE_SNAME"(key,value,uses)"
         " VALUES (%Q,(SELECT %s LIMIT 1),%d)",
         zTab, name, zVal, uses);
         name, zVal, uses);
    }else{
      zSql = smprintf("INSERT INTO %s(key,value,uses)"
                      " VALUES (%Q,edit('-- %q;%s', %Q),%d)",
                      zTab, name, name, "\n", zEditor, uses);
      zSql = smprintf
        ("INSERT INTO "PARAM_TABLE_SNAME"(key,value,uses)"
         " VALUES (%Q,edit('-- %q%s', %Q),%d)",
         name, name, "\n", zEditor, uses);
    }
  }
  shell_check_oom(zSql);
  if( eval!=0 ){
  }
  rc = sqlite3_exec(db, zSql, 0, 0, 0);
  sqlite3_free(zSql);
  sqlite3_free(zVal);
  return rc!=SQLITE_OK;
}
#endif

/* Space-join values in an argument list. *valLim is not included. */
char *values_join( char **valBeg, char **valLim ){
  char *z = 0;
  const char *zSep = 0;
  while( valBeg < valLim ){
    z = smprintf("%z%s%s", z, zSep, *valBeg);
    zSep = " ";
    ++valBeg;
  }
  return z;
}

/* Get a named parameter value in form of stepped prepared statement,
 * ready to have its value taken from the 0th column. If the name
 * cannot be found for the given ParamTableUse, 0 is returned.
 * The caller is responsible for calling sqlite3_finalize(pStmt),
 * where pStmt is the return from this function.
 */
static sqlite3_stmt *get_param_value(sqlite3 *db, char *name,
                                     ParamTableUse ptu){
  sqlite3_stmt *rv = 0;
  int rc;
  char *zSql = smprintf
    ( "SELECT value FROM "PARAM_TABLE_SNAME
      " WHERE key=%Q AND uses=%d", name, ptu );
  shell_check_oom(zSql);
  rc = sqlite3_prepare_v2(db, zSql, -1, &rv, 0);
  sqlite3_free(zSql);
  if( SQLITE_OK==rc ){
    if( SQLITE_ROW==sqlite3_step(rv) ) return rv;
    sqlite3_finalize(rv);
  }
  return 0;
}

static struct ParamSetOpts {
  const char cCast;
  const char *zTypename;
  int evalKind;
} param_set_opts[] = {
  /* { 'q', 0, 2 }, */
10757
10758
10759
10760
10761
10762
10763
10764
10765
10766
10767
10768
10769
10770
10771
10772
10773
10774
10775
10776
10777
10778
10779
10780
10781
10782
10783
10784
10785
10786

10787
10788
10789


10790
10791
10792
10793
10794
10795
10796
10797
10798
10799
10800
10801
10802
10803
10804
10805
10806
10807
10808
10809
10810

10811
10812
10813
10814
10815

10816
10817
10818
10819
10820
10821
10822
10823
10824
10825
10826
10827

10828
10829
10830
10831
10832
10833
10834
10835
10836
10837
10838
10839
10840
10841



10842

10843

10844
10845
10846
10847
10848
10849
10850
10851
10852
10853
10854
10855
10856
10857
10858
10859
10860
10861
10862

10863
10864

10865
10866
10867
10868
10869
10870
10871
10634
10635
10636
10637
10638
10639
10640























10641
10642
10643

10644
10645
10646
10647
10648
10649
10650
10651
10652
10653
10654
10655
10656
10657
10658
10659
10660
10661
10662
10663
10664
10665

10666

10667
10668
10669

10670
10671
10672
10673
10674
10675
10676
10677
10678
10679
10680
10681

10682
10683
10684
10685
10686
10687
10688
10689
10690
10691
10692
10693



10694
10695
10696
10697
10698

10699
10700
10701
10702
















10703


10704
10705
10706
10707
10708
10709
10710
10711







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+


-
+
+




















-
+
-



-
+











-
+











-
-
-
+
+
+

+
-
+



-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+







    ++zArg;
    if( zArg[0]=='-' ) ++zArg;
    if( zArg[0]!=0 && zArg[1]==0 ) return zArg[0];
  }
  return 0;
}

/* Most of .vars set
 * Return SQLITE_OK on success, else SQLITE_ERROR.
 */
static int shvar_set(sqlite3 *db, char *name, char **valBeg, char **valLim){
  int rc = SQLITE_OK;
  char *zValGlom = (valLim-valBeg>1)? values_join(valBeg, valLim) : 0;
  sqlite3_stmt *pStmtSet = 0;
  char *zValue = (zValGlom==0)? *valBeg : zValGlom;
  char *zSql
    = smprintf("REPLACE INTO "SHVAR_TABLE_SNAME"(key,value,uses)"
               "VALUES(%Q,%Q,%d);", name, zValue, PTU_Script);
  shell_check_oom(zSql);
  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmtSet, 0);
  assert(rc==SQLITE_OK);
  sqlite3_free(zSql);
  rc = (SQLITE_DONE==sqlite3_step(pStmtSet))? SQLITE_OK : SQLITE_ERROR;
  sqlite3_finalize(pStmtSet);
  sqlite3_free(zValGlom);
  return rc;
}


/* Most of the .parameter set subcommand (per help text)
/* The set subcommand (per help text)
 */
static int param_set(sqlite3 *db, char cCast,
                     char *name, char **valBeg, char **valLim){
                     char *name, char **valBeg, char **valLim,
                     ParamTableUse ptu){
  char *zSql = 0;
  int rc = SQLITE_OK, retries = 0, needsEval = 1;
  char *zValGlom = (valLim-valBeg>1)? values_join(valBeg, valLim) : 0;
  sqlite3_stmt *pStmtSet = 0;
  const char *zCastTo = 0;
  char *zValue = (zValGlom==0)? *valBeg : zValGlom;
  if( cCast ){
    struct ParamSetOpts *pSO = param_set_opts;
    for(; pSO-param_set_opts < ArraySize(param_set_opts); ++pSO ){
      if( cCast==pSO->cCast ){
        zCastTo = pSO->zTypename;
        needsEval = pSO->evalKind > 0;
        break;
      }
    }
  }
  if( needsEval ){
    if( zCastTo!=0 ){
      zSql = smprintf
        ( "REPLACE INTO "PARAM_TABLE_SNAME"(key,value,uses)"
          " VALUES(%Q,CAST((%s) AS %s),%d);",
          " VALUES(%Q,CAST((%s) AS %s),%d);", name, zValue, zCastTo, ptu );
          name, zValue, zCastTo, PTU_Binding );
    }else{
      zSql = smprintf
        ( "REPLACE INTO "PARAM_TABLE_SNAME"(key,value,uses)"
          "VALUES(%Q,(%s),%d);", name, zValue, PTU_Binding );
          "VALUES(%Q,(%s),%d);", name, zValue, ptu );
    }
    shell_check_oom(zSql);
    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmtSet, 0);
    sqlite3_free(zSql);
  }
  if( !needsEval || rc!=SQLITE_OK ){
    /* Reach here when value either requested to be cast to text, or must be. */
    sqlite3_finalize(pStmtSet);
    pStmtSet = 0;
    zSql = smprintf
      ( "REPLACE INTO "PARAM_TABLE_SNAME"(key,value,uses)"
        "VALUES(%Q,%Q,%d);", name, zValue, PTU_Binding );
        "VALUES(%Q,%Q,%d);", name, zValue, ptu );
    shell_check_oom(zSql);
    rc = sqlite3_prepare_v2(db, zSql, -1, &pStmtSet, 0);
    assert(rc==SQLITE_OK);
    sqlite3_free(zSql);
  }
  sqlite3_step(pStmtSet);
  sqlite3_finalize(pStmtSet);
  sqlite3_free(zValGlom);
  return rc;
}

/* list or ls subcommand for .parameter and .vars dot-commands */
static void list_pov_entries(ShellExState *psx, ParamTableUse ptu, u8 bShort,
                             char **pzArgs, int nArg){
/* list or ls subcommand for .parameter dot-command */
static void list_params(ShellExState *psx, ParamTableUse ptu, u8 bShort,
                        char **pzArgs, int nArg){
  sqlite3_stmt *pStmt = 0;
  sqlite3 *db = DBX(psx);
  sqlite3_str *sbList;
  sqlite3_str *sbList = sqlite3_str_new(db);
  int len = 0, rc;
  char *zFromWhere = 0;
  char *zSql = 0;
  sqlite3 *db;
  const char *zTab;

  switch( ptu ){
  case PTU_Binding:
    db = DBX(psx);
    zTab = PARAM_TABLE_SNAME;
    break;
  case PTU_Script:
    db = psx->dbShell;
    zTab = SHVAR_TABLE_NAME;
    break;
  default: assert(0); return;
  }
  sbList = sqlite3_str_new(db);
  sqlite3_str_appendf(sbList, "FROM ");
  sqlite3_str_appendf(sbList, "FROM "PARAM_TABLE_SNAME
  sqlite3_str_appendf(sbList, zTab);
  sqlite3_str_appendf(sbList, " WHERE (uses=?1) AND ");
                      " WHERE (?1=3 OR uses=?1) AND ");
  append_glob_terms(sbList, "key",
                    (const char **)pzArgs, (const char **)pzArgs+nArg);
  zFromWhere = sqlite3_str_finish(sbList);
  shell_check_oom(zFromWhere);
  zSql = smprintf("SELECT max(length(key)) %s", zFromWhere);
  shell_check_oom(zSql);
  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
10979
10980
10981
10982
10983
10984
10985
10986

10987
10988
10989
10990
10991
10992
10993
10994
10995




10996
10997
10998
10999
11000
11001
11002
10819
10820
10821
10822
10823
10824
10825

10826
10827
10828
10829
10830
10831
10832



10833
10834
10835
10836
10837
10838
10839
10840
10841
10842
10843







-
+






-
-
-
+
+
+
+







  ".parameter CMD ...       Manage SQL parameter bindings and scripts table",
  "   clear ?NAMES?           Erase all or only given named parameters",
#ifndef SQLITE_NOHAVE_SYSTEM
  "   edit ?OPT? NAME ...     Use edit() to create or alter parameter NAME",
  "      OPT may be -t to use edited value as text or -e to evaluate it.",
#endif
  "   init                    Initialize TEMP table for bindings and scripts",
  "   list ?PATTERNS?         List current DB parameters table binding values",
  "   list ?PATTERNS?         List parameters table binding and script values",
  "      Alternatively, to list just some or all names: ls ?PATTERNS?",
  "   load ?FILE? ?NAMES?     Load some or all named parameters from FILE",
  "      If FILE missing, empty or '~', it defaults to ~/sqlite_params.sdb",
  "   save ?FILE? ?NAMES?     Save some or all named parameters into FILE",
  "      If FILE missing, empty or '~', it defaults to ~/sqlite_params.sdb",
  "   set ?TOPT? NAME VALUE   Give SQL parameter NAME a value of VALUE",
  "      NAME must begin with one of $,:,@,? for bindings, VALUE is the space-",
  "      joined argument list. TOPT may be one of {-b -i -n -r -t} to cast the",
  "      effective value to BLOB, INT, NUMERIC, REAL or TEXT respectively.",
  "      NAME must begin with one of $,:,@,? for bindings, or with a letter",
  "      to be executable; value is the space-joined argument list.",
  "      Option TOPT may be one of {-b -i -n -r -t} to cast effective value",
  "      to BLOB, INT, NUMERIC, REAL or TEXT respectively.",
  "   unset ?NAMES?           Remove named parameter(s) from parameters table",
];
DISPATCHABLE_COMMAND( parameter 2 2 0 ){
  int rc = 0;
  open_db(p,0);
  sqlite3 *db = DBX(p);

11017
11018
11019
11020
11021
11022
11023

11024
11025
11026
11027
11028
11029
11030
11031
11032
11033
11034
11035
11036





11037
11038















11039

11040
11041
11042
11043
11044
11045
11046
11047


11048
11049
11050
11051
11052
11053


11054
11055
11056
11057
11058
11059
11060




11061
11062
11063

11064
11065

11066
11067
11068
11069
11070
11071
11072
11073
11074
11075
11076
11077
11078
11079
11080
11081
11082
11083
11084
11085

11086
11087
11088
11089
11090
11091
11092
11093

11094
11095
11096
11097
11098
11099
11100

11101
11102
11103
11104
11105
11106
11107
11108
11109
11110
11111
11112
11113

11114
11115
11116
11117
11118
11119


11120
11121
11122
11123
11124
11125
11126
11127
11128
11129
11130
11131
11132

11133
11134
11135
11136
11137
11138
11139
10858
10859
10860
10861
10862
10863
10864
10865
10866
10867
10868
10869
10870


10871
10872
10873
10874
10875
10876
10877
10878
10879
10880
10881


10882
10883
10884
10885
10886
10887
10888
10889
10890
10891
10892
10893
10894
10895
10896

10897
10898
10899
10900
10901
10902



10903
10904
10905
10906
10907
10908
10909

10910
10911
10912
10913
10914




10915
10916
10917
10918
10919
10920

10921
10922

10923
10924
10925
10926
10927
10928
10929
10930
10931
10932
10933
10934
10935
10936
10937
10938
10939
10940
10941
10942

10943
10944
10945
10946
10947
10948
10949
10950

10951
10952
10953
10954
10955
10956
10957

10958
10959
10960
10961
10962
10963
10964
10965
10966
10967
10968
10969
10970

10971
10972
10973
10974
10975
10976

10977
10978
10979
10980
10981
10982
10983
10984
10985
10986
10987
10988
10989
10990

10991
10992
10993
10994
10995
10996
10997
10998







+





-
-






+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+





-
-
-
+
+





-
+
+



-
-
-
-
+
+
+
+


-
+

-
+



















-
+







-
+






-
+












-
+





-
+
+












-
+







      sqlite3_exec(db, zSql, 0, 0, 0);
      sqlite3_free(zSql);
    }
  }else
#ifndef SQLITE_NOHAVE_SYSTEM
  /* .parameter edit ?NAMES?
  ** Edit the named parameters. Any that do not exist are created.
  ** New ones get a uses tag auto-selected by their leading char.
  */
  if( strcmp(azArg[1],"edit")==0 ){
    ShellInState *psi = ISS(p);
    int ia = 2;
    int eval = 0;
    int edSet;

    if( !INSOURCE_IS_INTERACTIVE(psi->pInSource) ){
      utf8_printf(STD_ERR, "Error: "
                  ".parameter edit can only be used interactively.\n");
      return DCR_Error;
    }
    param_table_init(db);
    if( psi->zEditor==0 ){
      const char *zE = getenv("VISUAL");
      if( zE!=0 ) psi->zEditor = smprintf("%s", zE);
    }
    if( nArg>=3 && azArg[2][0]=='-' ){
    edSet = attempt_editor_set(psi, azArg[0], (nArg>2)? azArg[2] : 0 );
    if( edSet < 0 ) return DCR_Error;
      char *zArg = (azArg[2][1]=='-')? azArg[2]+2 : azArg[2]+1;
      if( strncmp(zArg,"editor=",7)==0 ){
        sqlite3_free(psi->zEditor);
        /* Accept an initial -editor=? option. */
        psi->zEditor = smprintf("%s", zArg+7);
        ++ia;
      }
    }
    if( psi->zEditor==0 ){
      utf8_printf(STD_ERR,
                  "Either set env-var VISUAL to name an"
                  " editor and restart, or rerun\n "
                  ".parameter edit with an initial "
                  "edit option, --editor=EDITOR_COMMAND .\n");
      return DCR_Error;
    else ia += edSet;
    }
    /* Future: Allow an option whereby new value can be evaluated
     * the way that .parameter set ... does.
     */
    while( ia < nArg ){
      ParamTableUse ptu;
      char *zA = azArg[ia];
      char cf = (zA[0]=='-')? zA[1] : 0;
      if( cf!=0 && zA[2]==0 ){
      char cf = (azArg[ia][0]=='-')? azArg[ia][1] : 0;
      if( cf!=0 && azArg[ia][2]==0 ){
        ++ia;
        switch( cf ){
        case 'e': eval = 1; continue;
        case 't': eval = 0; continue;
        default:
          utf8_printf(STD_ERR, "Error: bad .parameter name: %s\n", zA);
          utf8_printf(STD_ERR, "Error: bad .parameter name: %s\n",
                      azArg[--ia]);
          return DCR_Error;
        }
      }
      ptu = classify_param_name(zA);
      if( ptu!=PTU_Binding ){
        utf8_printf(STD_ERR, "Error: "
                    "%s cannot be a binding parameter name.\n", zA);
      ptu = classify_param_name(azArg[ia]);
      if( ptu==PTU_Nil ){
        utf8_printf(STD_ERR, "Error: %s cannot be a binding or executable"
                    " parameter name.\n", azArg[ia]);
        return DCR_Error;
      }
      rc = edit_one_kvalue(db, zA, eval, ptu, psi->zEditor);
      rc = edit_one_param(db, azArg[ia], eval, ptu, psi->zEditor);
      ++ia;
      if( rc!=0 ) return DCR_Error;
      if( rc!=0 ) return rc;
    }
  }else
#endif

  /* .parameter init
  ** Make sure the TEMP table used to hold bind parameters exists.
  ** Create it if necessary.
  */
  if( nArg==2 && strcmp(azArg[1],"init")==0 ){
    param_table_init(db);
  }else

  /* .parameter list|ls
  ** List all or selected bind parameters.
  ** list displays names, values and uses.
  ** ls displays just the names.
  */
  if( nArg>=2 && ((strcmp(azArg[1],"list")==0)
                  || (strcmp(azArg[1],"ls")==0)) ){
    list_pov_entries(p, PTU_Binding, azArg[1][1]=='s', azArg+2, nArg-2);
    list_params(p, PTU_Nil, azArg[1][1]=='s', azArg+2, nArg-2);
  }else

  /* .parameter load
  ** Load all or named parameters from specified or default (DB) file.
  */
  if( strcmp(azArg[1],"load")==0 ){
    param_table_init(db);
    rc = kv_pairs_load(db, PTU_Binding, (const char **)azArg+1, nArg-1);
    rc = parameters_load(db, (const char **)azArg+1, nArg-1);
  }else

  /* .parameter save
  ** Save all or named parameters into specified or default (DB) file.
  */
  if( strcmp(azArg[1],"save")==0 ){
    rc = kv_pairs_save(db, PTU_Binding, (const char **)azArg+1, nArg-1);
    rc = parameters_save(db, (const char **)azArg+1, nArg-1);
  }else

  /* .parameter set NAME VALUE
  ** Set or reset a bind parameter.  NAME should be the full parameter
  ** name exactly as it appears in the query.  (ex: $abc, @def).  The
  ** VALUE can be in either SQL literal notation, or if not it will be
  ** understood to be a text string.
  */
  if( nArg>=4 && strcmp(azArg[1],"set")==0 ){
    char cCast = option_char(azArg[2]);
    int inv = 2 + (cCast != 0);
    ParamTableUse ptu = classify_param_name(azArg[inv]);
    if( ptu!=PTU_Binding ){
    if( ptu==PTU_Nil ){
      utf8_printf(STD_ERR,
                  "Error: %s is not a usable parameter name.\n", azArg[inv]);
      rc = 1;
    }else{
      param_table_init(db);
      rc = param_set(db, cCast, azArg[inv], &azArg[inv+1], &azArg[nArg]);
      rc = param_set(db, cCast, azArg[inv],
                     &azArg[inv+1], &azArg[nArg], ptu);
      if( rc!=SQLITE_OK ){
        utf8_printf(STD_ERR, "Error: %s\n", sqlite3_errmsg(db));
        rc = 1;
      }
    }
  }else

  {  /* If no command name and arg count matches, show a syntax error */
    showHelp(ISS(p)->out, "parameter", p);
    return DCR_CmdErred;
  }

  return DCR_Ok | (rc!=0);
  return rc;
}

/*****************
 * The .print, .progress and .prompt commands
 */
CONDITION_COMMAND( progress !defined(SQLITE_OMIT_PROGRESS_CALLBACK) );
COLLECT_HELP_TEXT[
13013
13014
13015
13016
13017
13018
13019
13020
13021
13022
13023
13024
13025
13026
13027
13028
13029
13030
13031
13032
13033
13034
13035
13036
13037
13038
13039
13040
13041
13042
13043
13044
13045
13046
13047
13048
13049
13050
13051
13052
13053
13054
13055
13056
13057
13058
13059
13060
13061
13062
13063
13064
13065
13066
13067
13068
13069
13070
13071
13072
13073
13074
13075
13076
13077
13078
13079
13080
13081
13082
13083
13084
13085
13086
13087
13088
13089
13090
13091
13092
13093
13094
13095
13096
13097
13098
13099
13100
13101
13102
13103
13104
13105
13106
13107
13108
13109
13110
13111
13112
13113
13114
13115
13116
13117
13118
13119
13120
13121
13122
13123
13124
13125
13126
13127
13128
13129
13130
13131
13132
13133
13134
13135
13136
13137
13138
13139
13140
13141
13142
13143
13144
13145
13146
13147
13148
13149
13150
13151
13152
13153
13154
13155
12872
12873
12874
12875
12876
12877
12878

































































































































12879
12880
12881
12882
12883
12884
12885







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







    }
  }else{
    goto teach_fail;
  }
  return DCR_Ok;
}

/*****************
 * The .vars command
 */
COLLECT_HELP_TEXT[
  ".vars ?OPTIONS? ...      Manipulate and display shell variables",
  "   clear ?NAMES?           Erase all or only given named variables",
#ifndef SQLITE_NOHAVE_SYSTEM
  "   edit ?-e? NAME        Use edit() to create or alter variable NAME",
  "      With a -e option, the edited value is evaluated as a SQL expression.",
#endif
  "   list ?PATTERNS?         List shell variables table values",
  "      Alternatively, to list just some or all names: ls ?PATTERNS?",
  "   load ?FILE? ?NAMES?     Load some or all named variables from FILE",
  "      If FILE missing, empty or '~', it defaults to ~/sqlite_vars.sdb",
  "   save ?FILE? ?NAMES?     Save some or all named variables into FILE",
  "      If FILE missing, empty or '~', it defaults to ~/sqlite_vars.sdb",
  "   set NAME VALUE          Give shell variable NAME a value of VALUE",
  "      NAME must begin with a letter to be executable by .x, Other leading",
  "      characters have special uses. VALUE is the space-joined arguments.",
  "   unset ?NAMES?           Remove named variables(s) from variables table",
];
DISPATCHABLE_COMMAND( vars 2 1 0 ){
  DotCmdRC rv = DCR_Ok;
  sqlite3 *dbs = p->dbShell;
  const char *zCmd = (nArg>1)? azArg[1] : "ls";
  int rc = 0;
  int ncCmd = strlen30(zCmd);

  if( *zCmd>'e' && ncCmd<2 ) return DCR_Ambiguous|1;
#define SUBCMD(scn) (strncmp(zCmd, scn, ncCmd)==0)

  /* This could be done lazily, but with more code. */
  if( (dbs && ensure_shvars_table(dbs)!=SQLITE_OK) ){
    return DCR_Error;
  }else{
    if( ensure_shell_db(p)!=SQLITE_OK ) return DCR_Error;
    dbs = p->dbShell;
    assert(dbs!=0);
    if( ensure_shvars_table(dbs)!=SQLITE_OK ) return DCR_Error;
  }

  /* .vars clear  and  .vars unset ?NAMES?
  **  Delete some or all key/value pairs from the shell variables table.
  **  Without any arguments, clear deletes them all and unset does nothing.
  */
  if( SUBCMD("clear") || SUBCMD("unset") ){
    if( (nArg>2 || zCmd[0]=='c') ){
      sqlite3_str *sbZap = sqlite3_str_new(dbs);
      char *zSql;
      sqlite3_str_appendf
        (sbZap, "DELETE FROM "SHVAR_TABLE_SNAME" WHERE key ");
      append_in_clause(sbZap,
                       (const char **)&azArg[2], (const char **)&azArg[nArg]);
      zSql = sqlite3_str_finish(sbZap);
      shell_check_oom(zSql);
      rc = sqlite3_exec(dbs, zSql, 0, 0, 0);
      sqlite3_free(zSql);
    }
#ifndef SQLITE_NOHAVE_SYSTEM
  }else if( SUBCMD("edit") ){
    ShellInState *psi = ISS(p);
    int ia = 2;
    int eval = 0;
    int edSet;

    if( !INSOURCE_IS_INTERACTIVE(psi->pInSource) ){
      utf8_printf(STD_ERR, "Error: "
                  ".vars edit can only be used interactively.\n");
      return DCR_Error;
    }
    edSet = attempt_editor_set(psi, azArg[0], (nArg>2)? azArg[2] : 0 );
    if( edSet < 0 ) return DCR_Error;
    else ia += edSet;
    while( ia < nArg ){
      ParamTableUse ptu;
      char *zA = azArg[ia];
      char cf = (zA[0]=='-')? zA[1] : 0;
      if( cf!=0 && zA[2]==0 ){
        ++ia;
        switch( cf ){
        case 'e': eval = 1; continue;
        case 't': eval = 0; continue;
        default:
          utf8_printf(STD_ERR, "Error: bad .vars edit option: %s\n", zA);
          return DCR_Error;
        }
      }
      ptu = classify_param_name(zA);
      if( ptu!=PTU_Script ){
        utf8_printf(STD_ERR,
                    "Error: %s cannot be a shell variable name.\n", zA);
        return DCR_Error;
      }
      rc = edit_one_kvalue(dbs, zA, eval, ptu, psi->zEditor);
      ++ia;
      if( rc!=0 ) return DCR_Error;
    }
#endif
  }else if( SUBCMD("list") || SUBCMD("ls") ){
    int nTailArgs = nArg - 1 - (nArg>1);
    char **pzTailArgs = azArg + 1 + (nArg>1);
    list_pov_entries(p, PTU_Script, zCmd[1]=='s', pzTailArgs, nTailArgs);
  }else if( SUBCMD("load") ){
    rc = kv_pairs_load(dbs, PTU_Script, (const char **)azArg+1, nArg-1);
  }else if( SUBCMD("save") ){
    rc = kv_pairs_save(dbs, PTU_Script, (const char **)azArg+1, nArg-1);
  }else if( SUBCMD("set") ){
    ParamTableUse ptu;
    if( nArg<4 ) return DCR_Missing;
    ptu = classify_param_name(azArg[2]);
    if( ptu!=PTU_Script ){
      utf8_printf(STD_ERR,
                  "Error: %s is not a valid shell variable name.\n", azArg[2]);
      rc = 1;
    }else{
      rc = shvar_set(dbs, azArg[2], &azArg[3], &azArg[nArg]);
      if( rc!=SQLITE_OK ){
        utf8_printf(STD_ERR, "Error: %s\n", sqlite3_errmsg(dbs));
        rc = 1;
      }
    }
  }else{
    showHelp(ISS(p)->out, "vars", p);
    return DCR_CmdErred;
  }
  return DCR_Ok | (rv!=0) | (rc!=0);
#undef SUBCMD
}

/*****************
 * The .vfsinfo, .vfslist, .vfsname and .version commands
 */
COLLECT_HELP_TEXT[
  ".version                 Show a variety of version info",
  ".vfsinfo ?AUX?           Information about the top-level VFS",
  ".vfslist                 List all available VFSes",
13378
13379
13380
13381
13382
13383
13384
13385

13386
13387

13388
13389
13390
13391
13392
13393
13394
13395
13396
13397
13398
13399
13400
13401
13402
13403
13404
13405

13406
13407


13408
13409
13410








13411
13412
13413

13414
13415
13416

13417
13418
13419
13420
13421
13422
13423
13108
13109
13110
13111
13112
13113
13114

13115
13116

13117
13118
13119
13120
13121
13122
13123
13124
13125
13126
13127
13128
13129
13130
13131
13132
13133
13134

13135
13136
13137
13138
13139



13140
13141
13142
13143
13144
13145
13146
13147
13148
13149

13150
13151
13152

13153
13154
13155
13156
13157
13158
13159
13160







-
+

-
+

















-
+


+
+
-
-
-
+
+
+
+
+
+
+
+


-
+


-
+







  }
  return rc;
}

DISPATCHABLE_COMMAND( x ? 1 0 ){
  int ia, nErrors = 0;
  sqlite3_stmt *pStmt = 0;
  sqlite3 *dbs = p->dbShell;
  sqlite3 *db = 0;
  DotCmdRC rv = DCR_Ok;
  enum { AsVar, AsString, AsFile } evalAs = AsVar;
  enum { AsParam, AsString, AsFile } evalAs = AsParam;

  for( ia=1; ia < nArg; ++ia ){
    char *zSubmit = 0;
    const char *zOpt = azArg[ia];
    if ( *zOpt == '-' ){
      static const char *azOpts[] = { "p", "s", "f" };
      int io = ArraySize(azOpts);
      while( io > 0 ){
        if( optionMatch(zOpt, azOpts[--io]) ){
          evalAs = io;
          zOpt = 0;
          break;
        }
      }
      if( zOpt==0 ) continue;
    }
    switch( evalAs ){
    case AsVar:
    case AsParam:
      if( pStmt==0 ){
        int rc;
        open_db(p, 0);
        db = DBX(p);
        if( dbs==0 || !shvars_table_exists(dbs) ){
          utf8_printf(STD_ERR,
                      "\".x vname\" can only be done after .var set ... .\n");
        if( db==0 ){
          utf8_printf(STD_ERR, ".x can only be done with a database open.\n");
          return DCR_Error;
        }
        if( sqlite3_table_column_metadata(db, PARAM_TABLE_SCHEMA,
                                          PARAM_TABLE_NAME, "key",
                                          0, 0, 0, 0, 0)!=SQLITE_OK ){
          utf8_printf(STD_ERR, "No "PARAM_TABLE_SNAME" table exists.\n");
          return DCR_Error;
        }
        rc = sqlite3_prepare_v2(dbs, "SELECT value FROM "SHVAR_TABLE_SNAME
        rc = sqlite3_prepare_v2(db, "SELECT value FROM "PARAM_TABLE_SNAME
                                " WHERE key=$1 AND uses=1", -1, &pStmt, 0);
        if( rc!=SQLITE_OK ){
          utf8_printf(STD_ERR, SHVAR_TABLE_SNAME" is wrongly created.\n");
          utf8_printf(STD_ERR, PARAM_TABLE_SNAME" is wrongly created.\n");
          return DCR_Error;
        }
      }
      if( isalpha(azArg[ia][0]) ){
        int rc = sqlite3_reset(pStmt);
        rc = sqlite3_bind_text(pStmt, 1, azArg[ia], -1, 0);
        rc = sqlite3_step(pStmt);
13436
13437
13438
13439
13440
13441
13442
13443

13444
13445
13446
13447
13448
13449
13450
13173
13174
13175
13176
13177
13178
13179

13180
13181
13182
13183
13184
13185
13186
13187







-
+







              if( rv>DCR_Error ) break;
            }
          }else{
            continue; /* All white (or OOM), ignore. */
          }
        }else{
          utf8_printf(STD_ERR,
                      "Skipping var '%s' (not set and executable.)\n",
                      "Skipping parameter '%s' (not set and executable.)\n",
                      azArg[ia]);
          ++nErrors;
        }
      }else{
        utf8_printf(STD_ERR,
                    "Skipping badly named %s. Run \".help x\"\n", azArg[ia]);
        ++nErrors;
13596
13597
13598
13599
13600
13601
13602
13603

13604
13605
13606
13607
13608
13609
13610

13611
13612
13613
13614
13615
13616
13617
13333
13334
13335
13336
13337
13338
13339

13340
13341
13342
13343
13344
13345
13346

13347
13348
13349
13350
13351
13352
13353
13354







-
+






-
+







} MetaMatchIter;

/* Prepare an iterator that will produce a sequence of MetaCommand
 * pointers whose referents names match the given cmdFragment. */
static MetaMatchIter findMatchingMetaCmds(const char *cmdFragment,
                                          ShellExState *psx){
  MetaMatchIter rv = { psx, 0, 0 };
  if( psx->dbShell==0 || ISS(psx)->bDbDispatch==0 ){
  if( psx->dbShell==0 ){
    rv.zPattern = smprintf("%s*", cmdFragment? cmdFragment : "");
    shell_check_oom((void *)rv.zPattern);
    rv.pMC = (MetaCommand *)command_table;
  }else{
    /* Prepare rv.stmt to yield results glob-matching cmdFragment. */
    const char *zSql =
      "SELECT name, extIx, cmdIx FROM "SHELL_DISP_VIEW" "
      "SELECT name, extIx, cmdIx FROM ActiveCommands "
      "WHERE name glob (?||'*') ORDER BY name";
    sqlite3_prepare_v2(psx->dbShell, zSql, -1, &rv.stmt, 0);
    sqlite3_bind_text(rv.stmt, 1, cmdFragment? cmdFragment : "", -1, 0);
  }
  return rv;
}
/* Produce the next MetaCommand pointer from the iterator, or 0 if no next. */
13664
13665
13666
13667
13668
13669
13670
13671

13672
13673
13674
13675
13676

13677
13678
13679
13680
13681
13682
13683
13401
13402
13403
13404
13405
13406
13407

13408
13409
13410
13411
13412

13413
13414
13415
13416
13417
13418
13419
13420







-
+




-
+







** If shell extensions are loaded, the match must be long enough to
** result in a unique lookup.
*/
MetaCommand *findMetaCommand(const char *cmdName, ShellExState *psx,
                             /* out */ int *pnFound){
  if( pnFound ) *pnFound = 0;
#if SHELL_DYNAMIC_EXTENSION
  if( ISS(psx)->bDbDispatch ){
  if( psx->dbShell!=0 ){
    int rc;
    int extIx = -1, cmdIx = -1, nf = 0;
    sqlite3_stmt *pStmt = 0;
    const char *zSql = "SELECT COUNT(*), extIx, cmdIx"
      " FROM "SHELL_DISP_VIEW" WHERE name glob (?||'*')";
      " FROM ActiveCommands WHERE name glob (?||'*')";
    rc = sqlite3_prepare_v2(psx->dbShell, zSql, -1, &pStmt, 0);
    sqlite3_bind_text(pStmt, 1, cmdName, -1, 0);
    rc = sqlite3_step(pStmt);
    nf = sqlite3_column_int(pStmt, 0);
    extIx = sqlite3_column_int(pStmt, 1);
    cmdIx = sqlite3_column_int(pStmt, 2);
    sqlite3_finalize(pStmt);
14636
14637
14638
14639
14640
14641
14642
14643

14644
14645
14646
14647
14648
14649
14650
14373
14374
14375
14376
14377
14378
14379

14380
14381
14382
14383
14384
14385
14386
14387







-
+







      } /* end glom another line */
    } /* end group collection loop */
    /* Here, the group is fully collected or known to be incomplete forever. */
    switch( disposition ){
    case Dumpable:
      echo_group_input(psi, *pzLineUse);
#if SHELL_DYNAMIC_EXTENSION
      if( inKind==Script && pSS!=0 ) pSS->pMethods->resetCompletionScan(pSS);
      if( inKind==Script && pSS!=0 ) pSS->pMethods->restartCompletionScan(pSS);
#endif
      break;
    case Runnable:
      switch( inKind ){
      case Sql:
        /* runOneSqlLine() does its own echo when requested. */
        nErrors += runOneSqlLine(XSS(psi), *pzLineUse,
14661
14662
14663
14664
14665
14666
14667
14668

14669
14670
14671
14672
14673
14674
14675
14398
14399
14400
14401
14402
14403
14404

14405
14406
14407
14408
14409
14410
14411
14412







-
+







        break;
      }
#if SHELL_DYNAMIC_EXTENSION
      case Script: {
        char *zErr = 0;
        DotCmdRC dcr;
        assert(pSS!=0);
        pSS->pMethods->resetCompletionScan(pSS);
        pSS->pMethods->restartCompletionScan(pSS);
        dcr = pSS->pMethods->runScript(pSS, *pzLineUse+nLeadWhite,
                                       XSS(psi), &zErr);
        if( dcr!=DCR_Ok || zErr!=0 ){
          /* ToDo: Handle errors more informatively and like dot commands. */
          nErrors += (dcr!=DCR_Ok);
          if( zErr!=0 ){
            utf8_printf(STD_ERR, "Error: %s\n", zErr);
14687
14688
14689
14690
14691
14692
14693
14694

14695
14696
14697
14698
14699
14700
14701
14424
14425
14426
14427
14428
14429
14430

14431
14432
14433
14434
14435
14436
14437
14438







-
+







        termKind = DCR_Exit;
      }
      break;
    case Erroneous:
      utf8_printf(STD_ERR, "Error: Input incomplete at line %d of \"%s\"\n",
                  psi->pInSource->lineno, psi->pInSource->zSourceSay);
#if SHELL_DYNAMIC_EXTENSION
      if( inKind==Script && pSS!=0 ) pSS->pMethods->resetCompletionScan(pSS);
      if( inKind==Script && pSS!=0 ) pSS->pMethods->restartCompletionScan(pSS);
#endif
      ++nErrors;
      break;
    case Ignore:
      break;
    default: assert(0);
    }
Changes to src/shext_linkage.h.
274
275
276
277
278
279
280
281

282
283
284
285
286
287
288
274
275
276
277
278
279
280

281
282
283
284
285
286
287
288







-
+







  DECORATE_METHOD(Derived,help), \
  DECORATE_METHOD(Derived,argsCheck), \
  DECORATE_METHOD(Derived,execute) \
}

/* This function pointer has the same signature as the sqlite3_X_init()
 * function that is called as SQLite3 completes loading an extension.
 * It is used as a process-unique identifier for a loaded extension.
 * It is used as a process-unique identifier for a loaded extention.
 */
typedef int (*ExtensionId)
  (sqlite3 *, char **, const struct sqlite3_api_routines *);

typedef struct Prompts {
  const char *zMain;
  const char *zContinue;
Changes to tool/mkshellc.tcl.
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
73
74
75
76
77
78
79

80
81
82
83
84
85
86







-








set ::topDir [file dir [file dir [file normal $argv0]]]
set runMode normal

set ::lineTags 0 ; # 0 => none, 1 => source change, 2 => line syncs, 3 => more

set ::tclGenerate 0
set ::invokeLeadWhite ""
set ::verbosity 0
set ::inFiles {}
set ::topInfile "?"
set ::presumedOutfile "?"
set ::targetProgram "?"
set ::defaultInfile "src/shell.c.in"
array set ::incTypes [list "*" "$::topDir/$::defaultInfile"]
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389

390
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
363
364
365
366
367
368
369

370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386

387
388
389
390


391
392
393

394
395
396
397
398
399
400
401







-

















-
+



-
-



-
+







  COMMENT {\s+(.*)$} \
  CONDITION_COMMAND {^\(\s*(\w+)\s+([^;]+)\);} \
  DISPATCH_CONFIG {^\[} \
  DISPATCHABLE_COMMAND {^\(([\w\? ]+)\)(\S)\s*$} \
  EMIT_METACMD_INIT {^\((\d*)\)} \
  INCLUDE {^(?:\(\s*(\w+)\s*\))|(?:\s+([\w./\\]+)\M)} \
  IGNORE_COMMANDS {^\(\s*([-+\w ]*)\)\s*;\s*} \
  TCL_CSTR_LITERAL {^\(([ \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_HELP_TEXT tailCapture_Empty
# DISPATCHABLE_COMMAND tailCapture_ArgsGlom_TrailChar
# EMIT_METACMD_INIT tailCapture_Indent
# IGNORED_COMMANDS tailCapture_SignedCmdGlom
# INCLUDE tailCapture_IncType_Filename

array set ::macroUsages [list \
  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  }" \
      "( name args... ){\n   <implementation code lines>\n  }" \
  EMIT_METACMD_INIT "( indent );" \
  INCLUDE {( <inc_type> )} \
  SKIP_COMMANDS "( <signed_names> );" \
  TCL_CSTR_LITERAL \
    "( C string literal decl = ) {\n <TCL code lines>\n };" \
]
# RE for early discard of non-macro lines, matching all above keywords
set ::macroKeywordTailRE \
 {^\s{0,40}((?:(?:CO)|(?:DI)|(?:EM)|(?:IN)|(?:TCL)|(?:SK))[A-Z_]+)\M(.+)$}
 {^\s{0,8}((?:(?:CO)|(?:DI)|(?:EM)|(?:IN)|(?:SK))[A-Z_]+)\M(.+)$}

########
# Macro procs, general signature and usage:
# inSrc is a triple, { input_filename open_input_stream input_lines_consumed }.
# Arg 2 is the macro tail as RE-captured by one of ::macroTailREs .
# ostrm is the open output stream for all regular output.
# The number of input lines consumed, including macro invocation, is returned.
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
504
505
506
507
508
509
510


















































511
512
513
514
515
516
517







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







    if {[regexp {^\s*(\w+)=(.+)$} $line ma k v]} {
      set ::dispCfg($k) $v
    }
  }
  return $iAte
}

# Translate a TCL line into a quoted/escaped C string literal line.
set ::bs "\\"
set ::dbs "\\\\"
set ::dq "\""
proc tcl2c {line} {
  regsub -all $::dbs $line {\\\\} line
  regsub -all $::dq $line "$::bs$::dq" line
  regsub {^\s*} $line "&$::dq" line
  return "$line${::bs}n$::dq"
}

proc TCL_CSTR_LITERAL {inSrc tailCapture ostrm} {
  # Collect and translate TCL code into a C string literal definition.
  # The macro call, TCL_CSTR_LITERAL( ... = ){, will name and declare
  # the type of the literal, and introduce the TCL lines, while lines
  # that follow, upto but not including one beginning with }, will be
  # quoted and escaped as needed to get the same logical content as a
  # multi-line initializer for the declared C string variable.
  foreach { srcFile istrm srcPrecLines } $inSrc break
  set litdef [lindex $tailCapture 0]
  set tc [lindex $tailCapture 1]
  if {$tc ne $::lb} {
    yap_usage "TCL_CSTR_LITERAL($litdef)$tc" TCL_CSTR_LITERAL
    incr $::iShuffleErrors
    return 0
  }
  set iAte 1
  set oIndent ""
  set firstIndent -1
  set clLines [list "$::invokeLeadWhite$litdef"]
  while {![eof $istrm]} {
    set tclLine [gets $istrm]
    incr iAte
    if {[regexp "^\\s*$::rb\\s*\;$" $tclLine]} {
      regsub "^(\\s*)$::rb" $tclLine {\1} tclLine
      lappend clLines $tclLine
      break
    }
    set lrt [string trimright $tclLine]
    set lt [string trimleft $lrt]
    if {$lt eq ""} {
      lappend clLines ""
      continue
    }
    lappend clLines [tcl2c $tclLine]
  }
  emit_sync $clLines $ostrm $srcPrecLines $srcFile
  return $iAte;
}

proc DISPATCHABLE_COMMAND {inSrc tailCapture ostrm} {
  # Generate and emit a function definition, maybe wrapped as set by
  # CONDITION_COMMAND(), and generate/collect its dispatch table entry,
  # as determined by its actual arguments and DISPATCH_CONFIG parameters.
  foreach { srcFile istrm srcPrecLines } $inSrc break
  set args [lindex $tailCapture 0]
  set tc [lindex $tailCapture 1]
674
675
676
677
678
679
680
681
682
683
684
685

686
687
688
689
690
691
692
620
621
622
623
624
625
626



627

628
629
630
631
632
633
634
635







-
-
-

-
+







# This function may consume additional lines via triple inSrc.
proc do_macro {inSrc lx ostrm} {
  if {![regexp $::macroKeywordTailRE $lx ma macro tail] \
          || ![info exists ::macroTailREs($macro)]} {
    return 0
  }
  # It's an attempted macro invocation line. Process or fail and yap.
  # First, capture its leading space for those macros respecting indent.
  regexp {^(\s*)} $lx ma ::invokeLeadWhite
  # Second, separate the tail according to this macro's RE for such.
  set tailCap [regexp -inline $::macroTailREs($macro) $tail]
  # Third, call like-named proc with args captured by the corresponding RE.
  # Call like-named proc with any args captured by the corresponding RE.
  return [$macro $inSrc [lrange $tailCap 1 end] $ostrm]
}

array set ::typedefsSeen {}
array set ::includesDone {}

# Filter redundant typedefs and certain includes and qualifiers, in place.