Index: src/shell.c ================================================================== --- src/shell.c +++ src/shell.c @@ -10,11 +10,11 @@ ** ************************************************************************* ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. ** -** $Id: shell.c,v 1.150 2006/09/25 13:09:23 drh Exp $ +** $Id: shell.c,v 1.151 2006/10/26 14:25:58 drh Exp $ */ #include #include #include #include @@ -58,10 +58,16 @@ /* Make sure isatty() has a prototype. */ extern int isatty(); #endif +/* +** Threat stdin as an interactive input if the following variable +** is true. Otherwise, assume stdin is connected to a file or pipe. +*/ +static int stdin_is_interactive = 1; + /* ** 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. */ @@ -182,14 +188,11 @@ zLine = realloc( zLine, n+1 ); return zLine; } /* -** Retrieve a single line of input text. "isatty" is true if text -** is coming from a terminal. In that case, we issue a prompt and -** attempt to use "readline" for command-line editing. If "isatty" -** is false, use "local_getline" instead of "readline" and issue no prompt. +** Retrieve a single line of input text. ** ** zPrior is a string of prior text retrieved. If not the empty ** string, then issue a continuation prompt. */ static char *one_input_line(const char *zPrior, FILE *in){ @@ -585,11 +588,11 @@ ** zIn, if it was not NULL, is freed. ** ** If the third argument, quote, is not '\0', then it is used as a ** quote character for zAppend. */ -static char * appendText(char *zIn, char const *zAppend, char quote){ +static char *appendText(char *zIn, char const *zAppend, char quote){ int len; int i; int nAppend = strlen(zAppend); int nIn = (zIn?strlen(zIn):0); @@ -791,11 +794,11 @@ ".timeout MS Try opening locked tables for MS milliseconds\n" ".width NUM NUM ... Set column widths for \"column\" mode\n" ; /* Forward reference */ -static void process_input(struct callback_data *p, FILE *in); +static int process_input(struct callback_data *p, FILE *in); /* ** Make sure the database is open. If it is not, then open it. If ** the database fails to open, print an error message and exit. */ @@ -850,10 +853,27 @@ } z[j] = c; } z[j] = 0; } + +/* +** Interpret zArg as a boolean value. Return either 0 or 1. +*/ +static int booleanValue(char *zArg){ + int val = atoi(zArg); + int j; + for(j=0; zArg[j]; j++){ + zArg[j] = tolower(zArg[j]); + } + if( strcmp(zArg,"on")==0 ){ + val = 1; + }else if( strcmp(zArg,"yes")==0 ){ + val = 1; + } + return val; +} /* ** If an input line begins with "." then invoke this routine to ** process that line. ** @@ -955,41 +975,19 @@ fprintf(p->out, "COMMIT;\n"); } }else if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 ){ - int j; - char *z = azArg[1]; - int val = atoi(azArg[1]); - for(j=0; z[j]; j++){ - z[j] = tolower((unsigned char)z[j]); - } - if( strcmp(z,"on")==0 ){ - val = 1; - }else if( strcmp(z,"yes")==0 ){ - val = 1; - } - p->echoOn = val; + p->echoOn = booleanValue(azArg[1]); }else if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){ rc = 1; }else if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){ - int j; - static char zOne[] = "1"; - char *z = nArg>=2 ? azArg[1] : zOne; - int val = atoi(z); - for(j=0; z[j]; j++){ - z[j] = tolower((unsigned char)z[j]); - } - if( strcmp(z,"on")==0 ){ - val = 1; - }else if( strcmp(z,"yes")==0 ){ - val = 1; - } + int val = nArg>=2 ? booleanValue(azArg[1]) : 1; if(val == 1) { if(!p->explainPrev.valid) { p->explainPrev.valid = 1; p->explainPrev.mode = p->mode; p->explainPrev.showHeader = p->showHeader; @@ -1016,25 +1014,13 @@ p->showHeader = p->explainPrev.showHeader; memcpy(p->colWidth,p->explainPrev.colWidth,sizeof(p->colWidth)); } }else - if( c=='h' && (strncmp(azArg[0], "header", n)==0 - || + if( c=='h' && (strncmp(azArg[0], "header", n)==0 || strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){ - int j; - char *z = azArg[1]; - int val = atoi(azArg[1]); - for(j=0; z[j]; j++){ - z[j] = tolower((unsigned char)z[j]); - } - if( strcmp(z,"on")==0 ){ - val = 1; - }else if( strcmp(z,"yes")==0 ){ - val = 1; - } - p->showHeader = val; + p->showHeader = booleanValue(azArg[1]); }else if( c=='h' && strncmp(azArg[0], "help", n)==0 ){ fprintf(stderr,zHelp); }else @@ -1480,22 +1466,30 @@ ** 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. +** +** Return the number of errors. */ -static void process_input(struct callback_data *p, FILE *in){ +static int process_input(struct callback_data *p, FILE *in){ char *zLine; char *zSql = 0; int nSql = 0; char *zErrMsg; - int rc; + int rc = 0; + int lineno = 0; + int startline = 0; while( fflush(p->out), (zLine = one_input_line(zSql, in))!=0 ){ if( seenInterrupt ){ if( in!=0 ) break; seenInterrupt = 0; } + if( rc && (in!=0 || !stdin_is_interactive) ){ + break; + } + lineno++; if( p->echoOn ) printf("%s\n", zLine); if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue; if( zLine && zLine[0]=='.' && nSql==0 ){ int rc = do_meta_command(zLine, p); free(zLine); @@ -1514,10 +1508,11 @@ if( zSql==0 ){ fprintf(stderr, "out of memory\n"); exit(1); } strcpy(zSql, zLine); + startline = lineno; } }else{ int len = strlen(zLine); zSql = realloc( zSql, nSql + len + 2 ); if( zSql==0 ){ @@ -1532,17 +1527,22 @@ if( zSql && _ends_with_semicolon(zSql, nSql) && sqlite3_complete(zSql) ){ p->cnt = 0; open_db(p); rc = sqlite3_exec(p->db, zSql, callback, p, &zErrMsg); if( rc || zErrMsg ){ - /* if( in!=0 && !p->echoOn ) printf("%s\n",zSql); */ + char zPrefix[100]; + if( in!=0 || !stdin_is_interactive ){ + sprintf(zPrefix, "SQL error near line %d:", startline); + }else{ + sprintf(zPrefix, "SQL error:"); + } if( zErrMsg!=0 ){ - printf("SQL error: %s\n", zErrMsg); + printf("%s %s\n", zPrefix, zErrMsg); sqlite3_free(zErrMsg); zErrMsg = 0; }else{ - printf("SQL error: %s\n", sqlite3_errmsg(p->db)); + printf("%s %s\n", zPrefix, sqlite3_errmsg(p->db)); } } free(zSql); zSql = 0; nSql = 0; @@ -1550,10 +1550,11 @@ } if( zSql ){ if( !_all_whitespace(zSql) ) printf("Incomplete SQL: %s\n", zSql); free(zSql); } + return rc; } /* ** Return a pathname which is the user's home directory. A ** 0 return indicates an error of some kind. Space to hold the @@ -1640,11 +1641,11 @@ free(home_dir); sqliterc = (const char*)zBuf; } in = fopen(sqliterc,"rb"); if( in ){ - if( isatty(fileno(stdout)) ){ + if( stdin_is_interactive ){ printf("Loading resources from %s\n",sqliterc); } process_input(p,in); fclose(in); } @@ -1696,17 +1697,19 @@ char *zErrMsg = 0; struct callback_data data; const char *zInitFile = 0; char *zFirstCmd = 0; int i; + int rc = 0; #ifdef __MACOS__ argc = ccommand(&argv); #endif Argv0 = argv[0]; main_init(&data); + stdin_is_interactive = isatty(0); /* Make sure we have a valid signal handler early, before anything ** else is done. */ #ifdef SIGINT @@ -1716,11 +1719,14 @@ /* Do an initial pass through the command-line argument to locate ** the name of the database file, the name of the initialization file, ** and the first command to execute. */ for(i=1; i