--- elwix/files/sqlite/dist/shell.c 2012/10/12 08:22:46 1.2 +++ elwix/files/sqlite/dist/shell.c 2013/01/28 01:47:36 1.3 @@ -36,7 +36,7 @@ #include #include -#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) +#if !defined(_WIN32) && !defined(WIN32) # include # if !defined(__RTP__) && !defined(_WRS_KERNEL) # include @@ -45,10 +45,6 @@ # include #endif -#ifdef __OS2__ -# include -#endif - #ifdef HAVE_EDITLINE # include #endif @@ -57,7 +53,7 @@ # include #endif #if !defined(HAVE_EDITLINE) && (!defined(HAVE_READLINE) || HAVE_READLINE!=1) -# define readline(p) local_getline(p,stdin) +# define readline(p) local_getline(p,stdin,0) # define add_history(X) # define read_history(X) # define write_history(X) @@ -68,6 +64,10 @@ # include #define isatty(h) _isatty(h) #define access(f,m) _access((f),(m)) +#undef popen +#define popen(a,b) _popen((a),(b)) +#undef pclose +#define pclose(x) _pclose(x) #else /* Make sure isatty() has a prototype. */ @@ -90,7 +90,7 @@ static int enableTimer = 0; #define IsDigit(X) isdigit((unsigned char)X) #define ToLower(X) (char)tolower((unsigned char)X) -#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(__RTP__) && !defined(_WRS_KERNEL) +#if !defined(_WIN32) && !defined(WIN32) && !defined(_WRS_KERNEL) #include #include @@ -334,10 +334,11 @@ static void shellstaticFunc( ** The interface is like "readline" but no command-line editing ** is done. */ -static char *local_getline(char *zPrompt, FILE *in){ +static char *local_getline(char *zPrompt, FILE *in, int csvFlag){ char *zLine; int nLine; int n; + int inQuote = 0; if( zPrompt && *zPrompt ){ printf("%s",zPrompt); @@ -361,8 +362,11 @@ static char *local_getline(char *zPrompt, FILE *in){ zLine[n] = 0; break; } - while( zLine[n] ){ n++; } - if( n>0 && zLine[n-1]=='\n' ){ + while( zLine[n] ){ + if( zLine[n]=='"' ) inQuote = !inQuote; + n++; + } + if( n>0 && zLine[n-1]=='\n' && (!inQuote || !csvFlag) ){ n--; if( n>0 && zLine[n-1]=='\r' ) n--; zLine[n] = 0; @@ -383,7 +387,7 @@ static char *one_input_line(const char *zPrior, FILE * char *zPrompt; char *zResult; if( in!=0 ){ - return local_getline(0, in); + return local_getline(0, in, 0); } if( zPrior && zPrior[0] ){ zPrompt = continuePrompt; @@ -415,6 +419,7 @@ struct callback_data { int statsOn; /* True to display memory stats before each finalize */ int cnt; /* Number of records displayed so far */ FILE *out; /* Write results here */ + FILE *traceOut; /* Output for sqlite3_trace() */ int nErr; /* Number of errors seen */ int mode; /* An output mode setting */ int writableSchema; /* True if PRAGMA writable_schema=ON */ @@ -492,7 +497,7 @@ static void output_hex_blob(FILE *out, const void *pBl int i; char *zBlob = (char *)pBlob; fprintf(out,"X'"); - for(i=0; iseparator is used for ** the separator, which may or may not be a comma. p->nullvalue is -** the null value. Strings are quoted using ANSI-C rules. Numbers -** appear outside of quotes. +** the null value. Strings are quoted if necessary. */ static void output_csv(struct callback_data *p, const char *z, int bSep){ FILE *out = p->out; @@ -692,7 +699,7 @@ static int shell_callback(void *pArg, int nArg, char * }else{ w = 0; } - if( w<=0 ){ + if( w==0 ){ w = strlen30(azCol[i] ? azCol[i] : ""); if( w<10 ) w = 10; n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullvalue); @@ -702,7 +709,11 @@ static int shell_callback(void *pArg, int nArg, char * p->actualWidth[i] = w; } if( p->showHeader ){ - fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " "); + if( w<0 ){ + fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], i==nArg-1 ? "\n": " "); + }else{ + fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " "); + } } } if( p->showHeader ){ @@ -710,6 +721,7 @@ static int shell_callback(void *pArg, int nArg, char * int w; if( iactualWidth) ){ w = p->actualWidth[i]; + if( w<0 ) w = -w; }else{ w = 10; } @@ -731,8 +743,13 @@ static int shell_callback(void *pArg, int nArg, char * strlen30(azArg[i])>w ){ w = strlen30(azArg[i]); } - fprintf(p->out,"%-*.*s%s",w,w, - azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); + if( w<0 ){ + fprintf(p->out,"%*.*s%s",-w,-w, + azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); + }else{ + fprintf(p->out,"%-*.*s%s",w,w, + azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); + } } break; } @@ -782,14 +799,14 @@ static int shell_callback(void *pArg, int nArg, char * if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,azCol[i] ? azCol[i] : ""); - fprintf(p->out, "%s", p->separator); + if(iout, "%s", p->separator); } fprintf(p->out,"\n"); } if( azArg==0 ) break; for(i=0; iout, azArg[i] ? azArg[i] : p->nullvalue); - fprintf(p->out, "%s", p->separator); + if(iout, "%s", p->separator); } fprintf(p->out,"\n"); break; @@ -934,11 +951,14 @@ static char *appendText(char *zIn, char const *zAppend /* -** Execute a query statement that has a single result column. Print -** that result column on a line by itself with a semicolon terminator. +** Execute a query statement that will generate SQL output. Print +** the result columns, comma-separated, on a line and then add a +** semicolon terminator to the end of that line. ** -** This is used, for example, to show the schema of the database by -** querying the SQLITE_MASTER table. +** If the number of columns is 1 and that column contains text "--" +** then write the semicolon on a separate line. That way, if a +** "--" comment occurs at the end of the statement, the comment +** won't consume the semicolon terminator. */ static int run_table_dump_query( struct callback_data *p, /* Query context */ @@ -947,6 +967,9 @@ static int run_table_dump_query( ){ sqlite3_stmt *pSelect; int rc; + int nResult; + int i; + const char *z; rc = sqlite3_prepare(p->db, zSelect, -1, &pSelect, 0); if( rc!=SQLITE_OK || !pSelect ){ fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); @@ -954,12 +977,24 @@ static int run_table_dump_query( return rc; } rc = sqlite3_step(pSelect); + nResult = sqlite3_column_count(pSelect); while( rc==SQLITE_ROW ){ if( zFirstRow ){ fprintf(p->out, "%s", zFirstRow); zFirstRow = 0; } - fprintf(p->out, "%s;\n", sqlite3_column_text(pSelect, 0)); + z = (const char*)sqlite3_column_text(pSelect, 0); + fprintf(p->out, "%s", z); + for(i=1; iout, ",%s", sqlite3_column_text(pSelect, i)); + } + if( z==0 ) z = ""; + while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; + if( z[0] ){ + fprintf(p->out, "\n;\n"); + }else{ + fprintf(p->out, ";\n"); + } rc = sqlite3_step(pSelect); } rc = sqlite3_finalize(pSelect); @@ -1056,6 +1091,9 @@ static int display_stats( sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); fprintf(pArg->out, "Page cache misses: %d\n", iCur); iHiwtr = iCur = -1; + sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); + fprintf(pArg->out, "Page cache writes: %d\n", iCur); + iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; @@ -1278,9 +1316,12 @@ static int dump_callback(void *pArg, int nArg, char ** } zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0); + /* Always quote the table name, even if it appears to be pure ascii, + ** in case it is a keyword. Ex: INSERT INTO "table" ... */ zTmp = appendText(zTmp, zTable, '"'); if( zTmp ){ zSelect = appendText(zSelect, zTmp, '\''); + free(zTmp); } zSelect = appendText(zSelect, " || ' VALUES(' || ", 0); rc = sqlite3_step(pTableInfo); @@ -1290,7 +1331,7 @@ static int dump_callback(void *pArg, int nArg, char ** zSelect = appendText(zSelect, zText, '"'); rc = sqlite3_step(pTableInfo); if( rc==SQLITE_ROW ){ - zSelect = appendText(zSelect, ") || ',' || ", 0); + zSelect = appendText(zSelect, "), ", 0); }else{ zSelect = appendText(zSelect, ") ", 0); } @@ -1309,7 +1350,7 @@ static int dump_callback(void *pArg, int nArg, char ** zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0); run_table_dump_query(p, zSelect, 0); } - if( zSelect ) free(zSelect); + free(zSelect); } return 0; } @@ -1339,7 +1380,7 @@ static int run_schema_dump_query( } zQ2 = malloc( len+100 ); if( zQ2==0 ) return rc; - sqlite3_snprintf(sizeof(zQ2), zQ2, "%s ORDER BY rowid DESC", zQuery); + sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery); rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); if( rc ){ fprintf(p->out, "/****** ERROR: %s ******/\n", zErr); @@ -1388,9 +1429,10 @@ static char zHelp[] = " list Values delimited by .separator string\n" " tabs Tab-separated values\n" " tcl TCL list elements\n" - ".nullvalue STRING Print STRING in place of NULL values\n" + ".nullvalue STRING Use STRING in place of NULL values\n" ".output FILENAME Send output to FILENAME\n" ".output stdout Send output to the screen\n" + ".print STRING... Print literal STRING\n" ".prompt MAIN CONTINUE Replace the standard prompts\n" ".quit Exit this program\n" ".read FILENAME Execute SQL in FILENAME\n" @@ -1405,6 +1447,7 @@ static char zHelp[] = " If TABLE specified, only list tables matching\n" " LIKE pattern TABLE.\n" ".timeout MS Try opening locked tables for MS milliseconds\n" + ".trace FILE|off Output each SQL statement as it is run\n" ".vfsname ?AUX? Print the name of the VFS stack\n" ".width NUM1 NUM2 ... Set column widths for \"column\" mode\n" ; @@ -1422,6 +1465,7 @@ static int process_input(struct callback_data *p, FILE */ static void open_db(struct callback_data *p){ if( p->db==0 ){ + sqlite3_initialize(); sqlite3_open(p->zDbFilename, &p->db); db = p->db; if( db && sqlite3_errcode(db)==SQLITE_OK ){ @@ -1495,6 +1539,52 @@ static int booleanValue(char *zArg){ } /* +** Close an output file, assuming it is not stderr or stdout +*/ +static void output_file_close(FILE *f){ + if( f && f!=stdout && f!=stderr ) fclose(f); +} + +/* +** Try to open an output file. The names "stdout" and "stderr" are +** recognized and do the right thing. NULL is returned if the output +** filename is "off". +*/ +static FILE *output_file_open(const char *zFile){ + FILE *f; + if( strcmp(zFile,"stdout")==0 ){ + f = stdout; + }else if( strcmp(zFile, "stderr")==0 ){ + f = stderr; + }else if( strcmp(zFile, "off")==0 ){ + f = 0; + }else{ + f = fopen(zFile, "wb"); + if( f==0 ){ + fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); + } + } + return f; +} + +/* +** A routine for handling output from sqlite3_trace(). +*/ +static void sql_trace_callback(void *pArg, const char *z){ + FILE *f = (FILE*)pArg; + if( f ) fprintf(f, "%s\n", z); +} + +/* +** A no-op routine that runs with the ".breakpoint" doc-command. This is +** a useful spot to set a debugger breakpoint. +*/ +static void test_breakpoint(void){ + static int nCall = 0; + nCall++; +} + +/* ** If an input line begins with "." then invoke this routine to ** process that line. ** @@ -1573,6 +1663,13 @@ static int do_meta_command(char *zLine, struct callbac bail_on_error = booleanValue(azArg[1]); }else + /* The undocumented ".breakpoint" command causes a call to the no-op + ** routine named test_breakpoint(). + */ + if( c=='b' && n>=3 && strncmp(azArg[0], "breakpoint", n)==0 ){ + test_breakpoint(); + }else + if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 && nArg==1 ){ struct callback_data data; char *zErrMsg = 0; @@ -1769,12 +1866,15 @@ static int do_meta_command(char *zLine, struct callbac } sqlite3_exec(p->db, "BEGIN", 0, 0, 0); zCommit = "COMMIT"; - while( (zLine = local_getline(0, in))!=0 ){ - char *z; + while( (zLine = local_getline(0, in, 1))!=0 ){ + char *z, c; + int inQuote = 0; lineno++; azCol[0] = zLine; - for(i=0, z=zLine; *z && *z!='\n' && *z!='\r'; z++){ - if( *z==p->separator[0] && strncmp(z, p->separator, nSep)==0 ){ + for(i=0, z=zLine; (c = *z)!=0; z++){ + if( c=='"' ) inQuote = !inQuote; + if( c=='\n' ) lineno++; + if( !inQuote && c==p->separator[0] && strncmp(z,p->separator,nSep)==0 ){ *z = 0; i++; if( i=2 ){ const char *zFile = azArg[1]; - if( p->pLog && p->pLog!=stdout && p->pLog!=stderr ){ - fclose(p->pLog); - p->pLog = 0; - } - if( strcmp(zFile,"stdout")==0 ){ - p->pLog = stdout; - }else if( strcmp(zFile, "stderr")==0 ){ - p->pLog = stderr; - }else if( strcmp(zFile, "off")==0 ){ - p->pLog = 0; - }else{ - p->pLog = fopen(zFile, "w"); - if( p->pLog==0 ){ - fprintf(stderr, "Error: cannot open \"%s\"\n", zFile); - } - } + output_file_close(p->pLog); + p->pLog = output_file_open(zFile); }else if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg==2 ){ @@ -1927,6 +2021,7 @@ static int do_meta_command(char *zLine, struct callbac p->mode = MODE_Html; }else if( n2==3 && strncmp(azArg[1],"tcl",n2)==0 ){ p->mode = MODE_Tcl; + sqlite3_snprintf(sizeof(p->separator), p->separator, " "); }else if( n2==3 && strncmp(azArg[1],"csv",n2)==0 ){ p->mode = MODE_Csv; sqlite3_snprintf(sizeof(p->separator), p->separator, ","); @@ -1961,24 +2056,44 @@ static int do_meta_command(char *zLine, struct callbac }else if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){ - if( p->out!=stdout ){ - fclose(p->out); + if( p->outfile[0]=='|' ){ + pclose(p->out); + }else{ + output_file_close(p->out); } - if( strcmp(azArg[1],"stdout")==0 ){ - p->out = stdout; - sqlite3_snprintf(sizeof(p->outfile), p->outfile, "stdout"); + p->outfile[0] = 0; + if( azArg[1][0]=='|' ){ + p->out = popen(&azArg[1][1], "w"); + if( p->out==0 ){ + fprintf(stderr,"Error: cannot open pipe \"%s\"\n", &azArg[1][1]); + p->out = stdout; + rc = 1; + }else{ + sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]); + } }else{ - p->out = fopen(azArg[1], "wb"); + p->out = output_file_open(azArg[1]); if( p->out==0 ){ - fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]); + if( strcmp(azArg[1],"off")!=0 ){ + fprintf(stderr,"Error: cannot write to \"%s\"\n", azArg[1]); + } p->out = stdout; rc = 1; } else { - sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]); + sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", azArg[1]); } } }else + if( c=='p' && n>=3 && strncmp(azArg[0], "print", n)==0 ){ + int i; + for(i=1; i1 ) fprintf(p->out, " "); + fprintf(p->out, "%s", azArg[i]); + } + fprintf(p->out, "\n"); + }else + if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){ if( nArg >= 2) { strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1); @@ -2092,23 +2207,25 @@ static int do_meta_command(char *zLine, struct callbac zShellStatic = azArg[1]; rc = sqlite3_exec(p->db, "SELECT sql FROM " - " (SELECT sql sql, type type, tbl_name tbl_name, name name" + " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" " FROM sqlite_master UNION ALL" - " SELECT sql, type, tbl_name, name FROM sqlite_temp_master) " + " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " "WHERE lower(tbl_name) LIKE shellstatic()" " AND type!='meta' AND sql NOTNULL " - "ORDER BY substr(type,2,1), name", + "ORDER BY substr(type,2,1), " + " CASE type WHEN 'view' THEN rowid ELSE name END", callback, &data, &zErrMsg); zShellStatic = 0; } }else{ rc = sqlite3_exec(p->db, "SELECT sql FROM " - " (SELECT sql sql, type type, tbl_name tbl_name, name name" + " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" " FROM sqlite_master UNION ALL" - " SELECT sql, type, tbl_name, name FROM sqlite_temp_master) " + " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%'" - "ORDER BY substr(type,2,1), name", + "ORDER BY substr(type,2,1)," + " CASE type WHEN 'view' THEN rowid ELSE name END", callback, &data, &zErrMsg ); } @@ -2156,46 +2273,71 @@ static int do_meta_command(char *zLine, struct callbac }else if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 && nArg<3 ){ + sqlite3_stmt *pStmt; char **azResult; - int nRow; - char *zErrMsg; + int nRow, nAlloc; + char *zSql = 0; + int ii; open_db(p); - if( nArg==1 ){ - rc = sqlite3_get_table(p->db, - "SELECT name FROM sqlite_master " - "WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' " - "UNION ALL " - "SELECT name FROM sqlite_temp_master " - "WHERE type IN ('table','view') " - "ORDER BY 1", - &azResult, &nRow, 0, &zErrMsg - ); - }else{ - zShellStatic = azArg[1]; - rc = sqlite3_get_table(p->db, - "SELECT name FROM sqlite_master " - "WHERE type IN ('table','view') AND name LIKE shellstatic() " - "UNION ALL " - "SELECT name FROM sqlite_temp_master " - "WHERE type IN ('table','view') AND name LIKE shellstatic() " - "ORDER BY 1", - &azResult, &nRow, 0, &zErrMsg - ); - zShellStatic = 0; + rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); + if( rc ) return rc; + zSql = sqlite3_mprintf( + "SELECT name FROM sqlite_master" + " WHERE type IN ('table','view')" + " AND name NOT LIKE 'sqlite_%%'" + " AND name LIKE ?1"); + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1); + if( zDbName==0 || strcmp(zDbName,"main")==0 ) continue; + if( strcmp(zDbName,"temp")==0 ){ + zSql = sqlite3_mprintf( + "%z UNION ALL " + "SELECT 'temp.' || name FROM sqlite_temp_master" + " WHERE type IN ('table','view')" + " AND name NOT LIKE 'sqlite_%%'" + " AND name LIKE ?1", zSql); + }else{ + zSql = sqlite3_mprintf( + "%z UNION ALL " + "SELECT '%q.' || name FROM \"%w\".sqlite_master" + " WHERE type IN ('table','view')" + " AND name NOT LIKE 'sqlite_%%'" + " AND name LIKE ?1", zSql, zDbName, zDbName); + } } - if( zErrMsg ){ - fprintf(stderr,"Error: %s\n", zErrMsg); - sqlite3_free(zErrMsg); - rc = 1; - }else if( rc != SQLITE_OK ){ - fprintf(stderr,"Error: querying sqlite_master and sqlite_temp_master\n"); - rc = 1; + sqlite3_finalize(pStmt); + zSql = sqlite3_mprintf("%z ORDER BY 1", zSql); + rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + if( rc ) return rc; + nRow = nAlloc = 0; + azResult = 0; + if( nArg>1 ){ + sqlite3_bind_text(pStmt, 1, azArg[1], -1, SQLITE_TRANSIENT); }else{ + sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC); + } + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + if( nRow>=nAlloc ){ + char **azNew; + int n = nAlloc*2 + 10; + azNew = sqlite3_realloc(azResult, sizeof(azResult[0])*n); + if( azNew==0 ){ + fprintf(stderr, "Error: out of memory\n"); + break; + } + nAlloc = n; + azResult = azNew; + } + azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0)); + if( azResult[nRow] ) nRow++; + } + sqlite3_finalize(pStmt); + if( nRow>0 ){ int len, maxlen = 0; int i, j; int nPrintCol, nPrintRow; - for(i=1; i<=nRow; i++){ - if( azResult[i]==0 ) continue; + for(i=0; imaxlen ) maxlen = len; } @@ -2203,14 +2345,15 @@ static int do_meta_command(char *zLine, struct callbac if( nPrintCol<1 ) nPrintCol = 1; nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; for(i=0; i=8 && strncmp(azArg[0], "testctrl", n)==0 && nArg>=2 ){ @@ -2344,6 +2487,19 @@ static int do_meta_command(char *zLine, struct callbac enableTimer = booleanValue(azArg[1]); }else + if( c=='t' && strncmp(azArg[0], "trace", n)==0 && nArg>1 ){ + open_db(p); + output_file_close(p->traceOut); + p->traceOut = output_file_open(azArg[1]); +#if !defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_OMIT_FLOATING_POINT) + if( p->traceOut==0 ){ + sqlite3_trace(p->db, 0, 0); + }else{ + sqlite3_trace(p->db, sql_trace_callback, p->traceOut); + } +#endif + }else + if( c=='v' && strncmp(azArg[0], "version", n)==0 ){ printf("SQLite %s %s\n" /*extra-version-info*/, sqlite3_libversion(), sqlite3_sourceid()); @@ -2361,6 +2517,13 @@ static int do_meta_command(char *zLine, struct callbac } }else +#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) + if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){ + extern int sqlite3WhereTrace; + sqlite3WhereTrace = atoi(azArg[1]); + }else +#endif + if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){ int j; assert( nArg<=ArraySize(azArg) ); @@ -2468,7 +2631,9 @@ static int process_input(struct callback_data *p, FILE free(zLine); zLine = one_input_line(zSql, in); if( zLine==0 ){ - break; /* We have reached EOF */ + /* End of input */ + if( stdin_is_interactive ) printf("\n"); + break; } if( seenInterrupt ){ if( in!=0 ) break; @@ -2550,33 +2715,34 @@ static int process_input(struct callback_data *p, FILE free(zSql); } free(zLine); - return errCnt; + return errCnt>0; } /* ** 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. +** 0 return indicates an error of some kind. */ static char *find_home_dir(void){ - char *home_dir = NULL; + static char *home_dir = NULL; + if( home_dir ) return home_dir; -#if !defined(_WIN32) && !defined(WIN32) && !defined(__OS2__) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL) - struct passwd *pwent; - uid_t uid = getuid(); - if( (pwent=getpwuid(uid)) != NULL) { - home_dir = pwent->pw_dir; +#if !defined(_WIN32) && !defined(WIN32) && !defined(_WIN32_WCE) && !defined(__RTP__) && !defined(_WRS_KERNEL) + { + struct passwd *pwent; + uid_t uid = getuid(); + if( (pwent=getpwuid(uid)) != NULL) { + home_dir = pwent->pw_dir; + } } #endif #if defined(_WIN32_WCE) /* Windows CE (arm-wince-mingw32ce-gcc) does not provide getenv() */ - home_dir = strdup("/"); + home_dir = "/"; #else -#if defined(_WIN32) || defined(WIN32) || defined(__OS2__) +#if defined(_WIN32) || defined(WIN32) if (!home_dir) { home_dir = getenv("USERPROFILE"); } @@ -2586,7 +2752,7 @@ static char *find_home_dir(void){ home_dir = getenv("HOME"); } -#if defined(_WIN32) || defined(WIN32) || defined(__OS2__) +#if defined(_WIN32) || defined(WIN32) if (!home_dir) { char *zDrive, *zPath; int n; @@ -2629,7 +2795,6 @@ static int process_sqliterc( const char *sqliterc = sqliterc_override; char *zBuf = 0; FILE *in = NULL; - int nBuf; int rc = 0; if (sqliterc == NULL) { @@ -2640,15 +2805,9 @@ static int process_sqliterc( #endif return 1; } - nBuf = strlen30(home_dir) + 16; - zBuf = malloc( nBuf ); - if( zBuf==0 ){ - fprintf(stderr,"%s: Error: out of memory\n",Argv0); - return 1; - } - sqlite3_snprintf(nBuf, zBuf,"%s/.sqliterc",home_dir); - free(home_dir); - sqliterc = (const char*)zBuf; + sqlite3_initialize(); + zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir); + sqliterc = zBuf; } in = fopen(sqliterc,"rb"); if( in ){ @@ -2658,7 +2817,7 @@ static int process_sqliterc( rc = process_input(p,in); fclose(in); } - free(zBuf); + sqlite3_free(zBuf); return rc; } @@ -2666,29 +2825,33 @@ static int process_sqliterc( ** Show available command line options */ static const char zOptions[] = - " -help show this message\n" - " -init filename read/process named file\n" - " -echo print commands before execution\n" - " -[no]header turn headers on or off\n" " -bail stop after hitting an error\n" - " -interactive force interactive I/O\n" " -batch force batch I/O\n" " -column set output mode to 'column'\n" + " -cmd COMMAND run \"COMMAND\" before reading stdin\n" " -csv set output mode to 'csv'\n" + " -echo print commands before execution\n" + " -init FILENAME read/process named file\n" + " -[no]header turn headers on or off\n" +#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) + " -heap SIZE Size of heap for memsys3 or memsys5\n" +#endif + " -help show this message\n" " -html set output mode to HTML\n" + " -interactive force interactive I/O\n" " -line set output mode to 'line'\n" " -list set output mode to 'list'\n" - " -separator 'x' set output field separator (|)\n" +#ifdef SQLITE_ENABLE_MULTIPLEX + " -multiplex enable the multiplexor VFS\n" +#endif + " -nullvalue TEXT set text string for NULL values. Default ''\n" + " -separator SEP set output field separator. Default: '|'\n" " -stats print memory stats before each finalize\n" - " -nullvalue 'text' set text string for NULL values\n" " -version show SQLite version\n" " -vfs NAME use NAME as the default VFS\n" #ifdef SQLITE_ENABLE_VFSTRACE " -vfstrace enable tracing of all VFS calls\n" #endif -#ifdef SQLITE_ENABLE_MULTIPLEX - " -multiplex enable the multiplexor VFS\n" -#endif ; static void usage(int showDetail){ fprintf(stderr, @@ -2718,6 +2881,19 @@ static void main_init(struct callback_data *data) { sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); } +/* +** Get the argument to an --option. Throw an error and die if no argument +** is available. +*/ +static char *cmdline_option_value(int argc, char **argv, int i){ + if( i==argc ){ + fprintf(stderr, "%s: Error: missing argument to %s\n", + argv[0], argv[argc-1]); + exit(1); + } + return argv[i]; +} + int main(int argc, char **argv){ char *zErrMsg = 0; struct callback_data data; @@ -2747,29 +2923,43 @@ int main(int argc, char **argv){ ** the size of the alternative malloc heap, ** and the first command to execute. */ - for(i=1; i=argc){ - fprintf(stderr,"%s: Error: missing argument for option: %s\n", Argv0, z); - fprintf(stderr,"Use -help for a list of options.\n"); - return 1; - } sqlite3_snprintf(sizeof(data.separator), data.separator, - "%.*s",(int)sizeof(data.separator)-1,argv[i]); + "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-nullvalue")==0 ){ - i++; - if(i>=argc){ - fprintf(stderr,"%s: Error: missing argument for option: %s\n", Argv0, z); - fprintf(stderr,"Use -help for a list of options.\n"); - return 1; - } sqlite3_snprintf(sizeof(data.nullvalue), data.nullvalue, - "%.*s",(int)sizeof(data.nullvalue)-1,argv[i]); + "%s",cmdline_option_value(argc,argv,++i)); }else if( strcmp(z,"-header")==0 ){ data.showHeader = 1; }else if( strcmp(z,"-noheader")==0 ){ @@ -2921,8 +3080,25 @@ int main(int argc, char **argv){ }else if( strcmp(z,"-multiplex")==0 ){ i++; #endif - }else if( strcmp(z,"-help")==0 || strcmp(z, "--help")==0 ){ + }else if( strcmp(z,"-help")==0 ){ usage(1); + }else if( strcmp(z,"-cmd")==0 ){ + if( i==argc-1 ) break; + z = cmdline_option_value(argc,argv,++i); + if( z[0]=='.' ){ + rc = do_meta_command(z, &data); + if( rc && bail_on_error ) return rc; + }else{ + open_db(&data); + rc = shell_exec(data.db, z, shell_callback, &data, &zErrMsg); + if( zErrMsg!=0 ){ + fprintf(stderr,"Error: %s\n", zErrMsg); + if( bail_on_error ) return rc!=0 ? rc : 1; + }else if( rc!=0 ){ + fprintf(stderr,"Error: unable to process SQL \"%s\"\n", z); + if( bail_on_error ) return rc; + } + } }else{ fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); fprintf(stderr,"Use -help for a list of options.\n"); @@ -2975,7 +3151,6 @@ int main(int argc, char **argv){ write_history(zHistory); free(zHistory); } - free(zHome); }else{ rc = process_input(&data, stdin); }