From 67505e78c6cb326effaf82116b20a926474feefd Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 19 Apr 2002 12:34:06 +0000 Subject: [PATCH] Add support for saving the sqlite shell command-line history across sessions. (CVS 536) FossilOrigin-Name: ca4abf3fe1f0e66802f9f98a20e0c8b82a6459aa --- manifest | 14 +++--- manifest.uuid | 2 +- src/shell.c | 136 +++++++++++++++++++++++++++++++++----------------- 3 files changed, 98 insertions(+), 54 deletions(-) diff --git a/manifest b/manifest index 65f1c08bbb..d1483440c8 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Added\srights\srelease\sfor\sMatthew\sO.\sPersico\s(CVS\s535) -D 2002-04-19T01:00:13 +C Add\ssupport\sfor\ssaving\sthe\ssqlite\sshell\scommand-line\shistory\sacross\ssessions.\s(CVS\s536) +D 2002-04-19T12:34:06 F Makefile.in 50f1b3351df109b5774771350d8c1b8d3640130d F Makefile.template 89e373b2dad0321df00400fa968dc14b61a03296 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -39,7 +39,7 @@ F src/parse.y 9a8a2311dd95101bb02b3991042e619eea49729a F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe F src/select.c 92aef3f69e90dc065d680d88b1f075409e9249bb -F src/shell.c 56ed7250b26c1205cfa18d90349909e3d705d249 +F src/shell.c c0800304c9f915f8dec2cb73119d6f4e98f30c12 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in ffcacf73b5ed1a4939205d29a704a185758fa6a6 F src/sqliteInt.h e47ca9267a4c4a98e9f8d90c2df994a18f23d699 @@ -131,7 +131,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P 0582168b8b853559b484f4a024d28c67192160c4 -R 0d1547c2e1e9f19b787a3e2b35ee0831 -U persicom -Z 36bb1b1acfa30e39b3ee9d958b5b917c +P 6c32c07e8218caffebd4503e7d8a90226ac81cdc +R fdf61026ce1ec867dd8bbc684e7b26b1 +U drh +Z efe3d88b41e036fdbd5e6a43e4d0b877 diff --git a/manifest.uuid b/manifest.uuid index becec8491d..1fc9a30e56 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6c32c07e8218caffebd4503e7d8a90226ac81cdc \ No newline at end of file +ca4abf3fe1f0e66802f9f98a20e0c8b82a6459aa \ No newline at end of file diff --git a/src/shell.c b/src/shell.c index b7cd9d8576..4b20ae4e8e 100644 --- a/src/shell.c +++ b/src/shell.c @@ -8,15 +8,11 @@ ** 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 $ +** $Id: shell.c,v 1.55 2002/04/19 12:34:06 drh Exp $ */ #include #include @@ -37,6 +33,9 @@ #else # define readline(p) getline(p,stdin) # define add_history(X) +# define read_history(X) +# define write_history(X) +# define stifle_history(X) #endif /* @@ -46,6 +45,11 @@ */ 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. @@ -273,6 +277,7 @@ static void output_html_string(FILE *out, const char *z){ ** This routine runs when the user presses Ctrl-C */ static void interrupt_handler(int NotUsed){ + seenInterrupt = 1; if( db ) sqlite_interrupt(db); } @@ -506,11 +511,14 @@ 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 void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){ +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. @@ -533,7 +541,7 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){ /* Process the input line. */ - if( nArg==0 ) return; + if( nArg==0 ) return rc; n = strlen(azArg[0]); c = azArg[0][0]; if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ @@ -581,8 +589,7 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){ }else if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ - sqlite_close(db); - exit(0); + rc = 1; }else if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ @@ -821,11 +828,12 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){ 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","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","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++) { @@ -892,21 +900,35 @@ static void do_meta_command(char *zLine, sqlite *db, struct callback_data *p){ }else { - fprintf(stderr, "unknown command or invalid arguments: \"%s\". Enter \".help\" for help\n", - azArg[0]); + 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 ){ - do_meta_command(zLine, db, p); + int rc = do_meta_command(zLine, db, p); free(zLine); + if( rc ) break; continue; } if( zSql==0 ){ @@ -949,54 +971,62 @@ static void process_input(struct callback_data *p, FILE *in){ } } -static void process_sqliterc(struct callback_data *p, - char *sqliterc_override){ - +/* +** 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; - 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; - } + 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("HOME"); - if (!home_dir) { - home_dir = getenv("HOMEPATH"); /* Windows? */ - } - if (!home_dir) { - printf("Cannot find home directory from which to load .sqliterc\n"); - return; - } + home_dir = getenv("HOMEPATH"); /* Windows? */ } + } - /* With the home directory, open the init file and process */ - sqliterc = (char *)calloc(strlen(home_dir) + - strlen("/.sqliterc") + - 1, - sizeof(char)); + 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) { - /* File either had an error or was not found. Since I cannot - * tell, I cannot bark. */ - return; - } else { + if(in) { printf("Loading resources from %s\n",sqliterc); process_input(p,in); fclose(in); @@ -1004,6 +1034,9 @@ static void process_sqliterc(struct callback_data *p, return; } +/* +** Initialize the state information in data +*/ void main_init(struct callback_data *data) { memset(data, 0, sizeof(*data)); data->mode = MODE_List; @@ -1125,12 +1158,23 @@ int main(int argc, char **argv){ }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); } -- 2.47.3