/ Check-in [ca4abf3f]
Login

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

Overview
Comment:Add support for saving the sqlite shell command-line history across sessions. (CVS 536)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:ca4abf3fe1f0e66802f9f98a20e0c8b82a6459aa
User & Date: drh 2002-04-19 12:34:06
Context
2002-04-20
14:24
Fix for ticket #1: Implement the GLOB and LIKE operators as functions that can be overridden. This way, a developer can change the LIKE operator to be case sensitive, for example. (CVS 537) check-in: 51572bf7 user: drh tags: trunk
2002-04-19
12:34
Add support for saving the sqlite shell command-line history across sessions. (CVS 536) check-in: ca4abf3f user: drh tags: trunk
01:00
Added rights release for Matthew O. Persico (CVS 535) check-in: 6c32c07e user: persicom tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/shell.c.

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
..
33
34
35
36
37
38
39



40
41
42
43
44
45
46
47
48





49
50
51
52
53
54
55
...
269
270
271
272
273
274
275

276
277
278
279
280
281
282
...
502
503
504
505
506
507
508


509
510
511
512
513

514
515
516
517
518
519
520
...
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
...
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
...
817
818
819
820
821
822
823
824
825
826
827

828
829
830
831
832
833
834
835
...
888
889
890
891
892
893
894
895
896
897
898

899








900
901
902
903
904
905




906
907
908
909

910
911
912
913
914
915
916
...
945
946
947
948
949
950
951
952

953






954































955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992

993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006



1007
1008
1009
1010
1011
1012
1013
....
1121
1122
1123
1124
1125
1126
1127


1128
1129
1130
1131
1132





1133




1134
1135
1136
1137
1138
1139
1140
1141
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
** 2002 April 18
**
** I, Matthew O. Persico, hereby release all edits made by me to
** this source into the public domain.
*************************************************************************
** This file contains code to implement the "sqlite" command line
** utility for accessing SQLite databases.
**
** $Id: shell.c,v 1.54 2002/04/19 01:00:13 persicom Exp $
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sqlite.h"
#include <ctype.h>

................................................................................

#if defined(HAVE_READLINE) && HAVE_READLINE==1
# include <readline/readline.h>
# include <readline/history.h>
#else
# define readline(p) getline(p,stdin)
# define add_history(X)



#endif

/*
** The following is the open SQLite database.  We make a pointer
** to this database a static variable so that it can be accessed
** by the SIGINT handler to interrupt database processing.
*/
static sqlite *db = 0;






/*
** This is the name of our program. It is set in main(), used
** in a number of other places, mostly for error messages.
*/
static char *Argv0;

/*
................................................................................
  }
}

/*
** This routine runs when the user presses Ctrl-C
*/
static void interrupt_handler(int NotUsed){

  if( db ) sqlite_interrupt(db);
}

/*
** This is the callback routine that the SQLite library
** invokes for each row of a query result.
*/
................................................................................

/* Forward reference */
static void process_input(struct callback_data *p, FILE *in);

/*
** If an input line begins with "." then invoke this routine to
** process that line.


*/
static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
  int i = 1;
  int nArg = 0;
  int n, c;

  char *azArg[50];

  /* Parse the input line into tokens.
  */
  while( zLine[i] && nArg<ArraySize(azArg) ){
    while( isspace(zLine[i]) ){ i++; }
    if( zLine[i]=='\'' || zLine[i]=='"' ){
................................................................................
      while( zLine[i] && !isspace(zLine[i]) ){ i++; }
      if( zLine[i] ) zLine[i++] = 0;
    }
  }

  /* Process the input line.
  */
  if( nArg==0 ) return;
  n = strlen(azArg[0]);
  c = azArg[0][0];
  if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
    char *zErrMsg = 0;
    fprintf(p->out, "BEGIN TRANSACTION;\n");
    if( nArg==1 ){
      sqlite_exec(db,
................................................................................
    }else if( strcmp(z,"yes")==0 ){
      val = 1;
    }
    p->echoOn = val;
  }else

  if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
    sqlite_close(db);
    exit(0);
  }else

  if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
    int j;
    char *z = nArg>=2 ? azArg[1] : "1";
    int val = atoi(z);
    for(j=0; z[j]; j++){
................................................................................
  if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
    sprintf(p->separator, "%.*s", (int)ArraySize(p->separator)-1, azArg[1]);
  }else

  if( c=='s' && strncmp(azArg[0], "show", n)==0){
    int i;
    fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
    fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" : "off");
    fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off");
    fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]);
    fprintf(p->out,"%9.9s: %s\n","nullvalue", p->nullvalue);

    fprintf(p->out,"%9.9s: %s\n","output", strlen(p->outfile) ? p->outfile : "stdout");
    fprintf(p->out,"%9.9s: %s\n","separator", p->separator);
    fprintf(p->out,"%9.9s: ","width");
    for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) {
        fprintf(p->out,"%d ",p->colWidth[i]);
    }
    fprintf(p->out,"\n\n");
  }else
................................................................................
    int j;
    for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
      p->colWidth[j-1] = atoi(azArg[j]);
    }
  }else

  {
    fprintf(stderr, "unknown command or invalid arguments: \"%s\". Enter \".help\" for help\n",
      azArg[0]);
  }
}










static void process_input(struct callback_data *p, FILE *in){
  char *zLine;
  char *zSql = 0;
  int nSql = 0;
  char *zErrMsg;
  while( fflush(p->out), (zLine = one_input_line(zSql, in))!=0 ){




    if( p->echoOn ) printf("%s\n", zLine);
    if( zLine && zLine[0]=='.' && nSql==0 ){
      do_meta_command(zLine, db, p);
      free(zLine);

      continue;
    }
    if( zSql==0 ){
      int i;
      for(i=0; zLine[i] && isspace(zLine[i]); i++){}
      if( zLine[i]!=0 ){
        nSql = strlen(zLine);
................................................................................
  }
  if( zSql ){
    printf("Incomplete SQL: %s\n", zSql);
    free(zSql);
  }
}

static void process_sqliterc(struct callback_data *p,

                             char *sqliterc_override){






































  char *home_dir = NULL;
  char *sqliterc = sqliterc_override;
  FILE *in = NULL;

  if (sqliterc == NULL) {
    /* Figure out the user's home directory */
#if !defined(_WIN32) && !defined(WIN32)
    struct passwd *pwent;
    uid_t uid = getuid();
    while( (pwent=getpwent()) != NULL) {
      if(pwent->pw_uid == uid) {
        home_dir = pwent->pw_dir;
        break;
      }
    }
#endif

    if (!home_dir) {
      home_dir = getenv("HOME");
      if (!home_dir) {
        home_dir = getenv("HOMEPATH"); /* Windows? */
      }
      if (!home_dir) {
        printf("Cannot find home directory from which to load .sqliterc\n");
        return;
      }
    }

    /* With the home directory, open the init file and process */
    sqliterc = (char *)calloc(strlen(home_dir) +
                              strlen("/.sqliterc") +
                              1,
                              sizeof(char));
    if( sqliterc==0 ){
      fprintf(stderr,"%s: out of memory!\n", Argv0);
      exit(1);
    }
    sprintf(sqliterc,"%s/.sqliterc",home_dir);

  }
  in = fopen(sqliterc,"r");
  if(!in) {
    /* File either had an error or was not found. Since I cannot
     * tell, I cannot bark. */
    return;
  } else {
    printf("Loading resources from %s\n",sqliterc);
    process_input(p,in);
    fclose(in);
  }
  return;
}




void main_init(struct callback_data *data) {
  memset(data, 0, sizeof(*data));
  data->mode = MODE_List;
  strcpy(data->separator,"|");
  data->showHeader = 0;
  strcpy(mainPrompt,"sqlite> ");
  strcpy(continuePrompt,"   ...> ");
................................................................................
        fprintf(stderr,"SQL error: %s\n", zErrMsg);
        exit(1);
      }
    }
  }else{
    extern int isatty();
    if( isatty(0) ){


      printf(
        "SQLite version %s\n"
        "Enter \".help\" for instructions\n",
        sqlite_version
      );





      process_input(&data, 0);




    }else{
      process_input(&data, stdin);
    }
  }
  set_table_name(&data, 0);
  sqlite_close(db);
  return 0;
}







<
<
<
<




|







 







>
>
>









>
>
>
>
>







 







>







 







>
>

|



>







 







|







 







|
<







 







|



>
|







 







|
|

|
>
|
>
>
>
>
>
>
>
>






>
>
>
>


|

>







 







<
>
|
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<





>


|
<
<
<
<







>
>
>







 







>
>





>
>
>
>
>

>
>
>
>








4
5
6
7
8
9
10




11
12
13
14
15
16
17
18
19
20
21
22
..
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
...
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
...
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
...
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
...
585
586
587
588
589
590
591
592

593
594
595
596
597
598
599
...
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
...
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
...
967
968
969
970
971
972
973

974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018






1019

















1020



1021
1022
1023
1024
1025
1026
1027
1028
1029




1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
....
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
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**




*************************************************************************
** This file contains code to implement the "sqlite" command line
** utility for accessing SQLite databases.
**
** $Id: shell.c,v 1.55 2002/04/19 12:34:06 drh Exp $
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sqlite.h"
#include <ctype.h>

................................................................................

#if defined(HAVE_READLINE) && HAVE_READLINE==1
# include <readline/readline.h>
# include <readline/history.h>
#else
# define readline(p) getline(p,stdin)
# define add_history(X)
# define read_history(X)
# define write_history(X)
# define stifle_history(X)
#endif

/*
** The following is the open SQLite database.  We make a pointer
** to this database a static variable so that it can be accessed
** by the SIGINT handler to interrupt database processing.
*/
static sqlite *db = 0;

/*
** True if an interrupt (Control-C) has been received.
*/
static int seenInterrupt = 0;

/*
** This is the name of our program. It is set in main(), used
** in a number of other places, mostly for error messages.
*/
static char *Argv0;

/*
................................................................................
  }
}

/*
** This routine runs when the user presses Ctrl-C
*/
static void interrupt_handler(int NotUsed){
  seenInterrupt = 1;
  if( db ) sqlite_interrupt(db);
}

/*
** This is the callback routine that the SQLite library
** invokes for each row of a query result.
*/
................................................................................

/* Forward reference */
static void process_input(struct callback_data *p, FILE *in);

/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
** Return 1 to exit and 0 to continue.
*/
static int do_meta_command(char *zLine, sqlite *db, struct callback_data *p){
  int i = 1;
  int nArg = 0;
  int n, c;
  int rc = 0;
  char *azArg[50];

  /* Parse the input line into tokens.
  */
  while( zLine[i] && nArg<ArraySize(azArg) ){
    while( isspace(zLine[i]) ){ i++; }
    if( zLine[i]=='\'' || zLine[i]=='"' ){
................................................................................
      while( zLine[i] && !isspace(zLine[i]) ){ i++; }
      if( zLine[i] ) zLine[i++] = 0;
    }
  }

  /* Process the input line.
  */
  if( nArg==0 ) return rc;
  n = strlen(azArg[0]);
  c = azArg[0][0];
  if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
    char *zErrMsg = 0;
    fprintf(p->out, "BEGIN TRANSACTION;\n");
    if( nArg==1 ){
      sqlite_exec(db,
................................................................................
    }else if( strcmp(z,"yes")==0 ){
      val = 1;
    }
    p->echoOn = val;
  }else

  if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
    rc = 1;

  }else

  if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
    int j;
    char *z = nArg>=2 ? azArg[1] : "1";
    int val = atoi(z);
    for(j=0; z[j]; j++){
................................................................................
  if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
    sprintf(p->separator, "%.*s", (int)ArraySize(p->separator)-1, azArg[1]);
  }else

  if( c=='s' && strncmp(azArg[0], "show", n)==0){
    int i;
    fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
    fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off");
    fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off");
    fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]);
    fprintf(p->out,"%9.9s: %s\n","nullvalue", p->nullvalue);
    fprintf(p->out,"%9.9s: %s\n","output",
                                 strlen(p->outfile) ? p->outfile : "stdout");
    fprintf(p->out,"%9.9s: %s\n","separator", p->separator);
    fprintf(p->out,"%9.9s: ","width");
    for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) {
        fprintf(p->out,"%d ",p->colWidth[i]);
    }
    fprintf(p->out,"\n\n");
  }else
................................................................................
    int j;
    for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
      p->colWidth[j-1] = atoi(azArg[j]);
    }
  }else

  {
    fprintf(stderr, "unknown command or invalid arguments: "
      " \"%s\". Enter \".help\" for help\n", azArg[0]);
  }

  return rc;
}

/*
** Read input from *in and process it.  If *in==0 then input
** is interactive - the user is typing it it.  Otherwise, input
** is coming from a file or device.  A prompt is issued and history
** is saved only if input is interactive.  An interrupt signal will
** cause this routine to exit immediately, unless input is interactive.
*/
static void process_input(struct callback_data *p, FILE *in){
  char *zLine;
  char *zSql = 0;
  int nSql = 0;
  char *zErrMsg;
  while( fflush(p->out), (zLine = one_input_line(zSql, in))!=0 ){
    if( seenInterrupt ){
      if( in!=0 ) break;
      seenInterrupt = 0;
    }
    if( p->echoOn ) printf("%s\n", zLine);
    if( zLine && zLine[0]=='.' && nSql==0 ){
      int rc = do_meta_command(zLine, db, p);
      free(zLine);
      if( rc ) break;
      continue;
    }
    if( zSql==0 ){
      int i;
      for(i=0; zLine[i] && isspace(zLine[i]); i++){}
      if( zLine[i]!=0 ){
        nSql = strlen(zLine);
................................................................................
  }
  if( zSql ){
    printf("Incomplete SQL: %s\n", zSql);
    free(zSql);
  }
}


/*
** Return a pathname which is the user's home directory.  A
** 0 return indicates an error of some kind.  Space to hold the
** resulting string is obtained from malloc().  The calling
** function should free the result.
*/
static char *find_home_dir(void){
  char *home_dir = NULL;

#if !defined(_WIN32) && !defined(WIN32)
  struct passwd *pwent;
  uid_t uid = getuid();
  while( (pwent=getpwent()) != NULL) {
    if(pwent->pw_uid == uid) {
      home_dir = pwent->pw_dir;
      break;
    }
  }
#endif

  if (!home_dir) {
    home_dir = getenv("HOME");
    if (!home_dir) {
      home_dir = getenv("HOMEPATH"); /* Windows? */
    }
  }

  if( home_dir ){
    char *z = malloc( strlen(home_dir)+1 );
    if( z ) strcpy(z, home_dir);
    home_dir = z;
  }
  return home_dir;
}

/*
** Read input from the file given by sqliterc_override.  Or if that
** parameter is NULL, take input from ~/.sqliterc
*/
static void process_sqliterc(struct callback_data *p, char *sqliterc_override){
  char *home_dir = NULL;
  char *sqliterc = sqliterc_override;
  FILE *in = NULL;

  if (sqliterc == NULL) {






    home_dir = find_home_dir();

















    sqliterc = home_dir ? malloc(strlen(home_dir) + 15) : 0;



    if( sqliterc==0 ){
      fprintf(stderr,"%s: out of memory!\n", Argv0);
      exit(1);
    }
    sprintf(sqliterc,"%s/.sqliterc",home_dir);
    free(home_dir);
  }
  in = fopen(sqliterc,"r");
  if(in) {




    printf("Loading resources from %s\n",sqliterc);
    process_input(p,in);
    fclose(in);
  }
  return;
}

/*
** Initialize the state information in data
*/
void main_init(struct callback_data *data) {
  memset(data, 0, sizeof(*data));
  data->mode = MODE_List;
  strcpy(data->separator,"|");
  data->showHeader = 0;
  strcpy(mainPrompt,"sqlite> ");
  strcpy(continuePrompt,"   ...> ");
................................................................................
        fprintf(stderr,"SQL error: %s\n", zErrMsg);
        exit(1);
      }
    }
  }else{
    extern int isatty();
    if( isatty(0) ){
      char *zHome;
      char *zHistory = 0;
      printf(
        "SQLite version %s\n"
        "Enter \".help\" for instructions\n",
        sqlite_version
      );
      zHome = find_home_dir();
      if( zHome && (zHistory = malloc(strlen(zHome)+20))!=0 ){
        sprintf(zHistory,"%s/.sqlite_history", zHome);
      }
      if( zHistory ) read_history(zHistory);
      process_input(&data, 0);
      if( zHistory ){
        stifle_history(100);
        write_history(zHistory);
      }
    }else{
      process_input(&data, stdin);
    }
  }
  set_table_name(&data, 0);
  sqlite_close(db);
  return 0;
}