[205] | 1 | /*
|
---|
| 2 | ** 2001 September 15
|
---|
| 3 | **
|
---|
| 4 | ** The author disclaims copyright to this source code. In place of
|
---|
| 5 | ** a legal notice, here is a blessing:
|
---|
| 6 | **
|
---|
| 7 | ** May you do good and not evil.
|
---|
| 8 | ** May you find forgiveness for yourself and forgive others.
|
---|
| 9 | ** May you share freely, never taking more than you give.
|
---|
| 10 | **
|
---|
| 11 | *************************************************************************
|
---|
| 12 | ** This file contains code to implement the "sqlite" command line
|
---|
| 13 | ** utility for accessing SQLite databases.
|
---|
| 14 | **
|
---|
| 15 | ** $Id: shell.c,v 1.93.2.1 2005/02/16 03:53:11 drh Exp $
|
---|
| 16 | */
|
---|
| 17 | #include <stdlib.h>
|
---|
| 18 | #include <string.h>
|
---|
| 19 | #include <stdio.h>
|
---|
| 20 | #include "sqlite.h"
|
---|
| 21 | #include <ctype.h>
|
---|
| 22 |
|
---|
| 23 | #if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__)
|
---|
| 24 | # include <signal.h>
|
---|
| 25 | # include <pwd.h>
|
---|
| 26 | # include <unistd.h>
|
---|
| 27 | # include <sys/types.h>
|
---|
| 28 | #endif
|
---|
| 29 |
|
---|
| 30 | #ifdef __MACOS__
|
---|
| 31 | # include <console.h>
|
---|
| 32 | # include <signal.h>
|
---|
| 33 | # include <unistd.h>
|
---|
| 34 | # include <extras.h>
|
---|
| 35 | # include <Files.h>
|
---|
| 36 | # include <Folders.h>
|
---|
| 37 | #endif
|
---|
| 38 |
|
---|
| 39 | #if defined(HAVE_READLINE) && HAVE_READLINE==1
|
---|
| 40 | # include <readline/readline.h>
|
---|
| 41 | # include <readline/history.h>
|
---|
| 42 | #else
|
---|
| 43 | # define readline(p) local_getline(p,stdin)
|
---|
| 44 | # define add_history(X)
|
---|
| 45 | # define read_history(X)
|
---|
| 46 | # define write_history(X)
|
---|
| 47 | # define stifle_history(X)
|
---|
| 48 | #endif
|
---|
| 49 |
|
---|
| 50 | /* Make sure isatty() has a prototype.
|
---|
| 51 | */
|
---|
| 52 | extern int isatty();
|
---|
| 53 |
|
---|
| 54 | /*
|
---|
| 55 | ** The following is the open SQLite database. We make a pointer
|
---|
| 56 | ** to this database a static variable so that it can be accessed
|
---|
| 57 | ** by the SIGINT handler to interrupt database processing.
|
---|
| 58 | */
|
---|
| 59 | static sqlite *db = 0;
|
---|
| 60 |
|
---|
| 61 | /*
|
---|
| 62 | ** True if an interrupt (Control-C) has been received.
|
---|
| 63 | */
|
---|
| 64 | static int seenInterrupt = 0;
|
---|
| 65 |
|
---|
| 66 | /*
|
---|
| 67 | ** This is the name of our program. It is set in main(), used
|
---|
| 68 | ** in a number of other places, mostly for error messages.
|
---|
| 69 | */
|
---|
| 70 | static char *Argv0;
|
---|
| 71 |
|
---|
| 72 | /*
|
---|
| 73 | ** Prompt strings. Initialized in main. Settable with
|
---|
| 74 | ** .prompt main continue
|
---|
| 75 | */
|
---|
| 76 | static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/
|
---|
| 77 | static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */
|
---|
| 78 |
|
---|
| 79 |
|
---|
| 80 | /*
|
---|
| 81 | ** Determines if a string is a number of not.
|
---|
| 82 | */
|
---|
| 83 | extern int sqliteIsNumber(const char*);
|
---|
| 84 |
|
---|
| 85 | /*
|
---|
| 86 | ** This routine reads a line of text from standard input, stores
|
---|
| 87 | ** the text in memory obtained from malloc() and returns a pointer
|
---|
| 88 | ** to the text. NULL is returned at end of file, or if malloc()
|
---|
| 89 | ** fails.
|
---|
| 90 | **
|
---|
| 91 | ** The interface is like "readline" but no command-line editing
|
---|
| 92 | ** is done.
|
---|
| 93 | */
|
---|
| 94 | static char *local_getline(char *zPrompt, FILE *in){
|
---|
| 95 | char *zLine;
|
---|
| 96 | int nLine;
|
---|
| 97 | int n;
|
---|
| 98 | int eol;
|
---|
| 99 |
|
---|
| 100 | if( zPrompt && *zPrompt ){
|
---|
| 101 | printf("%s",zPrompt);
|
---|
| 102 | fflush(stdout);
|
---|
| 103 | }
|
---|
| 104 | nLine = 100;
|
---|
| 105 | zLine = malloc( nLine );
|
---|
| 106 | if( zLine==0 ) return 0;
|
---|
| 107 | n = 0;
|
---|
| 108 | eol = 0;
|
---|
| 109 | while( !eol ){
|
---|
| 110 | if( n+100>nLine ){
|
---|
| 111 | nLine = nLine*2 + 100;
|
---|
| 112 | zLine = realloc(zLine, nLine);
|
---|
| 113 | if( zLine==0 ) return 0;
|
---|
| 114 | }
|
---|
| 115 | if( fgets(&zLine[n], nLine - n, in)==0 ){
|
---|
| 116 | if( n==0 ){
|
---|
| 117 | free(zLine);
|
---|
| 118 | return 0;
|
---|
| 119 | }
|
---|
| 120 | zLine[n] = 0;
|
---|
| 121 | eol = 1;
|
---|
| 122 | break;
|
---|
| 123 | }
|
---|
| 124 | while( zLine[n] ){ n++; }
|
---|
| 125 | if( n>0 && zLine[n-1]=='\n' ){
|
---|
| 126 | n--;
|
---|
| 127 | zLine[n] = 0;
|
---|
| 128 | eol = 1;
|
---|
| 129 | }
|
---|
| 130 | }
|
---|
| 131 | zLine = realloc( zLine, n+1 );
|
---|
| 132 | return zLine;
|
---|
| 133 | }
|
---|
| 134 |
|
---|
| 135 | /*
|
---|
| 136 | ** Retrieve a single line of input text. "isatty" is true if text
|
---|
| 137 | ** is coming from a terminal. In that case, we issue a prompt and
|
---|
| 138 | ** attempt to use "readline" for command-line editing. If "isatty"
|
---|
| 139 | ** is false, use "local_getline" instead of "readline" and issue no prompt.
|
---|
| 140 | **
|
---|
| 141 | ** zPrior is a string of prior text retrieved. If not the empty
|
---|
| 142 | ** string, then issue a continuation prompt.
|
---|
| 143 | */
|
---|
| 144 | static char *one_input_line(const char *zPrior, FILE *in){
|
---|
| 145 | char *zPrompt;
|
---|
| 146 | char *zResult;
|
---|
| 147 | if( in!=0 ){
|
---|
| 148 | return local_getline(0, in);
|
---|
| 149 | }
|
---|
| 150 | if( zPrior && zPrior[0] ){
|
---|
| 151 | zPrompt = continuePrompt;
|
---|
| 152 | }else{
|
---|
| 153 | zPrompt = mainPrompt;
|
---|
| 154 | }
|
---|
| 155 | zResult = readline(zPrompt);
|
---|
| 156 | if( zResult ) add_history(zResult);
|
---|
| 157 | return zResult;
|
---|
| 158 | }
|
---|
| 159 |
|
---|
| 160 | struct previous_mode_data {
|
---|
| 161 | int valid; /* Is there legit data in here? */
|
---|
| 162 | int mode;
|
---|
| 163 | int showHeader;
|
---|
| 164 | int colWidth[100];
|
---|
| 165 | };
|
---|
| 166 | /*
|
---|
| 167 | ** An pointer to an instance of this structure is passed from
|
---|
| 168 | ** the main program to the callback. This is used to communicate
|
---|
| 169 | ** state and mode information.
|
---|
| 170 | */
|
---|
| 171 | struct callback_data {
|
---|
| 172 | sqlite *db; /* The database */
|
---|
| 173 | int echoOn; /* True to echo input commands */
|
---|
| 174 | int cnt; /* Number of records displayed so far */
|
---|
| 175 | FILE *out; /* Write results here */
|
---|
| 176 | int mode; /* An output mode setting */
|
---|
| 177 | int showHeader; /* True to show column names in List or Column mode */
|
---|
| 178 | char *zDestTable; /* Name of destination table when MODE_Insert */
|
---|
| 179 | char separator[20]; /* Separator character for MODE_List */
|
---|
| 180 | int colWidth[100]; /* Requested width of each column when in column mode*/
|
---|
| 181 | int actualWidth[100]; /* Actual width of each column */
|
---|
| 182 | char nullvalue[20]; /* The text to print when a NULL comes back from
|
---|
| 183 | ** the database */
|
---|
| 184 | struct previous_mode_data explainPrev;
|
---|
| 185 | /* Holds the mode information just before
|
---|
| 186 | ** .explain ON */
|
---|
| 187 | char outfile[FILENAME_MAX]; /* Filename for *out */
|
---|
| 188 | const char *zDbFilename; /* name of the database file */
|
---|
| 189 | char *zKey; /* Encryption key */
|
---|
| 190 | };
|
---|
| 191 |
|
---|
| 192 | /*
|
---|
| 193 | ** These are the allowed modes.
|
---|
| 194 | */
|
---|
| 195 | #define MODE_Line 0 /* One column per line. Blank line between records */
|
---|
| 196 | #define MODE_Column 1 /* One record per line in neat columns */
|
---|
| 197 | #define MODE_List 2 /* One record per line with a separator */
|
---|
| 198 | #define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */
|
---|
| 199 | #define MODE_Html 4 /* Generate an XHTML table */
|
---|
| 200 | #define MODE_Insert 5 /* Generate SQL "insert" statements */
|
---|
| 201 | #define MODE_NUM_OF 6 /* The number of modes (not a mode itself) */
|
---|
| 202 |
|
---|
| 203 | char *modeDescr[MODE_NUM_OF] = {
|
---|
| 204 | "line",
|
---|
| 205 | "column",
|
---|
| 206 | "list",
|
---|
| 207 | "semi",
|
---|
| 208 | "html",
|
---|
| 209 | "insert"
|
---|
| 210 | };
|
---|
| 211 |
|
---|
| 212 | /*
|
---|
| 213 | ** Number of elements in an array
|
---|
| 214 | */
|
---|
| 215 | #define ArraySize(X) (sizeof(X)/sizeof(X[0]))
|
---|
| 216 |
|
---|
| 217 | /*
|
---|
| 218 | ** Output the given string as a quoted string using SQL quoting conventions.
|
---|
| 219 | */
|
---|
| 220 | static void output_quoted_string(FILE *out, const char *z){
|
---|
| 221 | int i;
|
---|
| 222 | int nSingle = 0;
|
---|
| 223 | for(i=0; z[i]; i++){
|
---|
| 224 | if( z[i]=='\'' ) nSingle++;
|
---|
| 225 | }
|
---|
| 226 | if( nSingle==0 ){
|
---|
| 227 | fprintf(out,"'%s'",z);
|
---|
| 228 | }else{
|
---|
| 229 | fprintf(out,"'");
|
---|
| 230 | while( *z ){
|
---|
| 231 | for(i=0; z[i] && z[i]!='\''; i++){}
|
---|
| 232 | if( i==0 ){
|
---|
| 233 | fprintf(out,"''");
|
---|
| 234 | z++;
|
---|
| 235 | }else if( z[i]=='\'' ){
|
---|
| 236 | fprintf(out,"%.*s''",i,z);
|
---|
| 237 | z += i+1;
|
---|
| 238 | }else{
|
---|
| 239 | fprintf(out,"%s",z);
|
---|
| 240 | break;
|
---|
| 241 | }
|
---|
| 242 | }
|
---|
| 243 | fprintf(out,"'");
|
---|
| 244 | }
|
---|
| 245 | }
|
---|
| 246 |
|
---|
| 247 | /*
|
---|
| 248 | ** Output the given string with characters that are special to
|
---|
| 249 | ** HTML escaped.
|
---|
| 250 | */
|
---|
| 251 | static void output_html_string(FILE *out, const char *z){
|
---|
| 252 | int i;
|
---|
| 253 | while( *z ){
|
---|
| 254 | for(i=0; z[i] && z[i]!='<' && z[i]!='&'; i++){}
|
---|
| 255 | if( i>0 ){
|
---|
| 256 | fprintf(out,"%.*s",i,z);
|
---|
| 257 | }
|
---|
| 258 | if( z[i]=='<' ){
|
---|
| 259 | fprintf(out,"<");
|
---|
| 260 | }else if( z[i]=='&' ){
|
---|
| 261 | fprintf(out,"&");
|
---|
| 262 | }else{
|
---|
| 263 | break;
|
---|
| 264 | }
|
---|
| 265 | z += i + 1;
|
---|
| 266 | }
|
---|
| 267 | }
|
---|
| 268 |
|
---|
| 269 | /*
|
---|
| 270 | ** This routine runs when the user presses Ctrl-C
|
---|
| 271 | */
|
---|
| 272 | static void interrupt_handler(int NotUsed){
|
---|
| 273 | seenInterrupt = 1;
|
---|
| 274 | if( db ) sqlite_interrupt(db);
|
---|
| 275 | }
|
---|
| 276 |
|
---|
| 277 | /*
|
---|
| 278 | ** This is the callback routine that the SQLite library
|
---|
| 279 | ** invokes for each row of a query result.
|
---|
| 280 | */
|
---|
| 281 | static int callback(void *pArg, int nArg, char **azArg, char **azCol){
|
---|
| 282 | int i;
|
---|
| 283 | struct callback_data *p = (struct callback_data*)pArg;
|
---|
| 284 | switch( p->mode ){
|
---|
| 285 | case MODE_Line: {
|
---|
| 286 | int w = 5;
|
---|
| 287 | if( azArg==0 ) break;
|
---|
| 288 | for(i=0; i<nArg; i++){
|
---|
| 289 | int len = strlen(azCol[i]);
|
---|
| 290 | if( len>w ) w = len;
|
---|
| 291 | }
|
---|
| 292 | if( p->cnt++>0 ) fprintf(p->out,"\n");
|
---|
| 293 | for(i=0; i<nArg; i++){
|
---|
| 294 | fprintf(p->out,"%*s = %s\n", w, azCol[i],
|
---|
| 295 | azArg[i] ? azArg[i] : p->nullvalue);
|
---|
| 296 | }
|
---|
| 297 | break;
|
---|
| 298 | }
|
---|
| 299 | case MODE_Column: {
|
---|
| 300 | if( p->cnt++==0 ){
|
---|
| 301 | for(i=0; i<nArg; i++){
|
---|
| 302 | int w, n;
|
---|
| 303 | if( i<ArraySize(p->colWidth) ){
|
---|
| 304 | w = p->colWidth[i];
|
---|
| 305 | }else{
|
---|
| 306 | w = 0;
|
---|
| 307 | }
|
---|
| 308 | if( w<=0 ){
|
---|
| 309 | w = strlen(azCol[i] ? azCol[i] : "");
|
---|
| 310 | if( w<10 ) w = 10;
|
---|
| 311 | n = strlen(azArg && azArg[i] ? azArg[i] : p->nullvalue);
|
---|
| 312 | if( w<n ) w = n;
|
---|
| 313 | }
|
---|
| 314 | if( i<ArraySize(p->actualWidth) ){
|
---|
| 315 | p->actualWidth[i] = w;
|
---|
| 316 | }
|
---|
| 317 | if( p->showHeader ){
|
---|
| 318 | fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " ");
|
---|
| 319 | }
|
---|
| 320 | }
|
---|
| 321 | if( p->showHeader ){
|
---|
| 322 | for(i=0; i<nArg; i++){
|
---|
| 323 | int w;
|
---|
| 324 | if( i<ArraySize(p->actualWidth) ){
|
---|
| 325 | w = p->actualWidth[i];
|
---|
| 326 | }else{
|
---|
| 327 | w = 10;
|
---|
| 328 | }
|
---|
| 329 | fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------"
|
---|
| 330 | "----------------------------------------------------------",
|
---|
| 331 | i==nArg-1 ? "\n": " ");
|
---|
| 332 | }
|
---|
| 333 | }
|
---|
| 334 | }
|
---|
| 335 | if( azArg==0 ) break;
|
---|
| 336 | for(i=0; i<nArg; i++){
|
---|
| 337 | int w;
|
---|
| 338 | if( i<ArraySize(p->actualWidth) ){
|
---|
| 339 | w = p->actualWidth[i];
|
---|
| 340 | }else{
|
---|
| 341 | w = 10;
|
---|
| 342 | }
|
---|
| 343 | fprintf(p->out,"%-*.*s%s",w,w,
|
---|
| 344 | azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " ");
|
---|
| 345 | }
|
---|
| 346 | break;
|
---|
| 347 | }
|
---|
| 348 | case MODE_Semi:
|
---|
| 349 | case MODE_List: {
|
---|
| 350 | if( p->cnt++==0 && p->showHeader ){
|
---|
| 351 | for(i=0; i<nArg; i++){
|
---|
| 352 | fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator);
|
---|
| 353 | }
|
---|
| 354 | }
|
---|
| 355 | if( azArg==0 ) break;
|
---|
| 356 | for(i=0; i<nArg; i++){
|
---|
| 357 | char *z = azArg[i];
|
---|
| 358 | if( z==0 ) z = p->nullvalue;
|
---|
| 359 | fprintf(p->out, "%s", z);
|
---|
| 360 | if( i<nArg-1 ){
|
---|
| 361 | fprintf(p->out, "%s", p->separator);
|
---|
| 362 | }else if( p->mode==MODE_Semi ){
|
---|
| 363 | fprintf(p->out, ";\n");
|
---|
| 364 | }else{
|
---|
| 365 | fprintf(p->out, "\n");
|
---|
| 366 | }
|
---|
| 367 | }
|
---|
| 368 | break;
|
---|
| 369 | }
|
---|
| 370 | case MODE_Html: {
|
---|
| 371 | if( p->cnt++==0 && p->showHeader ){
|
---|
| 372 | fprintf(p->out,"<TR>");
|
---|
| 373 | for(i=0; i<nArg; i++){
|
---|
| 374 | fprintf(p->out,"<TH>%s</TH>",azCol[i]);
|
---|
| 375 | }
|
---|
| 376 | fprintf(p->out,"</TR>\n");
|
---|
| 377 | }
|
---|
| 378 | if( azArg==0 ) break;
|
---|
| 379 | fprintf(p->out,"<TR>");
|
---|
| 380 | for(i=0; i<nArg; i++){
|
---|
| 381 | fprintf(p->out,"<TD>");
|
---|
| 382 | output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
|
---|
| 383 | fprintf(p->out,"</TD>\n");
|
---|
| 384 | }
|
---|
| 385 | fprintf(p->out,"</TR>\n");
|
---|
| 386 | break;
|
---|
| 387 | }
|
---|
| 388 | case MODE_Insert: {
|
---|
| 389 | if( azArg==0 ) break;
|
---|
| 390 | fprintf(p->out,"INSERT INTO %s VALUES(",p->zDestTable);
|
---|
| 391 | for(i=0; i<nArg; i++){
|
---|
| 392 | char *zSep = i>0 ? ",": "";
|
---|
| 393 | if( azArg[i]==0 ){
|
---|
| 394 | fprintf(p->out,"%sNULL",zSep);
|
---|
| 395 | }else if( sqliteIsNumber(azArg[i]) ){
|
---|
| 396 | fprintf(p->out,"%s%s",zSep, azArg[i]);
|
---|
| 397 | }else{
|
---|
| 398 | if( zSep[0] ) fprintf(p->out,"%s",zSep);
|
---|
| 399 | output_quoted_string(p->out, azArg[i]);
|
---|
| 400 | }
|
---|
| 401 | }
|
---|
| 402 | fprintf(p->out,");\n");
|
---|
| 403 | break;
|
---|
| 404 | }
|
---|
| 405 | }
|
---|
| 406 | return 0;
|
---|
| 407 | }
|
---|
| 408 |
|
---|
| 409 | /*
|
---|
| 410 | ** Set the destination table field of the callback_data structure to
|
---|
| 411 | ** the name of the table given. Escape any quote characters in the
|
---|
| 412 | ** table name.
|
---|
| 413 | */
|
---|
| 414 | static void set_table_name(struct callback_data *p, const char *zName){
|
---|
| 415 | int i, n;
|
---|
| 416 | int needQuote;
|
---|
| 417 | char *z;
|
---|
| 418 |
|
---|
| 419 | if( p->zDestTable ){
|
---|
| 420 | free(p->zDestTable);
|
---|
| 421 | p->zDestTable = 0;
|
---|
| 422 | }
|
---|
| 423 | if( zName==0 ) return;
|
---|
| 424 | needQuote = !isalpha(*zName) && *zName!='_';
|
---|
| 425 | for(i=n=0; zName[i]; i++, n++){
|
---|
| 426 | if( !isalnum(zName[i]) && zName[i]!='_' ){
|
---|
| 427 | needQuote = 1;
|
---|
| 428 | if( zName[i]=='\'' ) n++;
|
---|
| 429 | }
|
---|
| 430 | }
|
---|
| 431 | if( needQuote ) n += 2;
|
---|
| 432 | z = p->zDestTable = malloc( n+1 );
|
---|
| 433 | if( z==0 ){
|
---|
| 434 | fprintf(stderr,"Out of memory!\n");
|
---|
| 435 | exit(1);
|
---|
| 436 | }
|
---|
| 437 | n = 0;
|
---|
| 438 | if( needQuote ) z[n++] = '\'';
|
---|
| 439 | for(i=0; zName[i]; i++){
|
---|
| 440 | z[n++] = zName[i];
|
---|
| 441 | if( zName[i]=='\'' ) z[n++] = '\'';
|
---|
| 442 | }
|
---|
| 443 | if( needQuote ) z[n++] = '\'';
|
---|
| 444 | z[n] = 0;
|
---|
| 445 | }
|
---|
| 446 |
|
---|
| 447 | /*
|
---|
| 448 | ** This is a different callback routine used for dumping the database.
|
---|
| 449 | ** Each row received by this callback consists of a table name,
|
---|
| 450 | ** the table type ("index" or "table") and SQL to create the table.
|
---|
| 451 | ** This routine should print text sufficient to recreate the table.
|
---|
| 452 | */
|
---|
| 453 | static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
|
---|
| 454 | struct callback_data *p = (struct callback_data *)pArg;
|
---|
| 455 | if( nArg!=3 ) return 1;
|
---|
| 456 | fprintf(p->out, "%s;\n", azArg[2]);
|
---|
| 457 | if( strcmp(azArg[1],"table")==0 ){
|
---|
| 458 | struct callback_data d2;
|
---|
| 459 | d2 = *p;
|
---|
| 460 | d2.mode = MODE_Insert;
|
---|
| 461 | d2.zDestTable = 0;
|
---|
| 462 | set_table_name(&d2, azArg[0]);
|
---|
| 463 | sqlite_exec_printf(p->db,
|
---|
| 464 | "SELECT * FROM '%q'",
|
---|
| 465 | callback, &d2, 0, azArg[0]
|
---|
| 466 | );
|
---|
| 467 | set_table_name(&d2, 0);
|
---|
| 468 | }
|
---|
| 469 | return 0;
|
---|
| 470 | }
|
---|
| 471 |
|
---|
| 472 | /*
|
---|
| 473 | ** Text of a help message
|
---|
| 474 | */
|
---|
| 475 | static char zHelp[] =
|
---|
| 476 | ".databases List names and files of attached databases\n"
|
---|
| 477 | ".dump ?TABLE? ... Dump the database in a text format\n"
|
---|
| 478 | ".echo ON|OFF Turn command echo on or off\n"
|
---|
| 479 | ".exit Exit this program\n"
|
---|
| 480 | ".explain ON|OFF Turn output mode suitable for EXPLAIN on or off.\n"
|
---|
| 481 | ".header(s) ON|OFF Turn display of headers on or off\n"
|
---|
| 482 | ".help Show this message\n"
|
---|
| 483 | ".indices TABLE Show names of all indices on TABLE\n"
|
---|
| 484 | ".mode MODE Set mode to one of \"line(s)\", \"column(s)\", \n"
|
---|
| 485 | " \"insert\", \"list\", or \"html\"\n"
|
---|
| 486 | ".mode insert TABLE Generate SQL insert statements for TABLE\n"
|
---|
| 487 | ".nullvalue STRING Print STRING instead of nothing for NULL data\n"
|
---|
| 488 | ".output FILENAME Send output to FILENAME\n"
|
---|
| 489 | ".output stdout Send output to the screen\n"
|
---|
| 490 | ".prompt MAIN CONTINUE Replace the standard prompts\n"
|
---|
| 491 | ".quit Exit this program\n"
|
---|
| 492 | ".read FILENAME Execute SQL in FILENAME\n"
|
---|
| 493 | #ifdef SQLITE_HAS_CODEC
|
---|
| 494 | ".rekey OLD NEW NEW Change the encryption key\n"
|
---|
| 495 | #endif
|
---|
| 496 | ".schema ?TABLE? Show the CREATE statements\n"
|
---|
| 497 | ".separator STRING Change separator string for \"list\" mode\n"
|
---|
| 498 | ".show Show the current values for various settings\n"
|
---|
| 499 | ".tables ?PATTERN? List names of tables matching a pattern\n"
|
---|
| 500 | ".timeout MS Try opening locked tables for MS milliseconds\n"
|
---|
| 501 | ".width NUM NUM ... Set column widths for \"column\" mode\n"
|
---|
| 502 | ;
|
---|
| 503 |
|
---|
| 504 | /* Forward reference */
|
---|
| 505 | static void process_input(struct callback_data *p, FILE *in);
|
---|
| 506 |
|
---|
| 507 | /*
|
---|
| 508 | ** Make sure the database is open. If it is not, then open it. If
|
---|
| 509 | ** the database fails to open, print an error message and exit.
|
---|
| 510 | */
|
---|
| 511 | static void open_db(struct callback_data *p){
|
---|
| 512 | if( p->db==0 ){
|
---|
| 513 | char *zErrMsg = 0;
|
---|
| 514 | #ifdef SQLITE_HAS_CODEC
|
---|
| 515 | int n = p->zKey ? strlen(p->zKey) : 0;
|
---|
| 516 | db = p->db = sqlite_open_encrypted(p->zDbFilename, p->zKey, n, 0, &zErrMsg);
|
---|
| 517 | #else
|
---|
| 518 | db = p->db = sqlite_open(p->zDbFilename, 0, &zErrMsg);
|
---|
| 519 | #endif
|
---|
| 520 | if( p->db==0 ){
|
---|
| 521 | if( zErrMsg ){
|
---|
| 522 | fprintf(stderr,"Unable to open database \"%s\": %s\n",
|
---|
| 523 | p->zDbFilename, zErrMsg);
|
---|
| 524 | }else{
|
---|
| 525 | fprintf(stderr,"Unable to open database %s\n", p->zDbFilename);
|
---|
| 526 | }
|
---|
| 527 | exit(1);
|
---|
| 528 | }
|
---|
| 529 | }
|
---|
| 530 | }
|
---|
| 531 |
|
---|
| 532 | /*
|
---|
| 533 | ** If an input line begins with "." then invoke this routine to
|
---|
| 534 | ** process that line.
|
---|
| 535 | **
|
---|
| 536 | ** Return 1 to exit and 0 to continue.
|
---|
| 537 | */
|
---|
| 538 | static int do_meta_command(char *zLine, struct callback_data *p){
|
---|
| 539 | int i = 1;
|
---|
| 540 | int nArg = 0;
|
---|
| 541 | int n, c;
|
---|
| 542 | int rc = 0;
|
---|
| 543 | char *azArg[50];
|
---|
| 544 |
|
---|
| 545 | /* Parse the input line into tokens.
|
---|
| 546 | */
|
---|
| 547 | while( zLine[i] && nArg<ArraySize(azArg) ){
|
---|
| 548 | while( isspace(zLine[i]) ){ i++; }
|
---|
| 549 | if( zLine[i]==0 ) break;
|
---|
| 550 | if( zLine[i]=='\'' || zLine[i]=='"' ){
|
---|
| 551 | int delim = zLine[i++];
|
---|
| 552 | azArg[nArg++] = &zLine[i];
|
---|
| 553 | while( zLine[i] && zLine[i]!=delim ){ i++; }
|
---|
| 554 | if( zLine[i]==delim ){
|
---|
| 555 | zLine[i++] = 0;
|
---|
| 556 | }
|
---|
| 557 | }else{
|
---|
| 558 | azArg[nArg++] = &zLine[i];
|
---|
| 559 | while( zLine[i] && !isspace(zLine[i]) ){ i++; }
|
---|
| 560 | if( zLine[i] ) zLine[i++] = 0;
|
---|
| 561 | }
|
---|
| 562 | }
|
---|
| 563 |
|
---|
| 564 | /* Process the input line.
|
---|
| 565 | */
|
---|
| 566 | if( nArg==0 ) return rc;
|
---|
| 567 | n = strlen(azArg[0]);
|
---|
| 568 | c = azArg[0][0];
|
---|
| 569 | if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){
|
---|
| 570 | struct callback_data data;
|
---|
| 571 | char *zErrMsg = 0;
|
---|
| 572 | open_db(p);
|
---|
| 573 | memcpy(&data, p, sizeof(data));
|
---|
| 574 | data.showHeader = 1;
|
---|
| 575 | data.mode = MODE_Column;
|
---|
| 576 | data.colWidth[0] = 3;
|
---|
| 577 | data.colWidth[1] = 15;
|
---|
| 578 | data.colWidth[2] = 58;
|
---|
| 579 | sqlite_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg);
|
---|
| 580 | if( zErrMsg ){
|
---|
| 581 | fprintf(stderr,"Error: %s\n", zErrMsg);
|
---|
| 582 | sqlite_freemem(zErrMsg);
|
---|
| 583 | }
|
---|
| 584 | }else
|
---|
| 585 |
|
---|
| 586 | if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
|
---|
| 587 | char *zErrMsg = 0;
|
---|
| 588 | open_db(p);
|
---|
| 589 | fprintf(p->out, "BEGIN TRANSACTION;\n");
|
---|
| 590 | if( nArg==1 ){
|
---|
| 591 | sqlite_exec(p->db,
|
---|
| 592 | "SELECT name, type, sql FROM sqlite_master "
|
---|
| 593 | "WHERE type!='meta' AND sql NOT NULL "
|
---|
| 594 | "ORDER BY substr(type,2,1), rowid",
|
---|
| 595 | dump_callback, p, &zErrMsg
|
---|
| 596 | );
|
---|
| 597 | }else{
|
---|
| 598 | int i;
|
---|
| 599 | for(i=1; i<nArg && zErrMsg==0; i++){
|
---|
| 600 | sqlite_exec_printf(p->db,
|
---|
| 601 | "SELECT name, type, sql FROM sqlite_master "
|
---|
| 602 | "WHERE tbl_name LIKE '%q' AND type!='meta' AND sql NOT NULL "
|
---|
| 603 | "ORDER BY substr(type,2,1), rowid",
|
---|
| 604 | dump_callback, p, &zErrMsg, azArg[i]
|
---|
| 605 | );
|
---|
| 606 | }
|
---|
| 607 | }
|
---|
| 608 | if( zErrMsg ){
|
---|
| 609 | fprintf(stderr,"Error: %s\n", zErrMsg);
|
---|
| 610 | sqlite_freemem(zErrMsg);
|
---|
| 611 | }else{
|
---|
| 612 | fprintf(p->out, "COMMIT;\n");
|
---|
| 613 | }
|
---|
| 614 | }else
|
---|
| 615 |
|
---|
| 616 | if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 ){
|
---|
| 617 | int j;
|
---|
| 618 | char *z = azArg[1];
|
---|
| 619 | int val = atoi(azArg[1]);
|
---|
| 620 | for(j=0; z[j]; j++){
|
---|
| 621 | if( isupper(z[j]) ) z[j] = tolower(z[j]);
|
---|
| 622 | }
|
---|
| 623 | if( strcmp(z,"on")==0 ){
|
---|
| 624 | val = 1;
|
---|
| 625 | }else if( strcmp(z,"yes")==0 ){
|
---|
| 626 | val = 1;
|
---|
| 627 | }
|
---|
| 628 | p->echoOn = val;
|
---|
| 629 | }else
|
---|
| 630 |
|
---|
| 631 | if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
|
---|
| 632 | rc = 1;
|
---|
| 633 | }else
|
---|
| 634 |
|
---|
| 635 | if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
|
---|
| 636 | int j;
|
---|
| 637 | char *z = nArg>=2 ? azArg[1] : "1";
|
---|
| 638 | int val = atoi(z);
|
---|
| 639 | for(j=0; z[j]; j++){
|
---|
| 640 | if( isupper(z[j]) ) z[j] = tolower(z[j]);
|
---|
| 641 | }
|
---|
| 642 | if( strcmp(z,"on")==0 ){
|
---|
| 643 | val = 1;
|
---|
| 644 | }else if( strcmp(z,"yes")==0 ){
|
---|
| 645 | val = 1;
|
---|
| 646 | }
|
---|
| 647 | if(val == 1) {
|
---|
| 648 | if(!p->explainPrev.valid) {
|
---|
| 649 | p->explainPrev.valid = 1;
|
---|
| 650 | p->explainPrev.mode = p->mode;
|
---|
| 651 | p->explainPrev.showHeader = p->showHeader;
|
---|
| 652 | memcpy(p->explainPrev.colWidth,p->colWidth,sizeof(p->colWidth));
|
---|
| 653 | }
|
---|
| 654 | /* We could put this code under the !p->explainValid
|
---|
| 655 | ** condition so that it does not execute if we are already in
|
---|
| 656 | ** explain mode. However, always executing it allows us an easy
|
---|
| 657 | ** was to reset to explain mode in case the user previously
|
---|
| 658 | ** did an .explain followed by a .width, .mode or .header
|
---|
| 659 | ** command.
|
---|
| 660 | */
|
---|
| 661 | p->mode = MODE_Column;
|
---|
| 662 | p->showHeader = 1;
|
---|
| 663 | memset(p->colWidth,0,ArraySize(p->colWidth));
|
---|
| 664 | p->colWidth[0] = 4;
|
---|
| 665 | p->colWidth[1] = 12;
|
---|
| 666 | p->colWidth[2] = 10;
|
---|
| 667 | p->colWidth[3] = 10;
|
---|
| 668 | p->colWidth[4] = 35;
|
---|
| 669 | }else if (p->explainPrev.valid) {
|
---|
| 670 | p->explainPrev.valid = 0;
|
---|
| 671 | p->mode = p->explainPrev.mode;
|
---|
| 672 | p->showHeader = p->explainPrev.showHeader;
|
---|
| 673 | memcpy(p->colWidth,p->explainPrev.colWidth,sizeof(p->colWidth));
|
---|
| 674 | }
|
---|
| 675 | }else
|
---|
| 676 |
|
---|
| 677 | if( c=='h' && (strncmp(azArg[0], "header", n)==0
|
---|
| 678 | ||
|
---|
| 679 | strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){
|
---|
| 680 | int j;
|
---|
| 681 | char *z = azArg[1];
|
---|
| 682 | int val = atoi(azArg[1]);
|
---|
| 683 | for(j=0; z[j]; j++){
|
---|
| 684 | if( isupper(z[j]) ) z[j] = tolower(z[j]);
|
---|
| 685 | }
|
---|
| 686 | if( strcmp(z,"on")==0 ){
|
---|
| 687 | val = 1;
|
---|
| 688 | }else if( strcmp(z,"yes")==0 ){
|
---|
| 689 | val = 1;
|
---|
| 690 | }
|
---|
| 691 | p->showHeader = val;
|
---|
| 692 | }else
|
---|
| 693 |
|
---|
| 694 | if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
|
---|
| 695 | fprintf(stderr,zHelp);
|
---|
| 696 | }else
|
---|
| 697 |
|
---|
| 698 | if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg>1 ){
|
---|
| 699 | struct callback_data data;
|
---|
| 700 | char *zErrMsg = 0;
|
---|
| 701 | open_db(p);
|
---|
| 702 | memcpy(&data, p, sizeof(data));
|
---|
| 703 | data.showHeader = 0;
|
---|
| 704 | data.mode = MODE_List;
|
---|
| 705 | sqlite_exec_printf(p->db,
|
---|
| 706 | "SELECT name FROM sqlite_master "
|
---|
| 707 | "WHERE type='index' AND tbl_name LIKE '%q' "
|
---|
| 708 | "UNION ALL "
|
---|
| 709 | "SELECT name FROM sqlite_temp_master "
|
---|
| 710 | "WHERE type='index' AND tbl_name LIKE '%q' "
|
---|
| 711 | "ORDER BY 1",
|
---|
| 712 | callback, &data, &zErrMsg, azArg[1], azArg[1]
|
---|
| 713 | );
|
---|
| 714 | if( zErrMsg ){
|
---|
| 715 | fprintf(stderr,"Error: %s\n", zErrMsg);
|
---|
| 716 | sqlite_freemem(zErrMsg);
|
---|
| 717 | }
|
---|
| 718 | }else
|
---|
| 719 |
|
---|
| 720 | if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg>=2 ){
|
---|
| 721 | int n2 = strlen(azArg[1]);
|
---|
| 722 | if( strncmp(azArg[1],"line",n2)==0
|
---|
| 723 | ||
|
---|
| 724 | strncmp(azArg[1],"lines",n2)==0 ){
|
---|
| 725 | p->mode = MODE_Line;
|
---|
| 726 | }else if( strncmp(azArg[1],"column",n2)==0
|
---|
| 727 | ||
|
---|
| 728 | strncmp(azArg[1],"columns",n2)==0 ){
|
---|
| 729 | p->mode = MODE_Column;
|
---|
| 730 | }else if( strncmp(azArg[1],"list",n2)==0 ){
|
---|
| 731 | p->mode = MODE_List;
|
---|
| 732 | }else if( strncmp(azArg[1],"html",n2)==0 ){
|
---|
| 733 | p->mode = MODE_Html;
|
---|
| 734 | }else if( strncmp(azArg[1],"insert",n2)==0 ){
|
---|
| 735 | p->mode = MODE_Insert;
|
---|
| 736 | if( nArg>=3 ){
|
---|
| 737 | set_table_name(p, azArg[2]);
|
---|
| 738 | }else{
|
---|
| 739 | set_table_name(p, "table");
|
---|
| 740 | }
|
---|
| 741 | }else {
|
---|
| 742 | fprintf(stderr,"mode should be on of: column html insert line list\n");
|
---|
| 743 | }
|
---|
| 744 | }else
|
---|
| 745 |
|
---|
| 746 | if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) {
|
---|
| 747 | sprintf(p->nullvalue, "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
|
---|
| 748 | }else
|
---|
| 749 |
|
---|
| 750 | if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
|
---|
| 751 | if( p->out!=stdout ){
|
---|
| 752 | fclose(p->out);
|
---|
| 753 | }
|
---|
| 754 | if( strcmp(azArg[1],"stdout")==0 ){
|
---|
| 755 | p->out = stdout;
|
---|
| 756 | strcpy(p->outfile,"stdout");
|
---|
| 757 | }else{
|
---|
| 758 | p->out = fopen(azArg[1], "wb");
|
---|
| 759 | if( p->out==0 ){
|
---|
| 760 | fprintf(stderr,"can't write to \"%s\"\n", azArg[1]);
|
---|
| 761 | p->out = stdout;
|
---|
| 762 | } else {
|
---|
| 763 | strcpy(p->outfile,azArg[1]);
|
---|
| 764 | }
|
---|
| 765 | }
|
---|
| 766 | }else
|
---|
| 767 |
|
---|
| 768 | if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){
|
---|
| 769 | if( nArg >= 2) {
|
---|
| 770 | strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1);
|
---|
| 771 | }
|
---|
| 772 | if( nArg >= 3) {
|
---|
| 773 | strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1);
|
---|
| 774 | }
|
---|
| 775 | }else
|
---|
| 776 |
|
---|
| 777 | if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
|
---|
| 778 | rc = 1;
|
---|
| 779 | }else
|
---|
| 780 |
|
---|
| 781 | if( c=='r' && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
|
---|
| 782 | FILE *alt = fopen(azArg[1], "rb");
|
---|
| 783 | if( alt==0 ){
|
---|
| 784 | fprintf(stderr,"can't open \"%s\"\n", azArg[1]);
|
---|
| 785 | }else{
|
---|
| 786 | process_input(p, alt);
|
---|
| 787 | fclose(alt);
|
---|
| 788 | }
|
---|
| 789 | }else
|
---|
| 790 |
|
---|
| 791 | #ifdef SQLITE_HAS_CODEC
|
---|
| 792 | if( c=='r' && strncmp(azArg[0],"rekey", n)==0 && nArg==4 ){
|
---|
| 793 | char *zOld = p->zKey;
|
---|
| 794 | if( zOld==0 ) zOld = "";
|
---|
| 795 | if( strcmp(azArg[1],zOld) ){
|
---|
| 796 | fprintf(stderr,"old key is incorrect\n");
|
---|
| 797 | }else if( strcmp(azArg[2], azArg[3]) ){
|
---|
| 798 | fprintf(stderr,"2nd copy of new key does not match the 1st\n");
|
---|
| 799 | }else{
|
---|
| 800 | sqlite_freemem(p->zKey);
|
---|
| 801 | p->zKey = sqlite_mprintf("%s", azArg[2]);
|
---|
| 802 | sqlite_rekey(p->db, p->zKey, strlen(p->zKey));
|
---|
| 803 | }
|
---|
| 804 | }else
|
---|
| 805 | #endif
|
---|
| 806 |
|
---|
| 807 | if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
|
---|
| 808 | struct callback_data data;
|
---|
| 809 | char *zErrMsg = 0;
|
---|
| 810 | open_db(p);
|
---|
| 811 | memcpy(&data, p, sizeof(data));
|
---|
| 812 | data.showHeader = 0;
|
---|
| 813 | data.mode = MODE_Semi;
|
---|
| 814 | if( nArg>1 ){
|
---|
| 815 | extern int sqliteStrICmp(const char*,const char*);
|
---|
| 816 | if( sqliteStrICmp(azArg[1],"sqlite_master")==0 ){
|
---|
| 817 | char *new_argv[2], *new_colv[2];
|
---|
| 818 | new_argv[0] = "CREATE TABLE sqlite_master (\n"
|
---|
| 819 | " type text,\n"
|
---|
| 820 | " name text,\n"
|
---|
| 821 | " tbl_name text,\n"
|
---|
| 822 | " rootpage integer,\n"
|
---|
| 823 | " sql text\n"
|
---|
| 824 | ")";
|
---|
| 825 | new_argv[1] = 0;
|
---|
| 826 | new_colv[0] = "sql";
|
---|
| 827 | new_colv[1] = 0;
|
---|
| 828 | callback(&data, 1, new_argv, new_colv);
|
---|
| 829 | }else if( sqliteStrICmp(azArg[1],"sqlite_temp_master")==0 ){
|
---|
| 830 | char *new_argv[2], *new_colv[2];
|
---|
| 831 | new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n"
|
---|
| 832 | " type text,\n"
|
---|
| 833 | " name text,\n"
|
---|
| 834 | " tbl_name text,\n"
|
---|
| 835 | " rootpage integer,\n"
|
---|
| 836 | " sql text\n"
|
---|
| 837 | ")";
|
---|
| 838 | new_argv[1] = 0;
|
---|
| 839 | new_colv[0] = "sql";
|
---|
| 840 | new_colv[1] = 0;
|
---|
| 841 | callback(&data, 1, new_argv, new_colv);
|
---|
| 842 | }else{
|
---|
| 843 | sqlite_exec_printf(p->db,
|
---|
| 844 | "SELECT sql FROM "
|
---|
| 845 | " (SELECT * FROM sqlite_master UNION ALL"
|
---|
| 846 | " SELECT * FROM sqlite_temp_master) "
|
---|
| 847 | "WHERE tbl_name LIKE '%q' AND type!='meta' AND sql NOTNULL "
|
---|
| 848 | "ORDER BY substr(type,2,1), name",
|
---|
| 849 | callback, &data, &zErrMsg, azArg[1]);
|
---|
| 850 | }
|
---|
| 851 | }else{
|
---|
| 852 | sqlite_exec(p->db,
|
---|
| 853 | "SELECT sql FROM "
|
---|
| 854 | " (SELECT * FROM sqlite_master UNION ALL"
|
---|
| 855 | " SELECT * FROM sqlite_temp_master) "
|
---|
| 856 | "WHERE type!='meta' AND sql NOTNULL "
|
---|
| 857 | "ORDER BY substr(type,2,1), name",
|
---|
| 858 | callback, &data, &zErrMsg
|
---|
| 859 | );
|
---|
| 860 | }
|
---|
| 861 | if( zErrMsg ){
|
---|
| 862 | fprintf(stderr,"Error: %s\n", zErrMsg);
|
---|
| 863 | sqlite_freemem(zErrMsg);
|
---|
| 864 | }
|
---|
| 865 | }else
|
---|
| 866 |
|
---|
| 867 | if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
|
---|
| 868 | sprintf(p->separator, "%.*s", (int)ArraySize(p->separator)-1, azArg[1]);
|
---|
| 869 | }else
|
---|
| 870 |
|
---|
| 871 | if( c=='s' && strncmp(azArg[0], "show", n)==0){
|
---|
| 872 | int i;
|
---|
| 873 | fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
|
---|
| 874 | fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off");
|
---|
| 875 | fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off");
|
---|
| 876 | fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]);
|
---|
| 877 | fprintf(p->out,"%9.9s: %s\n","nullvalue", p->nullvalue);
|
---|
| 878 | fprintf(p->out,"%9.9s: %s\n","output",
|
---|
| 879 | strlen(p->outfile) ? p->outfile : "stdout");
|
---|
| 880 | fprintf(p->out,"%9.9s: %s\n","separator", p->separator);
|
---|
| 881 | fprintf(p->out,"%9.9s: ","width");
|
---|
| 882 | for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) {
|
---|
| 883 | fprintf(p->out,"%d ",p->colWidth[i]);
|
---|
| 884 | }
|
---|
| 885 | fprintf(p->out,"\n\n");
|
---|
| 886 | }else
|
---|
| 887 |
|
---|
| 888 | if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){
|
---|
| 889 | char **azResult;
|
---|
| 890 | int nRow, rc;
|
---|
| 891 | char *zErrMsg;
|
---|
| 892 | open_db(p);
|
---|
| 893 | if( nArg==1 ){
|
---|
| 894 | rc = sqlite_get_table(p->db,
|
---|
| 895 | "SELECT name FROM sqlite_master "
|
---|
| 896 | "WHERE type IN ('table','view') "
|
---|
| 897 | "UNION ALL "
|
---|
| 898 | "SELECT name FROM sqlite_temp_master "
|
---|
| 899 | "WHERE type IN ('table','view') "
|
---|
| 900 | "ORDER BY 1",
|
---|
| 901 | &azResult, &nRow, 0, &zErrMsg
|
---|
| 902 | );
|
---|
| 903 | }else{
|
---|
| 904 | rc = sqlite_get_table_printf(p->db,
|
---|
| 905 | "SELECT name FROM sqlite_master "
|
---|
| 906 | "WHERE type IN ('table','view') AND name LIKE '%%%q%%' "
|
---|
| 907 | "UNION ALL "
|
---|
| 908 | "SELECT name FROM sqlite_temp_master "
|
---|
| 909 | "WHERE type IN ('table','view') AND name LIKE '%%%q%%' "
|
---|
| 910 | "ORDER BY 1",
|
---|
| 911 | &azResult, &nRow, 0, &zErrMsg, azArg[1], azArg[1]
|
---|
| 912 | );
|
---|
| 913 | }
|
---|
| 914 | if( zErrMsg ){
|
---|
| 915 | fprintf(stderr,"Error: %s\n", zErrMsg);
|
---|
| 916 | sqlite_freemem(zErrMsg);
|
---|
| 917 | }
|
---|
| 918 | if( rc==SQLITE_OK ){
|
---|
| 919 | int len, maxlen = 0;
|
---|
| 920 | int i, j;
|
---|
| 921 | int nPrintCol, nPrintRow;
|
---|
| 922 | for(i=1; i<=nRow; i++){
|
---|
| 923 | if( azResult[i]==0 ) continue;
|
---|
| 924 | len = strlen(azResult[i]);
|
---|
| 925 | if( len>maxlen ) maxlen = len;
|
---|
| 926 | }
|
---|
| 927 | nPrintCol = 80/(maxlen+2);
|
---|
| 928 | if( nPrintCol<1 ) nPrintCol = 1;
|
---|
| 929 | nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
|
---|
| 930 | for(i=0; i<nPrintRow; i++){
|
---|
| 931 | for(j=i+1; j<=nRow; j+=nPrintRow){
|
---|
| 932 | char *zSp = j<=nPrintRow ? "" : " ";
|
---|
| 933 | printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : "");
|
---|
| 934 | }
|
---|
| 935 | printf("\n");
|
---|
| 936 | }
|
---|
| 937 | }
|
---|
| 938 | sqlite_free_table(azResult);
|
---|
| 939 | }else
|
---|
| 940 |
|
---|
| 941 | if( c=='t' && n>1 && strncmp(azArg[0], "timeout", n)==0 && nArg>=2 ){
|
---|
| 942 | open_db(p);
|
---|
| 943 | sqlite_busy_timeout(p->db, atoi(azArg[1]));
|
---|
| 944 | }else
|
---|
| 945 |
|
---|
| 946 | if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
|
---|
| 947 | int j;
|
---|
| 948 | for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
|
---|
| 949 | p->colWidth[j-1] = atoi(azArg[j]);
|
---|
| 950 | }
|
---|
| 951 | }else
|
---|
| 952 |
|
---|
| 953 | {
|
---|
| 954 | fprintf(stderr, "unknown command or invalid arguments: "
|
---|
| 955 | " \"%s\". Enter \".help\" for help\n", azArg[0]);
|
---|
| 956 | }
|
---|
| 957 |
|
---|
| 958 | return rc;
|
---|
| 959 | }
|
---|
| 960 |
|
---|
| 961 | /*
|
---|
| 962 | ** Return TRUE if the last non-whitespace character in z[] is a semicolon.
|
---|
| 963 | ** z[] is N characters long.
|
---|
| 964 | */
|
---|
| 965 | static int _ends_with_semicolon(const char *z, int N){
|
---|
| 966 | while( N>0 && isspace(z[N-1]) ){ N--; }
|
---|
| 967 | return N>0 && z[N-1]==';';
|
---|
| 968 | }
|
---|
| 969 |
|
---|
| 970 | /*
|
---|
| 971 | ** Test to see if a line consists entirely of whitespace.
|
---|
| 972 | */
|
---|
| 973 | static int _all_whitespace(const char *z){
|
---|
| 974 | for(; *z; z++){
|
---|
| 975 | if( isspace(*z) ) continue;
|
---|
| 976 | if( *z=='/' && z[1]=='*' ){
|
---|
| 977 | z += 2;
|
---|
| 978 | while( *z && (*z!='*' || z[1]!='/') ){ z++; }
|
---|
| 979 | if( *z==0 ) return 0;
|
---|
| 980 | z++;
|
---|
| 981 | continue;
|
---|
| 982 | }
|
---|
| 983 | if( *z=='-' && z[1]=='-' ){
|
---|
| 984 | z += 2;
|
---|
| 985 | while( *z && *z!='\n' ){ z++; }
|
---|
| 986 | if( *z==0 ) return 1;
|
---|
| 987 | continue;
|
---|
| 988 | }
|
---|
| 989 | return 0;
|
---|
| 990 | }
|
---|
| 991 | return 1;
|
---|
| 992 | }
|
---|
| 993 |
|
---|
| 994 | /*
|
---|
| 995 | ** Return TRUE if the line typed in is an SQL command terminator other
|
---|
| 996 | ** than a semi-colon. The SQL Server style "go" command is understood
|
---|
| 997 | ** as is the Oracle "/".
|
---|
| 998 | */
|
---|
| 999 | static int _is_command_terminator(const char *zLine){
|
---|
| 1000 | extern int sqliteStrNICmp(const char*,const char*,int);
|
---|
| 1001 | while( isspace(*zLine) ){ zLine++; };
|
---|
| 1002 | if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ) return 1; /* Oracle */
|
---|
| 1003 | if( sqliteStrNICmp(zLine,"go",2)==0 && _all_whitespace(&zLine[2]) ){
|
---|
| 1004 | return 1; /* SQL Server */
|
---|
| 1005 | }
|
---|
| 1006 | return 0;
|
---|
| 1007 | }
|
---|
| 1008 |
|
---|
| 1009 | /*
|
---|
| 1010 | ** Read input from *in and process it. If *in==0 then input
|
---|
| 1011 | ** is interactive - the user is typing it it. Otherwise, input
|
---|
| 1012 | ** is coming from a file or device. A prompt is issued and history
|
---|
| 1013 | ** is saved only if input is interactive. An interrupt signal will
|
---|
| 1014 | ** cause this routine to exit immediately, unless input is interactive.
|
---|
| 1015 | */
|
---|
| 1016 | static void process_input(struct callback_data *p, FILE *in){
|
---|
| 1017 | char *zLine;
|
---|
| 1018 | char *zSql = 0;
|
---|
| 1019 | int nSql = 0;
|
---|
| 1020 | char *zErrMsg;
|
---|
| 1021 | int rc;
|
---|
| 1022 | while( fflush(p->out), (zLine = one_input_line(zSql, in))!=0 ){
|
---|
| 1023 | if( seenInterrupt ){
|
---|
| 1024 | if( in!=0 ) break;
|
---|
| 1025 | seenInterrupt = 0;
|
---|
| 1026 | }
|
---|
| 1027 | if( p->echoOn ) printf("%s\n", zLine);
|
---|
| 1028 | if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue;
|
---|
| 1029 | if( zLine && zLine[0]=='.' && nSql==0 ){
|
---|
| 1030 | int rc = do_meta_command(zLine, p);
|
---|
| 1031 | free(zLine);
|
---|
| 1032 | if( rc ) break;
|
---|
| 1033 | continue;
|
---|
| 1034 | }
|
---|
| 1035 | if( _is_command_terminator(zLine) ){
|
---|
| 1036 | strcpy(zLine,";");
|
---|
| 1037 | }
|
---|
| 1038 | if( zSql==0 ){
|
---|
| 1039 | int i;
|
---|
| 1040 | for(i=0; zLine[i] && isspace(zLine[i]); i++){}
|
---|
| 1041 | if( zLine[i]!=0 ){
|
---|
| 1042 | nSql = strlen(zLine);
|
---|
| 1043 | zSql = malloc( nSql+1 );
|
---|
| 1044 | strcpy(zSql, zLine);
|
---|
| 1045 | }
|
---|
| 1046 | }else{
|
---|
| 1047 | int len = strlen(zLine);
|
---|
| 1048 | zSql = realloc( zSql, nSql + len + 2 );
|
---|
| 1049 | if( zSql==0 ){
|
---|
| 1050 | fprintf(stderr,"%s: out of memory!\n", Argv0);
|
---|
| 1051 | exit(1);
|
---|
| 1052 | }
|
---|
| 1053 | strcpy(&zSql[nSql++], "\n");
|
---|
| 1054 | strcpy(&zSql[nSql], zLine);
|
---|
| 1055 | nSql += len;
|
---|
| 1056 | }
|
---|
| 1057 | free(zLine);
|
---|
| 1058 | if( zSql && _ends_with_semicolon(zSql, nSql) && sqlite_complete(zSql) ){
|
---|
| 1059 | p->cnt = 0;
|
---|
| 1060 | open_db(p);
|
---|
| 1061 | rc = sqlite_exec(p->db, zSql, callback, p, &zErrMsg);
|
---|
| 1062 | if( rc || zErrMsg ){
|
---|
| 1063 | if( in!=0 && !p->echoOn ) printf("%s\n",zSql);
|
---|
| 1064 | if( zErrMsg!=0 ){
|
---|
| 1065 | printf("SQL error: %s\n", zErrMsg);
|
---|
| 1066 | sqlite_freemem(zErrMsg);
|
---|
| 1067 | zErrMsg = 0;
|
---|
| 1068 | }else{
|
---|
| 1069 | printf("SQL error: %s\n", sqlite_error_string(rc));
|
---|
| 1070 | }
|
---|
| 1071 | }
|
---|
| 1072 | free(zSql);
|
---|
| 1073 | zSql = 0;
|
---|
| 1074 | nSql = 0;
|
---|
| 1075 | }
|
---|
| 1076 | }
|
---|
| 1077 | if( zSql ){
|
---|
| 1078 | if( !_all_whitespace(zSql) ) printf("Incomplete SQL: %s\n", zSql);
|
---|
| 1079 | free(zSql);
|
---|
| 1080 | }
|
---|
| 1081 | }
|
---|
| 1082 |
|
---|
| 1083 | /*
|
---|
| 1084 | ** Return a pathname which is the user's home directory. A
|
---|
| 1085 | ** 0 return indicates an error of some kind. Space to hold the
|
---|
| 1086 | ** resulting string is obtained from malloc(). The calling
|
---|
| 1087 | ** function should free the result.
|
---|
| 1088 | */
|
---|
| 1089 | static char *find_home_dir(void){
|
---|
| 1090 | char *home_dir = NULL;
|
---|
| 1091 |
|
---|
| 1092 | #if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__)
|
---|
| 1093 | struct passwd *pwent;
|
---|
| 1094 | uid_t uid = getuid();
|
---|
| 1095 | if( (pwent=getpwuid(uid)) != NULL) {
|
---|
| 1096 | home_dir = pwent->pw_dir;
|
---|
| 1097 | }
|
---|
| 1098 | #endif
|
---|
| 1099 |
|
---|
| 1100 | #ifdef __MACOS__
|
---|
| 1101 | char home_path[_MAX_PATH+1];
|
---|
| 1102 | home_dir = getcwd(home_path, _MAX_PATH);
|
---|
| 1103 | #endif
|
---|
| 1104 |
|
---|
| 1105 | if (!home_dir) {
|
---|
| 1106 | home_dir = getenv("HOME");
|
---|
| 1107 | if (!home_dir) {
|
---|
| 1108 | home_dir = getenv("HOMEPATH"); /* Windows? */
|
---|
| 1109 | }
|
---|
| 1110 | }
|
---|
| 1111 |
|
---|
| 1112 | #if defined(_WIN32) || defined(WIN32)
|
---|
| 1113 | if (!home_dir) {
|
---|
| 1114 | home_dir = "c:";
|
---|
| 1115 | }
|
---|
| 1116 | #endif
|
---|
| 1117 |
|
---|
| 1118 | if( home_dir ){
|
---|
| 1119 | char *z = malloc( strlen(home_dir)+1 );
|
---|
| 1120 | if( z ) strcpy(z, home_dir);
|
---|
| 1121 | home_dir = z;
|
---|
| 1122 | }
|
---|
| 1123 |
|
---|
| 1124 | return home_dir;
|
---|
| 1125 | }
|
---|
| 1126 |
|
---|
| 1127 | /*
|
---|
| 1128 | ** Read input from the file given by sqliterc_override. Or if that
|
---|
| 1129 | ** parameter is NULL, take input from ~/.sqliterc
|
---|
| 1130 | */
|
---|
| 1131 | static void process_sqliterc(
|
---|
| 1132 | struct callback_data *p, /* Configuration data */
|
---|
| 1133 | const char *sqliterc_override /* Name of config file. NULL to use default */
|
---|
| 1134 | ){
|
---|
| 1135 | char *home_dir = NULL;
|
---|
| 1136 | const char *sqliterc = sqliterc_override;
|
---|
| 1137 | char *zBuf;
|
---|
| 1138 | FILE *in = NULL;
|
---|
| 1139 |
|
---|
| 1140 | if (sqliterc == NULL) {
|
---|
| 1141 | home_dir = find_home_dir();
|
---|
| 1142 | if( home_dir==0 ){
|
---|
| 1143 | fprintf(stderr,"%s: cannot locate your home directory!\n", Argv0);
|
---|
| 1144 | return;
|
---|
| 1145 | }
|
---|
| 1146 | zBuf = malloc(strlen(home_dir) + 15);
|
---|
| 1147 | if( zBuf==0 ){
|
---|
| 1148 | fprintf(stderr,"%s: out of memory!\n", Argv0);
|
---|
| 1149 | exit(1);
|
---|
| 1150 | }
|
---|
| 1151 | sprintf(zBuf,"%s/.sqliterc",home_dir);
|
---|
| 1152 | free(home_dir);
|
---|
| 1153 | sqliterc = (const char*)zBuf;
|
---|
| 1154 | }
|
---|
| 1155 | in = fopen(sqliterc,"rb");
|
---|
| 1156 | if( in ){
|
---|
| 1157 | if( isatty(fileno(stdout)) ){
|
---|
| 1158 | printf("Loading resources from %s\n",sqliterc);
|
---|
| 1159 | }
|
---|
| 1160 | process_input(p,in);
|
---|
| 1161 | fclose(in);
|
---|
| 1162 | }
|
---|
| 1163 | return;
|
---|
| 1164 | }
|
---|
| 1165 |
|
---|
| 1166 | /*
|
---|
| 1167 | ** Show available command line options
|
---|
| 1168 | */
|
---|
| 1169 | static const char zOptions[] =
|
---|
| 1170 | " -init filename read/process named file\n"
|
---|
| 1171 | " -echo print commands before execution\n"
|
---|
| 1172 | " -[no]header turn headers on or off\n"
|
---|
| 1173 | " -column set output mode to 'column'\n"
|
---|
| 1174 | " -html set output mode to HTML\n"
|
---|
| 1175 | #ifdef SQLITE_HAS_CODEC
|
---|
| 1176 | " -key KEY encryption key\n"
|
---|
| 1177 | #endif
|
---|
| 1178 | " -line set output mode to 'line'\n"
|
---|
| 1179 | " -list set output mode to 'list'\n"
|
---|
| 1180 | " -separator 'x' set output field separator (|)\n"
|
---|
| 1181 | " -nullvalue 'text' set text string for NULL values\n"
|
---|
| 1182 | " -version show SQLite version\n"
|
---|
| 1183 | " -help show this text, also show dot-commands\n"
|
---|
| 1184 | ;
|
---|
| 1185 | static void usage(int showDetail){
|
---|
| 1186 | fprintf(stderr, "Usage: %s [OPTIONS] FILENAME [SQL]\n", Argv0);
|
---|
| 1187 | if( showDetail ){
|
---|
| 1188 | fprintf(stderr, "Options are:\n%s", zOptions);
|
---|
| 1189 | }else{
|
---|
| 1190 | fprintf(stderr, "Use the -help option for additional information\n");
|
---|
| 1191 | }
|
---|
| 1192 | exit(1);
|
---|
| 1193 | }
|
---|
| 1194 |
|
---|
| 1195 | /*
|
---|
| 1196 | ** Initialize the state information in data
|
---|
| 1197 | */
|
---|
| 1198 | void main_init(struct callback_data *data) {
|
---|
| 1199 | memset(data, 0, sizeof(*data));
|
---|
| 1200 | data->mode = MODE_List;
|
---|
| 1201 | strcpy(data->separator,"|");
|
---|
| 1202 | data->showHeader = 0;
|
---|
| 1203 | strcpy(mainPrompt,"sqlite> ");
|
---|
| 1204 | strcpy(continuePrompt," ...> ");
|
---|
| 1205 | }
|
---|
| 1206 |
|
---|
| 1207 | int main(int argc, char **argv){
|
---|
| 1208 | char *zErrMsg = 0;
|
---|
| 1209 | struct callback_data data;
|
---|
| 1210 | const char *zInitFile = 0;
|
---|
| 1211 | char *zFirstCmd = 0;
|
---|
| 1212 | int i;
|
---|
| 1213 | extern int sqliteOsFileExists(const char*);
|
---|
| 1214 |
|
---|
| 1215 | #ifdef __MACOS__
|
---|
| 1216 | argc = ccommand(&argv);
|
---|
| 1217 | #endif
|
---|
| 1218 |
|
---|
| 1219 | Argv0 = argv[0];
|
---|
| 1220 | main_init(&data);
|
---|
| 1221 |
|
---|
| 1222 | /* Make sure we have a valid signal handler early, before anything
|
---|
| 1223 | ** else is done.
|
---|
| 1224 | */
|
---|
| 1225 | #ifdef SIGINT
|
---|
| 1226 | signal(SIGINT, interrupt_handler);
|
---|
| 1227 | #endif
|
---|
| 1228 |
|
---|
| 1229 | /* Do an initial pass through the command-line argument to locate
|
---|
| 1230 | ** the name of the database file, the name of the initialization file,
|
---|
| 1231 | ** and the first command to execute.
|
---|
| 1232 | */
|
---|
| 1233 | for(i=1; i<argc-1; i++){
|
---|
| 1234 | if( argv[i][0]!='-' ) break;
|
---|
| 1235 | if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){
|
---|
| 1236 | i++;
|
---|
| 1237 | }else if( strcmp(argv[i],"-init")==0 ){
|
---|
| 1238 | i++;
|
---|
| 1239 | zInitFile = argv[i];
|
---|
| 1240 | }else if( strcmp(argv[i],"-key")==0 ){
|
---|
| 1241 | i++;
|
---|
| 1242 | data.zKey = sqlite_mprintf("%s",argv[i]);
|
---|
| 1243 | }
|
---|
| 1244 | }
|
---|
| 1245 | if( i<argc ){
|
---|
| 1246 | data.zDbFilename = argv[i++];
|
---|
| 1247 | }else{
|
---|
| 1248 | data.zDbFilename = ":memory:";
|
---|
| 1249 | }
|
---|
| 1250 | if( i<argc ){
|
---|
| 1251 | zFirstCmd = argv[i++];
|
---|
| 1252 | }
|
---|
| 1253 | data.out = stdout;
|
---|
| 1254 |
|
---|
| 1255 | /* Go ahead and open the database file if it already exists. If the
|
---|
| 1256 | ** file does not exist, delay opening it. This prevents empty database
|
---|
| 1257 | ** files from being created if a user mistypes the database name argument
|
---|
| 1258 | ** to the sqlite command-line tool.
|
---|
| 1259 | */
|
---|
| 1260 | if( sqliteOsFileExists(data.zDbFilename) ){
|
---|
| 1261 | open_db(&data);
|
---|
| 1262 | }
|
---|
| 1263 |
|
---|
| 1264 | /* Process the initialization file if there is one. If no -init option
|
---|
| 1265 | ** is given on the command line, look for a file named ~/.sqliterc and
|
---|
| 1266 | ** try to process it.
|
---|
| 1267 | */
|
---|
| 1268 | process_sqliterc(&data,zInitFile);
|
---|
| 1269 |
|
---|
| 1270 | /* Make a second pass through the command-line argument and set
|
---|
| 1271 | ** options. This second pass is delayed until after the initialization
|
---|
| 1272 | ** file is processed so that the command-line arguments will override
|
---|
| 1273 | ** settings in the initialization file.
|
---|
| 1274 | */
|
---|
| 1275 | for(i=1; i<argc && argv[i][0]=='-'; i++){
|
---|
| 1276 | char *z = argv[i];
|
---|
| 1277 | if( strcmp(z,"-init")==0 || strcmp(z,"-key")==0 ){
|
---|
| 1278 | i++;
|
---|
| 1279 | }else if( strcmp(z,"-html")==0 ){
|
---|
| 1280 | data.mode = MODE_Html;
|
---|
| 1281 | }else if( strcmp(z,"-list")==0 ){
|
---|
| 1282 | data.mode = MODE_List;
|
---|
| 1283 | }else if( strcmp(z,"-line")==0 ){
|
---|
| 1284 | data.mode = MODE_Line;
|
---|
| 1285 | }else if( strcmp(z,"-column")==0 ){
|
---|
| 1286 | data.mode = MODE_Column;
|
---|
| 1287 | }else if( strcmp(z,"-separator")==0 ){
|
---|
| 1288 | i++;
|
---|
| 1289 | sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[i]);
|
---|
| 1290 | }else if( strcmp(z,"-nullvalue")==0 ){
|
---|
| 1291 | i++;
|
---|
| 1292 | sprintf(data.nullvalue,"%.*s",(int)sizeof(data.nullvalue)-1,argv[i]);
|
---|
| 1293 | }else if( strcmp(z,"-header")==0 ){
|
---|
| 1294 | data.showHeader = 1;
|
---|
| 1295 | }else if( strcmp(z,"-noheader")==0 ){
|
---|
| 1296 | data.showHeader = 0;
|
---|
| 1297 | }else if( strcmp(z,"-echo")==0 ){
|
---|
| 1298 | data.echoOn = 1;
|
---|
| 1299 | }else if( strcmp(z,"-version")==0 ){
|
---|
| 1300 | printf("%s\n", sqlite_version);
|
---|
| 1301 | return 1;
|
---|
| 1302 | }else if( strcmp(z,"-help")==0 ){
|
---|
| 1303 | usage(1);
|
---|
| 1304 | }else{
|
---|
| 1305 | fprintf(stderr,"%s: unknown option: %s\n", Argv0, z);
|
---|
| 1306 | fprintf(stderr,"Use -help for a list of options.\n");
|
---|
| 1307 | return 1;
|
---|
| 1308 | }
|
---|
| 1309 | }
|
---|
| 1310 |
|
---|
| 1311 | if( zFirstCmd ){
|
---|
| 1312 | /* Run just the command that follows the database name
|
---|
| 1313 | */
|
---|
| 1314 | if( zFirstCmd[0]=='.' ){
|
---|
| 1315 | do_meta_command(zFirstCmd, &data);
|
---|
| 1316 | exit(0);
|
---|
| 1317 | }else{
|
---|
| 1318 | int rc;
|
---|
| 1319 | open_db(&data);
|
---|
| 1320 | rc = sqlite_exec(data.db, zFirstCmd, callback, &data, &zErrMsg);
|
---|
| 1321 | if( rc!=0 && zErrMsg!=0 ){
|
---|
| 1322 | fprintf(stderr,"SQL error: %s\n", zErrMsg);
|
---|
| 1323 | exit(1);
|
---|
| 1324 | }
|
---|
| 1325 | }
|
---|
| 1326 | }else{
|
---|
| 1327 | /* Run commands received from standard input
|
---|
| 1328 | */
|
---|
| 1329 | if( isatty(fileno(stdout)) && isatty(fileno(stdin)) ){
|
---|
| 1330 | char *zHome;
|
---|
| 1331 | char *zHistory = 0;
|
---|
| 1332 | printf(
|
---|
| 1333 | "SQLite version %s\n"
|
---|
| 1334 | "Enter \".help\" for instructions\n",
|
---|
| 1335 | sqlite_version
|
---|
| 1336 | );
|
---|
| 1337 | zHome = find_home_dir();
|
---|
| 1338 | if( zHome && (zHistory = malloc(strlen(zHome)+20))!=0 ){
|
---|
| 1339 | sprintf(zHistory,"%s/.sqlite_history", zHome);
|
---|
| 1340 | }
|
---|
| 1341 | if( zHistory ) read_history(zHistory);
|
---|
| 1342 | process_input(&data, 0);
|
---|
| 1343 | if( zHistory ){
|
---|
| 1344 | stifle_history(100);
|
---|
| 1345 | write_history(zHistory);
|
---|
| 1346 | }
|
---|
| 1347 | }else{
|
---|
| 1348 | process_input(&data, stdin);
|
---|
| 1349 | }
|
---|
| 1350 | }
|
---|
| 1351 | set_table_name(&data, 0);
|
---|
| 1352 | if( db ) sqlite_close(db);
|
---|
| 1353 | return 0;
|
---|
| 1354 | }
|
---|