diff options
Diffstat (limited to 'src/shell.c.in')
-rw-r--r-- | src/shell.c.in | 2961 |
1 files changed, 1890 insertions, 1071 deletions
diff --git a/src/shell.c.in b/src/shell.c.in index f389c1f47..33dd30697 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -104,9 +104,6 @@ typedef unsigned short int u16; typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; typedef unsigned char u8; -#if SQLITE_USER_AUTHENTICATION -# include "sqlite3userauth.h" -#endif #include <ctype.h> #include <stdarg.h> @@ -192,8 +189,6 @@ typedef unsigned char u8; # ifndef strdup # define strdup _strdup # endif -# undef popen -# define popen _popen # undef pclose # define pclose _pclose # endif @@ -223,6 +218,8 @@ typedef unsigned char u8; #define IsSpace(X) isspace((unsigned char)X) #define IsDigit(X) isdigit((unsigned char)X) #define ToLower(X) (char)tolower((unsigned char)X) +#define IsAlnum(X) isalnum((unsigned char)X) +#define IsAlpha(X) isalpha((unsigned char)X) #if defined(_WIN32) || defined(WIN32) #if SQLITE_OS_WINRT @@ -237,6 +234,9 @@ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR); extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); #endif +INCLUDE ../ext/misc/sqlite3_stdio.h +INCLUDE ../ext/misc/sqlite3_stdio.c + /* Use console I/O package as a direct INCLUDE. */ #define SQLITE_INTERNAL_LINKAGE static @@ -246,56 +246,11 @@ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText); # define SQLITE_CIO_NO_CLASSIFY # define SQLITE_CIO_NO_TRANSLATE # define SQLITE_CIO_NO_SETMODE +# define SQLITE_CIO_NO_FLUSH #endif -INCLUDE ../ext/consio/console_io.h -INCLUDE ../ext/consio/console_io.c - -#ifndef SQLITE_SHELL_FIDDLE -/* From here onward, fgets() is redirected to the console_io library. */ -# define fgets(b,n,f) fGetsUtf8(b,n,f) -/* - * Define macros for emitting output text in various ways: - * sputz(s, z) => emit 0-terminated string z to given stream s - * sputf(s, f, ...) => emit varargs per format f to given stream s - * oputz(z) => emit 0-terminated string z to default stream - * oputf(f, ...) => emit varargs per format f to default stream - * eputz(z) => emit 0-terminated string z to error stream - * eputf(f, ...) => emit varargs per format f to error stream - * oputb(b, n) => emit char buffer b[0..n-1] to default stream - * - * Note that the default stream is whatever has been last set via: - * setOutputStream(FILE *pf) - * This is normally the stream that CLI normal output goes to. - * For the stand-alone CLI, it is stdout with no .output redirect. - * - * The ?putz(z) forms are required for the Fiddle builds for string literal - * output, in aid of enforcing format string to argument correspondence. - */ -# define sputz(s,z) fPutsUtf8(z,s) -# define sputf fPrintfUtf8 -# define oputz(z) oPutsUtf8(z) -# define oputf oPrintfUtf8 -# define eputz(z) ePutsUtf8(z) -# define eputf ePrintfUtf8 -# define oputb(buf,na) oPutbUtf8(buf,na) - -#else -/* For Fiddle, all console handling and emit redirection is omitted. */ -/* These next 3 macros are for emitting formatted output. When complaints - * from the WASM build are issued for non-formatted output, (when a mere - * string literal is to be emitted, the ?putz(z) forms should be used. - * (This permits compile-time checking of format string / argument mismatch.) - */ -# define oputf(fmt, ...) printf(fmt,__VA_ARGS__) -# define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__) -# define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__) -/* These next 3 macros are for emitting simple string literals. */ -# define oputz(z) fputs(z,stdout) -# define eputz(z) fputs(z,stderr) -# define sputz(fp,z) fputs(z,fp) -# define oputb(buf,na) fwrite(buf,1,na,stdout) -#endif +#define eputz(z) sqlite3_fputs(z,stderr) +#define sputz(fp,z) sqlite3_fputs(z,fp) /* True if the timer is enabled */ static int enableTimer = 0; @@ -341,6 +296,7 @@ struct rusage { #define getrusage(A,B) memset(B,0,sizeof(*B)) #endif + /* Saved resource information for the beginning of an operation */ static struct rusage sBegin; /* CPU time at start */ static sqlite3_int64 iBegin; /* Wall-clock time at start */ @@ -364,12 +320,12 @@ static double timeDiff(struct timeval *pStart, struct timeval *pEnd){ /* ** Print the timing results. */ -static void endTimer(void){ +static void endTimer(FILE *out){ if( enableTimer ){ sqlite3_int64 iEnd = timeOfDay(); struct rusage sEnd; getrusage(RUSAGE_SELF, &sEnd); - sputf(stdout, "Run Time: real %.3f user %f sys %f\n", + sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n", (iEnd - iBegin)*0.001, timeDiff(&sBegin.ru_utime, &sEnd.ru_utime), timeDiff(&sBegin.ru_stime, &sEnd.ru_stime)); @@ -377,7 +333,7 @@ static void endTimer(void){ } #define BEGIN_TIMER beginTimer() -#define END_TIMER endTimer() +#define END_TIMER(X) endTimer(X) #define HAS_TIMER 1 #elif (defined(_WIN32) || defined(WIN32)) @@ -443,12 +399,12 @@ static double timeDiff(FILETIME *pStart, FILETIME *pEnd){ /* ** Print the timing results. */ -static void endTimer(void){ +static void endTimer(FILE *out){ if( enableTimer && getProcessTimesAddr){ FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd; sqlite3_int64 ftWallEnd = timeOfDay(); getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd); - sputf(stdout, "Run Time: real %.3f user %f sys %f\n", + sqlite3_fprintf(out, "Run Time: real %.3f user %f sys %f\n", (ftWallEnd - ftWallBegin)*0.001, timeDiff(&ftUserBegin, &ftUserEnd), timeDiff(&ftKernelBegin, &ftKernelEnd)); @@ -456,12 +412,12 @@ static void endTimer(void){ } #define BEGIN_TIMER beginTimer() -#define END_TIMER endTimer() +#define END_TIMER(X) endTimer(X) #define HAS_TIMER hasTimer() #else #define BEGIN_TIMER -#define END_TIMER +#define END_TIMER(X) /*no-op*/ #define HAS_TIMER 0 #endif @@ -516,7 +472,7 @@ static char *Argv0; ** Prompt strings. Initialized in main. Settable with ** .prompt main continue */ -#define PROMPT_LEN_MAX 20 +#define PROMPT_LEN_MAX 128 /* First line prompt. default: "sqlite> " */ static char mainPrompt[PROMPT_LEN_MAX]; /* Continuation prompt. default: " ...> " */ @@ -535,6 +491,14 @@ static char *shell_strncpy(char *dest, const char *src, size_t n){ } /* +** strcpy() workalike to squelch an unwarranted link-time warning +** from OpenBSD. +*/ +static void shell_strcpy(char *dest, const char *src){ + while( (*(dest++) = *(src++))!=0 ){} +} + +/* ** Optionally disable dynamic continuation prompt. ** Unless disabled, the continuation prompt shows open SQL lexemes if any, ** or open parentheses level if non-zero, or continuation prompt as set. @@ -599,7 +563,7 @@ static char *dynamicContinuePrompt(void){ size_t ncp = strlen(continuePrompt); size_t ndp = strlen(dynPrompt.zScannerAwaits); if( ndp > ncp-3 ) return continuePrompt; - strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits); + shell_strcpy(dynPrompt.dynamicPrompt, dynPrompt.zScannerAwaits); while( ndp<3 ) dynPrompt.dynamicPrompt[ndp++] = ' '; shell_strncpy(dynPrompt.dynamicPrompt+3, continuePrompt+3, PROMPT_LEN_MAX-4); @@ -654,37 +618,232 @@ static void SQLITE_CDECL iotracePrintf(const char *zFormat, ...){ va_start(ap, zFormat); z = sqlite3_vmprintf(zFormat, ap); va_end(ap); - sputf(iotrace, "%s", z); + sqlite3_fprintf(iotrace, "%s", z); sqlite3_free(z); } #endif +/* Lookup table to estimate the number of columns consumed by a Unicode +** character. +*/ +static const struct { + unsigned char w; /* Width of the character in columns */ + int iFirst; /* First character in a span having this width */ +} aUWidth[] = { + /* {1, 0x00000}, */ + {0, 0x00300}, {1, 0x00370}, {0, 0x00483}, {1, 0x00487}, {0, 0x00488}, + {1, 0x0048a}, {0, 0x00591}, {1, 0x005be}, {0, 0x005bf}, {1, 0x005c0}, + {0, 0x005c1}, {1, 0x005c3}, {0, 0x005c4}, {1, 0x005c6}, {0, 0x005c7}, + {1, 0x005c8}, {0, 0x00600}, {1, 0x00604}, {0, 0x00610}, {1, 0x00616}, + {0, 0x0064b}, {1, 0x0065f}, {0, 0x00670}, {1, 0x00671}, {0, 0x006d6}, + {1, 0x006e5}, {0, 0x006e7}, {1, 0x006e9}, {0, 0x006ea}, {1, 0x006ee}, + {0, 0x0070f}, {1, 0x00710}, {0, 0x00711}, {1, 0x00712}, {0, 0x00730}, + {1, 0x0074b}, {0, 0x007a6}, {1, 0x007b1}, {0, 0x007eb}, {1, 0x007f4}, + {0, 0x00901}, {1, 0x00903}, {0, 0x0093c}, {1, 0x0093d}, {0, 0x00941}, + {1, 0x00949}, {0, 0x0094d}, {1, 0x0094e}, {0, 0x00951}, {1, 0x00955}, + {0, 0x00962}, {1, 0x00964}, {0, 0x00981}, {1, 0x00982}, {0, 0x009bc}, + {1, 0x009bd}, {0, 0x009c1}, {1, 0x009c5}, {0, 0x009cd}, {1, 0x009ce}, + {0, 0x009e2}, {1, 0x009e4}, {0, 0x00a01}, {1, 0x00a03}, {0, 0x00a3c}, + {1, 0x00a3d}, {0, 0x00a41}, {1, 0x00a43}, {0, 0x00a47}, {1, 0x00a49}, + {0, 0x00a4b}, {1, 0x00a4e}, {0, 0x00a70}, {1, 0x00a72}, {0, 0x00a81}, + {1, 0x00a83}, {0, 0x00abc}, {1, 0x00abd}, {0, 0x00ac1}, {1, 0x00ac6}, + {0, 0x00ac7}, {1, 0x00ac9}, {0, 0x00acd}, {1, 0x00ace}, {0, 0x00ae2}, + {1, 0x00ae4}, {0, 0x00b01}, {1, 0x00b02}, {0, 0x00b3c}, {1, 0x00b3d}, + {0, 0x00b3f}, {1, 0x00b40}, {0, 0x00b41}, {1, 0x00b44}, {0, 0x00b4d}, + {1, 0x00b4e}, {0, 0x00b56}, {1, 0x00b57}, {0, 0x00b82}, {1, 0x00b83}, + {0, 0x00bc0}, {1, 0x00bc1}, {0, 0x00bcd}, {1, 0x00bce}, {0, 0x00c3e}, + {1, 0x00c41}, {0, 0x00c46}, {1, 0x00c49}, {0, 0x00c4a}, {1, 0x00c4e}, + {0, 0x00c55}, {1, 0x00c57}, {0, 0x00cbc}, {1, 0x00cbd}, {0, 0x00cbf}, + {1, 0x00cc0}, {0, 0x00cc6}, {1, 0x00cc7}, {0, 0x00ccc}, {1, 0x00cce}, + {0, 0x00ce2}, {1, 0x00ce4}, {0, 0x00d41}, {1, 0x00d44}, {0, 0x00d4d}, + {1, 0x00d4e}, {0, 0x00dca}, {1, 0x00dcb}, {0, 0x00dd2}, {1, 0x00dd5}, + {0, 0x00dd6}, {1, 0x00dd7}, {0, 0x00e31}, {1, 0x00e32}, {0, 0x00e34}, + {1, 0x00e3b}, {0, 0x00e47}, {1, 0x00e4f}, {0, 0x00eb1}, {1, 0x00eb2}, + {0, 0x00eb4}, {1, 0x00eba}, {0, 0x00ebb}, {1, 0x00ebd}, {0, 0x00ec8}, + {1, 0x00ece}, {0, 0x00f18}, {1, 0x00f1a}, {0, 0x00f35}, {1, 0x00f36}, + {0, 0x00f37}, {1, 0x00f38}, {0, 0x00f39}, {1, 0x00f3a}, {0, 0x00f71}, + {1, 0x00f7f}, {0, 0x00f80}, {1, 0x00f85}, {0, 0x00f86}, {1, 0x00f88}, + {0, 0x00f90}, {1, 0x00f98}, {0, 0x00f99}, {1, 0x00fbd}, {0, 0x00fc6}, + {1, 0x00fc7}, {0, 0x0102d}, {1, 0x01031}, {0, 0x01032}, {1, 0x01033}, + {0, 0x01036}, {1, 0x01038}, {0, 0x01039}, {1, 0x0103a}, {0, 0x01058}, + {1, 0x0105a}, {2, 0x01100}, {0, 0x01160}, {1, 0x01200}, {0, 0x0135f}, + {1, 0x01360}, {0, 0x01712}, {1, 0x01715}, {0, 0x01732}, {1, 0x01735}, + {0, 0x01752}, {1, 0x01754}, {0, 0x01772}, {1, 0x01774}, {0, 0x017b4}, + {1, 0x017b6}, {0, 0x017b7}, {1, 0x017be}, {0, 0x017c6}, {1, 0x017c7}, + {0, 0x017c9}, {1, 0x017d4}, {0, 0x017dd}, {1, 0x017de}, {0, 0x0180b}, + {1, 0x0180e}, {0, 0x018a9}, {1, 0x018aa}, {0, 0x01920}, {1, 0x01923}, + {0, 0x01927}, {1, 0x01929}, {0, 0x01932}, {1, 0x01933}, {0, 0x01939}, + {1, 0x0193c}, {0, 0x01a17}, {1, 0x01a19}, {0, 0x01b00}, {1, 0x01b04}, + {0, 0x01b34}, {1, 0x01b35}, {0, 0x01b36}, {1, 0x01b3b}, {0, 0x01b3c}, + {1, 0x01b3d}, {0, 0x01b42}, {1, 0x01b43}, {0, 0x01b6b}, {1, 0x01b74}, + {0, 0x01dc0}, {1, 0x01dcb}, {0, 0x01dfe}, {1, 0x01e00}, {0, 0x0200b}, + {1, 0x02010}, {0, 0x0202a}, {1, 0x0202f}, {0, 0x02060}, {1, 0x02064}, + {0, 0x0206a}, {1, 0x02070}, {0, 0x020d0}, {1, 0x020f0}, {2, 0x02329}, + {1, 0x0232b}, {2, 0x02e80}, {0, 0x0302a}, {2, 0x03030}, {1, 0x0303f}, + {2, 0x03040}, {0, 0x03099}, {2, 0x0309b}, {1, 0x0a4d0}, {0, 0x0a806}, + {1, 0x0a807}, {0, 0x0a80b}, {1, 0x0a80c}, {0, 0x0a825}, {1, 0x0a827}, + {2, 0x0ac00}, {1, 0x0d7a4}, {2, 0x0f900}, {1, 0x0fb00}, {0, 0x0fb1e}, + {1, 0x0fb1f}, {0, 0x0fe00}, {2, 0x0fe10}, {1, 0x0fe1a}, {0, 0x0fe20}, + {1, 0x0fe24}, {2, 0x0fe30}, {1, 0x0fe70}, {0, 0x0feff}, {2, 0x0ff00}, + {1, 0x0ff61}, {2, 0x0ffe0}, {1, 0x0ffe7}, {0, 0x0fff9}, {1, 0x0fffc}, + {0, 0x10a01}, {1, 0x10a04}, {0, 0x10a05}, {1, 0x10a07}, {0, 0x10a0c}, + {1, 0x10a10}, {0, 0x10a38}, {1, 0x10a3b}, {0, 0x10a3f}, {1, 0x10a40}, + {0, 0x1d167}, {1, 0x1d16a}, {0, 0x1d173}, {1, 0x1d183}, {0, 0x1d185}, + {1, 0x1d18c}, {0, 0x1d1aa}, {1, 0x1d1ae}, {0, 0x1d242}, {1, 0x1d245}, + {2, 0x20000}, {1, 0x2fffe}, {2, 0x30000}, {1, 0x3fffe}, {0, 0xe0001}, + {1, 0xe0002}, {0, 0xe0020}, {1, 0xe0080}, {0, 0xe0100}, {1, 0xe01f0} +}; + /* -** Output string zUtf to Out stream as w characters. If w is negative, +** Return an estimate of the width, in columns, for the single Unicode +** character c. For normal characters, the answer is always 1. But the +** estimate might be 0 or 2 for zero-width and double-width characters. +** +** Different display devices display unicode using different widths. So +** it is impossible to know that true display width with 100% accuracy. +** Inaccuracies in the width estimates might cause columns to be misaligned. +** Unfortunately, there is nothing we can do about that. +*/ +int cli_wcwidth(int c){ + int iFirst, iLast; + + /* Fast path for common characters */ + if( c<=0x300 ) return 1; + + /* The general case */ + iFirst = 0; + iLast = sizeof(aUWidth)/sizeof(aUWidth[0]) - 1; + while( iFirst<iLast-1 ){ + int iMid = (iFirst+iLast)/2; + int cMid = aUWidth[iMid].iFirst; + if( cMid < c ){ + iFirst = iMid; + }else if( cMid > c ){ + iLast = iMid - 1; + }else{ + return aUWidth[iMid].w; + } + } + if( aUWidth[iLast].iFirst > c ) return aUWidth[iFirst].w; + return aUWidth[iLast].w; +} + +/* +** Compute the value and length of a multi-byte UTF-8 character that +** begins at z[0]. Return the length. Write the Unicode value into *pU. +** +** This routine only works for *multi-byte* UTF-8 characters. +*/ +static int decodeUtf8(const unsigned char *z, int *pU){ + if( (z[0] & 0xe0)==0xc0 && (z[1] & 0xc0)==0x80 ){ + *pU = ((z[0] & 0x1f)<<6) | (z[1] & 0x3f); + return 2; + } + if( (z[0] & 0xf0)==0xe0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 ){ + *pU = ((z[0] & 0x0f)<<12) | ((z[1] & 0x3f)<<6) | (z[2] & 0x3f); + return 3; + } + if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80 + && (z[3] & 0xc0)==0x80 + ){ + *pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6 + | (z[4] & 0x3f); + return 4; + } + *pU = 0; + return 1; +} + + +#if 0 /* NOT USED */ +/* +** Return the width, in display columns, of a UTF-8 string. +** +** Each normal character counts as 1. Zero-width characters count +** as zero, and double-width characters count as 2. +*/ +int cli_wcswidth(const char *z){ + const unsigned char *a = (const unsigned char*)z; + int n = 0; + int i = 0; + unsigned char c; + while( (c = a[i])!=0 ){ + if( c>=0xc0 ){ + int u; + int len = decodeUtf8(&a[i], &u); + i += len; + n += cli_wcwidth(u); + }else if( c>=' ' ){ + n++; + i++; + }else{ + i++; + } + } + return n; +} +#endif + +/* +** Check to see if z[] is a valid VT100 escape. If it is, then +** return the number of bytes in the escape sequence. Return 0 if +** z[] is not a VT100 escape. +** +** This routine assumes that z[0] is \033 (ESC). +*/ +static int isVt100(const unsigned char *z){ + int i; + if( z[1]!='[' ) return 0; + i = 2; + while( z[i]>=0x30 && z[i]<=0x3f ){ i++; } + while( z[i]>=0x20 && z[i]<=0x2f ){ i++; } + if( z[i]<0x40 || z[i]>0x7e ) return 0; + return i+1; +} + +/* +** Output string zUtf to stdout as w characters. If w is negative, ** then right-justify the text. W is the width in UTF-8 characters, not ** in bytes. This is different from the %*.*s specification in printf ** since with %*.*s the width is measured in bytes, not characters. +** +** Take into account zero-width and double-width Unicode characters. +** In other words, a zero-width character does not count toward the +** the w limit. A double-width character counts as two. */ -static void utf8_width_print(int w, const char *zUtf){ - int i; - int n; +static void utf8_width_print(FILE *out, int w, const char *zUtf){ + const unsigned char *a = (const unsigned char*)zUtf; + unsigned char c; + int i = 0; + int n = 0; + int k; int aw = w<0 ? -w : w; if( zUtf==0 ) zUtf = ""; - for(i=n=0; zUtf[i]; i++){ - if( (zUtf[i]&0xc0)!=0x80 ){ - n++; - if( n==aw ){ - do{ i++; }while( (zUtf[i]&0xc0)==0x80 ); + while( (c = a[i])!=0 ){ + if( (c&0xc0)==0xc0 ){ + int u; + int len = decodeUtf8(a+i, &u); + int x = cli_wcwidth(u); + if( x+n>aw ){ break; } + i += len; + n += x; + }else if( c==0x1b && (k = isVt100(&a[i]))>0 ){ + i += k; + }else if( n>=aw ){ + break; + }else{ + n++; + i++; } } if( n>=aw ){ - oputf("%.*s", i, zUtf); + sqlite3_fprintf(out, "%.*s", i, zUtf); }else if( w<0 ){ - oputf("%*s%s", aw-n, "", zUtf); + sqlite3_fprintf(out, "%*s%s", aw-n, "", zUtf); }else{ - oputf("%s%*s", zUtf, aw-n, ""); + sqlite3_fprintf(out, "%s%*s", zUtf, aw-n, ""); } } @@ -728,12 +887,21 @@ static int strlen30(const char *z){ /* ** Return the length of a string in characters. Multibyte UTF8 characters -** count as a single character. +** count as a single character for single-width characters, or as two +** characters for double-width characters. */ static int strlenChar(const char *z){ int n = 0; while( *z ){ - if( (0xc0&*(z++))!=0x80 ) n++; + if( (0x80&z[0])==0 ){ + n++; + z++; + }else{ + int u = 0; + int len = decodeUtf8((const u8*)z, &u); + z += len; + n += cli_wcwidth(u); + } } return n; } @@ -750,7 +918,7 @@ static FILE * openChrSource(const char *zFile){ /* On Windows, open first, then check the stream nature. This order ** is necessary because _stat() and sibs, when checking a named pipe, ** effectively break the pipe as its supplier sees it. */ - FILE *rv = fopen(zFile, "rb"); + FILE *rv = sqlite3_fopen(zFile, "rb"); if( rv==0 ) return 0; if( _fstat64(_fileno(rv), &x) != 0 || !STAT_CHR_SRC(x.st_mode)){ @@ -764,7 +932,7 @@ static FILE * openChrSource(const char *zFile){ # define STAT_CHR_SRC(mode) (S_ISREG(mode)||S_ISFIFO(mode)||S_ISCHR(mode)) if( rc!=0 ) return 0; if( STAT_CHR_SRC(x.st_mode) ){ - return fopen(zFile, "rb"); + return sqlite3_fopen(zFile, "rb"); }else{ return 0; } @@ -791,7 +959,7 @@ static char *local_getline(char *zLine, FILE *in){ zLine = realloc(zLine, nLine); shell_check_oom(zLine); } - if( fgets(&zLine[n], nLine - n, in)==0 ){ + if( sqlite3_fgets(&zLine[n], nLine - n, in)==0 ){ if( n==0 ){ free(zLine); return 0; @@ -990,9 +1158,9 @@ static void appendText(ShellText *p, const char *zAppend, char quote){ static char quoteChar(const char *zName){ int i; if( zName==0 ) return '"'; - if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; + if( !IsAlpha(zName[0]) && zName[0]!='_' ) return '"'; for(i=0; zName[i]; i++){ - if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; + if( !IsAlnum(zName[i]) && zName[i]!='_' ) return '"'; } return sqlite3_keyword_check(zName, i) ? '"' : 0; } @@ -1084,30 +1252,6 @@ static void shellDtostr( sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT); } - -/* -** SQL function: shell_module_schema(X) -** -** Return a fake schema for the table-valued function or eponymous virtual -** table X. -*/ -static void shellModuleSchema( - sqlite3_context *pCtx, - int nVal, - sqlite3_value **apVal -){ - const char *zName; - char *zFake; - UNUSED_PARAMETER(nVal); - zName = (const char*)sqlite3_value_text(apVal[0]); - zFake = zName? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0; - if( zFake ){ - sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake), - -1, sqlite3_free); - free(zFake); - } -} - /* ** SQL function: shell_add_schema(S,X) ** @@ -1190,14 +1334,11 @@ static void shellAddSchemaName( #define SQLITE_EXTENSION_INIT1 #define SQLITE_EXTENSION_INIT2(X) (void)(X) -#if defined(_WIN32) && defined(_MSC_VER) -INCLUDE test_windirent.h -INCLUDE test_windirent.c -#define dirent DIRENT -#endif +INCLUDE ../ext/misc/windirent.h INCLUDE ../ext/misc/memtrace.c INCLUDE ../ext/misc/pcachetrace.c INCLUDE ../ext/misc/shathree.c +INCLUDE ../ext/misc/sha1.c INCLUDE ../ext/misc/uint.c INCLUDE ../ext/misc/decimal.c INCLUDE ../ext/misc/percentile.c @@ -1225,6 +1366,7 @@ INCLUDE ../ext/expert/sqlite3expert.c INCLUDE ../ext/intck/sqlite3intck.h INCLUDE ../ext/intck/sqlite3intck.c INCLUDE ../ext/misc/stmtrand.c +INCLUDE ../ext/misc/vfstrace.c #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) #define SQLITE_SHELL_HAVE_RECOVER 1 @@ -1306,6 +1448,8 @@ struct ShellState { u8 bSafeMode; /* True to prohibit unsafe operations */ u8 bSafeModePersist; /* The long-term value of bSafeMode */ u8 eRestoreState; /* See comments above doAutoDetectRestore() */ + u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */ + u8 eEscMode; /* Escape mode for text output */ ColModeOpts cmOpts; /* Option values affecting columnar mode output */ unsigned statsOn; /* True to display memory stats before each finalize */ unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ @@ -1406,6 +1550,15 @@ static ShellState shellState; ** top-level SQL statement */ #define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */ +/* Allowed values for ShellState.eEscMode. The default value should +** be 0, so to change the default, reorder the names. +*/ +#define SHELL_ESC_ASCII 0 /* Substitute ^Y for X where Y=X+0x40 */ +#define SHELL_ESC_SYMBOL 1 /* Substitute U+2400 graphics */ +#define SHELL_ESC_OFF 2 /* Send characters verbatim */ + +static const char *shell_EscModeNames[] = { "ascii", "symbol", "off" }; + /* ** These are the allowed shellFlgs values */ @@ -1451,6 +1604,7 @@ static ShellState shellState; #define MODE_Count 17 /* Output only a count of the rows of output */ #define MODE_Off 18 /* No query output shown */ #define MODE_ScanExp 19 /* Like MODE_Explain, but for ".scanstats vm" */ +#define MODE_Www 20 /* Full web-page output */ static const char *modeDescr[] = { "line", @@ -1471,7 +1625,9 @@ static const char *modeDescr[] = { "table", "box", "count", - "off" + "off", + "scanexp", + "www", }; /* @@ -1499,7 +1655,7 @@ static const char *modeDescr[] = { static void shellLog(void *pArg, int iErrCode, const char *zMsg){ ShellState *p = (ShellState*)pArg; if( p->pLog==0 ) return; - sputf(p->pLog, "(%d) %s\n", iErrCode, zMsg); + sqlite3_fprintf(p->pLog, "(%d) %s\n", iErrCode, zMsg); fflush(p->pLog); } @@ -1514,9 +1670,9 @@ static void shellPutsFunc( int nVal, sqlite3_value **apVal ){ - /* Unused: (ShellState*)sqlite3_user_data(pCtx); */ + ShellState *p = (ShellState*)sqlite3_user_data(pCtx); (void)nVal; - oputf("%s\n", sqlite3_value_text(apVal[0])); + sqlite3_fprintf(p->out, "%s\n", sqlite3_value_text(apVal[0])); sqlite3_result_value(pCtx, apVal[0]); } @@ -1535,7 +1691,7 @@ static void failIfSafeMode( va_start(ap, zErrMsg); zMsg = sqlite3_vmprintf(zErrMsg, ap); va_end(ap); - eputf("line %d: %s\n", p->lineno, zMsg); + sqlite3_fprintf(stderr, "line %d: %s\n", p->lineno, zMsg); exit(1); } } @@ -1568,7 +1724,7 @@ static void editFunc( char *zCmd = 0; int bBin; int rc; - int hasCRNL = 0; + int hasCRLF = 0; FILE *f = 0; sqlite3_int64 sz; sqlite3_int64 x; @@ -1602,7 +1758,7 @@ static void editFunc( bBin = sqlite3_value_type(argv[0])==SQLITE_BLOB; /* When writing the file to be edited, do \n to \r\n conversions on systems ** that want \r\n line endings */ - f = fopen(zTempFile, bBin ? "wb" : "w"); + f = sqlite3_fopen(zTempFile, bBin ? "wb" : "w"); if( f==0 ){ sqlite3_result_error(context, "edit() cannot open temp file", -1); goto edit_func_end; @@ -1613,7 +1769,7 @@ static void editFunc( }else{ const char *z = (const char*)sqlite3_value_text(argv[0]); /* Remember whether or not the value originally contained \r\n */ - if( z && strstr(z,"\r\n")!=0 ) hasCRNL = 1; + if( z && strstr(z,"\r\n")!=0 ) hasCRLF = 1; x = fwrite(sqlite3_value_text(argv[0]), 1, (size_t)sz, f); } fclose(f); @@ -1633,7 +1789,7 @@ static void editFunc( sqlite3_result_error(context, "EDITOR returned non-zero", -1); goto edit_func_end; } - f = fopen(zTempFile, "rb"); + f = sqlite3_fopen(zTempFile, "rb"); if( f==0 ){ sqlite3_result_error(context, "edit() cannot reopen temp file after edit", -1); @@ -1658,7 +1814,7 @@ static void editFunc( sqlite3_result_blob64(context, p, sz, sqlite3_free); }else{ sqlite3_int64 i, j; - if( hasCRNL ){ + if( hasCRLF ){ /* If the original contains \r\n then do no conversions back to \n */ }else{ /* If the file did not originally contain \r\n then convert any new @@ -1701,9 +1857,24 @@ static void outputModePop(ShellState *p){ } /* +** Set output mode to text or binary for Windows. +*/ +static void setCrlfMode(ShellState *p){ +#ifdef _WIN32 + if( p->crlfMode ){ + sqlite3_fsetmode(p->out, _O_TEXT); + }else{ + sqlite3_fsetmode(p->out, _O_BINARY); + } +#else + UNUSED_PARAMETER(p); +#endif +} + +/* ** Output the given string as a hex-encoded blob (eg. X'1234' ) */ -static void output_hex_blob(const void *pBlob, int nBlob){ +static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){ int i; unsigned char *aBlob = (unsigned char*)pBlob; @@ -1720,72 +1891,82 @@ static void output_hex_blob(const void *pBlob, int nBlob){ } zStr[i*2] = '\0'; - oputf("X'%s'", zStr); + sqlite3_fprintf(out, "X'%s'", zStr); sqlite3_free(zStr); } /* -** Find a string that is not found anywhere in z[]. Return a pointer -** to that string. +** Output the given string as a quoted string using SQL quoting conventions: ** -** Try to use zA and zB first. If both of those are already found in z[] -** then make up some string and store it in the buffer zBuf. -*/ -static const char *unused_string( - const char *z, /* Result must not appear anywhere in z */ - const char *zA, const char *zB, /* Try these first */ - char *zBuf /* Space to store a generated string */ -){ - unsigned i = 0; - if( strstr(z, zA)==0 ) return zA; - if( strstr(z, zB)==0 ) return zB; - do{ - sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++); - }while( strstr(z,zBuf)!=0 ); - return zBuf; -} - -/* -** Output the given string as a quoted string using SQL quoting conventions. +** (1) Single quotes (') within the string are doubled +** (2) The whle string is enclosed in '...' +** (3) Control characters other than \n, \t, and \r\n are escaped +** using \u00XX notation and if such substitutions occur, +** the whole string is enclosed in unistr('...') instead of '...'. +** +** Step (3) is omitted if the control-character escape mode is OFF. ** -** See also: output_quoted_escaped_string() +** See also: output_quoted_escaped_string() which does the same except +** that it does not make exceptions for \n, \t, and \r\n in step (3). */ -static void output_quoted_string(const char *z){ +static void output_quoted_string(ShellState *p, const char *zInX){ int i; - char c; -#ifndef SQLITE_SHELL_FIDDLE - FILE *pfO = setOutputStream(invalidFileStream); - setBinaryMode(pfO, 1); -#endif + int needUnistr = 0; + int needDblQuote = 0; + const unsigned char *z = (const unsigned char*)zInX; + unsigned char c; + FILE *out = p->out; + sqlite3_fsetmode(out, _O_BINARY); if( z==0 ) return; - for(i=0; (c = z[i])!=0 && c!='\''; i++){} - if( c==0 ){ - oputf("'%s'",z); + for(i=0; (c = z[i])!=0; i++){ + if( c=='\'' ){ needDblQuote = 1; } + if( c>0x1f ) continue; + if( c=='\t' || c=='\n' ) continue; + if( c=='\r' && z[i+1]=='\n' ) continue; + needUnistr = 1; + break; + } + if( (needDblQuote==0 && needUnistr==0) + || (needDblQuote==0 && p->eEscMode==SHELL_ESC_OFF) + ){ + sqlite3_fprintf(out, "'%s'",z); + }else if( p->eEscMode==SHELL_ESC_OFF ){ + char *zEncoded = sqlite3_mprintf("%Q", z); + sqlite3_fputs(zEncoded, out); + sqlite3_free(zEncoded); }else{ - oputz("'"); + if( needUnistr ){ + sqlite3_fputs("unistr('", out); + }else{ + sqlite3_fputs("'", out); + } while( *z ){ - for(i=0; (c = z[i])!=0 && c!='\''; i++){} - if( c=='\'' ) i++; + for(i=0; (c = z[i])!=0; i++){ + if( c=='\'' ) break; + if( c>0x1f ) continue; + if( c=='\t' || c=='\n' ) continue; + if( c=='\r' && z[i+1]=='\n' ) continue; + break; + } if( i ){ - oputf("%.*s", i, z); + sqlite3_fprintf(out, "%.*s", i, z); z += i; } + if( c==0 ) break; if( c=='\'' ){ - oputz("'"); - continue; - } - if( c==0 ){ - break; + sqlite3_fputs("''", out); + }else{ + sqlite3_fprintf(out, "\\u%04x", c); } z++; } - oputz("'"); + if( needUnistr ){ + sqlite3_fputs("')", out); + }else{ + sqlite3_fputs("'", out); + } } -#ifndef SQLITE_SHELL_FIDDLE - setTextMode(pfO, 1); -#else - setTextMode(stdout, 1); -#endif + setCrlfMode(p); } /* @@ -1797,69 +1978,17 @@ static void output_quoted_string(const char *z){ ** This is like output_quoted_string() but with the addition of the \r\n ** escape mechanism. */ -static void output_quoted_escaped_string(const char *z){ - int i; - char c; -#ifndef SQLITE_SHELL_FIDDLE - FILE *pfO = setOutputStream(invalidFileStream); - setBinaryMode(pfO, 1); -#endif - for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){} - if( c==0 ){ - oputf("'%s'",z); +static void output_quoted_escaped_string(ShellState *p, const char *z){ + char *zEscaped; + sqlite3_fsetmode(p->out, _O_BINARY); + if( p->eEscMode==SHELL_ESC_OFF ){ + zEscaped = sqlite3_mprintf("%Q", z); }else{ - const char *zNL = 0; - const char *zCR = 0; - int nNL = 0; - int nCR = 0; - char zBuf1[20], zBuf2[20]; - for(i=0; z[i]; i++){ - if( z[i]=='\n' ) nNL++; - if( z[i]=='\r' ) nCR++; - } - if( nNL ){ - oputz("replace("); - zNL = unused_string(z, "\\n", "\\012", zBuf1); - } - if( nCR ){ - oputz("replace("); - zCR = unused_string(z, "\\r", "\\015", zBuf2); - } - oputz("'"); - while( *z ){ - for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){} - if( c=='\'' ) i++; - if( i ){ - oputf("%.*s", i, z); - z += i; - } - if( c=='\'' ){ - oputz("'"); - continue; - } - if( c==0 ){ - break; - } - z++; - if( c=='\n' ){ - oputz(zNL); - continue; - } - oputz(zCR); - } - oputz("'"); - if( nCR ){ - oputf(",'%s',char(13))", zCR); - } - if( nNL ){ - oputf(",'%s',char(10))", zNL); - } + zEscaped = sqlite3_mprintf("%#Q", z); } -#ifndef SQLITE_SHELL_FIDDLE - setTextMode(pfO, 1); -#else - setTextMode(stdout, 1); -#endif + sqlite3_fputs(zEscaped, p->out); + sqlite3_free(zEscaped); + setCrlfMode(p); } /* @@ -1879,22 +2008,60 @@ static const char *anyOfInStr(const char *s, const char *zAny, size_t ns){ } return pcFirst; } + +/* Skip over as much z[] input char sequence as is valid UTF-8, +** limited per nAccept char's or whole characters and containing +** no char cn such that ((1<<cn) & ccm)!=0. On return, the +** sequence z:return (inclusive:exclusive) is validated UTF-8. +** Limit: nAccept>=0 => char count, nAccept<0 => character + */ +const char *zSkipValidUtf8(const char *z, int nAccept, long ccm){ + int ng = (nAccept<0)? -nAccept : 0; + const char *pcLimit = (nAccept>=0)? z+nAccept : 0; + assert(z!=0); + while( (pcLimit)? (z<pcLimit) : (ng-- != 0) ){ + unsigned char c = *(u8*)z; + if( c<0x7f ){ + if( ccm != 0L && c < 0x20 && ((1L<<c) & ccm) != 0 ) return z; + ++z; /* ASCII */ + }else if( (c & 0xC0) != 0xC0 ) return z; /* not a lead byte */ + else{ + const char *zt = z+1; /* Got lead byte, look at trail bytes.*/ + do{ + if( pcLimit && zt >= pcLimit ) return z; + else{ + char ct = *zt++; + if( ct==0 || (zt-z)>4 || (ct & 0xC0)!=0x80 ){ + /* Trailing bytes are too few, too many, or invalid. */ + return z; + } + } + } while( ((c <<= 1) & 0x40) == 0x40 ); /* Eat lead byte's count. */ + z = zt; + } + } + return z; +} + + /* ** Output the given string as a quoted according to C or TCL quoting rules. */ -static void output_c_string(const char *z){ +static void output_c_string(FILE *out, const char *z){ char c; static const char *zq = "\""; static long ctrlMask = ~0L; static const char *zDQBSRO = "\"\\\x7f"; /* double-quote, backslash, rubout */ char ace[3] = "\\?"; char cbsSay; - oputz(zq); + sqlite3_fputs(zq, out); while( *z!=0 ){ const char *pcDQBSRO = anyOfInStr(z, zDQBSRO, ~(size_t)0); const char *pcPast = zSkipValidUtf8(z, INT_MAX, ctrlMask); const char *pcEnd = (pcDQBSRO && pcDQBSRO < pcPast)? pcDQBSRO : pcPast; - if( pcEnd > z ) oputb(z, (int)(pcEnd-z)); + if( pcEnd > z ){ + sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z); + } if( (c = *pcEnd)==0 ) break; ++pcEnd; switch( c ){ @@ -1909,23 +2076,23 @@ static void output_c_string(const char *z){ } if( cbsSay ){ ace[1] = cbsSay; - oputz(ace); + sqlite3_fputs(ace, out); }else if( !isprint(c&0xff) ){ - oputf("\\%03o", c&0xff); + sqlite3_fprintf(out, "\\%03o", c&0xff); }else{ ace[1] = (char)c; - oputz(ace+1); + sqlite3_fputs(ace+1, out); } z = pcEnd; } - oputz(zq); + sqlite3_fputs(zq, out); } /* -** Output the given string as a quoted according to JSON quoting rules. +** Output the given string as quoted according to JSON quoting rules. */ -static void output_json_string(const char *z, i64 n){ - char c; +static void output_json_string(FILE *out, const char *z, i64 n){ + unsigned char c; static const char *zq = "\""; static long ctrlMask = ~0L; static const char *zDQBS = "\"\\"; @@ -1935,17 +2102,17 @@ static void output_json_string(const char *z, i64 n){ if( z==0 ) z = ""; pcLimit = z + ((n<0)? strlen(z) : (size_t)n); - oputz(zq); + sqlite3_fputs(zq, out); while( z < pcLimit ){ const char *pcDQBS = anyOfInStr(z, zDQBS, pcLimit-z); const char *pcPast = zSkipValidUtf8(z, (int)(pcLimit-z), ctrlMask); const char *pcEnd = (pcDQBS && pcDQBS < pcPast)? pcDQBS : pcPast; if( pcEnd > z ){ - oputb(z, (int)(pcEnd-z)); + sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z); z = pcEnd; } if( z >= pcLimit ) break; - c = *(z++); + c = (unsigned char)*(z++); switch( c ){ case '"': case '\\': cbsSay = (char)c; @@ -1959,22 +2126,109 @@ static void output_json_string(const char *z, i64 n){ } if( cbsSay ){ ace[1] = cbsSay; - oputz(ace); - }else if( c<=0x1f ){ - oputf("u%04x", c); + sqlite3_fputs(ace, out); + }else if( c<=0x1f || c>=0x7f ){ + sqlite3_fprintf(out, "\\u%04x", c); }else{ ace[1] = (char)c; - oputz(ace+1); + sqlite3_fputs(ace+1, out); } } - oputz(zq); + sqlite3_fputs(zq, out); +} + +/* +** Escape the input string if it is needed and in accordance with +** eEscMode. +** +** Escaping is needed if the string contains any control characters +** other than \t, \n, and \r\n +** +** If no escaping is needed (the common case) then set *ppFree to NULL +** and return the original string. If escapingn is needed, write the +** escaped string into memory obtained from sqlite3_malloc64() or the +** equivalent, and return the new string and set *ppFree to the new string +** as well. +** +** The caller is responsible for freeing *ppFree if it is non-NULL in order +** to reclaim memory. +*/ +static const char *escapeOutput( + ShellState *p, + const char *zInX, + char **ppFree +){ + i64 i, j; + i64 nCtrl = 0; + unsigned char *zIn; + unsigned char c; + unsigned char *zOut; + + + /* No escaping if disabled */ + if( p->eEscMode==SHELL_ESC_OFF ){ + *ppFree = 0; + return zInX; + } + + /* Count the number of control characters in the string. */ + zIn = (unsigned char*)zInX; + for(i=0; (c = zIn[i])!=0; i++){ + if( c<=0x1f + && c!='\t' + && c!='\n' + && (c!='\r' || zIn[i+1]!='\n') + ){ + nCtrl++; + } + } + if( nCtrl==0 ){ + *ppFree = 0; + return zInX; + } + if( p->eEscMode==SHELL_ESC_SYMBOL ) nCtrl *= 2; + zOut = sqlite3_malloc64( i + nCtrl + 1 ); + shell_check_oom(zOut); + for(i=j=0; (c = zIn[i])!=0; i++){ + if( c>0x1f + || c=='\t' + || c=='\n' + || (c=='\r' && zIn[i+1]=='\n') + ){ + continue; + } + if( i>0 ){ + memcpy(&zOut[j], zIn, i); + j += i; + } + zIn += i+1; + i = -1; + switch( p->eEscMode ){ + case SHELL_ESC_SYMBOL: + zOut[j++] = 0xe2; + zOut[j++] = 0x90; + zOut[j++] = 0x80+c; + break; + case SHELL_ESC_ASCII: + zOut[j++] = '^'; + zOut[j++] = 0x40+c; + break; + } + } + if( i>0 ){ + memcpy(&zOut[j], zIn, i); + j += i; + } + zOut[j] = 0; + *ppFree = (char*)zOut; + return (char*)zOut; } /* ** Output the given string with characters that are special to ** HTML escaped. */ -static void output_html_string(const char *z){ +static void output_html_string(FILE *out, const char *z){ int i; if( z==0 ) z = ""; while( *z ){ @@ -1986,18 +2240,18 @@ static void output_html_string(const char *z){ && z[i]!='\''; i++){} if( i>0 ){ - oputf("%.*s",i,z); + sqlite3_fprintf(out, "%.*s",i,z); } if( z[i]=='<' ){ - oputz("<"); + sqlite3_fputs("<", out); }else if( z[i]=='&' ){ - oputz("&"); + sqlite3_fputs("&", out); }else if( z[i]=='>' ){ - oputz(">"); + sqlite3_fputs(">", out); }else if( z[i]=='\"' ){ - oputz("""); + sqlite3_fputs(""", out); }else if( z[i]=='\'' ){ - oputz("'"); + sqlite3_fputs("'", out); }else{ break; } @@ -2036,7 +2290,7 @@ static const char needCsvQuote[] = { */ static void output_csv(ShellState *p, const char *z, int bSep){ if( z==0 ){ - oputf("%s",p->nullValue); + sqlite3_fprintf(p->out, "%s",p->nullValue); }else{ unsigned i; for(i=0; z[i]; i++){ @@ -2048,14 +2302,14 @@ static void output_csv(ShellState *p, const char *z, int bSep){ if( i==0 || strstr(z, p->colSeparator)!=0 ){ char *zQuoted = sqlite3_mprintf("\"%w\"", z); shell_check_oom(zQuoted); - oputz(zQuoted); + sqlite3_fputs(zQuoted, p->out); sqlite3_free(zQuoted); }else{ - oputz(z); + sqlite3_fputs(z, p->out); } } if( bSep ){ - oputz(p->colSeparator); + sqlite3_fputs(p->colSeparator, p->out); } } @@ -2163,16 +2417,16 @@ static int shellAuth( az[1] = zA2; az[2] = zA3; az[3] = zA4; - oputf("authorizer: %s", azAction[op]); + sqlite3_fprintf(p->out, "authorizer: %s", azAction[op]); for(i=0; i<4; i++){ - oputz(" "); + sqlite3_fputs(" ", p->out); if( az[i] ){ - output_c_string(az[i]); + output_c_string(p->out, az[i]); }else{ - oputz("NULL"); + sqlite3_fputs("NULL", p->out); } } - oputz("\n"); + sqlite3_fputs("\n", p->out); if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4); return SQLITE_OK; } @@ -2188,7 +2442,7 @@ static int shellAuth( ** sqlite3_complete() returns false, try to terminate the comment before ** printing the result. https://sqlite.org/forum/forumpost/d7be961c5c */ -static void printSchemaLine(const char *z, const char *zTail){ +static void printSchemaLine(FILE *out, const char *z, const char *zTail){ char *zToFree = 0; if( z==0 ) return; if( zTail==0 ) return; @@ -2210,16 +2464,16 @@ static void printSchemaLine(const char *z, const char *zTail){ } } if( sqlite3_strglob("CREATE TABLE ['\"]*", z)==0 ){ - oputf("CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); + sqlite3_fprintf(out, "CREATE TABLE IF NOT EXISTS %s%s", z+13, zTail); }else{ - oputf("%s%s", z, zTail); + sqlite3_fprintf(out, "%s%s", z, zTail); } sqlite3_free(zToFree); } -static void printSchemaLineN(char *z, int n, const char *zTail){ +static void printSchemaLineN(FILE *out, char *z, int n, const char *zTail){ char c = z[n]; z[n] = 0; - printSchemaLine(z, zTail); + printSchemaLine(out, z, zTail); z[n] = c; } @@ -2247,7 +2501,7 @@ static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){ if( zText==0 ) return; nText = strlen(zText); if( p->autoEQPtest ){ - oputf("%d,%d,%s\n", iEqpId, p2, zText); + sqlite3_fprintf(p->out, "%d,%d,%s\n", iEqpId, p2, zText); } pNew = sqlite3_malloc64( sizeof(*pNew) + nText ); shell_check_oom(pNew); @@ -2295,7 +2549,8 @@ static void eqp_render_level(ShellState *p, int iEqpId){ for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){ pNext = eqp_next_row(p, iEqpId, pRow); z = pRow->zText; - oputf("%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z); + sqlite3_fprintf(p->out, "%s%s%s\n", p->sGraph.zPrefix, + pNext ? "|--" : "`--", z); if( n<(i64)sizeof(p->sGraph.zPrefix)-7 ){ memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4); eqp_render_level(p, pRow->iEqpId); @@ -2315,13 +2570,13 @@ static void eqp_render(ShellState *p, i64 nCycle){ eqp_reset(p); return; } - oputf("%s\n", pRow->zText+3); + sqlite3_fprintf(p->out, "%s\n", pRow->zText+3); p->sGraph.pRow = pRow->pNext; sqlite3_free(pRow); }else if( nCycle>0 ){ - oputf("QUERY PLAN (cycles=%lld [100%%])\n", nCycle); + sqlite3_fprintf(p->out, "QUERY PLAN (cycles=%lld [100%%])\n", nCycle); }else{ - oputz("QUERY PLAN\n"); + sqlite3_fputs("QUERY PLAN\n", p->out); } p->sGraph.zPrefix[0] = 0; eqp_render_level(p, 0); @@ -2337,13 +2592,13 @@ static int progress_handler(void *pClientData) { ShellState *p = (ShellState*)pClientData; p->nProgress++; if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){ - oputf("Progress limit reached (%u)\n", p->nProgress); + sqlite3_fprintf(p->out, "Progress limit reached (%u)\n", p->nProgress); if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0; return 1; } if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){ - oputf("Progress %u\n", p->nProgress); + sqlite3_fprintf(p->out, "Progress %u\n", p->nProgress); } return 0; } @@ -2352,14 +2607,14 @@ static int progress_handler(void *pClientData) { /* ** Print N dashes */ -static void print_dashes(int N){ +static void print_dashes(FILE *out, int N){ const char zDash[] = "--------------------------------------------------"; const int nDash = sizeof(zDash) - 1; while( N>nDash ){ - oputz(zDash); + sqlite3_fputs(zDash, out); N -= nDash; } - oputf("%.*s", N, zDash); + sqlite3_fprintf(out, "%.*s", N, zDash); } /* @@ -2372,15 +2627,15 @@ static void print_row_separator( ){ int i; if( nArg>0 ){ - oputz(zSep); - print_dashes(p->actualWidth[0]+2); + sqlite3_fputs(zSep, p->out); + print_dashes(p->out, p->actualWidth[0]+2); for(i=1; i<nArg; i++){ - oputz(zSep); - print_dashes(p->actualWidth[i]+2); + sqlite3_fputs(zSep, p->out); + print_dashes(p->out, p->actualWidth[i]+2); } - oputz(zSep); + sqlite3_fputs(zSep, p->out); } - oputz("\n"); + sqlite3_fputs("\n", p->out); } /* @@ -2410,10 +2665,14 @@ static int shell_callback( int len = strlen30(azCol[i] ? azCol[i] : ""); if( len>w ) w = len; } - if( p->cnt++>0 ) oputz(p->rowSeparator); + if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out); for(i=0; i<nArg; i++){ - oputf("%*s = %s%s", w, azCol[i], - azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); + char *pFree = 0; + const char *pDisplay; + pDisplay = escapeOutput(p, azArg[i] ? azArg[i] : p->nullValue, &pFree); + sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i], + pDisplay, p->rowSeparator); + if( pFree ) sqlite3_free(pFree); } break; } @@ -2440,12 +2699,12 @@ static int shell_callback( /* If this is the first row seen, print out the headers */ if( p->cnt++==0 ){ for(i=0; i<nArg; i++){ - utf8_width_print(aWidth[i], azCol[ aMap[i] ]); - oputz(i==nArg-1 ? "\n" : " "); + utf8_width_print(p->out, aWidth[i], azCol[ aMap[i] ]); + sqlite3_fputs(i==nArg-1 ? "\n" : " ", p->out); } for(i=0; i<nArg; i++){ - print_dashes(aWidth[i]); - oputz(i==nArg-1 ? "\n" : " "); + print_dashes(p->out, aWidth[i]); + sqlite3_fputs(i==nArg-1 ? "\n" : " ", p->out); } } @@ -2463,17 +2722,17 @@ static int shell_callback( } if( i==iIndent && p->aiIndent && p->pStmt ){ if( p->iIndent<p->nIndent ){ - oputf("%*.s", p->aiIndent[p->iIndent], ""); + sqlite3_fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); } p->iIndent++; } - utf8_width_print(w, zVal ? zVal : p->nullValue); - oputz(i==nArg-1 ? "\n" : zSep); + utf8_width_print(p->out, w, zVal ? zVal : p->nullValue); + sqlite3_fputs(i==nArg-1 ? "\n" : zSep, p->out); } break; } case MODE_Semi: { /* .schema and .fullschema output */ - printSchemaLine(azArg[0], ";\n"); + printSchemaLine(p->out, azArg[0], ";\n"); break; } case MODE_Pretty: { /* .schema and .fullschema with --indent */ @@ -2483,14 +2742,18 @@ static int shell_callback( char cEnd = 0; char c; int nLine = 0; + int isIndex; + int isWhere = 0; assert( nArg==1 ); if( azArg[0]==0 ) break; if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 ){ - oputf("%s;\n", azArg[0]); + sqlite3_fprintf(p->out, "%s;\n", azArg[0]); break; } + isIndex = sqlite3_strlike("CREATE INDEX%", azArg[0], 0)==0 + || sqlite3_strlike("CREATE UNIQUE INDEX%", azArg[0], 0)==0; z = sqlite3_mprintf("%s", azArg[0]); shell_check_oom(z); j = 0; @@ -2520,17 +2783,29 @@ static int shell_callback( nParen++; }else if( c==')' ){ nParen--; - if( nLine>0 && nParen==0 && j>0 ){ - printSchemaLineN(z, j, "\n"); + if( nLine>0 && nParen==0 && j>0 && !isWhere ){ + printSchemaLineN(p->out, z, j, "\n"); j = 0; } + }else if( (c=='w' || c=='W') + && nParen==0 && isIndex + && sqlite3_strnicmp("WHERE",&z[i],5)==0 + && !IsAlnum(z[i+5]) && z[i+5]!='_' ){ + isWhere = 1; + }else if( isWhere && (c=='A' || c=='a') + && nParen==0 + && sqlite3_strnicmp("AND",&z[i],3)==0 + && !IsAlnum(z[i+3]) && z[i+3]!='_' ){ + printSchemaLineN(p->out, z, j, "\n "); + j = 0; } z[j++] = c; if( nParen==1 && cEnd==0 && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) + && !isWhere ){ if( c=='\n' ) j--; - printSchemaLineN(z, j, "\n "); + printSchemaLineN(p->out, z, j, "\n "); j = 0; nLine++; while( IsSpace(z[i+1]) ){ i++; } @@ -2538,118 +2813,136 @@ static int shell_callback( } z[j] = 0; } - printSchemaLine(z, ";\n"); + printSchemaLine(p->out, z, ";\n"); sqlite3_free(z); break; } case MODE_List: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ - oputf("%s%s",azCol[i], i==nArg-1 ? p->rowSeparator : p->colSeparator); + char *z = azCol[i]; + char *pFree; + const char *zOut = escapeOutput(p, z, &pFree); + sqlite3_fprintf(p->out, "%s%s", zOut, + i==nArg-1 ? p->rowSeparator : p->colSeparator); + if( pFree ) sqlite3_free(pFree); } } if( azArg==0 ) break; for(i=0; i<nArg; i++){ char *z = azArg[i]; + char *pFree; + const char *zOut; if( z==0 ) z = p->nullValue; - oputz(z); - oputz((i<nArg-1)? p->colSeparator : p->rowSeparator); + zOut = escapeOutput(p, z, &pFree); + sqlite3_fputs(zOut, p->out); + if( pFree ) sqlite3_free(pFree); + sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out); } break; } + case MODE_Www: case MODE_Html: { - if( p->cnt++==0 && p->showHeader ){ - oputz("<TR>"); + if( p->cnt==0 && p->cMode==MODE_Www ){ + sqlite3_fputs( + "</PRE>\n" + "<TABLE border='1' cellspacing='0' cellpadding='2'>\n" + ,p->out + ); + } + if( p->cnt==0 && (p->showHeader || p->cMode==MODE_Www) ){ + sqlite3_fputs("<TR>", p->out); for(i=0; i<nArg; i++){ - oputz("<TH>"); - output_html_string(azCol[i]); - oputz("</TH>\n"); + sqlite3_fputs("<TH>", p->out); + output_html_string(p->out, azCol[i]); + sqlite3_fputs("</TH>\n", p->out); } - oputz("</TR>\n"); + sqlite3_fputs("</TR>\n", p->out); } + p->cnt++; if( azArg==0 ) break; - oputz("<TR>"); + sqlite3_fputs("<TR>", p->out); for(i=0; i<nArg; i++){ - oputz("<TD>"); - output_html_string(azArg[i] ? azArg[i] : p->nullValue); - oputz("</TD>\n"); + sqlite3_fputs("<TD>", p->out); + output_html_string(p->out, azArg[i] ? azArg[i] : p->nullValue); + sqlite3_fputs("</TD>\n", p->out); } - oputz("</TR>\n"); + sqlite3_fputs("</TR>\n", p->out); break; } case MODE_Tcl: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ - output_c_string(azCol[i] ? azCol[i] : ""); - if(i<nArg-1) oputz(p->colSeparator); + output_c_string(p->out, azCol[i] ? azCol[i] : ""); + if(i<nArg-1) sqlite3_fputs(p->colSeparator, p->out); } - oputz(p->rowSeparator); + sqlite3_fputs(p->rowSeparator, p->out); } if( azArg==0 ) break; for(i=0; i<nArg; i++){ - output_c_string(azArg[i] ? azArg[i] : p->nullValue); - if(i<nArg-1) oputz(p->colSeparator); + output_c_string(p->out, azArg[i] ? azArg[i] : p->nullValue); + if(i<nArg-1) sqlite3_fputs(p->colSeparator, p->out); } - oputz(p->rowSeparator); + sqlite3_fputs(p->rowSeparator, p->out); break; } case MODE_Csv: { - setBinaryMode(p->out, 1); + sqlite3_fsetmode(p->out, _O_BINARY); if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ output_csv(p, azCol[i] ? azCol[i] : "", i<nArg-1); } - oputz(p->rowSeparator); + sqlite3_fputs(p->rowSeparator, p->out); } if( nArg>0 ){ for(i=0; i<nArg; i++){ output_csv(p, azArg[i], i<nArg-1); } - oputz(p->rowSeparator); + sqlite3_fputs(p->rowSeparator, p->out); } - setTextMode(p->out, 1); + setCrlfMode(p); break; } case MODE_Insert: { if( azArg==0 ) break; - oputf("INSERT INTO %s",p->zDestTable); + sqlite3_fprintf(p->out, "INSERT INTO %s",p->zDestTable); if( p->showHeader ){ - oputz("("); + sqlite3_fputs("(", p->out); for(i=0; i<nArg; i++){ - if( i>0 ) oputz(","); + if( i>0 ) sqlite3_fputs(",", p->out); if( quoteChar(azCol[i]) ){ char *z = sqlite3_mprintf("\"%w\"", azCol[i]); shell_check_oom(z); - oputz(z); + sqlite3_fputs(z, p->out); sqlite3_free(z); }else{ - oputf("%s", azCol[i]); + sqlite3_fprintf(p->out, "%s", azCol[i]); } } - oputz(")"); + sqlite3_fputs(")", p->out); } p->cnt++; for(i=0; i<nArg; i++){ - oputz(i>0 ? "," : " VALUES("); + sqlite3_fputs(i>0 ? "," : " VALUES(", p->out); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - oputz("NULL"); + sqlite3_fputs("NULL", p->out); }else if( aiType && aiType[i]==SQLITE_TEXT ){ if( ShellHasFlag(p, SHFLG_Newlines) ){ - output_quoted_string(azArg[i]); + output_quoted_string(p, azArg[i]); }else{ - output_quoted_escaped_string(azArg[i]); + output_quoted_escaped_string(p, azArg[i]); } }else if( aiType && aiType[i]==SQLITE_INTEGER ){ - oputz(azArg[i]); + sqlite3_fputs(azArg[i], p->out); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ - oputz("9.0e+999"); + sqlite3_fputs("9.0e+999", p->out); }else if( ur==0xfff0000000000000LL ){ - oputz("-9.0e+999"); + sqlite3_fputs("-9.0e+999", p->out); }else{ sqlite3_int64 ir = (sqlite3_int64)r; if( r==(double)ir ){ @@ -2657,115 +2950,115 @@ static int shell_callback( }else{ sqlite3_snprintf(50,z,"%!.20g", r); } - oputz(z); + sqlite3_fputs(z, p->out); } }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_hex_blob(pBlob, nBlob); + output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ - oputz(azArg[i]); + sqlite3_fputs(azArg[i], p->out); }else if( ShellHasFlag(p, SHFLG_Newlines) ){ - output_quoted_string(azArg[i]); + output_quoted_string(p, azArg[i]); }else{ - output_quoted_escaped_string(azArg[i]); + output_quoted_escaped_string(p, azArg[i]); } } - oputz(");\n"); + sqlite3_fputs(");\n", p->out); break; } case MODE_Json: { if( azArg==0 ) break; if( p->cnt==0 ){ - fputs("[{", p->out); + sqlite3_fputs("[{", p->out); }else{ - fputs(",\n{", p->out); + sqlite3_fputs(",\n{", p->out); } p->cnt++; for(i=0; i<nArg; i++){ - output_json_string(azCol[i], -1); - oputz(":"); + output_json_string(p->out, azCol[i], -1); + sqlite3_fputs(":", p->out); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - oputz("null"); + sqlite3_fputs("null", p->out); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_uint64 ur; memcpy(&ur,&r,sizeof(r)); if( ur==0x7ff0000000000000LL ){ - oputz("9.0e+999"); + sqlite3_fputs("9.0e+999", p->out); }else if( ur==0xfff0000000000000LL ){ - oputz("-9.0e+999"); + sqlite3_fputs("-9.0e+999", p->out); }else{ sqlite3_snprintf(50,z,"%!.20g", r); - oputz(z); + sqlite3_fputs(z, p->out); } }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_json_string(pBlob, nBlob); + output_json_string(p->out, pBlob, nBlob); }else if( aiType && aiType[i]==SQLITE_TEXT ){ - output_json_string(azArg[i], -1); + output_json_string(p->out, azArg[i], -1); }else{ - oputz(azArg[i]); + sqlite3_fputs(azArg[i], p->out); } if( i<nArg-1 ){ - oputz(","); + sqlite3_fputs(",", p->out); } } - oputz("}"); + sqlite3_fputs("}", p->out); break; } case MODE_Quote: { if( azArg==0 ) break; if( p->cnt==0 && p->showHeader ){ for(i=0; i<nArg; i++){ - if( i>0 ) fputs(p->colSeparator, p->out); - output_quoted_string(azCol[i]); + if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); + output_quoted_string(p, azCol[i]); } - fputs(p->rowSeparator, p->out); + sqlite3_fputs(p->rowSeparator, p->out); } p->cnt++; for(i=0; i<nArg; i++){ - if( i>0 ) fputs(p->colSeparator, p->out); + if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); if( (azArg[i]==0) || (aiType && aiType[i]==SQLITE_NULL) ){ - oputz("NULL"); + sqlite3_fputs("NULL", p->out); }else if( aiType && aiType[i]==SQLITE_TEXT ){ - output_quoted_string(azArg[i]); + output_quoted_string(p, azArg[i]); }else if( aiType && aiType[i]==SQLITE_INTEGER ){ - oputz(azArg[i]); + sqlite3_fputs(azArg[i], p->out); }else if( aiType && aiType[i]==SQLITE_FLOAT ){ char z[50]; double r = sqlite3_column_double(p->pStmt, i); sqlite3_snprintf(50,z,"%!.20g", r); - oputz(z); + sqlite3_fputs(z, p->out); }else if( aiType && aiType[i]==SQLITE_BLOB && p->pStmt ){ const void *pBlob = sqlite3_column_blob(p->pStmt, i); int nBlob = sqlite3_column_bytes(p->pStmt, i); - output_hex_blob(pBlob, nBlob); + output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ - oputz(azArg[i]); + sqlite3_fputs(azArg[i], p->out); }else{ - output_quoted_string(azArg[i]); + output_quoted_string(p, azArg[i]); } } - fputs(p->rowSeparator, p->out); + sqlite3_fputs(p->rowSeparator, p->out); break; } case MODE_Ascii: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i<nArg; i++){ - if( i>0 ) oputz(p->colSeparator); - oputz(azCol[i] ? azCol[i] : ""); + if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); + sqlite3_fputs(azCol[i] ? azCol[i] : "", p->out); } - oputz(p->rowSeparator); + sqlite3_fputs(p->rowSeparator, p->out); } if( azArg==0 ) break; for(i=0; i<nArg; i++){ - if( i>0 ) oputz(p->colSeparator); - oputz(azArg[i] ? azArg[i] : p->nullValue); + if( i>0 ) sqlite3_fputs(p->colSeparator, p->out); + sqlite3_fputs(azArg[i] ? azArg[i] : p->nullValue, p->out); } - oputz(p->rowSeparator); + sqlite3_fputs(p->rowSeparator, p->out); break; } case MODE_EQP: { @@ -2844,7 +3137,7 @@ static void createSelftestTable(ShellState *p){ "DROP TABLE [_shell$self];" ,0,0,&zErrMsg); if( zErrMsg ){ - eputf("SELFTEST initialization failure: %s\n", zErrMsg); + sqlite3_fprintf(stderr, "SELFTEST initialization failure: %s\n", zErrMsg); sqlite3_free(zErrMsg); } sqlite3_exec(p->db, "RELEASE selftest_init",0,0,0); @@ -2947,7 +3240,7 @@ static int run_table_dump_query( rc = sqlite3_prepare_v2(p->db, zSelect, -1, &pSelect, 0); if( rc!=SQLITE_OK || !pSelect ){ char *zContext = shell_error_context(zSelect, p->db); - oputf("/**** ERROR: (%d) %s *****/\n%s", + sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n%s", rc, sqlite3_errmsg(p->db), zContext); sqlite3_free(zContext); if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; @@ -2957,22 +3250,23 @@ static int run_table_dump_query( nResult = sqlite3_column_count(pSelect); while( rc==SQLITE_ROW ){ z = (const char*)sqlite3_column_text(pSelect, 0); - oputf("%s", z); + sqlite3_fprintf(p->out, "%s", z); for(i=1; i<nResult; i++){ - oputf(",%s", sqlite3_column_text(pSelect, i)); + sqlite3_fprintf(p->out, ",%s", sqlite3_column_text(pSelect, i)); } if( z==0 ) z = ""; while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; if( z[0] ){ - oputz("\n;\n"); + sqlite3_fputs("\n;\n", p->out); }else{ - oputz(";\n"); + sqlite3_fputs(";\n", p->out); } rc = sqlite3_step(pSelect); } rc = sqlite3_finalize(pSelect); if( rc!=SQLITE_OK ){ - oputf("/**** ERROR: (%d) %s *****/\n", rc, sqlite3_errmsg(p->db)); + sqlite3_fprintf(p->out, "/**** ERROR: (%d) %s *****/\n", + rc, sqlite3_errmsg(p->db)); if( (rc&0xff)!=SQLITE_CORRUPT ) p->nErr++; } return rc; @@ -3008,13 +3302,13 @@ static char *save_err_msg( /* ** Attempt to display I/O stats on Linux using /proc/PID/io */ -static void displayLinuxIoStats(void){ +static void displayLinuxIoStats(FILE *out){ FILE *in; char z[200]; sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid()); - in = fopen(z, "rb"); + in = sqlite3_fopen(z, "rb"); if( in==0 ) return; - while( fgets(z, sizeof(z), in)!=0 ){ + while( sqlite3_fgets(z, sizeof(z), in)!=0 ){ static const struct { const char *zPattern; const char *zDesc; @@ -3031,7 +3325,7 @@ static void displayLinuxIoStats(void){ for(i=0; i<ArraySize(aTrans); i++){ int n = strlen30(aTrans[i].zPattern); if( cli_strncmp(aTrans[i].zPattern, z, n)==0 ){ - oputf("%-36s %s", aTrans[i].zDesc, &z[n]); + sqlite3_fprintf(out, "%-36s %s", aTrans[i].zDesc, &z[n]); break; } } @@ -3044,6 +3338,7 @@ static void displayLinuxIoStats(void){ ** Display a single line of status using 64-bit values. */ static void displayStatLine( + FILE *out, /* Write to this channel */ char *zLabel, /* Label for this one line */ char *zFormat, /* Format for the result */ int iStatusCtrl, /* Which status to display */ @@ -3062,7 +3357,7 @@ static void displayStatLine( }else{ sqlite3_snprintf(sizeof(zLine), zLine, zFormat, iHiwtr); } - oputf("%-36s %s\n", zLabel, zLine); + sqlite3_fprintf(out, "%-36s %s\n", zLabel, zLine); } /* @@ -3075,28 +3370,31 @@ static int display_stats( ){ int iCur; int iHiwtr; + FILE *out; if( pArg==0 || pArg->out==0 ) return 0; + out = pArg->out; if( pArg->pStmt && pArg->statsOn==2 ){ int nCol, i, x; sqlite3_stmt *pStmt = pArg->pStmt; char z[100]; nCol = sqlite3_column_count(pStmt); - oputf("%-36s %d\n", "Number of output columns:", nCol); + sqlite3_fprintf(out, "%-36s %d\n", "Number of output columns:", nCol); for(i=0; i<nCol; i++){ sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x); - oputf("%-36s %s\n", z, sqlite3_column_name(pStmt,i)); + sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_name(pStmt,i)); #ifndef SQLITE_OMIT_DECLTYPE sqlite3_snprintf(30, z+x, "declared type:"); - oputf("%-36s %s\n", z, sqlite3_column_decltype(pStmt, i)); + sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_decltype(pStmt, i)); #endif #ifdef SQLITE_ENABLE_COLUMN_METADATA sqlite3_snprintf(30, z+x, "database name:"); - oputf("%-36s %s\n", z, sqlite3_column_database_name(pStmt,i)); + sqlite3_fprintf(out, "%-36s %s\n", z, + sqlite3_column_database_name(pStmt,i)); sqlite3_snprintf(30, z+x, "table name:"); - oputf("%-36s %s\n", z, sqlite3_column_table_name(pStmt,i)); + sqlite3_fprintf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i)); sqlite3_snprintf(30, z+x, "origin name:"); - oputf("%-36s %s\n", z, sqlite3_column_origin_name(pStmt,i)); + sqlite3_fprintf(out, "%-36s %s\n", z,sqlite3_column_origin_name(pStmt,i)); #endif } } @@ -3104,27 +3402,27 @@ static int display_stats( if( pArg->statsOn==3 ){ if( pArg->pStmt ){ iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP,bReset); - oputf("VM-steps: %d\n", iCur); + sqlite3_fprintf(out, "VM-steps: %d\n", iCur); } return 0; } - displayStatLine("Memory Used:", + displayStatLine(out, "Memory Used:", "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset); - displayStatLine("Number of Outstanding Allocations:", + displayStatLine(out, "Number of Outstanding Allocations:", "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset); if( pArg->shellFlgs & SHFLG_Pagecache ){ - displayStatLine("Number of Pcache Pages Used:", + displayStatLine(out, "Number of Pcache Pages Used:", "%lld (max %lld) pages", SQLITE_STATUS_PAGECACHE_USED, bReset); } - displayStatLine("Number of Pcache Overflow Bytes:", + displayStatLine(out, "Number of Pcache Overflow Bytes:", "%lld (max %lld) bytes", SQLITE_STATUS_PAGECACHE_OVERFLOW, bReset); - displayStatLine("Largest Allocation:", + displayStatLine(out, "Largest Allocation:", "%lld bytes", SQLITE_STATUS_MALLOC_SIZE, bReset); - displayStatLine("Largest Pcache Allocation:", + displayStatLine(out, "Largest Pcache Allocation:", "%lld bytes", SQLITE_STATUS_PAGECACHE_SIZE, bReset); #ifdef YYTRACKMAXSTACKDEPTH - displayStatLine("Deepest Parser Stack:", + displayStatLine(out, "Deepest Parser Stack:", "%lld (max %lld)", SQLITE_STATUS_PARSER_STACK, bReset); #endif @@ -3133,68 +3431,87 @@ static int display_stats( iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_USED, &iCur, &iHiwtr, bReset); - oputf("Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); + sqlite3_fprintf(out, + "Lookaside Slots Used: %d (max %d)\n", iCur, iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &iCur, &iHiwtr, bReset); - oputf("Successful lookaside attempts: %d\n", iHiwtr); + sqlite3_fprintf(out, + "Successful lookaside attempts: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &iCur, &iHiwtr, bReset); - oputf("Lookaside failures due to size: %d\n", iHiwtr); + sqlite3_fprintf(out, + "Lookaside failures due to size: %d\n", iHiwtr); sqlite3_db_status(db, SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, &iCur, &iHiwtr, bReset); - oputf("Lookaside failures due to OOM: %d\n", iHiwtr); + sqlite3_fprintf(out, + "Lookaside failures due to OOM: %d\n", iHiwtr); } iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset); - oputf("Pager Heap Usage: %d bytes\n", iCur); + sqlite3_fprintf(out, + "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1); - oputf("Page cache hits: %d\n", iCur); + sqlite3_fprintf(out, + "Page cache hits: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1); - oputf("Page cache misses: %d\n", iCur); + sqlite3_fprintf(out, + "Page cache misses: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1); - oputf("Page cache writes: %d\n", iCur); + sqlite3_fprintf(out, + "Page cache writes: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1); - oputf("Page cache spills: %d\n", iCur); + sqlite3_fprintf(out, + "Page cache spills: %d\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset); - oputf("Schema Heap Usage: %d bytes\n", iCur); + sqlite3_fprintf(out, + "Schema Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1; sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset); - oputf("Statement Heap/Lookaside Usage: %d bytes\n", iCur); + sqlite3_fprintf(out, + "Statement Heap/Lookaside Usage: %d bytes\n", iCur); } if( pArg->pStmt ){ int iHit, iMiss; iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FULLSCAN_STEP, bReset); - oputf("Fullscan Steps: %d\n", iCur); + sqlite3_fprintf(out, + "Fullscan Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_SORT, bReset); - oputf("Sort Operations: %d\n", iCur); + sqlite3_fprintf(out, + "Sort Operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_AUTOINDEX,bReset); - oputf("Autoindex Inserts: %d\n", iCur); + sqlite3_fprintf(out, + "Autoindex Inserts: %d\n", iCur); iHit = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_HIT, bReset); iMiss = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_FILTER_MISS, bReset); if( iHit || iMiss ){ - oputf("Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss); + sqlite3_fprintf(out, + "Bloom filter bypass taken: %d/%d\n", iHit, iHit+iMiss); } iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset); - oputf("Virtual Machine Steps: %d\n", iCur); + sqlite3_fprintf(out, + "Virtual Machine Steps: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_REPREPARE,bReset); - oputf("Reprepare operations: %d\n", iCur); + sqlite3_fprintf(out, + "Reprepare operations: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_RUN, bReset); - oputf("Number of times run: %d\n", iCur); + sqlite3_fprintf(out, + "Number of times run: %d\n", iCur); iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_MEMUSED, bReset); - oputf("Memory used by prepared stmt: %d\n", iCur); + sqlite3_fprintf(out, + "Memory used by prepared stmt: %d\n", iCur); } #ifdef __linux__ - displayLinuxIoStats(); + displayLinuxIoStats(pArg->out); #endif /* Do not remove this machine readable comment: extra-stats-output-here */ @@ -3545,6 +3862,15 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){ }else if( sqlite3_strlike("_INF", zVar, 0)==0 ){ sqlite3_bind_double(pStmt, i, INFINITY); #endif + }else if( strncmp(zVar, "$int_", 5)==0 ){ + sqlite3_bind_int(pStmt, i, atoi(&zVar[5])); + }else if( strncmp(zVar, "$text_", 6)==0 ){ + size_t szVar = strlen(zVar); + char *zBuf = sqlite3_malloc64( szVar-5 ); + if( zBuf ){ + memcpy(zBuf, &zVar[6], szVar-5); + sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8); + } }else{ sqlite3_bind_null(pStmt, i); } @@ -3581,17 +3907,17 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){ /* Draw horizontal line N characters long using unicode box ** characters */ -static void print_box_line(int N){ +static void print_box_line(FILE *out, int N){ const char zDash[] = BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24 BOX_24; const int nDash = sizeof(zDash) - 1; N *= 3; while( N>nDash ){ - oputz(zDash); + sqlite3_fputs(zDash, out); N -= nDash; } - oputf("%.*s", N, zDash); + sqlite3_fprintf(out, "%.*s", N, zDash); } /* @@ -3606,15 +3932,15 @@ static void print_box_row_separator( ){ int i; if( nArg>0 ){ - oputz(zSep1); - print_box_line(p->actualWidth[0]+2); + sqlite3_fputs(zSep1, p->out); + print_box_line(p->out, p->actualWidth[0]+2); for(i=1; i<nArg; i++){ - oputz(zSep2); - print_box_line(p->actualWidth[i]+2); + sqlite3_fputs(zSep2, p->out); + print_box_line(p->out, p->actualWidth[i]+2); } - oputz(zSep3); + sqlite3_fputs(zSep3, p->out); } - oputz("\n"); + sqlite3_fputs("\n", p->out); } /* @@ -3629,6 +3955,7 @@ static void print_box_row_separator( ** the last line, write a NULL into *pzTail. (*pzTail is not allocated.) */ static char *translateForDisplayAndDup( + ShellState *p, /* To access current settings */ const unsigned char *z, /* Input text to be transformed */ const unsigned char **pzTail, /* OUT: Tail of the input for next line */ int mxWidth, /* Max width. 0 means no limit */ @@ -3648,12 +3975,23 @@ static char *translateForDisplayAndDup( if( mxWidth==0 ) mxWidth = 1000000; i = j = n = 0; while( n<mxWidth ){ - if( z[i]>=' ' ){ + unsigned char c = z[i]; + if( c>=0xc0 ){ + int u; + int len = decodeUtf8(&z[i], &u); + i += len; + j += len; + n += cli_wcwidth(u); + continue; + } + if( c>=' ' ){ n++; - do{ i++; j++; }while( (z[i]&0xc0)==0x80 ); + i++; + j++; continue; } - if( z[i]=='\t' ){ + if( c==0 || c=='\n' || (c=='\r' && z[i+1]=='\n') ) break; + if( c=='\t' ){ do{ n++; j++; @@ -3661,16 +3999,23 @@ static char *translateForDisplayAndDup( i++; continue; } - break; + if( c==0x1b && p->eEscMode==SHELL_ESC_OFF && (k = isVt100(&z[i]))>0 ){ + i += k; + j += k; + }else{ + n++; + j += 3; + i++; + } } if( n>=mxWidth && bWordWrap ){ /* Perhaps try to back up to a better place to break the line */ for(k=i; k>i/2; k--){ - if( isspace(z[k-1]) ) break; + if( IsSpace(z[k-1]) ) break; } if( k<=i/2 ){ for(k=i; k>i/2; k--){ - if( isalnum(z[k-1])!=isalnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; + if( IsAlnum(z[k-1])!=IsAlnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; } } if( k<=i/2 ){ @@ -3695,11 +4040,20 @@ static char *translateForDisplayAndDup( shell_check_oom(zOut); i = j = n = 0; while( i<k ){ - if( z[i]>=' ' ){ + unsigned char c = z[i]; + if( c>=0xc0 ){ + int u; + int len = decodeUtf8(&z[i], &u); + do{ zOut[j++] = z[i++]; }while( (--len)>0 ); + n += cli_wcwidth(u); + continue; + } + if( c>=' ' ){ n++; - do{ zOut[j++] = z[i++]; }while( (z[i]&0xc0)==0x80 ); + zOut[j++] = z[i++]; continue; } + if( c==0 ) break; if( z[i]=='\t' ){ do{ n++; @@ -3708,12 +4062,44 @@ static char *translateForDisplayAndDup( i++; continue; } - break; + switch( p->eEscMode ){ + case SHELL_ESC_SYMBOL: + zOut[j++] = 0xe2; + zOut[j++] = 0x90; + zOut[j++] = 0x80 + c; + break; + case SHELL_ESC_ASCII: + zOut[j++] = '^'; + zOut[j++] = 0x40 + c; + break; + case SHELL_ESC_OFF: { + int nn; + if( c==0x1b && (nn = isVt100(&z[i]))>0 ){ + memcpy(&zOut[j], &z[i], nn); + j += nn; + i += nn - 1; + }else{ + zOut[j++] = c; + } + break; + } + } + i++; } zOut[j] = 0; return (char*)zOut; } +/* Return true if the text string z[] contains characters that need +** unistr() escaping. +*/ +static int needUnistr(const unsigned char *z){ + unsigned char c; + if( z==0 ) return 0; + while( (c = *z)>0x1f || c=='\t' || c=='\n' || (c=='\r' && z[1]=='\n') ){ z++; } + return c!=0; +} + /* Extract the value of the i-th current column for pStmt as an SQL literal ** value. Memory is obtained from sqlite3_malloc64() and must be freed by ** the caller. @@ -3728,7 +4114,8 @@ static char *quoted_column(sqlite3_stmt *pStmt, int i){ return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i)); } case SQLITE_TEXT: { - return sqlite3_mprintf("%Q",sqlite3_column_text(pStmt,i)); + const unsigned char *zText = sqlite3_column_text(pStmt,i); + return sqlite3_mprintf(needUnistr(zText)?"%#Q":"%Q",zText); } case SQLITE_BLOB: { int j; @@ -3820,7 +4207,7 @@ static void exec_prepared_stmt_columnar( if( wx<0 ) wx = -wx; uz = (const unsigned char*)sqlite3_column_name(pStmt,i); if( uz==0 ) uz = (u8*)""; - azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw); + azData[i] = translateForDisplayAndDup(p, uz, &zNotUsed, wx, bw); } do{ int useNextLine = bNextLine; @@ -3844,6 +4231,7 @@ static void exec_prepared_stmt_columnar( uz = azNextLine[i]; if( uz==0 ) uz = (u8*)zEmpty; }else if( p->cmOpts.bQuote ){ + assert( azQuoted!=0 ); sqlite3_free(azQuoted[i]); azQuoted[i] = quoted_column(pStmt,i); uz = (const unsigned char*)azQuoted[i]; @@ -3852,7 +4240,7 @@ static void exec_prepared_stmt_columnar( if( uz==0 ) uz = (u8*)zShowNull; } azData[nRow*nColumn + i] - = translateForDisplayAndDup(uz, &azNextLine[i], wx, bw); + = translateForDisplayAndDup(p, uz, &azNextLine[i], wx, bw); if( azNextLine[i] ){ bNextLine = 1; abRowDiv[nRow-1] = 0; @@ -3877,12 +4265,12 @@ static void exec_prepared_stmt_columnar( for(i=0; i<nColumn; i++){ w = p->actualWidth[i]; if( p->colWidth[i]<0 ) w = -w; - utf8_width_print(w, azData[i]); - fputs(i==nColumn-1?"\n":" ", p->out); + utf8_width_print(p->out, w, azData[i]); + sqlite3_fputs(i==nColumn-1?"\n":" ", p->out); } for(i=0; i<nColumn; i++){ - print_dashes(p->actualWidth[i]); - fputs(i==nColumn-1?"\n":" ", p->out); + print_dashes(p->out, p->actualWidth[i]); + sqlite3_fputs(i==nColumn-1?"\n":" ", p->out); } } break; @@ -3891,12 +4279,13 @@ static void exec_prepared_stmt_columnar( colSep = " | "; rowSep = " |\n"; print_row_separator(p, nColumn, "+"); - fputs("| ", p->out); + sqlite3_fputs("| ", p->out); for(i=0; i<nColumn; i++){ w = p->actualWidth[i]; n = strlenChar(azData[i]); - oputf("%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - oputz(i==nColumn-1?" |\n":" | "); + sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "", + azData[i], (w-n+1)/2, ""); + sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out); } print_row_separator(p, nColumn, "+"); break; @@ -3904,12 +4293,13 @@ static void exec_prepared_stmt_columnar( case MODE_Markdown: { colSep = " | "; rowSep = " |\n"; - fputs("| ", p->out); + sqlite3_fputs("| ", p->out); for(i=0; i<nColumn; i++){ w = p->actualWidth[i]; n = strlenChar(azData[i]); - oputf("%*s%s%*s", (w-n)/2, "", azData[i], (w-n+1)/2, ""); - oputz(i==nColumn-1?" |\n":" | "); + sqlite3_fprintf(p->out, "%*s%s%*s", (w-n)/2, "", + azData[i], (w-n+1)/2, ""); + sqlite3_fputs(i==nColumn-1?" |\n":" | ", p->out); } print_row_separator(p, nColumn, "|"); break; @@ -3918,11 +4308,11 @@ static void exec_prepared_stmt_columnar( colSep = " " BOX_13 " "; rowSep = " " BOX_13 "\n"; print_box_row_separator(p, nColumn, BOX_23, BOX_234, BOX_34); - oputz(BOX_13 " "); + sqlite3_fputs(BOX_13 " ", p->out); for(i=0; i<nColumn; i++){ w = p->actualWidth[i]; n = strlenChar(azData[i]); - oputf("%*s%s%*s%s", + sqlite3_fprintf(p->out, "%*s%s%*s%s", (w-n)/2, "", azData[i], (w-n+1)/2, "", i==nColumn-1?" "BOX_13"\n":" "BOX_13" "); } @@ -3932,28 +4322,28 @@ static void exec_prepared_stmt_columnar( } for(i=nColumn, j=0; i<nTotal; i++, j++){ if( j==0 && p->cMode!=MODE_Column ){ - oputz(p->cMode==MODE_Box?BOX_13" ":"| "); + sqlite3_fputs(p->cMode==MODE_Box?BOX_13" ":"| ", p->out); } z = azData[i]; if( z==0 ) z = p->nullValue; w = p->actualWidth[j]; if( p->colWidth[j]<0 ) w = -w; - utf8_width_print(w, z); + utf8_width_print(p->out, w, z); if( j==nColumn-1 ){ - oputz(rowSep); + sqlite3_fputs(rowSep, p->out); if( bMultiLineRowExists && abRowDiv[i/nColumn-1] && i+1<nTotal ){ if( p->cMode==MODE_Table ){ print_row_separator(p, nColumn, "+"); }else if( p->cMode==MODE_Box ){ print_box_row_separator(p, nColumn, BOX_123, BOX_1234, BOX_134); }else if( p->cMode==MODE_Column ){ - oputz("\n"); + sqlite3_fputs("\n", p->out); } } j = -1; if( seenInterrupt ) goto columnar_end; }else{ - oputz(colSep); + sqlite3_fputs(colSep, p->out); } } if( p->cMode==MODE_Table ){ @@ -3963,7 +4353,7 @@ static void exec_prepared_stmt_columnar( } columnar_end: if( seenInterrupt ){ - oputz("Interrupt\n"); + sqlite3_fputs("Interrupt\n", p->out); } nData = (nRow+1)*nColumn; for(i=0; i<nData; i++){ @@ -4050,7 +4440,9 @@ static void exec_prepared_stmt( } while( SQLITE_ROW == rc ); sqlite3_free(pData); if( pArg->cMode==MODE_Json ){ - fputs("]\n", pArg->out); + sqlite3_fputs("]\n", pArg->out); + }else if( pArg->cMode==MODE_Www ){ + sqlite3_fputs("</TABLE>\n<PRE>\n", pArg->out); }else if( pArg->cMode==MODE_Count ){ char zBuf[200]; sqlite3_snprintf(sizeof(zBuf), zBuf, "%llu row%s\n", @@ -4099,6 +4491,7 @@ static int expertFinish( ){ int rc = SQLITE_OK; sqlite3expert *p = pState->expert.pExpert; + FILE *out = pState->out; assert( p ); assert( bCancel || pzErr==0 || *pzErr==0 ); if( bCancel==0 ){ @@ -4111,8 +4504,8 @@ static int expertFinish( if( bVerbose ){ const char *zCand = sqlite3_expert_report(p,0,EXPERT_REPORT_CANDIDATES); - oputz("-- Candidates -----------------------------\n"); - oputf("%s\n", zCand); + sqlite3_fputs("-- Candidates -----------------------------\n", out); + sqlite3_fprintf(out, "%s\n", zCand); } for(i=0; i<nQuery; i++){ const char *zSql = sqlite3_expert_report(p, i, EXPERT_REPORT_SQL); @@ -4120,11 +4513,12 @@ static int expertFinish( const char *zEQP = sqlite3_expert_report(p, i, EXPERT_REPORT_PLAN); if( zIdx==0 ) zIdx = "(no new indexes)\n"; if( bVerbose ){ - oputf("-- Query %d --------------------------------\n",i+1); - oputf("%s\n\n", zSql); + sqlite3_fprintf(out, + "-- Query %d --------------------------------\n" + "%s\n\n" + ,i+1, zSql); } - oputf("%s\n", zIdx); - oputf("%s\n", zEQP); + sqlite3_fprintf(out, "%s\n%s\n", zIdx, zEQP); } } } @@ -4159,18 +4553,18 @@ static int expertDotCommand( } else if( n>=2 && 0==cli_strncmp(z, "-sample", n) ){ if( i==(nArg-1) ){ - eputf("option requires an argument: %s\n", z); + sqlite3_fprintf(stderr, "option requires an argument: %s\n", z); rc = SQLITE_ERROR; }else{ iSample = (int)integerValue(azArg[++i]); if( iSample<0 || iSample>100 ){ - eputf("value out of range: %s\n", azArg[i]); + sqlite3_fprintf(stderr,"value out of range: %s\n", azArg[i]); rc = SQLITE_ERROR; } } } else{ - eputf("unknown option: %s\n", z); + sqlite3_fprintf(stderr,"unknown option: %s\n", z); rc = SQLITE_ERROR; } } @@ -4178,7 +4572,8 @@ static int expertDotCommand( if( rc==SQLITE_OK ){ pState->expert.pExpert = sqlite3_expert_new(pState->db, &zErr); if( pState->expert.pExpert==0 ){ - eputf("sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory"); + sqlite3_fprintf(stderr, + "sqlite3_expert_new: %s\n", zErr ? zErr : "out of memory"); rc = SQLITE_ERROR; }else{ sqlite3_expert_config( @@ -4507,9 +4902,9 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ noSys = (p->shellFlgs & SHFLG_DumpNoSys)!=0; if( cli_strcmp(zTable, "sqlite_sequence")==0 && !noSys ){ - if( !dataOnly ) oputz("DELETE FROM sqlite_sequence;\n"); + /* no-op */ }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 && !noSys ){ - if( !dataOnly ) oputz("ANALYZE sqlite_schema;\n"); + if( !dataOnly ) sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out); }else if( cli_strncmp(zTable, "sqlite_", 7)==0 ){ return 0; }else if( dataOnly ){ @@ -4517,7 +4912,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ }else if( cli_strncmp(zSql, "CREATE VIRTUAL TABLE", 20)==0 ){ char *zIns; if( !p->writableSchema ){ - oputz("PRAGMA writable_schema=ON;\n"); + sqlite3_fputs("PRAGMA writable_schema=ON;\n", p->out); p->writableSchema = 1; } zIns = sqlite3_mprintf( @@ -4525,11 +4920,11 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ "VALUES('table','%q','%q',0,'%q');", zTable, zTable, zSql); shell_check_oom(zIns); - oputf("%s\n", zIns); + sqlite3_fprintf(p->out, "%s\n", zIns); sqlite3_free(zIns); return 0; }else{ - printSchemaLine(zSql, ";\n"); + printSchemaLine(p->out, zSql, ";\n"); } if( cli_strcmp(zType, "table")==0 ){ @@ -4587,7 +4982,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azNotUsed){ p->mode = p->cMode = MODE_Insert; rc = shell_exec(p, sSelect.z, 0); if( (rc&0xff)==SQLITE_CORRUPT ){ - oputz("/****** CORRUPTION ERROR *******/\n"); + sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out); toggleSelectOrder(p->db); shell_exec(p, sSelect.z, 0); toggleSelectOrder(p->db); @@ -4618,9 +5013,9 @@ static int run_schema_dump_query( if( rc==SQLITE_CORRUPT ){ char *zQ2; int len = strlen30(zQuery); - oputz("/****** CORRUPTION ERROR *******/\n"); + sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out); if( zErr ){ - oputf("/****** %s ******/\n", zErr); + sqlite3_fprintf(p->out, "/****** %s ******/\n", zErr); sqlite3_free(zErr); zErr = 0; } @@ -4629,13 +5024,13 @@ static int run_schema_dump_query( sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery); rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr); if( rc ){ - oputf("/****** ERROR: %s ******/\n", zErr); + sqlite3_fprintf(p->out, "/****** ERROR: %s ******/\n", zErr); }else{ rc = SQLITE_CORRUPT; } - sqlite3_free(zErr); free(zQ2); } + sqlite3_free(zErr); return rc; } @@ -4692,14 +5087,13 @@ static const char *(azHelp[]) = { ".clone NEWDB Clone data into NEWDB from the existing database", #endif ".connection [close] [#] Open or close an auxiliary database connection", -#if defined(_WIN32) || defined(WIN32) - ".crnl on|off Translate \\n to \\r\\n. Default ON", -#endif + ".crlf ?on|off? Whether or not to use \\r\\n line endings", ".databases List names and files of attached databases", ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", #if SQLITE_SHELL_HAVE_RECOVER ".dbinfo ?DB? Show status information about the database", #endif + ".dbtotxt Hex dump of the database file", ".dump ?OBJECTS? Render database content as SQL", " Options:", " --data-only Output only INSERT statements", @@ -4769,7 +5163,7 @@ static const char *(azHelp[]) = { #else ".log on|off Turn logging on or off.", #endif - ".mode MODE ?OPTIONS? Set output mode", + ".mode ?MODE? ?OPTIONS? Set output mode", " MODE is one of:", " ascii Columns/rows delimited by 0x1F and 0x1E", " box Tables using unicode box-drawing characters", @@ -4787,6 +5181,7 @@ static const char *(azHelp[]) = { " tabs Tab-separated values", " tcl TCL list elements", " OPTIONS: (for columnar modes or insert mode):", + " --escape T ctrl-char escape; T is one of: symbol, ascii, off", " --wrap N Wrap output lines to no longer than N characters", " --wordwrap B Wrap or not at word boundaries per B (on/off)", " --ww Shorthand for \"--wordwrap 1\"", @@ -4800,9 +5195,11 @@ static const char *(azHelp[]) = { #ifndef SQLITE_SHELL_FIDDLE ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE", " If FILE begins with '|' then open as a pipe", - " --bom Put a UTF8 byte-order mark at the beginning", - " -e Send output to the system text editor", - " -x Send output as CSV to a spreadsheet (same as \".excel\")", + " --bom Put a UTF8 byte-order mark at the beginning", + " -e Send output to the system text editor", + " --plain Use text/plain output instead of HTML for -w option", + " -w Send output as HTML to a web browser (same as \".www\")", + " -x Send output as CSV to a spreadsheet (same as \".excel\")", /* Note that .open is (partially) available in WASM builds but is ** currently only intended to be used by the fiddle tool, not ** end users, so is "undocumented." */ @@ -4822,9 +5219,12 @@ static const char *(azHelp[]) = { #ifndef SQLITE_SHELL_FIDDLE ".output ?FILE? Send output to FILE or stdout if FILE is omitted", " If FILE begins with '|' then open it as a pipe.", + " If FILE is 'off' then output is disabled.", " Options:", " --bom Prefix output with a UTF8 byte-order mark", " -e Send output to the system text editor", + " --plain Use text/plain for -w option", + " -w Send output to a web browser", " -x Send output as CSV to a spreadsheet", #endif ".parameter CMD ... Manage SQL parameter bindings", @@ -4938,106 +5338,111 @@ static const char *(azHelp[]) = { ".vfsname ?AUX? Print the name of the VFS stack", ".width NUM1 NUM2 ... Set minimum column widths for columnar output", " Negative values right-justify", +#ifndef SQLITE_SHELL_FIDDLE + ".www Display output of the next command in web browser", + " --plain Show results as text/plain, not as HTML", +#endif }; /* -** Output help text. +** Output help text for commands that match zPattern. +** +** * If zPattern is NULL, then show all documented commands, but +** only give a one-line summary of each. +** +** * If zPattern is "-a" or "-all" or "--all" then show all help text +** for all commands except undocumented commands. ** -** zPattern describes the set of commands for which help text is provided. -** If zPattern is NULL, then show all commands, but only give a one-line -** description of each. +** * If zPattern is "0" then show all help for undocumented commands. +** Undocumented commands begin with "," instead of "." in the azHelp[] +** array. ** -** Return the number of matches. +** * If zPattern is a prefix for one or more documented commands, then +** show help for those commands. If only a single command matches the +** prefix, show the full text of the help. If multiple commands match, +** Only show just the first line of each. +** +** * Otherwise, show the complete text of any documented command for which +** zPattern is a LIKE match for any text within that command help +** text. +** +** Return the number commands that match zPattern. */ static int showHelp(FILE *out, const char *zPattern){ int i = 0; int j = 0; int n = 0; char *zPat; - if( zPattern==0 - || zPattern[0]=='0' - || cli_strcmp(zPattern,"-a")==0 - || cli_strcmp(zPattern,"-all")==0 - || cli_strcmp(zPattern,"--all")==0 + if( zPattern==0 ){ + /* Show just the first line for all help topics */ + zPattern = "[a-z]"; + }else if( cli_strcmp(zPattern,"-a")==0 + || cli_strcmp(zPattern,"-all")==0 + || cli_strcmp(zPattern,"--all")==0 ){ - enum HelpWanted { HW_NoCull = 0, HW_SummaryOnly = 1, HW_Undoc = 2 }; - enum HelpHave { HH_Undoc = 2, HH_Summary = 1, HH_More = 0 }; - /* Show all or most commands - ** *zPattern==0 => summary of documented commands only - ** *zPattern=='0' => whole help for undocumented commands - ** Otherwise => whole help for documented commands - */ - enum HelpWanted hw = HW_SummaryOnly; - enum HelpHave hh = HH_More; - if( zPattern!=0 ){ - hw = (*zPattern=='0')? HW_NoCull|HW_Undoc : HW_NoCull; - } - for(i=0; i<ArraySize(azHelp); i++){ - switch( azHelp[i][0] ){ - case ',': - hh = HH_Summary|HH_Undoc; - break; - case '.': - hh = HH_Summary; - break; - default: - hh &= ~HH_Summary; - break; - } - if( ((hw^hh)&HH_Undoc)==0 ){ - if( (hh&HH_Summary)!=0 ){ - sputf(out, ".%s\n", azHelp[i]+1); - ++n; - }else if( (hw&HW_SummaryOnly)==0 ){ - sputf(out, "%s\n", azHelp[i]); - } - } - } - }else{ - /* Seek documented commands for which zPattern is an exact prefix */ - zPat = sqlite3_mprintf(".%s*", zPattern); - shell_check_oom(zPat); + /* Show everything except undocumented commands */ + zPattern = "."; + }else if( cli_strcmp(zPattern,"0")==0 ){ + /* Show complete help text of undocumented commands */ + int show = 0; for(i=0; i<ArraySize(azHelp); i++){ - if( sqlite3_strglob(zPat, azHelp[i])==0 ){ - sputf(out, "%s\n", azHelp[i]); - j = i+1; + if( azHelp[i][0]=='.' ){ + show = 0; + }else if( azHelp[i][0]==',' ){ + show = 1; + sqlite3_fprintf(out, ".%s\n", &azHelp[i][1]); n++; + }else if( show ){ + sqlite3_fprintf(out, "%s\n", azHelp[i]); } } - sqlite3_free(zPat); - if( n ){ - if( n==1 ){ - /* when zPattern is a prefix of exactly one command, then include - ** the details of that command, which should begin at offset j */ - while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){ - sputf(out, "%s\n", azHelp[j]); - j++; - } - } - return n; + return n; + } + + /* Seek documented commands for which zPattern is an exact prefix */ + zPat = sqlite3_mprintf(".%s*", zPattern); + shell_check_oom(zPat); + for(i=0; i<ArraySize(azHelp); i++){ + if( sqlite3_strglob(zPat, azHelp[i])==0 ){ + sqlite3_fprintf(out, "%s\n", azHelp[i]); + j = i+1; + n++; } - /* Look for documented commands that contain zPattern anywhere. - ** Show complete text of all documented commands that match. */ - zPat = sqlite3_mprintf("%%%s%%", zPattern); - shell_check_oom(zPat); - for(i=0; i<ArraySize(azHelp); i++){ - if( azHelp[i][0]==',' ){ - while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i; - continue; + } + sqlite3_free(zPat); + if( n ){ + if( n==1 ){ + /* when zPattern is a prefix of exactly one command, then include + ** the details of that command, which should begin at offset j */ + while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){ + sqlite3_fprintf(out, "%s\n", azHelp[j]); + j++; } - if( azHelp[i][0]=='.' ) j = i; - if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){ - sputf(out, "%s\n", azHelp[j]); - while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){ - j++; - sputf(out, "%s\n", azHelp[j]); - } - i = j; - n++; + } + return n; + } + + /* Look for documented commands that contain zPattern anywhere. + ** Show complete text of all documented commands that match. */ + zPat = sqlite3_mprintf("%%%s%%", zPattern); + shell_check_oom(zPat); + for(i=0; i<ArraySize(azHelp); i++){ + if( azHelp[i][0]==',' ){ + while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i; + continue; + } + if( azHelp[i][0]=='.' ) j = i; + if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){ + sqlite3_fprintf(out, "%s\n", azHelp[j]); + while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){ + j++; + sqlite3_fprintf(out, "%s\n", azHelp[j]); } + i = j; + n++; } - sqlite3_free(zPat); } + sqlite3_free(zPat); return n; } @@ -5060,7 +5465,7 @@ static int process_input(ShellState *p); ** is undefined in this case. */ static char *readFile(const char *zName, int *pnByte){ - FILE *in = fopen(zName, "rb"); + FILE *in = sqlite3_fopen(zName, "rb"); long nIn; size_t nRead; char *pBuf; @@ -5068,7 +5473,7 @@ static char *readFile(const char *zName, int *pnByte){ if( in==0 ) return 0; rc = fseek(in, 0, SEEK_END); if( rc!=0 ){ - eputf("Error: '%s' not seekable\n", zName); + sqlite3_fprintf(stderr,"Error: '%s' not seekable\n", zName); fclose(in); return 0; } @@ -5076,7 +5481,7 @@ static char *readFile(const char *zName, int *pnByte){ rewind(in); pBuf = sqlite3_malloc64( nIn+1 ); if( pBuf==0 ){ - eputz("Error: out of memory\n"); + sqlite3_fputs("Error: out of memory\n", stderr); fclose(in); return 0; } @@ -5084,7 +5489,7 @@ static char *readFile(const char *zName, int *pnByte){ fclose(in); if( nRead!=1 ){ sqlite3_free(pBuf); - eputf("Error: cannot read '%s'\n", zName); + sqlite3_fprintf(stderr,"Error: cannot read '%s'\n", zName); return 0; } pBuf[nIn] = 0; @@ -5150,7 +5555,7 @@ static int session_filter(void *pCtx, const char *zTab){ ** the type cannot be determined from content. */ int deduceDatabaseType(const char *zName, int dfltZip){ - FILE *f = fopen(zName, "rb"); + FILE *f = sqlite3_fopen(zName, "rb"); size_t n; int rc = SHELL_OPEN_UNSPEC; char zBuf[100]; @@ -5203,9 +5608,9 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ unsigned int x[16]; char zLine[1000]; if( zDbFilename ){ - in = fopen(zDbFilename, "r"); + in = sqlite3_fopen(zDbFilename, "r"); if( in==0 ){ - eputf("cannot open \"%s\" for reading\n", zDbFilename); + sqlite3_fprintf(stderr,"cannot open \"%s\" for reading\n", zDbFilename); return 0; } nLine = 0; @@ -5216,7 +5621,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ } *pnData = 0; nLine++; - if( fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error; + if( sqlite3_fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error; rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz); if( rc!=2 ) goto readHexDb_error; if( n<0 ) goto readHexDb_error; @@ -5226,10 +5631,10 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){ shell_check_oom(a); memset(a, 0, n); if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){ - eputz("invalid pagesize\n"); + sqlite3_fputs("invalid pagesize\n", stderr); goto readHexDb_error; } - for(nLine++; fgets(zLine, sizeof(zLine), in)!=0; nLine++){ + for(nLine++; sqlite3_fgets(zLine, sizeof(zLine), in)!=0; nLine++){ rc = sscanf(zLine, "| page %d offset %d", &j, &k); if( rc==2 ){ iOffset = k; @@ -5261,14 +5666,14 @@ readHexDb_error: if( in!=p->in ){ fclose(in); }else{ - while( fgets(zLine, sizeof(zLine), p->in)!=0 ){ + while( sqlite3_fgets(zLine, sizeof(zLine), p->in)!=0 ){ nLine++; if(cli_strncmp(zLine, "| end ", 6)==0 ) break; } p->lineno = nLine; } sqlite3_free(a); - eputf("Error on line %d of --hexdb input\n", nLine); + sqlite3_fprintf(stderr,"Error on line %d of --hexdb input\n", nLine); return 0; } #endif /* SQLITE_OMIT_DESERIALIZE */ @@ -5287,6 +5692,39 @@ static void shellUSleepFunc( sqlite3_result_int(context, sleep); } +/* +** SQL function: shell_module_schema(X) +** +** Return a fake schema for the table-valued function or eponymous virtual +** table X. +*/ +static void shellModuleSchema( + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +){ + const char *zName; + char *zFake; + ShellState *p = (ShellState*)sqlite3_user_data(pCtx); + FILE *pSavedLog = p->pLog; + UNUSED_PARAMETER(nVal); + zName = (const char*)sqlite3_value_text(apVal[0]); + + /* Temporarily disable the ".log" when calling shellFakeSchema() because + ** shellFakeSchema() might generate failures for some ephemeral virtual + ** tables due to missing arguments. Example: fts4aux. + ** https://sqlite.org/forum/forumpost/42fe6520b803be51 */ + p->pLog = 0; + zFake = zName? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0; + p->pLog = pSavedLog; + + if( zFake ){ + sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake), + -1, sqlite3_free); + free(zFake); + } +} + /* Flags for open_db(). ** ** The default behavior of open_db() is to exit(1) if the database fails to @@ -5343,7 +5781,7 @@ static void open_db(ShellState *p, int openFlags){ } } if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ - eputf("Error: unable to open database \"%s\": %s\n", + sqlite3_fprintf(stderr,"Error: unable to open database \"%s\": %s\n", zDbFilename, sqlite3_errmsg(p->db)); if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){ exit(1); @@ -5351,10 +5789,12 @@ static void open_db(ShellState *p, int openFlags){ sqlite3_close(p->db); sqlite3_open(":memory:", &p->db); if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ - eputz("Also: unable to open substitute in-memory database.\n"); + sqlite3_fputs("Also: unable to open substitute in-memory database.\n", + stderr); exit(1); }else{ - eputf("Notice: using substitute in-memory database instead of \"%s\"\n", + sqlite3_fprintf(stderr, + "Notice: using substitute in-memory database instead of \"%s\"\n", zDbFilename); } } @@ -5371,6 +5811,7 @@ static void open_db(ShellState *p, int openFlags){ #ifndef SQLITE_OMIT_LOAD_EXTENSION sqlite3_enable_load_extension(p->db, 1); #endif + sqlite3_sha_init(p->db, 0, 0); sqlite3_shathree_init(p->db, 0, 0); sqlite3_uint_init(p->db, 0, 0); sqlite3_stmtrand_init(p->db, 0, 0); @@ -5427,7 +5868,7 @@ static void open_db(ShellState *p, int openFlags){ shellDtostr, 0, 0); sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0, shellAddSchemaName, 0, 0); - sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0, + sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, p, shellModuleSchema, 0, 0); sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p, shellPutsFunc, 0, 0); @@ -5465,7 +5906,7 @@ static void open_db(ShellState *p, int openFlags){ SQLITE_DESERIALIZE_RESIZEABLE | SQLITE_DESERIALIZE_FREEONCLOSE); if( rc ){ - eputf("Error: sqlite3_deserialize() returns %d\n", rc); + sqlite3_fprintf(stderr,"Error: sqlite3_deserialize() returns %d\n", rc); } if( p->szMax>0 ){ sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax); @@ -5489,11 +5930,13 @@ static void open_db(ShellState *p, int openFlags){ void close_db(sqlite3 *db){ int rc = sqlite3_close(db); if( rc ){ - eputf("Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db)); + sqlite3_fprintf(stderr, + "Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db)); } } -#if HAVE_READLINE || HAVE_EDITLINE +#if (HAVE_READLINE || HAVE_EDITLINE) \ + && !defined(SQLITE_OMIT_READLINE_COMPLETION) /* ** Readline completion callbacks */ @@ -5531,18 +5974,25 @@ static char **readline_completion(const char *zText, int iStart, int iEnd){ ** Linenoise completion callback. Note that the 3rd argument is from ** the "msteveb" version of linenoise, not the "antirez" version. */ -static void linenoise_completion(const char *zLine, linenoiseCompletions *lc, - void *pUserData){ +static void linenoise_completion( + const char *zLine, + linenoiseCompletions *lc +#if HAVE_LINENOISE==2 + ,void *pUserData +#endif +){ i64 nLine = strlen(zLine); i64 i, iStart; sqlite3_stmt *pStmt = 0; char *zSql; char zBuf[1000]; +#if HAVE_LINENOISE==2 UNUSED_PARAMETER(pUserData); +#endif if( nLine>(i64)sizeof(zBuf)-30 ) return; if( zLine[0]=='.' || zLine[0]=='#') return; - for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){} + for(i=nLine-1; i>=0 && (IsAlnum(zLine[i]) || zLine[i]=='_'); i--){} if( i==nLine-1 ) return; iStart = i+1; memcpy(zBuf, zLine, iStart); @@ -5653,7 +6103,8 @@ static int booleanValue(const char *zArg){ if( sqlite3_stricmp(zArg, "off")==0 || sqlite3_stricmp(zArg,"no")==0 ){ return 0; } - eputf("ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg); + sqlite3_fprintf(stderr, + "ERROR: Not a boolean value: \"%s\". Assuming \"no\".\n", zArg); return 0; } @@ -5680,7 +6131,7 @@ static void output_file_close(FILE *f){ ** recognized and do the right thing. NULL is returned if the output ** filename is "off". */ -static FILE *output_file_open(const char *zFile, int bTextMode){ +static FILE *output_file_open(const char *zFile){ FILE *f; if( cli_strcmp(zFile,"stdout")==0 ){ f = stdout; @@ -5689,9 +6140,9 @@ static FILE *output_file_open(const char *zFile, int bTextMode){ }else if( cli_strcmp(zFile, "off")==0 ){ f = 0; }else{ - f = fopen(zFile, bTextMode ? "w" : "wb"); + f = sqlite3_fopen(zFile, "w"); if( f==0 ){ - eputf("Error: cannot open \"%s\"\n", zFile); + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile); } } return f; @@ -5744,12 +6195,13 @@ static int sql_trace_callback( switch( mType ){ case SQLITE_TRACE_ROW: case SQLITE_TRACE_STMT: { - sputf(p->traceOut, "%.*s;\n", (int)nSql, zSql); + sqlite3_fprintf(p->traceOut, "%.*s;\n", (int)nSql, zSql); break; } case SQLITE_TRACE_PROFILE: { sqlite3_int64 nNanosec = pX ? *(sqlite3_int64*)pX : 0; - sputf(p->traceOut, "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec); + sqlite3_fprintf(p->traceOut, + "%.*s; -- %lld ns\n", (int)nSql, zSql, nNanosec); break; } } @@ -5856,10 +6308,11 @@ static char *SQLITE_CDECL csv_read_one_field(ImportCtx *p){ break; } if( pc==cQuote && c!='\r' ){ - eputf("%s:%d: unescaped %c character\n", p->zFile, p->nLine, cQuote); + sqlite3_fprintf(stderr,"%s:%d: unescaped %c character\n", + p->zFile, p->nLine, cQuote); } if( c==EOF ){ - eputf("%s:%d: unterminated %c-quoted field\n", + sqlite3_fprintf(stderr,"%s:%d: unterminated %c-quoted field\n", p->zFile, startLine, cQuote); p->cTerm = c; break; @@ -5958,7 +6411,7 @@ static void tryToCloneData( shell_check_oom(zQuery); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ - eputf("Error %d: %s on [%s]\n", + sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n", sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_data_xfer; } @@ -5975,7 +6428,7 @@ static void tryToCloneData( memcpy(zInsert+i, ");", 3); rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0); if( rc ){ - eputf("Error %d: %s on [%s]\n", + sqlite3_fprintf(stderr,"Error %d: %s on [%s]\n", sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zInsert); goto end_data_xfer; } @@ -6011,7 +6464,7 @@ static void tryToCloneData( } /* End for */ rc = sqlite3_step(pInsert); if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){ - eputf("Error %d: %s\n", + sqlite3_fprintf(stderr,"Error %d: %s\n", sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb)); } sqlite3_reset(pInsert); @@ -6029,7 +6482,7 @@ static void tryToCloneData( shell_check_oom(zQuery); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ - eputf("Warning: cannot step \"%s\" backwards", zTable); + sqlite3_fprintf(stderr,"Warning: cannot step \"%s\" backwards", zTable); break; } } /* End for(k=0...) */ @@ -6066,7 +6519,8 @@ static void tryToCloneSchema( shell_check_oom(zQuery); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ - eputf("Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), + sqlite3_fprintf(stderr, + "Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_schema_xfer; } @@ -6075,10 +6529,10 @@ static void tryToCloneSchema( zSql = sqlite3_column_text(pQuery, 1); if( zName==0 || zSql==0 ) continue; if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ){ - sputf(stdout, "%s... ", zName); fflush(stdout); + sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout); sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); if( zErrMsg ){ - eputf("Error: %s\nSQL: [%s]\n", zErrMsg, zSql); + sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); sqlite3_free(zErrMsg); zErrMsg = 0; } @@ -6096,7 +6550,7 @@ static void tryToCloneSchema( shell_check_oom(zQuery); rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); if( rc ){ - eputf("Error: (%d) %s on [%s]\n", + sqlite3_fprintf(stderr,"Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); goto end_schema_xfer; } @@ -6105,10 +6559,10 @@ static void tryToCloneSchema( zSql = sqlite3_column_text(pQuery, 1); if( zName==0 || zSql==0 ) continue; if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue; - sputf(stdout, "%s... ", zName); fflush(stdout); + sqlite3_fprintf(stdout, "%s... ", zName); fflush(stdout); sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); if( zErrMsg ){ - eputf("Error: %s\nSQL: [%s]\n", zErrMsg, zSql); + sqlite3_fprintf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); sqlite3_free(zErrMsg); zErrMsg = 0; } @@ -6132,12 +6586,13 @@ static void tryToClone(ShellState *p, const char *zNewDb){ int rc; sqlite3 *newDb = 0; if( access(zNewDb,0)==0 ){ - eputf("File \"%s\" already exists.\n", zNewDb); + sqlite3_fprintf(stderr,"File \"%s\" already exists.\n", zNewDb); return; } rc = sqlite3_open(zNewDb, &newDb); if( rc ){ - eputf("Cannot create output database: %s\n", sqlite3_errmsg(newDb)); + sqlite3_fprintf(stderr, + "Cannot create output database: %s\n", sqlite3_errmsg(newDb)); }else{ sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); @@ -6154,10 +6609,18 @@ static void tryToClone(ShellState *p, const char *zNewDb){ ** Change the output stream (file or pipe or console) to something else. */ static void output_redir(ShellState *p, FILE *pfNew){ - if( p->out != stdout ) eputz("Output already redirected.\n"); - else{ + if( p->out != stdout ){ + sqlite3_fputs("Output already redirected.\n", stderr); + }else{ p->out = pfNew; - setOutputStream(pfNew); + setCrlfMode(p); + if( p->mode==MODE_Www ){ + sqlite3_fputs( + "<!DOCTYPE html>\n" + "<HTML><BODY><PRE>\n", + p->out + ); + } } } @@ -6174,6 +6637,9 @@ static void output_reset(ShellState *p){ pclose(p->out); #endif }else{ + if( p->mode==MODE_Www ){ + sqlite3_fputs("</PRE></BODY></HTML>\n", p->out); + } output_file_close(p->out); #ifndef SQLITE_NOHAVE_SYSTEM if( p->doXdgOpen ){ @@ -6188,7 +6654,7 @@ static void output_reset(ShellState *p){ char *zCmd; zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile); if( system(zCmd) ){ - eputf("Failed: [%s]\n", zCmd); + sqlite3_fprintf(stderr,"Failed: [%s]\n", zCmd); }else{ /* Give the start/open/xdg-open command some time to get ** going before we continue, and potential delete the @@ -6203,7 +6669,7 @@ static void output_reset(ShellState *p){ } p->outfile[0] = 0; p->out = stdout; - setOutputStream(stdout); + setCrlfMode(p); } #else # define output_redir(SS,pfO) @@ -6213,14 +6679,20 @@ static void output_reset(ShellState *p){ /* ** Run an SQL command and return the single integer result. */ -static int db_int(sqlite3 *db, const char *zSql){ +static int db_int(sqlite3 *db, const char *zSql, ...){ sqlite3_stmt *pStmt; int res = 0; - sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + char *z; + va_list ap; + va_start(ap, zSql); + z = sqlite3_vmprintf(zSql, ap); + va_end(ap); + sqlite3_prepare_v2(db, z, -1, &pStmt, 0); if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ res = sqlite3_column_int(pStmt,0); } sqlite3_finalize(pStmt); + sqlite3_free(z); return res; } @@ -6279,7 +6751,7 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1", -1, &pStmt, 0); if( rc ){ - eputf("error: %s\n", sqlite3_errmsg(p->db)); + sqlite3_fprintf(stderr,"error: %s\n", sqlite3_errmsg(p->db)); sqlite3_finalize(pStmt); return 1; } @@ -6292,28 +6764,28 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ memcpy(aHdr, pb, 100); sqlite3_finalize(pStmt); }else{ - eputz("unable to read database header\n"); + sqlite3_fputs("unable to read database header\n", stderr); sqlite3_finalize(pStmt); return 1; } i = get2byteInt(aHdr+16); if( i==1 ) i = 65536; - oputf("%-20s %d\n", "database page size:", i); - oputf("%-20s %d\n", "write format:", aHdr[18]); - oputf("%-20s %d\n", "read format:", aHdr[19]); - oputf("%-20s %d\n", "reserved bytes:", aHdr[20]); + sqlite3_fprintf(p->out, "%-20s %d\n", "database page size:", i); + sqlite3_fprintf(p->out, "%-20s %d\n", "write format:", aHdr[18]); + sqlite3_fprintf(p->out, "%-20s %d\n", "read format:", aHdr[19]); + sqlite3_fprintf(p->out, "%-20s %d\n", "reserved bytes:", aHdr[20]); for(i=0; i<ArraySize(aField); i++){ int ofst = aField[i].ofst; unsigned int val = get4byteInt(aHdr + ofst); - oputf("%-20s %u", aField[i].zName, val); + sqlite3_fprintf(p->out, "%-20s %u", aField[i].zName, val); switch( ofst ){ case 56: { - if( val==1 ) oputz(" (utf8)"); - if( val==2 ) oputz(" (utf16le)"); - if( val==3 ) oputz(" (utf16be)"); + if( val==1 ) sqlite3_fputs(" (utf8)", p->out); + if( val==2 ) sqlite3_fputs(" (utf16le)", p->out); + if( val==3 ) sqlite3_fputs(" (utf16be)", p->out); } } - oputz("\n"); + sqlite3_fputs("\n", p->out); } if( zDb==0 ){ zSchemaTab = sqlite3_mprintf("main.sqlite_schema"); @@ -6323,24 +6795,120 @@ static int shell_dbinfo_command(ShellState *p, int nArg, char **azArg){ zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb); } for(i=0; i<ArraySize(aQuery); i++){ - char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab); - int val = db_int(p->db, zSql); - sqlite3_free(zSql); - oputf("%-20s %d\n", aQuery[i].zName, val); + int val = db_int(p->db, aQuery[i].zSql, zSchemaTab); + sqlite3_fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val); } sqlite3_free(zSchemaTab); sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion); - oputf("%-20s %u\n", "data version", iDataVersion); + sqlite3_fprintf(p->out, "%-20s %u\n", "data version", iDataVersion); return 0; } #endif /* SQLITE_SHELL_HAVE_RECOVER */ /* +** Implementation of the ".dbtotxt" command. +** +** Return 1 on error, 2 to exit, and 0 otherwise. +*/ +static int shell_dbtotxt_command(ShellState *p, int nArg, char **azArg){ + sqlite3_stmt *pStmt = 0; + sqlite3_int64 nPage = 0; + int pgSz = 0; + const char *zTail; + char *zName = 0; + int rc, i, j; + unsigned char bShow[256]; /* Characters ok to display */ + + UNUSED_PARAMETER(nArg); + UNUSED_PARAMETER(azArg); + memset(bShow, '.', sizeof(bShow)); + for(i=' '; i<='~'; i++){ + if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i; + } + rc = sqlite3_prepare_v2(p->db, "PRAGMA page_size", -1, &pStmt, 0); + if( rc ) goto dbtotxt_error; + rc = 0; + if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error; + pgSz = sqlite3_column_int(pStmt, 0); + sqlite3_finalize(pStmt); + pStmt = 0; + if( pgSz<512 || pgSz>65536 || (pgSz&(pgSz-1))!=0 ) goto dbtotxt_error; + rc = sqlite3_prepare_v2(p->db, "PRAGMA page_count", -1, &pStmt, 0); + if( rc ) goto dbtotxt_error; + rc = 0; + if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error; + nPage = sqlite3_column_int64(pStmt, 0); + sqlite3_finalize(pStmt); + pStmt = 0; + if( nPage<1 ) goto dbtotxt_error; + rc = sqlite3_prepare_v2(p->db, "PRAGMA databases", -1, &pStmt, 0); + if( rc ) goto dbtotxt_error; + if( sqlite3_step(pStmt)!=SQLITE_ROW ){ + zTail = "unk.db"; + }else{ + const char *zFilename = (const char*)sqlite3_column_text(pStmt, 2); + if( zFilename==0 || zFilename[0]==0 ) zFilename = "unk.db"; + zTail = strrchr(zFilename, '/'); +#if defined(_WIN32) + if( zTail==0 ) zTail = strrchr(zFilename, '\\'); +#endif + } + zName = strdup(zTail); + shell_check_oom(zName); + sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n", + nPage*pgSz, pgSz, zName); + sqlite3_finalize(pStmt); + pStmt = 0; + rc = sqlite3_prepare_v2(p->db, + "SELECT pgno, data FROM sqlite_dbpage ORDER BY pgno", -1, &pStmt, 0); + if( rc ) goto dbtotxt_error; + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + sqlite3_int64 pgno = sqlite3_column_int64(pStmt, 0); + const u8 *aData = sqlite3_column_blob(pStmt, 1); + int seenPageLabel = 0; + for(i=0; i<pgSz; i+=16){ + const u8 *aLine = aData+i; + for(j=0; j<16 && aLine[j]==0; j++){} + if( j==16 ) continue; + if( !seenPageLabel ){ + sqlite3_fprintf(p->out, "| page %lld offset %lld\n",pgno,(pgno-1)*pgSz); + seenPageLabel = 1; + } + sqlite3_fprintf(p->out, "| %5d:", i); + for(j=0; j<16; j++) sqlite3_fprintf(p->out, " %02x", aLine[j]); + sqlite3_fprintf(p->out, " "); + for(j=0; j<16; j++){ + unsigned char c = (unsigned char)aLine[j]; + sqlite3_fprintf(p->out, "%c", bShow[c]); + } + sqlite3_fprintf(p->out, "\n"); + } + } + sqlite3_finalize(pStmt); + sqlite3_fprintf(p->out, "| end %s\n", zName); + free(zName); + return 0; + +dbtotxt_error: + if( rc ){ + sqlite3_fprintf(stderr, "ERROR: %s\n", sqlite3_errmsg(p->db)); + } + sqlite3_finalize(pStmt); + free(zName); + return 1; +} + +/* +** Print the given string as an error message. +*/ +static void shellEmitError(const char *zErr){ + sqlite3_fprintf(stderr,"Error: %s\n", zErr); +} +/* ** Print the current sqlite3_errmsg() value to stderr and return 1. */ static int shellDatabaseError(sqlite3 *db){ - const char *zErr = sqlite3_errmsg(db); - eputf("Error: %s\n", zErr); + shellEmitError(sqlite3_errmsg(db)); return 1; } @@ -6581,6 +7149,7 @@ static int lintFkeyIndexes( const char *zIndent = ""; /* How much to indent CREATE INDEX by */ int rc; /* Return code */ sqlite3_stmt *pSql = 0; /* Compiled version of SQL statement below */ + FILE *out = pState->out; /* Send output here */ /* ** This SELECT statement returns one row for each foreign key constraint @@ -6656,7 +7225,8 @@ static int lintFkeyIndexes( zIndent = " "; } else{ - eputf("Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1]); + sqlite3_fprintf(stderr, + "Usage: %s %s ?-verbose? ?-groupbyparent?\n", azArg[0], azArg[1]); return SQLITE_ERROR; } } @@ -6700,22 +7270,23 @@ static int lintFkeyIndexes( if( rc!=SQLITE_OK ) break; if( res<0 ){ - eputz("Error: internal error"); + sqlite3_fputs("Error: internal error", stderr); break; }else{ if( bGroupByParent && (bVerbose || res==0) && (zPrev==0 || sqlite3_stricmp(zParent, zPrev)) ){ - oputf("-- Parent table %s\n", zParent); + sqlite3_fprintf(out, "-- Parent table %s\n", zParent); sqlite3_free(zPrev); zPrev = sqlite3_mprintf("%s", zParent); } if( res==0 ){ - oputf("%s%s --> %s\n", zIndent, zCI, zTarget); + sqlite3_fprintf(out, "%s%s --> %s\n", zIndent, zCI, zTarget); }else if( bVerbose ){ - oputf("%s/* no extra indexes required for %s -> %s */\n", + sqlite3_fprintf(out, + "%s/* no extra indexes required for %s -> %s */\n", zIndent, zFrom, zTarget ); } @@ -6724,16 +7295,16 @@ static int lintFkeyIndexes( sqlite3_free(zPrev); if( rc!=SQLITE_OK ){ - eputf("%s\n", sqlite3_errmsg(db)); + sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db)); } rc2 = sqlite3_finalize(pSql); if( rc==SQLITE_OK && rc2!=SQLITE_OK ){ rc = rc2; - eputf("%s\n", sqlite3_errmsg(db)); + sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db)); } }else{ - eputf("%s\n", sqlite3_errmsg(db)); + sqlite3_fprintf(stderr,"%s\n", sqlite3_errmsg(db)); } return rc; @@ -6753,9 +7324,9 @@ static int lintDotCommand( return lintFkeyIndexes(pState, azArg, nArg); usage: - eputf("Usage %s sub-command ?switches...?\n", azArg[0]); - eputz("Where sub-commands are:\n"); - eputz(" fkey-indexes\n"); + sqlite3_fprintf(stderr,"Usage %s sub-command ?switches...?\n", azArg[0]); + sqlite3_fprintf(stderr, "Where sub-commands are:\n"); + sqlite3_fprintf(stderr, " fkey-indexes\n"); return SQLITE_ERROR; } @@ -6769,7 +7340,8 @@ static void shellPrepare( if( *pRc==SQLITE_OK ){ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0); if( rc!=SQLITE_OK ){ - eputf("sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db)); + sqlite3_fprintf(stderr, + "sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db)); *pRc = rc; } } @@ -6813,7 +7385,7 @@ static void shellFinalize( int rc = sqlite3_finalize(pStmt); if( *pRc==SQLITE_OK ){ if( rc!=SQLITE_OK ){ - eputf("SQL error: %s\n", sqlite3_errmsg(db)); + sqlite3_fprintf(stderr,"SQL error: %s\n", sqlite3_errmsg(db)); } *pRc = rc; } @@ -6835,7 +7407,7 @@ void shellReset( if( *pRc==SQLITE_OK ){ if( rc!=SQLITE_OK ){ sqlite3 *db = sqlite3_db_handle(pStmt); - eputf("SQL error: %s\n", sqlite3_errmsg(db)); + sqlite3_fprintf(stderr,"SQL error: %s\n", sqlite3_errmsg(db)); } *pRc = rc; } @@ -6864,6 +7436,7 @@ struct ArCommand { const char *zDir; /* --directory argument, or NULL */ char **azArg; /* Array of command arguments */ ShellState *p; /* Shell state */ + FILE *out; /* Output to this stream */ sqlite3 *db; /* Database containing the archive */ }; @@ -6885,11 +7458,11 @@ static int arErrorMsg(ArCommand *pAr, const char *zFmt, ...){ va_start(ap, zFmt); z = sqlite3_vmprintf(zFmt, ap); va_end(ap); - eputf("Error: %s\n", z); + shellEmitError(z); if( pAr->fromCmdLine ){ - eputz("Use \"-A\" for more help\n"); + sqlite3_fputs("Use \"-A\" for more help\n", stderr); }else{ - eputz("Use \".archive --help\" for more help\n"); + sqlite3_fputs("Use \".archive --help\" for more help\n", stderr); } sqlite3_free(z); return SQLITE_ERROR; @@ -6942,7 +7515,7 @@ static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){ break; case AR_SWITCH_APPEND: pAr->bAppend = 1; - deliberate_fall_through; + deliberate_fall_through; /* FALLTHRU */ case AR_SWITCH_FILE: pAr->zFile = zArg; break; @@ -6989,7 +7562,7 @@ static int arParseCommand( struct ArSwitch *pEnd = &aSwitch[nSwitch]; if( nArg<=1 ){ - eputz("Wrong number of arguments. Usage:\n"); + sqlite3_fprintf(stderr, "Wrong number of arguments. Usage:\n"); return arUsage(stderr); }else{ char *z = azArg[1]; @@ -7095,7 +7668,7 @@ static int arParseCommand( } } if( pAr->eCmd==0 ){ - eputz("Required argument missing. Usage:\n"); + sqlite3_fprintf(stderr, "Required argument missing. Usage:\n"); return arUsage(stderr); } return SQLITE_OK; @@ -7138,7 +7711,7 @@ static int arCheckEntries(ArCommand *pAr){ } shellReset(&rc, pTest); if( rc==SQLITE_OK && bOk==0 ){ - eputf("not found in archive: %s\n", z); + sqlite3_fprintf(stderr,"not found in archive: %s\n", z); rc = SQLITE_ERROR; } } @@ -7205,15 +7778,15 @@ static int arListCommand(ArCommand *pAr){ shellPreparePrintf(pAr->db, &rc, &pSql, zSql, azCols[pAr->bVerbose], pAr->zSrcTable, zWhere); if( pAr->bDryRun ){ - oputf("%s\n", sqlite3_sql(pSql)); + sqlite3_fprintf(pAr->out, "%s\n", sqlite3_sql(pSql)); }else{ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ if( pAr->bVerbose ){ - oputf("%s % 10d %s %s\n", + sqlite3_fprintf(pAr->out, "%s % 10d %s %s\n", sqlite3_column_text(pSql, 0), sqlite3_column_int(pSql, 1), sqlite3_column_text(pSql, 2),sqlite3_column_text(pSql, 3)); }else{ - oputf("%s\n", sqlite3_column_text(pSql, 0)); + sqlite3_fprintf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); } } } @@ -7240,7 +7813,7 @@ static int arRemoveCommand(ArCommand *pAr){ zSql = sqlite3_mprintf("DELETE FROM %s WHERE %s;", pAr->zSrcTable, zWhere); if( pAr->bDryRun ){ - oputf("%s\n", zSql); + sqlite3_fprintf(pAr->out, "%s\n", zSql); }else{ char *zErr = 0; rc = sqlite3_exec(pAr->db, "SAVEPOINT ar;", 0, 0, 0); @@ -7253,7 +7826,7 @@ static int arRemoveCommand(ArCommand *pAr){ } } if( zErr ){ - sputf(stdout, "ERROR: %s\n", zErr); /* stdout? */ + sqlite3_fprintf(stdout, "ERROR: %s\n", zErr); /* stdout? */ sqlite3_free(zErr); } } @@ -7317,11 +7890,11 @@ static int arExtractCommand(ArCommand *pAr){ j = sqlite3_bind_parameter_index(pSql, "$dirOnly"); sqlite3_bind_int(pSql, j, i); if( pAr->bDryRun ){ - oputf("%s\n", sqlite3_sql(pSql)); + sqlite3_fprintf(pAr->out, "%s\n", sqlite3_sql(pSql)); }else{ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSql) ){ if( i==0 && pAr->bVerbose ){ - oputf("%s\n", sqlite3_column_text(pSql, 0)); + sqlite3_fprintf(pAr->out, "%s\n", sqlite3_column_text(pSql, 0)); } } } @@ -7341,13 +7914,13 @@ static int arExtractCommand(ArCommand *pAr){ static int arExecSql(ArCommand *pAr, const char *zSql){ int rc; if( pAr->bDryRun ){ - oputf("%s\n", zSql); + sqlite3_fprintf(pAr->out, "%s\n", zSql); rc = SQLITE_OK; }else{ char *zErr = 0; rc = sqlite3_exec(pAr->db, zSql, 0, 0, &zErr); if( zErr ){ - sputf(stdout, "ERROR: %s\n", zErr); + sqlite3_fprintf(stdout, "ERROR: %s\n", zErr); sqlite3_free(zErr); } } @@ -7496,6 +8069,7 @@ static int arDotCommand( if( rc==SQLITE_OK ){ int eDbType = SHELL_OPEN_UNSPEC; cmd.p = pState; + cmd.out = pState->out; cmd.db = pState->db; if( cmd.zFile ){ eDbType = deduceDatabaseType(cmd.zFile, 1); @@ -7522,13 +8096,14 @@ static int arDotCommand( } cmd.db = 0; if( cmd.bDryRun ){ - oputf("-- open database '%s'%s\n", cmd.zFile, + sqlite3_fprintf(cmd.out, "-- open database '%s'%s\n", cmd.zFile, eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : ""); } rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags, eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0); if( rc!=SQLITE_OK ){ - eputf("cannot open file: %s (%s)\n", cmd.zFile, sqlite3_errmsg(cmd.db)); + sqlite3_fprintf(stderr, "cannot open file: %s (%s)\n", + cmd.zFile, sqlite3_errmsg(cmd.db)); goto end_ar_command; } sqlite3_fileio_init(cmd.db, 0, 0); @@ -7541,7 +8116,7 @@ static int arDotCommand( if( cmd.eCmd!=AR_CMD_CREATE && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0) ){ - eputz("database does not contain an 'sqlar' table\n"); + sqlite3_fprintf(stderr, "database does not contain an 'sqlar' table\n"); rc = SQLITE_ERROR; goto end_ar_command; } @@ -7599,7 +8174,7 @@ end_ar_command: */ static int recoverSqlCb(void *pCtx, const char *zSql){ ShellState *pState = (ShellState*)pCtx; - sputf(pState->out, "%s;\n", zSql); + sqlite3_fprintf(pState->out, "%s;\n", zSql); return SQLITE_OK; } @@ -7642,7 +8217,7 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){ bRowids = 0; } else{ - eputf("unexpected option: %s\n", azArg[i]); + sqlite3_fprintf(stderr,"unexpected option: %s\n", azArg[i]); showHelp(pState->out, azArg[0]); return 1; } @@ -7657,11 +8232,12 @@ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){ sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids); sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist); + sqlite3_fprintf(pState->out, ".dbconfig defensive off\n"); sqlite3_recover_run(p); if( sqlite3_recover_errcode(p)!=SQLITE_OK ){ const char *zErr = sqlite3_recover_errmsg(p); int errCode = sqlite3_recover_errcode(p); - eputf("sql error: %s (%d)\n", zErr, errCode); + sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode); } rc = sqlite3_recover_finish(p); return rc; @@ -7683,7 +8259,7 @@ static int intckDatabaseCmd(ShellState *pState, i64 nStepPerUnlock){ while( SQLITE_OK==sqlite3_intck_step(p) ){ const char *zMsg = sqlite3_intck_message(p); if( zMsg ){ - oputf("%s\n", zMsg); + sqlite3_fprintf(pState->out, "%s\n", zMsg); nError++; } nStep++; @@ -7693,11 +8269,11 @@ static int intckDatabaseCmd(ShellState *pState, i64 nStepPerUnlock){ } rc = sqlite3_intck_error(p, &zErr); if( zErr ){ - eputf("%s\n", zErr); + sqlite3_fprintf(stderr,"%s\n", zErr); } sqlite3_intck_close(p); - oputf("%lld steps, %lld errors\n", nStep, nError); + sqlite3_fprintf(pState->out, "%lld steps, %lld errors\n", nStep, nError); } return rc; @@ -7720,7 +8296,7 @@ static int intckDatabaseCmd(ShellState *pState, i64 nStepPerUnlock){ #define rc_err_oom_die(rc) \ if( rc==SQLITE_NOMEM ) shell_check_oom(0); \ else if(!(rc==SQLITE_OK||rc==SQLITE_DONE)) \ - eputf("E:%d\n",rc), assert(0) + sqlite3_fprintf(stderr,"E:%d\n",rc), assert(0) #else static void rc_err_oom_die(int rc){ if( rc==SQLITE_NOMEM ) shell_check_oom(0); @@ -7878,8 +8454,8 @@ FROM (\ }else{ /* Formulate the columns spec, close the DB, zero *pDb. */ char *zColsSpec = 0; - int hasDupes = db_int(*pDb, zHasDupes); - int nDigits = (hasDupes)? db_int(*pDb, zColDigits) : 0; + int hasDupes = db_int(*pDb, "%s", zHasDupes); + int nDigits = (hasDupes)? db_int(*pDb, "%s", zColDigits) : 0; if( hasDupes ){ #ifdef SHELL_COLUMN_RENAME_CLEAN rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0); @@ -7894,7 +8470,7 @@ FROM (\ sqlite3_finalize(pStmt); if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM); } - assert(db_int(*pDb, zHasDupes)==0); /* Consider: remove this */ + assert(db_int(*pDb, "%s", zHasDupes)==0); /* Consider: remove this */ rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0); rc_err_oom_die(rc); rc = sqlite3_step(pStmt); @@ -7937,8 +8513,9 @@ static int outputDumpWarning(ShellState *p, const char *zLike){ "sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true" ); if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ - oputz("/* WARNING: " - "Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n" + sqlite3_fputs("/* WARNING: " + "Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n", + p->out ); } shellFinalize(&rc, pStmt); @@ -7969,12 +8546,14 @@ static int faultsim_callback(int iArg){ if( faultsim_state.iCnt ){ if( faultsim_state.iCnt>0 ) faultsim_state.iCnt--; if( faultsim_state.eVerbose>=2 ){ - oputf("FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt); + sqlite3_fprintf(stdout, + "FAULT-SIM id=%d no-fault (cnt=%d)\n", iArg, faultsim_state.iCnt); } return SQLITE_OK; } if( faultsim_state.eVerbose>=1 ){ - oputf("FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr); + sqlite3_fprintf(stdout, + "FAULT-SIM id=%d returns %d\n", iArg, faultsim_state.iErr); } faultsim_state.iCnt = faultsim_state.iInterval; faultsim_state.nHit++; @@ -8037,7 +8616,7 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifndef SQLITE_OMIT_AUTHORIZATION if( c=='a' && cli_strncmp(azArg[0], "auth", n)==0 ){ if( nArg!=2 ){ - eputz("Usage: .auth ON|OFF\n"); + sqlite3_fprintf(stderr, "Usage: .auth ON|OFF\n"); rc = 1; goto meta_command_exit; } @@ -8084,7 +8663,7 @@ static int do_meta_command(char *zLine, ShellState *p){ bAsync = 1; }else { - eputf("unknown option: %s\n", azArg[j]); + sqlite3_fprintf(stderr,"unknown option: %s\n", azArg[j]); return 1; } }else if( zDestFile==0 ){ @@ -8093,19 +8672,19 @@ static int do_meta_command(char *zLine, ShellState *p){ zDb = zDestFile; zDestFile = azArg[j]; }else{ - eputz("Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); + sqlite3_fprintf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n"); return 1; } } if( zDestFile==0 ){ - eputz("missing FILENAME argument on .backup\n"); + sqlite3_fprintf(stderr, "missing FILENAME argument on .backup\n"); return 1; } if( zDb==0 ) zDb = "main"; rc = sqlite3_open_v2(zDestFile, &pDest, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs); if( rc!=SQLITE_OK ){ - eputf("Error: cannot open \"%s\"\n", zDestFile); + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zDestFile); close_db(pDest); return 1; } @@ -8116,7 +8695,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb); if( pBackup==0 ){ - eputf("Error: %s\n", sqlite3_errmsg(pDest)); + shellDatabaseError(pDest); close_db(pDest); return 1; } @@ -8125,7 +8704,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( rc==SQLITE_DONE ){ rc = 0; }else{ - eputf("Error: %s\n", sqlite3_errmsg(pDest)); + shellDatabaseError(pDest); rc = 1; } close_db(pDest); @@ -8141,19 +8720,10 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else - /* Undocumented. Legacy only. See "crnl" below */ + /* Undocumented. Legacy only. See "crlf" below */ if( c=='b' && n>=3 && cli_strncmp(azArg[0], "binary", n)==0 ){ - if( nArg==2 ){ - if( booleanValue(azArg[1]) ){ - setBinaryMode(p->out, 1); - }else{ - setTextMode(p->out, 1); - } - }else{ - eputz("The \".binary\" command is deprecated. Use \".crnl\" instead.\n" - "Usage: .binary on|off\n"); - rc = 1; - } + eputz("The \".binary\" command is deprecated.\n"); + rc = 1; }else /* The undocumented ".breakpoint" command causes a call to the no-op @@ -8175,7 +8745,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = chdir(azArg[1]); #endif if( rc ){ - eputf("Cannot change to directory \"%s\"\n", azArg[1]); + sqlite3_fprintf(stderr,"Cannot change to directory \"%s\"\n", azArg[1]); rc = 1; } }else{ @@ -8208,11 +8778,12 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( (zRes = readFile("testcase-out.txt", 0))==0 ){ rc = 2; }else if( testcase_glob(azArg[1],zRes)==0 ){ - eputf("testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", + sqlite3_fprintf(stderr, + "testcase-%s FAILED\n Expected: [%s]\n Got: [%s]\n", p->zTestcase, azArg[1], zRes); rc = 1; }else{ - oputf("testcase-%s ok\n", p->zTestcase); + sqlite3_fprintf(p->out, "testcase-%s ok\n", p->zTestcase); p->nCheck++; } sqlite3_free(zRes); @@ -8245,9 +8816,9 @@ static int do_meta_command(char *zLine, ShellState *p){ zFile = "(temporary-file)"; } if( p->pAuxDb == &p->aAuxDb[i] ){ - sputf(stdout, "ACTIVE %d: %s\n", i, zFile); + sqlite3_fprintf(stdout, "ACTIVE %d: %s\n", i, zFile); }else if( p->aAuxDb[i].db!=0 ){ - sputf(stdout, " %d: %s\n", i, zFile); + sqlite3_fprintf(stdout, " %d: %s\n", i, zFile); } } }else if( nArg==2 && IsDigit(azArg[1][0]) && azArg[1][1]==0 ){ @@ -8277,20 +8848,18 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else - if( c=='c' && n==4 && cli_strncmp(azArg[0], "crnl", n)==0 ){ + if( c=='c' && n==4 + && (cli_strncmp(azArg[0], "crlf", n)==0 + || cli_strncmp(azArg[0], "crnl",n)==0) + ){ if( nArg==2 ){ - if( booleanValue(azArg[1]) ){ - setTextMode(p->out, 1); - }else{ - setBinaryMode(p->out, 1); - } - }else{ -#if !defined(_WIN32) && !defined(WIN32) - eputz("The \".crnl\" is a no-op on non-Windows machines.\n"); +#ifdef _WIN32 + p->crlfMode = booleanValue(azArg[1]); +#else + p->crlfMode = 0; #endif - eputz("Usage: .crnl on|off\n"); - rc = 1; } + sqlite3_fprintf(stderr, "crlf is %s\n", p->crlfMode ? "ON" : "OFF"); }else if( c=='d' && n>1 && cli_strncmp(azArg[0], "databases", n)==0 ){ @@ -8301,7 +8870,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); if( rc ){ - eputf("Error: %s\n", sqlite3_errmsg(p->db)); + shellDatabaseError(p->db); rc = 1; }else{ while( sqlite3_step(pStmt)==SQLITE_ROW ){ @@ -8320,7 +8889,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int eTxn = sqlite3_txn_state(p->db, azName[i*2]); int bRdonly = sqlite3_db_readonly(p->db, azName[i*2]); const char *z = azName[i*2+1]; - oputf("%s: %s %s%s\n", + sqlite3_fprintf(p->out, "%s: %s %s%s\n", azName[i*2], z && z[0] ? z : "\"\"", bRdonly ? "r/o" : "r/w", eTxn==SQLITE_TXN_NONE ? "" : eTxn==SQLITE_TXN_READ ? " read-txn" : " write-txn"); @@ -8335,6 +8904,9 @@ static int do_meta_command(char *zLine, ShellState *p){ const char *zName; int op; } aDbConfig[] = { + { "attach_create", SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE }, + { "attach_write", SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE }, + { "comments", SQLITE_DBCONFIG_ENABLE_COMMENTS }, { "defensive", SQLITE_DBCONFIG_DEFENSIVE }, { "dqs_ddl", SQLITE_DBCONFIG_DQS_DDL }, { "dqs_dml", SQLITE_DBCONFIG_DQS_DML }, @@ -8362,11 +8934,12 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_db_config(p->db, aDbConfig[ii].op, booleanValue(azArg[2]), 0); } sqlite3_db_config(p->db, aDbConfig[ii].op, -1, &v); - oputf("%19s %s\n", aDbConfig[ii].zName, v ? "on" : "off"); + sqlite3_fprintf(p->out, "%19s %s\n", + aDbConfig[ii].zName, v ? "on" : "off"); if( nArg>1 ) break; } if( nArg>1 && ii==ArraySize(aDbConfig) ){ - eputf("Error: unknown dbconfig \"%s\"\n", azArg[1]); + sqlite3_fprintf(stderr,"Error: unknown dbconfig \"%s\"\n", azArg[1]); eputz("Enter \".dbconfig\" with no arguments for a list\n"); } }else @@ -8416,7 +8989,8 @@ static int do_meta_command(char *zLine, ShellState *p){ ShellSetFlag(p, SHFLG_DumpNoSys); }else { - eputf("Unknown option \"%s\" on \".dump\"\n", azArg[i]); + sqlite3_fprintf(stderr, + "Unknown option \"%s\" on \".dump\"\n", azArg[i]); rc = 1; sqlite3_free(zLike); goto meta_command_exit; @@ -8451,8 +9025,8 @@ static int do_meta_command(char *zLine, ShellState *p){ /* When playing back a "dump", the content might appear in an order ** which causes immediate foreign key constraints to be violated. ** So disable foreign-key constraint enforcement to prevent problems. */ - oputz("PRAGMA foreign_keys=OFF;\n"); - oputz("BEGIN TRANSACTION;\n"); + sqlite3_fputs("PRAGMA foreign_keys=OFF;\n", p->out); + sqlite3_fputs("BEGIN TRANSACTION;\n", p->out); } p->writableSchema = 0; p->showHeader = 0; @@ -8484,13 +9058,13 @@ static int do_meta_command(char *zLine, ShellState *p){ } sqlite3_free(zLike); if( p->writableSchema ){ - oputz("PRAGMA writable_schema=OFF;\n"); + sqlite3_fputs("PRAGMA writable_schema=OFF;\n", p->out); p->writableSchema = 0; } sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); sqlite3_exec(p->db, "RELEASE dump;", 0, 0, 0); if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){ - oputz(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n"); + sqlite3_fputs(p->nErr?"ROLLBACK; -- due to errors\n":"COMMIT;\n", p->out); } p->showHeader = savedShowHeader; p->shellFlgs = savedShellFlags; @@ -8505,6 +9079,10 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else + if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){ + rc = shell_dbtotxt_command(p, nArg, azArg); + }else + if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){ if( nArg==2 ){ p->autoEQPtest = 0; @@ -8570,7 +9148,8 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifndef SQLITE_OMIT_VIRTUALTABLE if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){ if( p->bSafeMode ){ - eputf("Cannot run experimental commands such as \"%s\" in safe mode\n", + sqlite3_fprintf(stderr, + "Cannot run experimental commands such as \"%s\" in safe mode\n", azArg[0]); rc = 1; }else{ @@ -8627,9 +9206,10 @@ static int do_meta_command(char *zLine, ShellState *p){ /* --help lists all file-controls */ if( cli_strcmp(zCmd,"help")==0 ){ - oputz("Available file-controls:\n"); + sqlite3_fputs("Available file-controls:\n", p->out); for(i=0; i<ArraySize(aCtrl); i++){ - oputf(" .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); + sqlite3_fprintf(p->out, + " .filectrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); } rc = 1; goto meta_command_exit; @@ -8644,7 +9224,7 @@ static int do_meta_command(char *zLine, ShellState *p){ filectrl = aCtrl[i].ctrlCode; iCtrl = i; }else{ - eputf("Error: ambiguous file-control: \"%s\"\n" + sqlite3_fprintf(stderr,"Error: ambiguous file-control: \"%s\"\n" "Use \".filectrl --help\" for help\n", zCmd); rc = 1; goto meta_command_exit; @@ -8652,7 +9232,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( filectrl<0 ){ - eputf("Error: unknown file-control: %s\n" + sqlite3_fprintf(stderr,"Error: unknown file-control: %s\n" "Use \".filectrl --help\" for help\n", zCmd); }else{ switch(filectrl){ @@ -8696,7 +9276,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg!=2 ) break; sqlite3_file_control(p->db, zSchema, filectrl, &z); if( z ){ - oputf("%s\n", z); + sqlite3_fprintf(p->out, "%s\n", z); sqlite3_free(z); } isOk = 2; @@ -8710,19 +9290,20 @@ static int do_meta_command(char *zLine, ShellState *p){ } x = -1; sqlite3_file_control(p->db, zSchema, filectrl, &x); - oputf("%d\n", x); + sqlite3_fprintf(p->out, "%d\n", x); isOk = 2; break; } } } if( isOk==0 && iCtrl>=0 ){ - oputf("Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); + sqlite3_fprintf(p->out, "Usage: .filectrl %s %s\n", + zCmd, aCtrl[iCtrl].zUsage); rc = 1; }else if( isOk==1 ){ char zBuf[100]; sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", iRes); - oputf("%s\n", zBuf); + sqlite3_fprintf(p->out, "%s\n", zBuf); } }else @@ -8747,7 +9328,8 @@ static int do_meta_command(char *zLine, ShellState *p){ " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" " FROM sqlite_schema UNION ALL" " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_schema) " - "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' " + "WHERE type!='meta' AND sql NOTNULL" + " AND name NOT LIKE 'sqlite__%' ESCAPE '_' " "ORDER BY x", callback, &data, 0 ); @@ -8763,15 +9345,15 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( doStats==0 ){ - oputz("/* No STAT tables available */\n"); + sqlite3_fputs("/* No STAT tables available */\n", p->out); }else{ - oputz("ANALYZE sqlite_schema;\n"); + sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out); data.cMode = data.mode = MODE_Insert; data.zDestTable = "sqlite_stat1"; shell_exec(&data, "SELECT * FROM sqlite_stat1", 0); data.zDestTable = "sqlite_stat4"; shell_exec(&data, "SELECT * FROM sqlite_stat4", 0); - oputz("ANALYZE sqlite_schema;\n"); + sqlite3_fputs("ANALYZE sqlite_schema;\n", p->out); } }else @@ -8789,7 +9371,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nArg>=2 ){ n = showHelp(p->out, azArg[1]); if( n==0 ){ - oputf("Nothing matches '%s'\n", azArg[1]); + sqlite3_fprintf(p->out, "Nothing matches '%s'\n", azArg[1]); } }else{ showHelp(p->out, 0); @@ -8832,7 +9414,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( zTable==0 ){ zTable = z; }else{ - oputf("ERROR: extra argument: \"%s\". Usage:\n", z); + sqlite3_fprintf(p->out, "ERROR: extra argument: \"%s\". Usage:\n",z); showHelp(p->out, "import"); goto meta_command_exit; } @@ -8853,13 +9435,13 @@ static int do_meta_command(char *zLine, ShellState *p){ xRead = csv_read_one_field; useOutputMode = 0; }else{ - oputf("ERROR: unknown option: \"%s\". Usage:\n", z); + sqlite3_fprintf(p->out, "ERROR: unknown option: \"%s\". Usage:\n", z); showHelp(p->out, "import"); goto meta_command_exit; } } if( zTable==0 ){ - oputf("ERROR: missing %s argument. Usage:\n", + sqlite3_fprintf(p->out, "ERROR: missing %s argument. Usage:\n", zFile==0 ? "FILE" : "TABLE"); showHelp(p->out, "import"); goto meta_command_exit; @@ -8909,28 +9491,28 @@ static int do_meta_command(char *zLine, ShellState *p){ eputz("Error: pipes are not supported in this OS\n"); goto meta_command_exit; #else - sCtx.in = popen(sCtx.zFile+1, "r"); + sCtx.in = sqlite3_popen(sCtx.zFile+1, "r"); sCtx.zFile = "<pipe>"; sCtx.xCloser = pclose; #endif }else{ - sCtx.in = fopen(sCtx.zFile, "rb"); + sCtx.in = sqlite3_fopen(sCtx.zFile, "rb"); sCtx.xCloser = fclose; } if( sCtx.in==0 ){ - eputf("Error: cannot open \"%s\"\n", zFile); + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zFile); goto meta_command_exit; } if( eVerbose>=2 || (eVerbose>=1 && useOutputMode) ){ char zSep[2]; zSep[1] = 0; zSep[0] = sCtx.cColSep; - oputz("Column separator "); - output_c_string(zSep); - oputz(", row separator "); + sqlite3_fputs("Column separator ", p->out); + output_c_string(p->out, zSep); + sqlite3_fputs(", row separator ", p->out); zSep[0] = sCtx.cRowSep; - output_c_string(zSep); - oputz("\n"); + output_c_string(p->out, zSep); + sqlite3_fputs("\n", p->out); } sCtx.z = sqlite3_malloc64(120); if( sCtx.z==0 ){ @@ -8942,7 +9524,11 @@ static int do_meta_command(char *zLine, ShellState *p){ while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){} } import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */ - if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) ){ + if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0) + && 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema" + " WHERE name=%Q AND type='view'", + zSchema ? zSchema : "main", zTable) + ){ /* Table does not exist. Create it. */ sqlite3 *dbCols = 0; char *zRenames = 0; @@ -8955,14 +9541,14 @@ static int do_meta_command(char *zLine, ShellState *p){ } zColDefs = zAutoColumn(0, &dbCols, &zRenames); if( zRenames!=0 ){ - sputf((stdin_is_interactive && p->in==stdin)? p->out : stderr, + sqlite3_fprintf((stdin_is_interactive && p->in==stdin)? p->out : stderr, "Columns renamed during .import %s due to duplicates:\n" "%s\n", sCtx.zFile, zRenames); sqlite3_free(zRenames); } assert(dbCols==0); if( zColDefs==0 ){ - eputf("%s: empty file\n", sCtx.zFile); + sqlite3_fprintf(stderr,"%s: empty file\n", sCtx.zFile); import_cleanup(&sCtx); rc = 1; sqlite3_free(zCreate); @@ -8974,13 +9560,16 @@ static int do_meta_command(char *zLine, ShellState *p){ shell_out_of_memory(); } if( eVerbose>=1 ){ - oputf("%s\n", zCreate); + sqlite3_fprintf(p->out, "%s\n", zCreate); } rc = sqlite3_exec(p->db, zCreate, 0, 0, 0); + if( rc ){ + sqlite3_fprintf(stderr, + "%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); + } sqlite3_free(zCreate); zCreate = 0; if( rc ){ - eputf("%s failed:\n%s\n", zCreate, sqlite3_errmsg(p->db)); import_cleanup(&sCtx); rc = 1; goto meta_command_exit; @@ -8997,7 +9586,7 @@ static int do_meta_command(char *zLine, ShellState *p){ zSql = 0; if( rc ){ if (pStmt) sqlite3_finalize(pStmt); - eputf("Error: %s\n", sqlite3_errmsg(p->db)); + shellDatabaseError(p->db); import_cleanup(&sCtx); rc = 1; goto meta_command_exit; @@ -9035,13 +9624,13 @@ static int do_meta_command(char *zLine, ShellState *p){ zSql[j] = 0; assert( j<nByte ); if( eVerbose>=2 ){ - oputf("Insert using: %s\n", zSql); + sqlite3_fprintf(p->out, "Insert using: %s\n", zSql); } rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); zSql = 0; if( rc ){ - eputf("Error: %s\n", sqlite3_errmsg(p->db)); + shellDatabaseError(p->db); if (pStmt) sqlite3_finalize(pStmt); import_cleanup(&sCtx); rc = 1; @@ -9074,7 +9663,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } sqlite3_bind_text(pStmt, i+1, z, -1, SQLITE_TRANSIENT); if( i<nCol-1 && sCtx.cTerm!=sCtx.cColSep ){ - eputf("%s:%d: expected %d columns but found %d" + sqlite3_fprintf(stderr,"%s:%d: expected %d columns but found %d" " - filling the rest with NULL\n", sCtx.zFile, startLine, nCol, i+1); i += 2; @@ -9086,14 +9675,15 @@ static int do_meta_command(char *zLine, ShellState *p){ xRead(&sCtx); i++; }while( sCtx.cTerm==sCtx.cColSep ); - eputf("%s:%d: expected %d columns but found %d - extras ignored\n", + sqlite3_fprintf(stderr, + "%s:%d: expected %d columns but found %d - extras ignored\n", sCtx.zFile, startLine, nCol, i); } if( i>=nCol ){ sqlite3_step(pStmt); rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK ){ - eputf("%s:%d: INSERT failed: %s\n", + sqlite3_fprintf(stderr,"%s:%d: INSERT failed: %s\n", sCtx.zFile, startLine, sqlite3_errmsg(p->db)); sCtx.nErr++; }else{ @@ -9106,7 +9696,8 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_finalize(pStmt); if( needCommit ) sqlite3_exec(p->db, "COMMIT", 0, 0, 0); if( eVerbose>0 ){ - oputf("Added %d rows with %d errors using %d lines of input\n", + sqlite3_fprintf(p->out, + "Added %d rows with %d errors using %d lines of input\n", sCtx.nRow, sCtx.nErr, sCtx.nLine-1); } }else @@ -9122,7 +9713,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */ int i; if( !ShellHasFlag(p,SHFLG_TestingMode) ){ - eputf(".%s unavailable without --unsafe-testing\n", + sqlite3_fprintf(stderr,".%s unavailable without --unsafe-testing\n", "imposter"); rc = 1; goto meta_command_exit; @@ -9188,7 +9779,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } sqlite3_finalize(pStmt); if( i==0 || tnum==0 ){ - eputf("no such index: \"%s\"\n", azArg[1]); + sqlite3_fprintf(stderr,"no such index: \"%s\"\n", azArg[1]); rc = 1; sqlite3_free(zCollist); goto meta_command_exit; @@ -9203,14 +9794,16 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3_exec(p->db, zSql, 0, 0, 0); sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0); if( rc ){ - eputf("Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); + sqlite3_fprintf(stderr, + "Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db)); }else{ - sputf(stdout, "%s;\n", zSql); - sputf(stdout, "WARNING: writing to an imposter table will corrupt" + sqlite3_fprintf(stdout, "%s;\n", zSql); + sqlite3_fprintf(stdout, + "WARNING: writing to an imposter table will corrupt" " the \"%s\" %s!\n", azArg[1], isWO ? "table" : "index"); } }else{ - eputf("SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); + sqlite3_fprintf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc); rc = 1; } sqlite3_free(zSql); @@ -9224,7 +9817,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( iArg==0 ) iArg = -1; } if( (nArg!=1 && nArg!=2) || iArg<0 ){ - eputf("%s","Usage: .intck STEPS_PER_UNLOCK\n"); + sqlite3_fprintf(stderr,"%s","Usage: .intck STEPS_PER_UNLOCK\n"); rc = 1; goto meta_command_exit; } @@ -9243,9 +9836,9 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3IoTrace = iotracePrintf; iotrace = stdout; }else{ - iotrace = fopen(azArg[1], "w"); + iotrace = sqlite3_fopen(azArg[1], "w"); if( iotrace==0 ){ - eputf("Error: cannot open \"%s\"\n", azArg[1]); + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); sqlite3IoTrace = 0; rc = 1; }else{ @@ -9277,7 +9870,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); if( nArg==1 ){ for(i=0; i<ArraySize(aLimit); i++){ - sputf(stdout, "%20s %d\n", aLimit[i].zLimitName, + sqlite3_fprintf(stdout, "%20s %d\n", aLimit[i].zLimitName, sqlite3_limit(p->db, aLimit[i].limitCode, -1)); } }else if( nArg>3 ){ @@ -9292,14 +9885,14 @@ static int do_meta_command(char *zLine, ShellState *p){ if( iLimit<0 ){ iLimit = i; }else{ - eputf("ambiguous limit: \"%s\"\n", azArg[1]); + sqlite3_fprintf(stderr,"ambiguous limit: \"%s\"\n", azArg[1]); rc = 1; goto meta_command_exit; } } } if( iLimit<0 ){ - eputf("unknown limit: \"%s\"\n" + sqlite3_fprintf(stderr,"unknown limit: \"%s\"\n" "enter \".limits\" with no arguments for a list.\n", azArg[1]); rc = 1; @@ -9309,7 +9902,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_limit(p->db, aLimit[iLimit].limitCode, (int)integerValue(azArg[2])); } - sputf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, + sqlite3_fprintf(stdout, "%20s %d\n", aLimit[iLimit].zLimitName, sqlite3_limit(p->db, aLimit[iLimit].limitCode, -1)); } }else @@ -9335,7 +9928,7 @@ static int do_meta_command(char *zLine, ShellState *p){ open_db(p, 0); rc = sqlite3_load_extension(p->db, zFile, zProc, &zErrMsg); if( rc!=SQLITE_OK ){ - eputf("Error: %s\n", zErrMsg); + shellEmitError(zErrMsg); sqlite3_free(zErrMsg); rc = 1; } @@ -9358,7 +9951,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } output_file_close(p->pLog); if( cli_strcmp(zFile,"on")==0 ) zFile = "stdout"; - p->pLog = output_file_open(zFile, 0); + p->pLog = output_file_open(zFile); } }else @@ -9366,24 +9959,52 @@ static int do_meta_command(char *zLine, ShellState *p){ const char *zMode = 0; const char *zTabname = 0; int i, n2; + int chng = 0; /* 0x01: change to cmopts. 0x02: Any other change */ ColModeOpts cmOpts = ColModeOpts_default; for(i=1; i<nArg; i++){ const char *z = azArg[i]; if( optionMatch(z,"wrap") && i+1<nArg ){ cmOpts.iWrap = integerValue(azArg[++i]); + chng |= 1; }else if( optionMatch(z,"ww") ){ cmOpts.bWordWrap = 1; + chng |= 1; }else if( optionMatch(z,"wordwrap") && i+1<nArg ){ cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]); + chng |= 1; }else if( optionMatch(z,"quote") ){ cmOpts.bQuote = 1; + chng |= 1; }else if( optionMatch(z,"noquote") ){ cmOpts.bQuote = 0; + chng |= 1; + }else if( optionMatch(z,"escape") && i+1<nArg ){ + /* See similar code at tag-20250224-1 */ + const char *zEsc = azArg[++i]; + int k; + for(k=0; k<ArraySize(shell_EscModeNames); k++){ + if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){ + p->eEscMode = k; + chng |= 2; + break; + } + } + if( k>=ArraySize(shell_EscModeNames) ){ + sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\"" + " - choices:", zEsc); + for(k=0; k<ArraySize(shell_EscModeNames); k++){ + sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]); + } + sqlite3_fprintf(stderr, "\n"); + rc = 1; + goto meta_command_exit; + } }else if( zMode==0 ){ zMode = z; /* Apply defaults for qbox pseudo-mode. If that * overwrites already-set values, user was informed of this. */ + chng |= 1; if( cli_strcmp(z, "qbox")==0 ){ ColModeOpts cmo = ColModeOpts_default_qbox; zMode = "box"; @@ -9392,8 +10013,9 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( zTabname==0 ){ zTabname = z; }else if( z[0]=='-' ){ - eputf("unknown option: %s\n", z); + sqlite3_fprintf(stderr,"unknown option: %s\n", z); eputz("options:\n" + " --escape MODE\n" " --noquote\n" " --quote\n" " --wordwrap on/off\n" @@ -9402,23 +10024,34 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 1; goto meta_command_exit; }else{ - eputf("extra argument: \"%s\"\n", z); + sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z); rc = 1; goto meta_command_exit; } } - if( zMode==0 ){ + if( !chng ){ if( p->mode==MODE_Column || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) ){ - oputf("current output mode: %s --wrap %d --wordwrap %s --%squote\n", + sqlite3_fprintf(p->out, + "current output mode: %s --wrap %d --wordwrap %s " + "--%squote --escape %s\n", modeDescr[p->mode], p->cmOpts.iWrap, p->cmOpts.bWordWrap ? "on" : "off", - p->cmOpts.bQuote ? "" : "no"); + p->cmOpts.bQuote ? "" : "no", + shell_EscModeNames[p->eEscMode] + ); }else{ - oputf("current output mode: %s\n", modeDescr[p->mode]); + sqlite3_fprintf(p->out, + "current output mode: %s --escape %s\n", + modeDescr[p->mode], + shell_EscModeNames[p->eEscMode] + ); } + } + if( zMode==0 ){ zMode = modeDescr[p->mode]; + if( (chng&1)==0 ) cmOpts = p->cmOpts; } n2 = strlen30(zMode); if( cli_strncmp(zMode,"lines",n2)==0 ){ @@ -9451,6 +10084,11 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( cli_strncmp(zMode,"insert",n2)==0 ){ p->mode = MODE_Insert; set_table_name(p, zTabname ? zTabname : "table"); + if( p->eEscMode==SHELL_ESC_OFF ){ + ShellSetFlag(p, SHFLG_Newlines); + }else{ + ShellClearFlag(p, SHFLG_Newlines); + } }else if( cli_strncmp(zMode,"quote",n2)==0 ){ p->mode = MODE_Quote; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); @@ -9489,7 +10127,7 @@ static int do_meta_command(char *zLine, ShellState *p){ eputz("Usage: .nonce NONCE\n"); rc = 1; }else if( p->zNonce==0 || cli_strcmp(azArg[1],p->zNonce)!=0 ){ - eputf("line %d: incorrect nonce: \"%s\"\n", + sqlite3_fprintf(stderr,"line %d: incorrect nonce: \"%s\"\n", p->lineno, azArg[1]); exit(1); }else{ @@ -9544,11 +10182,11 @@ static int do_meta_command(char *zLine, ShellState *p){ }else #endif /* !SQLITE_SHELL_FIDDLE */ if( z[0]=='-' ){ - eputf("unknown option: %s\n", z); + sqlite3_fprintf(stderr,"unknown option: %s\n", z); rc = 1; goto meta_command_exit; }else if( zFN ){ - eputf("extra argument: \"%s\"\n", z); + sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z); rc = 1; goto meta_command_exit; }else{ @@ -9590,7 +10228,7 @@ static int do_meta_command(char *zLine, ShellState *p){ p->pAuxDb->zDbFilename = zNewFilename; open_db(p, OPEN_DB_KEEPALIVE); if( p->db==0 ){ - eputf("Error: cannot open '%s'\n", zNewFilename); + sqlite3_fprintf(stderr,"Error: cannot open '%s'\n", zNewFilename); sqlite3_free(zNewFilename); }else{ p->pAuxDb->zFreeOnClose = zNewFilename; @@ -9608,19 +10246,23 @@ static int do_meta_command(char *zLine, ShellState *p){ && (cli_strncmp(azArg[0], "output", n)==0 || cli_strncmp(azArg[0], "once", n)==0)) || (c=='e' && n==5 && cli_strcmp(azArg[0],"excel")==0) + || (c=='w' && n==3 && cli_strcmp(azArg[0],"www")==0) ){ char *zFile = 0; - int bTxtMode = 0; int i; - int eMode = 0; - int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */ - static const char *zBomUtf8 = "\xef\xbb\xbf"; + int eMode = 0; /* 0: .outout/.once, 'x'=.excel, 'w'=.www */ + int bOnce = 0; /* 0: .output, 1: .once, 2: .excel/.www */ + int bPlain = 0; /* --plain option */ + static const char *zBomUtf8 = "\357\273\277"; const char *zBom = 0; failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]); if( c=='e' ){ eMode = 'x'; bOnce = 2; + }else if( c=='w' ){ + eMode = 'w'; + bOnce = 2; }else if( cli_strncmp(azArg[0],"once",n)==0 ){ bOnce = 1; } @@ -9630,24 +10272,38 @@ static int do_meta_command(char *zLine, ShellState *p){ if( z[1]=='-' ) z++; if( cli_strcmp(z,"-bom")==0 ){ zBom = zBomUtf8; - }else if( c!='e' && cli_strcmp(z,"-x")==0 ){ + }else if( cli_strcmp(z,"-plain")==0 ){ + bPlain = 1; + }else if( c=='o' && cli_strcmp(z,"-x")==0 ){ eMode = 'x'; /* spreadsheet */ - }else if( c!='e' && cli_strcmp(z,"-e")==0 ){ + }else if( c=='o' && cli_strcmp(z,"-e")==0 ){ eMode = 'e'; /* text editor */ + }else if( c=='o' && cli_strcmp(z,"-w")==0 ){ + eMode = 'w'; /* Web browser */ }else{ - oputf("ERROR: unknown option: \"%s\". Usage:\n", azArg[i]); + sqlite3_fprintf(p->out, + "ERROR: unknown option: \"%s\". Usage:\n", azArg[i]); showHelp(p->out, azArg[0]); rc = 1; goto meta_command_exit; } - }else if( zFile==0 && eMode!='e' && eMode!='x' ){ - zFile = sqlite3_mprintf("%s", z); + }else if( zFile==0 && eMode==0 ){ + if( cli_strcmp(z, "off")==0 ){ +#ifdef _WIN32 + zFile = sqlite3_mprintf("nul"); +#else + zFile = sqlite3_mprintf("/dev/null"); +#endif + }else{ + zFile = sqlite3_mprintf("%s", z); + } if( zFile && zFile[0]=='|' ){ while( i+1<nArg ) zFile = sqlite3_mprintf("%z %s", zFile, azArg[++i]); break; } }else{ - oputf("ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]); + sqlite3_fprintf(p->out, + "ERROR: extra parameter: \"%s\". Usage:\n", azArg[i]); showHelp(p->out, azArg[0]); rc = 1; sqlite3_free(zFile); @@ -9657,6 +10313,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( zFile==0 ){ zFile = sqlite3_mprintf("stdout"); } + shell_check_oom(zFile); if( bOnce ){ p->outCount = 2; }else{ @@ -9664,7 +10321,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } output_reset(p); #ifndef SQLITE_NOHAVE_SYSTEM - if( eMode=='e' || eMode=='x' ){ + if( eMode=='e' || eMode=='x' || eMode=='w' ){ p->doXdgOpen = 1; outputModePush(p); if( eMode=='x' ){ @@ -9674,10 +10331,17 @@ static int do_meta_command(char *zLine, ShellState *p){ p->mode = MODE_Csv; sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf); +#ifdef _WIN32 + zBom = zBomUtf8; /* Always include the BOM on Windows, as Excel does + ** not work without it. */ +#endif + }else if( eMode=='w' ){ + /* web-browser mode. */ + newTempFile(p, "html"); + if( !bPlain ) p->mode = MODE_Www; }else{ /* text editor mode */ newTempFile(p, "txt"); - bTxtMode = 1; } sqlite3_free(zFile); zFile = sqlite3_mprintf("%s", p->zTempFile); @@ -9690,26 +10354,34 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 1; output_redir(p, stdout); #else - FILE *pfPipe = popen(zFile + 1, "w"); + FILE *pfPipe = sqlite3_popen(zFile + 1, "w"); if( pfPipe==0 ){ - eputf("Error: cannot open pipe \"%s\"\n", zFile + 1); + assert( stderr!=NULL ); + sqlite3_fprintf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1); rc = 1; }else{ output_redir(p, pfPipe); - if( zBom ) oputz(zBom); + if( zBom ) sqlite3_fputs(zBom, pfPipe); sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } #endif }else{ - FILE *pfFile = output_file_open(zFile, bTxtMode); + FILE *pfFile = output_file_open(zFile); if( pfFile==0 ){ if( cli_strcmp(zFile,"off")!=0 ){ - eputf("Error: cannot write to \"%s\"\n", zFile); + assert( stderr!=NULL ); + sqlite3_fprintf(stderr,"Error: cannot write to \"%s\"\n", zFile); } rc = 1; } else { output_redir(p, pfFile); - if( zBom ) oputz(zBom); + if( zBom ) sqlite3_fputs(zBom, pfFile); + if( bPlain && eMode=='w' ){ + sqlite3_fputs( + "<!DOCTYPE html>\n<BODY>\n<PLAINTEXT>\n", + pfFile + ); + } sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile); } } @@ -9750,7 +10422,8 @@ static int do_meta_command(char *zLine, ShellState *p){ "SELECT key, quote(value) " "FROM temp.sqlite_parameters;", -1, &pStmt, 0); while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ - oputf("%-*s %s\n", len, sqlite3_column_text(pStmt,0), + sqlite3_fprintf(p->out, + "%-*s %s\n", len, sqlite3_column_text(pStmt,0), sqlite3_column_text(pStmt,1)); } sqlite3_finalize(pStmt); @@ -9795,12 +10468,13 @@ static int do_meta_command(char *zLine, ShellState *p){ rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rx!=SQLITE_OK ){ - oputf("Error: %s\n", sqlite3_errmsg(p->db)); + sqlite3_fprintf(p->out, "Error: %s\n", sqlite3_errmsg(p->db)); sqlite3_finalize(pStmt); pStmt = 0; rc = 1; } } + bind_prepared_stmt(p, pStmt); sqlite3_step(pStmt); sqlite3_finalize(pStmt); }else @@ -9824,10 +10498,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( c=='p' && n>=3 && cli_strncmp(azArg[0], "print", n)==0 ){ int i; for(i=1; i<nArg; i++){ - if( i>1 ) oputz(" "); - oputz(azArg[i]); + if( i>1 ) sqlite3_fputs(" ", p->out); + sqlite3_fputs(azArg[i], p->out); } - oputz("\n"); + sqlite3_fputs("\n", p->out); }else #ifndef SQLITE_OMIT_PROGRESS_CALLBACK @@ -9864,7 +10538,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } continue; } - eputf("Error: unknown option: \"%s\"\n", azArg[i]); + sqlite3_fprintf(stderr,"Error: unknown option: \"%s\"\n", azArg[i]); rc = 1; goto meta_command_exit; }else{ @@ -9905,11 +10579,10 @@ static int do_meta_command(char *zLine, ShellState *p){ #ifdef SQLITE_OMIT_POPEN eputz("Error: pipes are not supported in this OS\n"); rc = 1; - p->out = stdout; #else - p->in = popen(azArg[1]+1, "r"); + p->in = sqlite3_popen(azArg[1]+1, "r"); if( p->in==0 ){ - eputf("Error: cannot open \"%s\"\n", azArg[1]); + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ rc = process_input(p); @@ -9917,7 +10590,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } #endif }else if( (p->in = openChrSource(azArg[1]))==0 ){ - eputf("Error: cannot open \"%s\"\n", azArg[1]); + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ rc = process_input(p); @@ -9950,14 +10623,14 @@ static int do_meta_command(char *zLine, ShellState *p){ } rc = sqlite3_open(zSrcFile, &pSrc); if( rc!=SQLITE_OK ){ - eputf("Error: cannot open \"%s\"\n", zSrcFile); + sqlite3_fprintf(stderr,"Error: cannot open \"%s\"\n", zSrcFile); close_db(pSrc); return 1; } open_db(p, 0); pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); if( pBackup==0 ){ - eputf("Error: %s\n", sqlite3_errmsg(p->db)); + shellDatabaseError(p->db); close_db(pSrc); return 1; } @@ -9975,14 +10648,17 @@ static int do_meta_command(char *zLine, ShellState *p){ eputz("Error: source database is busy\n"); rc = 1; }else{ - eputf("Error: %s\n", sqlite3_errmsg(p->db)); + shellDatabaseError(p->db); rc = 1; } close_db(pSrc); }else #endif /* !defined(SQLITE_SHELL_FIDDLE) */ - if( c=='s' && cli_strncmp(azArg[0], "scanstats", n)==0 ){ + if( c=='s' && + (cli_strncmp(azArg[0], "scanstats", n)==0 || + cli_strncmp(azArg[0], "scanstatus", n)==0) + ){ if( nArg==2 ){ if( cli_strcmp(azArg[1], "vm")==0 ){ p->scanstatsOn = 3; @@ -10033,7 +10709,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( optionMatch(azArg[ii],"nosys") ){ bNoSystemTabs = 1; }else if( azArg[ii][0]=='-' ){ - eputf("Unknown option: \"%s\"\n", azArg[ii]); + sqlite3_fprintf(stderr,"Unknown option: \"%s\"\n", azArg[ii]); rc = 1; goto meta_command_exit; }else if( zName==0 ){ @@ -10072,7 +10748,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", -1, &pStmt, 0); if( rc ){ - eputf("Error: %s\n", sqlite3_errmsg(p->db)); + shellDatabaseError(p->db); sqlite3_finalize(pStmt); rc = 1; goto meta_command_exit; @@ -10129,19 +10805,19 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_free(zQarg); } if( bNoSystemTabs ){ - appendText(&sSelect, "name NOT LIKE 'sqlite_%%' AND ", 0); + appendText(&sSelect, "name NOT LIKE 'sqlite__%%' ESCAPE '_' AND ", 0); } appendText(&sSelect, "sql IS NOT NULL" " ORDER BY snum, rowid", 0); if( bDebug ){ - oputf("SQL: %s;\n", sSelect.z); + sqlite3_fprintf(p->out, "SQL: %s;\n", sSelect.z); }else{ rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); } freeText(&sSelect); } if( zErrMsg ){ - eputf("Error: %s\n", zErrMsg); + shellEmitError(zErrMsg); sqlite3_free(zErrMsg); rc = 1; }else if( rc != SQLITE_OK ){ @@ -10195,7 +10871,8 @@ static int do_meta_command(char *zLine, ShellState *p){ }else{ rc = sqlite3session_attach(pSession->p, azCmd[1]); if( rc ){ - eputf("ERROR: sqlite3session_attach() returns %d\n",rc); + sqlite3_fprintf(stderr, + "ERROR: sqlite3session_attach() returns %d\n",rc); rc = 0; } } @@ -10212,9 +10889,9 @@ static int do_meta_command(char *zLine, ShellState *p){ failIfSafeMode(p, "cannot run \".session %s\" in safe mode", azCmd[0]); if( nCmd!=2 ) goto session_syntax_error; if( pSession->p==0 ) goto session_not_open; - out = fopen(azCmd[1], "wb"); + out = sqlite3_fopen(azCmd[1], "wb"); if( out==0 ){ - eputf("ERROR: cannot open \"%s\" for writing\n", + sqlite3_fprintf(stderr,"ERROR: cannot open \"%s\" for writing\n", azCmd[1]); }else{ int szChng; @@ -10225,12 +10902,13 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3session_patchset(pSession->p, &szChng, &pChng); } if( rc ){ - sputf(stdout, "Error: error code %d\n", rc); + sqlite3_fprintf(stdout, "Error: error code %d\n", rc); rc = 0; } if( pChng && fwrite(pChng, szChng, 1, out)!=1 ){ - eputf("ERROR: Failed to write entire %d-byte output\n", szChng); + sqlite3_fprintf(stderr, + "ERROR: Failed to write entire %d-byte output\n", szChng); } sqlite3_free(pChng); fclose(out); @@ -10257,7 +10935,8 @@ static int do_meta_command(char *zLine, ShellState *p){ ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); if( pAuxDb->nSession ){ ii = sqlite3session_enable(pSession->p, ii); - oputf("session %s enable flag = %d\n", pSession->zName, ii); + sqlite3_fprintf(p->out, + "session %s enable flag = %d\n", pSession->zName, ii); } }else @@ -10292,7 +10971,8 @@ static int do_meta_command(char *zLine, ShellState *p){ ii = nCmd==1 ? -1 : booleanValue(azCmd[1]); if( pAuxDb->nSession ){ ii = sqlite3session_indirect(pSession->p, ii); - oputf("session %s indirect flag = %d\n", pSession->zName, ii); + sqlite3_fprintf(p->out, + "session %s indirect flag = %d\n", pSession->zName, ii); } }else @@ -10304,7 +10984,8 @@ static int do_meta_command(char *zLine, ShellState *p){ if( nCmd!=1 ) goto session_syntax_error; if( pAuxDb->nSession ){ ii = sqlite3session_isempty(pSession->p); - oputf("session %s isempty flag = %d\n", pSession->zName, ii); + sqlite3_fprintf(p->out, + "session %s isempty flag = %d\n", pSession->zName, ii); } }else @@ -10313,7 +10994,7 @@ static int do_meta_command(char *zLine, ShellState *p){ */ if( cli_strcmp(azCmd[0],"list")==0 ){ for(i=0; i<pAuxDb->nSession; i++){ - oputf("%d %s\n", i, pAuxDb->aSession[i].zName); + sqlite3_fprintf(p->out, "%d %s\n", i, pAuxDb->aSession[i].zName); } }else @@ -10328,18 +11009,19 @@ static int do_meta_command(char *zLine, ShellState *p){ if( zName[0]==0 ) goto session_syntax_error; for(i=0; i<pAuxDb->nSession; i++){ if( cli_strcmp(pAuxDb->aSession[i].zName,zName)==0 ){ - eputf("Session \"%s\" already exists\n", zName); + sqlite3_fprintf(stderr,"Session \"%s\" already exists\n", zName); goto meta_command_exit; } } if( pAuxDb->nSession>=ArraySize(pAuxDb->aSession) ){ - eputf("Maximum of %d sessions\n", ArraySize(pAuxDb->aSession)); + sqlite3_fprintf(stderr, + "Maximum of %d sessions\n", ArraySize(pAuxDb->aSession)); goto meta_command_exit; } pSession = &pAuxDb->aSession[pAuxDb->nSession]; rc = sqlite3session_create(p->db, azCmd[1], &pSession->p); if( rc ){ - eputf("Cannot open session: error code=%d\n", rc); + sqlite3_fprintf(stderr,"Cannot open session: error code=%d\n", rc); rc = 0; goto meta_command_exit; } @@ -10363,7 +11045,7 @@ static int do_meta_command(char *zLine, ShellState *p){ int i, v; for(i=1; i<nArg; i++){ v = booleanValue(azArg[i]); - oputf("%s: %d 0x%x\n", azArg[i], v, v); + sqlite3_fprintf(p->out, "%s: %d 0x%x\n", azArg[i], v, v); } } if( cli_strncmp(azArg[0]+9, "integer", n-9)==0 ){ @@ -10372,7 +11054,7 @@ static int do_meta_command(char *zLine, ShellState *p){ char zBuf[200]; v = integerValue(azArg[i]); sqlite3_snprintf(sizeof(zBuf),zBuf,"%s: %lld 0x%llx\n", azArg[i],v,v); - oputz(zBuf); + sqlite3_fputs(zBuf, p->out); } } }else @@ -10399,8 +11081,9 @@ static int do_meta_command(char *zLine, ShellState *p){ bVerbose++; }else { - eputf("Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); - eputz("Should be one of: --init -v\n"); + sqlite3_fprintf(stderr, + "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); + sqlite3_fputs("Should be one of: --init -v\n", stderr); rc = 1; goto meta_command_exit; } @@ -10445,10 +11128,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( zAns==0 ) continue; k = 0; if( bVerbose>0 ){ - sputf(stdout, "%d: %s %s\n", tno, zOp, zSql); + sqlite3_fprintf(stdout, "%d: %s %s\n", tno, zOp, zSql); } if( cli_strcmp(zOp,"memo")==0 ){ - oputf("%s\n", zSql); + sqlite3_fprintf(p->out, "%s\n", zSql); }else if( cli_strcmp(zOp,"run")==0 ){ char *zErrMsg = 0; @@ -10457,22 +11140,23 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); nTest++; if( bVerbose ){ - oputf("Result: %s\n", str.z); + sqlite3_fprintf(p->out, "Result: %s\n", str.z); } if( rc || zErrMsg ){ nErr++; rc = 1; - oputf("%d: error-code-%d: %s\n", tno, rc, zErrMsg); + sqlite3_fprintf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg); sqlite3_free(zErrMsg); }else if( cli_strcmp(zAns,str.z)!=0 ){ nErr++; rc = 1; - oputf("%d: Expected: [%s]\n", tno, zAns); - oputf("%d: Got: [%s]\n", tno, str.z); + sqlite3_fprintf(p->out, "%d: Expected: [%s]\n", tno, zAns); + sqlite3_fprintf(p->out, "%d: Got: [%s]\n", tno, str.z); } } else{ - eputf("Unknown operation \"%s\" on selftest line %d\n", zOp, tno); + sqlite3_fprintf(stderr, + "Unknown operation \"%s\" on selftest line %d\n", zOp, tno); rc = 1; break; } @@ -10480,7 +11164,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_finalize(pStmt); } /* End loop over k */ freeText(&str); - oputf("%d errors out of %d tests\n", nErr, nTest); + sqlite3_fprintf(p->out, "%d errors out of %d tests\n", nErr, nTest); }else if( c=='s' && cli_strncmp(azArg[0], "separator", n)==0 ){ @@ -10528,7 +11212,8 @@ static int do_meta_command(char *zLine, ShellState *p){ bDebug = 1; }else { - eputf("Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); + sqlite3_fprintf(stderr, + "Unknown option \"%s\" on \"%s\"\n", azArg[i], azArg[0]); showHelp(p->out, azArg[0]); rc = 1; goto meta_command_exit; @@ -10551,7 +11236,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else{ zSql = "SELECT lower(name) as tname FROM sqlite_schema" " WHERE type='table' AND coalesce(rootpage,0)>1" - " AND name NOT LIKE 'sqlite_%'" + " AND name NOT LIKE 'sqlite__%' ESCAPE '_'" " ORDER BY 1 collate nocase"; } sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); @@ -10606,7 +11291,7 @@ static int do_meta_command(char *zLine, ShellState *p){ freeText(&sQuery); freeText(&sSql); if( bDebug ){ - oputf("%s\n", zSql); + sqlite3_fprintf(p->out, "%s\n", zSql); }else{ shell_exec(p, zSql, 0); } @@ -10616,7 +11301,7 @@ static int do_meta_command(char *zLine, ShellState *p){ char *zRevText = /* Query for reversible to-blob-to-text check */ "SELECT lower(name) as tname FROM sqlite_schema\n" "WHERE type='table' AND coalesce(rootpage,0)>1\n" - "AND name NOT LIKE 'sqlite_%%'%s\n" + "AND name NOT LIKE 'sqlite__%%' ESCAPE '_'%s\n" "ORDER BY 1 collate nocase"; zRevText = sqlite3_mprintf(zRevText, zLike? " AND name LIKE $tspec" : ""); zRevText = sqlite3_mprintf( @@ -10636,7 +11321,7 @@ static int do_meta_command(char *zLine, ShellState *p){ "' OR ') as query, tname from tabcols group by tname)" , zRevText); shell_check_oom(zRevText); - if( bDebug ) oputf("%s\n", zRevText); + if( bDebug ) sqlite3_fprintf(p->out, "%s\n", zRevText); lrc = sqlite3_prepare_v2(p->db, zRevText, -1, &pStmt, 0); if( lrc!=SQLITE_OK ){ /* assert(lrc==SQLITE_NOMEM); // might also be SQLITE_ERROR if the @@ -10649,7 +11334,7 @@ static int do_meta_command(char *zLine, ShellState *p){ const char *zGenQuery = (char*)sqlite3_column_text(pStmt,0); sqlite3_stmt *pCheckStmt; lrc = sqlite3_prepare_v2(p->db, zGenQuery, -1, &pCheckStmt, 0); - if( bDebug ) oputf("%s\n", zGenQuery); + if( bDebug ) sqlite3_fprintf(p->out, "%s\n", zGenQuery); if( lrc!=SQLITE_OK ){ rc = 1; }else{ @@ -10657,7 +11342,8 @@ static int do_meta_command(char *zLine, ShellState *p){ double countIrreversible = sqlite3_column_double(pCheckStmt, 0); if( countIrreversible>0 ){ int sz = (int)(countIrreversible + 0.5); - eputf("Digest includes %d invalidly encoded text field%s.\n", + sqlite3_fprintf(stderr, + "Digest includes %d invalidly encoded text field%s.\n", sz, (sz>1)? "s": ""); } } @@ -10691,11 +11377,11 @@ static int do_meta_command(char *zLine, ShellState *p){ zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"", zCmd, azArg[i]); } - consoleRestore(); + /*consoleRestore();*/ x = zCmd!=0 ? system(zCmd) : 1; - consoleRenewSetup(); + /*consoleRenewSetup();*/ sqlite3_free(zCmd); - if( x ) eputf("System command returns %d\n", x); + if( x ) sqlite3_fprintf(stderr,"System command returns %d\n", x); }else #endif /* !defined(SQLITE_NOHAVE_SYSTEM) && !defined(SQLITE_SHELL_FIDDLE) */ @@ -10708,46 +11394,48 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 1; goto meta_command_exit; } - oputf("%12.12s: %s\n","echo", + sqlite3_fprintf(p->out, "%12.12s: %s\n","echo", azBool[ShellHasFlag(p, SHFLG_Echo)]); - oputf("%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); - oputf("%12.12s: %s\n","explain", + sqlite3_fprintf(p->out, "%12.12s: %s\n","eqp", azBool[p->autoEQP&3]); + sqlite3_fprintf(p->out, "%12.12s: %s\n","explain", p->mode==MODE_Explain ? "on" : p->autoExplain ? "auto" : "off"); - oputf("%12.12s: %s\n","headers", azBool[p->showHeader!=0]); + sqlite3_fprintf(p->out, "%12.12s: %s\n","headers", + azBool[p->showHeader!=0]); if( p->mode==MODE_Column || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) ){ - oputf("%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", + sqlite3_fprintf(p->out, + "%12.12s: %s --wrap %d --wordwrap %s --%squote\n", "mode", modeDescr[p->mode], p->cmOpts.iWrap, p->cmOpts.bWordWrap ? "on" : "off", p->cmOpts.bQuote ? "" : "no"); }else{ - oputf("%12.12s: %s\n","mode", modeDescr[p->mode]); + sqlite3_fprintf(p->out, "%12.12s: %s\n","mode", modeDescr[p->mode]); } - oputf("%12.12s: ", "nullvalue"); - output_c_string(p->nullValue); - oputz("\n"); - oputf("%12.12s: %s\n","output", + sqlite3_fprintf(p->out, "%12.12s: ", "nullvalue"); + output_c_string(p->out, p->nullValue); + sqlite3_fputs("\n", p->out); + sqlite3_fprintf(p->out, "%12.12s: %s\n","output", strlen30(p->outfile) ? p->outfile : "stdout"); - oputf("%12.12s: ", "colseparator"); - output_c_string(p->colSeparator); - oputz("\n"); - oputf("%12.12s: ", "rowseparator"); - output_c_string(p->rowSeparator); - oputz("\n"); + sqlite3_fprintf(p->out, "%12.12s: ", "colseparator"); + output_c_string(p->out, p->colSeparator); + sqlite3_fputs("\n", p->out); + sqlite3_fprintf(p->out, "%12.12s: ", "rowseparator"); + output_c_string(p->out, p->rowSeparator); + sqlite3_fputs("\n", p->out); switch( p->statsOn ){ case 0: zOut = "off"; break; default: zOut = "on"; break; case 2: zOut = "stmt"; break; case 3: zOut = "vmstep"; break; } - oputf("%12.12s: %s\n","stats", zOut); - oputf("%12.12s: ", "width"); + sqlite3_fprintf(p->out, "%12.12s: %s\n","stats", zOut); + sqlite3_fprintf(p->out, "%12.12s: ", "width"); for (i=0;i<p->nWidth;i++) { - oputf("%d ", p->colWidth[i]); + sqlite3_fprintf(p->out, "%d ", p->colWidth[i]); } - oputz("\n"); - oputf("%12.12s: %s\n", "filename", + sqlite3_fputs("\n", p->out); + sqlite3_fprintf(p->out, "%12.12s: %s\n", "filename", p->pAuxDb->zDbFilename ? p->pAuxDb->zDbFilename : ""); }else @@ -10809,7 +11497,7 @@ static int do_meta_command(char *zLine, ShellState *p){ appendText(&s, ".sqlite_schema ", 0); if( c=='t' ){ appendText(&s," WHERE type IN ('table','view')" - " AND name NOT LIKE 'sqlite_%'" + " AND name NOT LIKE 'sqlite__%' ESCAPE '_'" " AND name LIKE ?1", 0); }else{ appendText(&s," WHERE type='index'" @@ -10865,9 +11553,10 @@ static int do_meta_command(char *zLine, ShellState *p){ for(i=0; i<nPrintRow; i++){ for(j=i; j<nRow; j+=nPrintRow){ char *zSp = j<nPrintRow ? "" : " "; - oputf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); + sqlite3_fprintf(p->out, + "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); } - oputz("\n"); + sqlite3_fputs("\n", p->out); } } @@ -10879,7 +11568,7 @@ static int do_meta_command(char *zLine, ShellState *p){ /* Begin redirecting output to the file "testcase-out.txt" */ if( c=='t' && cli_strcmp(azArg[0],"testcase")==0 ){ output_reset(p); - p->out = output_file_open("testcase-out.txt", 0); + p->out = output_file_open("testcase-out.txt"); if( p->out==0 ){ eputz("Error: cannot open 'testcase-out.txt'\n"); } @@ -10902,7 +11591,7 @@ static int do_meta_command(char *zLine, ShellState *p){ {"always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" }, {"assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" }, /*{"benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/ - /*{"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/ + {"bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "SIZE INT-ARRAY"}, {"byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" }, {"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" }, {"fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"args..." }, @@ -10912,7 +11601,7 @@ static int do_meta_command(char *zLine, ShellState *p){ {"json_selfcheck", SQLITE_TESTCTRL_JSON_SELFCHECK ,0,"BOOLEAN" }, {"localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" }, {"never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" }, - {"optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK" }, + {"optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK ..."}, #ifdef YYCOVERAGE {"parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" }, #endif @@ -10923,7 +11612,6 @@ static int do_meta_command(char *zLine, ShellState *p){ {"seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" }, {"sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" }, {"tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" }, - {"uselongdouble", SQLITE_TESTCTRL_USELONGDOUBLE,0,"?BOOLEAN|\"default\"?"}, }; int testctrl = -1; int iCtrl = -1; @@ -10943,10 +11631,10 @@ static int do_meta_command(char *zLine, ShellState *p){ /* --help lists all test-controls */ if( cli_strcmp(zCmd,"help")==0 ){ - oputz("Available test-controls:\n"); + sqlite3_fputs("Available test-controls:\n", p->out); for(i=0; i<ArraySize(aCtrl); i++){ if( aCtrl[i].unSafe && !ShellHasFlag(p,SHFLG_TestingMode) ) continue; - oputf(" .testctrl %s %s\n", + sqlite3_fprintf(p->out, " .testctrl %s %s\n", aCtrl[i].zCtrlName, aCtrl[i].zUsage); } rc = 1; @@ -10963,7 +11651,7 @@ static int do_meta_command(char *zLine, ShellState *p){ testctrl = aCtrl[i].ctrlCode; iCtrl = i; }else{ - eputf("Error: ambiguous test-control: \"%s\"\n" + sqlite3_fprintf(stderr,"Error: ambiguous test-control: \"%s\"\n" "Use \".testctrl --help\" for help\n", zCmd); rc = 1; goto meta_command_exit; @@ -10971,13 +11659,131 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( testctrl<0 ){ - eputf("Error: unknown test-control: %s\n" + sqlite3_fprintf(stderr,"Error: unknown test-control: %s\n" "Use \".testctrl --help\" for help\n", zCmd); }else{ switch(testctrl){ + /* Special processing for .testctrl opt MASK ... + ** Each MASK argument can be one of: + ** + ** +LABEL Enable the named optimization + ** + ** -LABEL Disable the named optimization + ** + ** INTEGER Mask of optimizations to disable + */ + case SQLITE_TESTCTRL_OPTIMIZATIONS: { + static const struct { + unsigned int mask; /* Mask for this optimization */ + unsigned int bDsply; /* Display this on output */ + const char *zLabel; /* Name of optimization */ + } aLabel[] = { + { 0x00000001, 1, "QueryFlattener" }, + { 0x00000001, 0, "Flatten" }, + { 0x00000002, 1, "WindowFunc" }, + { 0x00000004, 1, "GroupByOrder" }, + { 0x00000008, 1, "FactorOutConst" }, + { 0x00000010, 1, "DistinctOpt" }, + { 0x00000020, 1, "CoverIdxScan" }, + { 0x00000040, 1, "OrderByIdxJoin" }, + { 0x00000080, 1, "Transitive" }, + { 0x00000100, 1, "OmitNoopJoin" }, + { 0x00000200, 1, "CountOfView" }, + { 0x00000400, 1, "CurosrHints" }, + { 0x00000800, 1, "Stat4" }, + { 0x00001000, 1, "PushDown" }, + { 0x00002000, 1, "SimplifyJoin" }, + { 0x00004000, 1, "SkipScan" }, + { 0x00008000, 1, "PropagateConst" }, + { 0x00010000, 1, "MinMaxOpt" }, + { 0x00020000, 1, "SeekScan" }, + { 0x00040000, 1, "OmitOrderBy" }, + { 0x00080000, 1, "BloomFilter" }, + { 0x00100000, 1, "BloomPulldown" }, + { 0x00200000, 1, "BalancedMerge" }, + { 0x00400000, 1, "ReleaseReg" }, + { 0x00800000, 1, "FlttnUnionAll" }, + { 0x01000000, 1, "IndexedEXpr" }, + { 0x02000000, 1, "Coroutines" }, + { 0x04000000, 1, "NullUnusedCols" }, + { 0x08000000, 1, "OnePass" }, + { 0x10000000, 1, "OrderBySubq" }, + { 0x20000000, 1, "StarQuery" }, + { 0xffffffff, 0, "All" }, + }; + unsigned int curOpt; + unsigned int newOpt; + unsigned int m; + int ii; + int nOff; + sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt); + newOpt = curOpt; + for(ii=2; ii<nArg; ii++){ + const char *z = azArg[ii]; + int useLabel = 0; + const char *zLabel = 0; + if( (z[0]=='+'|| z[0]=='-') && !IsDigit(z[1]) ){ + useLabel = z[0]; + zLabel = &z[1]; + }else if( !IsDigit(z[0]) && z[0]!=0 && !IsDigit(z[1]) ){ + useLabel = '+'; + zLabel = z; + }else{ + newOpt = (unsigned int)strtol(z,0,0); + } + if( useLabel ){ + int jj; + for(jj=0; jj<ArraySize(aLabel); jj++){ + if( sqlite3_stricmp(zLabel, aLabel[jj].zLabel)==0 ) break; + } + if( jj>=ArraySize(aLabel) ){ + sqlite3_fprintf(stderr, + "Error: no such optimization: \"%s\"\n", zLabel); + sqlite3_fputs("Should be one of:", stderr); + for(jj=0; jj<ArraySize(aLabel); jj++){ + sqlite3_fprintf(stderr," %s", aLabel[jj].zLabel); + } + sqlite3_fputs("\n", stderr); + rc = 1; + goto meta_command_exit; + } + if( useLabel=='+' ){ + newOpt &= ~aLabel[jj].mask; + }else{ + newOpt |= aLabel[jj].mask; + } + } + } + if( curOpt!=newOpt ){ + sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt); + } + for(ii=nOff=0, m=1; ii<32; ii++, m <<= 1){ + if( m & newOpt ) nOff++; + } + if( nOff<12 ){ + sqlite3_fputs("+All", p->out); + for(ii=0; ii<ArraySize(aLabel); ii++){ + if( !aLabel[ii].bDsply ) continue; + if( (newOpt & aLabel[ii].mask)!=0 ){ + sqlite3_fprintf(p->out, " -%s", aLabel[ii].zLabel); + } + } + }else{ + sqlite3_fputs("-All", p->out); + for(ii=0; ii<ArraySize(aLabel); ii++){ + if( !aLabel[ii].bDsply ) continue; + if( (newOpt & aLabel[ii].mask)==0 ){ + sqlite3_fprintf(p->out, " +%s", aLabel[ii].zLabel); + } + } + } + sqlite3_fputs("\n", p->out); + rc2 = isOk = 3; + break; + } + /* sqlite3_test_control(int, db, int) */ - case SQLITE_TESTCTRL_OPTIMIZATIONS: case SQLITE_TESTCTRL_FK_NO_ACTION: if( nArg==3 ){ unsigned int opt = (unsigned int)strtol(azArg[2], 0, 0); @@ -11012,7 +11818,7 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3 *db; if( ii==0 && cli_strcmp(azArg[2],"random")==0 ){ sqlite3_randomness(sizeof(ii),&ii); - sputf(stdout, "-- random seed: %d\n", ii); + sqlite3_fprintf(stdout, "-- random seed: %d\n", ii); } if( nArg==3 ){ db = 0; @@ -11046,21 +11852,6 @@ static int do_meta_command(char *zLine, ShellState *p){ } break; - /* sqlite3_test_control(int, int) */ - case SQLITE_TESTCTRL_USELONGDOUBLE: { - int opt = -1; - if( nArg==3 ){ - if( cli_strcmp(azArg[2],"default")==0 ){ - opt = 2; - }else{ - opt = booleanValue(azArg[2]); - } - } - rc2 = sqlite3_test_control(testctrl, opt); - isOk = 1; - break; - } - /* sqlite3_test_control(sqlite3*) */ case SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: rc2 = sqlite3_test_control(testctrl, p->db); @@ -11080,7 +11871,7 @@ static int do_meta_command(char *zLine, ShellState *p){ case SQLITE_TESTCTRL_SEEK_COUNT: { u64 x = 0; rc2 = sqlite3_test_control(testctrl, p->db, &x); - oputf("%llu\n", x); + sqlite3_fprintf(p->out, "%llu\n", x); isOk = 3; break; } @@ -11111,11 +11902,11 @@ static int do_meta_command(char *zLine, ShellState *p){ int val = 0; rc2 = sqlite3_test_control(testctrl, -id, &val); if( rc2!=SQLITE_OK ) break; - if( id>1 ) oputz(" "); - oputf("%d: %d", id, val); + if( id>1 ) sqlite3_fputs(" ", p->out); + sqlite3_fprintf(p->out, "%d: %d", id, val); id++; } - if( id>1 ) oputz("\n"); + if( id>1 ) sqlite3_fputs("\n", p->out); isOk = 3; } break; @@ -11138,6 +11929,49 @@ static int do_meta_command(char *zLine, ShellState *p){ } sqlite3_test_control(testctrl, &rc2); break; + case SQLITE_TESTCTRL_BITVEC_TEST: { + /* Examples: + ** .testctrl bitvec_test 100 6,1 -- Show BITVEC constants + ** .testctrl bitvec_test 1000 1,12,7,3 -- Simple test + ** ---- -------- + ** size of Bitvec -----^ ^--- aOp array. 0 added at end. + ** + ** See comments on sqlite3BitvecBuiltinTest() for more information + ** about the aOp[] array. + */ + int iSize; + const char *zTestArg; + int nOp; + int ii, jj, x; + int *aOp; + if( nArg!=4 ){ + sqlite3_fprintf(stderr, + "ERROR - should be: \".testctrl bitvec_test SIZE INT-ARRAY\"\n" + ); + rc = 1; + goto meta_command_exit; + } + isOk = 3; + iSize = (int)integerValue(azArg[2]); + zTestArg = azArg[3]; + nOp = (int)strlen(zTestArg)+1; + aOp = malloc( sizeof(int)*(nOp+1) ); + shell_check_oom(aOp); + memset(aOp, 0, sizeof(int)*(nOp+1) ); + for(ii = jj = x = 0; zTestArg[ii]!=0; ii++){ + if( IsDigit(zTestArg[ii]) ){ + x = x*10 + zTestArg[ii] - '0'; + }else{ + aOp[jj++] = x; + x = 0; + } + } + aOp[jj] = x; + x = sqlite3_test_control(testctrl, iSize, aOp); + sqlite3_fprintf(p->out, "result: %d\n", x); + free(aOp); + break; + } case SQLITE_TESTCTRL_FAULT_INSTALL: { int kk; int bShowHelp = nArg<=2; @@ -11157,14 +11991,22 @@ static int do_meta_command(char *zLine, ShellState *p){ faultsim_state.nHit = 0; sqlite3_test_control(testctrl, faultsim_callback); }else if( cli_strcmp(z,"status")==0 ){ - oputf("faultsim.iId: %d\n", faultsim_state.iId); - oputf("faultsim.iErr: %d\n", faultsim_state.iErr); - oputf("faultsim.iCnt: %d\n", faultsim_state.iCnt); - oputf("faultsim.nHit: %d\n", faultsim_state.nHit); - oputf("faultsim.iInterval: %d\n", faultsim_state.iInterval); - oputf("faultsim.eVerbose: %d\n", faultsim_state.eVerbose); - oputf("faultsim.nRepeat: %d\n", faultsim_state.nRepeat); - oputf("faultsim.nSkip: %d\n", faultsim_state.nSkip); + sqlite3_fprintf(p->out, "faultsim.iId: %d\n", + faultsim_state.iId); + sqlite3_fprintf(p->out, "faultsim.iErr: %d\n", + faultsim_state.iErr); + sqlite3_fprintf(p->out, "faultsim.iCnt: %d\n", + faultsim_state.iCnt); + sqlite3_fprintf(p->out, "faultsim.nHit: %d\n", + faultsim_state.nHit); + sqlite3_fprintf(p->out, "faultsim.iInterval: %d\n", + faultsim_state.iInterval); + sqlite3_fprintf(p->out, "faultsim.eVerbose: %d\n", + faultsim_state.eVerbose); + sqlite3_fprintf(p->out, "faultsim.nRepeat: %d\n", + faultsim_state.nRepeat); + sqlite3_fprintf(p->out, "faultsim.nSkip: %d\n", + faultsim_state.nSkip); }else if( cli_strcmp(z,"-v")==0 ){ if( faultsim_state.eVerbose<2 ) faultsim_state.eVerbose++; }else if( cli_strcmp(z,"-q")==0 ){ @@ -11182,7 +12024,8 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( cli_strcmp(z,"-?")==0 || sqlite3_strglob("*help*",z)==0){ bShowHelp = 1; }else{ - eputf("Unrecognized fault_install argument: \"%s\"\n", + sqlite3_fprintf(stderr, + "Unrecognized fault_install argument: \"%s\"\n", azArg[kk]); rc = 1; bShowHelp = 1; @@ -11190,7 +12033,7 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( bShowHelp ){ - oputz( + sqlite3_fputs( "Usage: .testctrl fault_install ARGS\n" "Possible arguments:\n" " off Disable faultsim\n" @@ -11204,6 +12047,7 @@ static int do_meta_command(char *zLine, ShellState *p){ " --interval N Trigger only after every N-th call\n" " --repeat N Turn off after N hits. 0 means never\n" " --skip N Skip the first N encounters\n" + ,p->out ); } break; @@ -11211,12 +12055,13 @@ static int do_meta_command(char *zLine, ShellState *p){ } } if( isOk==0 && iCtrl>=0 ){ - oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); + sqlite3_fprintf(p->out, + "Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage); rc = 1; }else if( isOk==1 ){ - oputf("%d\n", rc2); + sqlite3_fprintf(p->out, "%d\n", rc2); }else if( isOk==2 ){ - oputf("0x%08x\n", rc2); + sqlite3_fprintf(p->out, "0x%08x\n", rc2); } }else #endif /* !defined(SQLITE_UNTESTABLE) */ @@ -11271,13 +12116,13 @@ static int do_meta_command(char *zLine, ShellState *p){ mType |= SQLITE_TRACE_CLOSE; } else { - eputf("Unknown option \"%s\" on \".trace\"\n", z); + sqlite3_fprintf(stderr,"Unknown option \"%s\" on \".trace\"\n", z); rc = 1; goto meta_command_exit; } }else{ output_file_close(p->traceOut); - p->traceOut = output_file_open(z, 0); + p->traceOut = output_file_open(z); } } if( p->traceOut==0 ){ @@ -11314,86 +12159,23 @@ static int do_meta_command(char *zLine, ShellState *p){ }else #endif -#if SQLITE_USER_AUTHENTICATION - if( c=='u' && cli_strncmp(azArg[0], "user", n)==0 ){ - if( nArg<2 ){ - eputz("Usage: .user SUBCOMMAND ...\n"); - rc = 1; - goto meta_command_exit; - } - open_db(p, 0); - if( cli_strcmp(azArg[1],"login")==0 ){ - if( nArg!=4 ){ - eputz("Usage: .user login USER PASSWORD\n"); - rc = 1; - goto meta_command_exit; - } - rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3], - strlen30(azArg[3])); - if( rc ){ - eputf("Authentication failed for user %s\n", azArg[2]); - rc = 1; - } - }else if( cli_strcmp(azArg[1],"add")==0 ){ - if( nArg!=5 ){ - eputz("Usage: .user add USER PASSWORD ISADMIN\n"); - rc = 1; - goto meta_command_exit; - } - rc = sqlite3_user_add(p->db, azArg[2], azArg[3], strlen30(azArg[3]), - booleanValue(azArg[4])); - if( rc ){ - eputf("User-Add failed: %d\n", rc); - rc = 1; - } - }else if( cli_strcmp(azArg[1],"edit")==0 ){ - if( nArg!=5 ){ - eputz("Usage: .user edit USER PASSWORD ISADMIN\n"); - rc = 1; - goto meta_command_exit; - } - rc = sqlite3_user_change(p->db, azArg[2], azArg[3], strlen30(azArg[3]), - booleanValue(azArg[4])); - if( rc ){ - eputf("User-Edit failed: %d\n", rc); - rc = 1; - } - }else if( cli_strcmp(azArg[1],"delete")==0 ){ - if( nArg!=3 ){ - eputz("Usage: .user delete USER\n"); - rc = 1; - goto meta_command_exit; - } - rc = sqlite3_user_delete(p->db, azArg[2]); - if( rc ){ - eputf("User-Delete failed: %d\n", rc); - rc = 1; - } - }else{ - eputz("Usage: .user login|add|edit|delete ...\n"); - rc = 1; - goto meta_command_exit; - } - }else -#endif /* SQLITE_USER_AUTHENTICATION */ - if( c=='v' && cli_strncmp(azArg[0], "version", n)==0 ){ char *zPtrSz = sizeof(void*)==8 ? "64-bit" : "32-bit"; - oputf("SQLite %s %s\n" /*extra-version-info*/, + sqlite3_fprintf(p->out, "SQLite %s %s\n" /*extra-version-info*/, sqlite3_libversion(), sqlite3_sourceid()); #if SQLITE_HAVE_ZLIB - oputf("zlib version %s\n", zlibVersion()); + sqlite3_fprintf(p->out, "zlib version %s\n", zlibVersion()); #endif #define CTIMEOPT_VAL_(opt) #opt #define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt) #if defined(__clang__) && defined(__clang_major__) - oputf("clang-" CTIMEOPT_VAL(__clang_major__) "." + sqlite3_fprintf(p->out, "clang-" CTIMEOPT_VAL(__clang_major__) "." CTIMEOPT_VAL(__clang_minor__) "." CTIMEOPT_VAL(__clang_patchlevel__) " (%s)\n", zPtrSz); #elif defined(_MSC_VER) - oputf("msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz); + sqlite3_fprintf(p->out, "msvc-" CTIMEOPT_VAL(_MSC_VER) " (%s)\n", zPtrSz); #elif defined(__GNUC__) && defined(__VERSION__) - oputf("gcc-" __VERSION__ " (%s)\n", zPtrSz); + sqlite3_fprintf(p->out, "gcc-" __VERSION__ " (%s)\n", zPtrSz); #endif }else @@ -11403,10 +12185,10 @@ static int do_meta_command(char *zLine, ShellState *p){ if( p->db ){ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs); if( pVfs ){ - oputf("vfs.zName = \"%s\"\n", pVfs->zName); - oputf("vfs.iVersion = %d\n", pVfs->iVersion); - oputf("vfs.szOsFile = %d\n", pVfs->szOsFile); - oputf("vfs.mxPathname = %d\n", pVfs->mxPathname); + sqlite3_fprintf(p->out, "vfs.zName = \"%s\"\n", pVfs->zName); + sqlite3_fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); + sqlite3_fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); + sqlite3_fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); } } }else @@ -11418,13 +12200,13 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_file_control(p->db, "main", SQLITE_FCNTL_VFS_POINTER, &pCurrent); } for(pVfs=sqlite3_vfs_find(0); pVfs; pVfs=pVfs->pNext){ - oputf("vfs.zName = \"%s\"%s\n", pVfs->zName, + sqlite3_fprintf(p->out, "vfs.zName = \"%s\"%s\n", pVfs->zName, pVfs==pCurrent ? " <--- CURRENT" : ""); - oputf("vfs.iVersion = %d\n", pVfs->iVersion); - oputf("vfs.szOsFile = %d\n", pVfs->szOsFile); - oputf("vfs.mxPathname = %d\n", pVfs->mxPathname); + sqlite3_fprintf(p->out, "vfs.iVersion = %d\n", pVfs->iVersion); + sqlite3_fprintf(p->out, "vfs.szOsFile = %d\n", pVfs->szOsFile); + sqlite3_fprintf(p->out, "vfs.mxPathname = %d\n", pVfs->mxPathname); if( pVfs->pNext ){ - oputz("-----------------------------------\n"); + sqlite3_fputs("-----------------------------------\n", p->out); } } }else @@ -11435,7 +12217,7 @@ static int do_meta_command(char *zLine, ShellState *p){ if( p->db ){ sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName); if( zVfsName ){ - oputf("%s\n", zVfsName); + sqlite3_fprintf(p->out, "%s\n", zVfsName); sqlite3_free(zVfsName); } } @@ -11459,7 +12241,7 @@ static int do_meta_command(char *zLine, ShellState *p){ }else { - eputf("Error: unknown command or invalid arguments: " + sqlite3_fprintf(stderr,"Error: unknown command or invalid arguments: " " \"%s\". Enter \".help\" for help\n", azArg[0]); rc = 1; } @@ -11500,7 +12282,6 @@ static QuickScanState quickscan(char *zLine, QuickScanState qss, char cWait = (char)qss; /* intentional narrowing loss */ if( cWait==0 ){ PlainScan: - assert( cWait==0 ); while( (cin = *zLine++)!=0 ){ if( IsSpace(cin) ) continue; @@ -11526,7 +12307,7 @@ static QuickScanState quickscan(char *zLine, QuickScanState qss, break; case '[': cin = ']'; - deliberate_fall_through; + deliberate_fall_through; /* FALLTHRU */ case '`': case '\'': case '"': cWait = cin; qss = QSS_HasDark | cWait; @@ -11552,7 +12333,6 @@ static QuickScanState quickscan(char *zLine, QuickScanState qss, if( *zLine != '/' ) continue; ++zLine; - cWait = 0; CONTINUE_PROMPT_AWAITC(pst, 0); qss = QSS_SETV(qss, 0); goto PlainScan; @@ -11562,9 +12342,8 @@ static QuickScanState quickscan(char *zLine, QuickScanState qss, ++zLine; continue; } - deliberate_fall_through; + deliberate_fall_through; /* FALLTHRU */ case ']': - cWait = 0; CONTINUE_PROMPT_AWAITC(pst, 0); qss = QSS_SETV(qss, 0); goto PlainScan; @@ -11597,7 +12376,7 @@ static int line_is_command_terminator(char *zLine){ ** out of the build if compiling with SQLITE_OMIT_COMPLETE. */ #ifdef SQLITE_OMIT_COMPLETE -# error the CLI application is imcompatable with SQLITE_OMIT_COMPLETE. +# error the CLI application is incompatible with SQLITE_OMIT_COMPLETE. #endif /* @@ -11640,7 +12419,10 @@ static int doAutoDetectRestore(ShellState *p, const char *zSql){ case 0: { const char *zExpect = "PRAGMA foreign_keys=OFF;"; assert( strlen(zExpect)==24 ); - if( p->bSafeMode==0 && memcmp(zSql, zExpect, 25)==0 ){ + if( p->bSafeMode==0 + && strlen(zSql)>=24 + && memcmp(zSql, zExpect, 25)==0 + ){ p->eRestoreState = 1; }else{ p->eRestoreState = 7; @@ -11708,7 +12490,7 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0; BEGIN_TIMER; rc = shell_exec(p, zSql, &zErrMsg); - END_TIMER; + END_TIMER(p->out); if( rc || zErrMsg ){ char zPrefix[100]; const char *zErrorTail; @@ -11732,7 +12514,7 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ }else{ sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%s:", zErrorType); } - eputf("%s %s\n", zPrefix, zErrorTail); + sqlite3_fprintf(stderr,"%s %s\n", zPrefix, zErrorTail); sqlite3_free(zErrMsg); zErrMsg = 0; return 1; @@ -11741,7 +12523,7 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ sqlite3_snprintf(sizeof(zLineBuf), zLineBuf, "changes: %lld total_changes: %lld", sqlite3_changes64(p->db), sqlite3_total_changes64(p->db)); - oputf("%s\n", zLineBuf); + sqlite3_fprintf(p->out, "%s\n", zLineBuf); } if( doAutoDetectRestore(p, zSql) ) return 1; @@ -11749,7 +12531,10 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){ } static void echo_group_input(ShellState *p, const char *zDo){ - if( ShellHasFlag(p, SHFLG_Echo) ) oputf("%s\n", zDo); + if( ShellHasFlag(p, SHFLG_Echo) ){ + sqlite3_fprintf(p->out, "%s\n", zDo); + fflush(p->out); + } } #ifdef SQLITE_SHELL_FIDDLE @@ -11770,7 +12555,7 @@ static char *one_input_line(FILE *in, char *zPrior, int isContinuation){ if(!z || !*z){ return 0; } - while(*z && isspace(*z)) ++z; + while(*z && IsSpace(*z)) ++z; zBegin = z; for(; *z && '\n'!=*z; ++nZ, ++z){} if(nZ>0 && '\r'==zBegin[nZ-1]){ @@ -11807,7 +12592,7 @@ static int process_input(ShellState *p){ if( p->inputNesting==MAX_INPUT_NESTING ){ /* This will be more informative in a later version. */ - eputf("Input nesting limit (%d) reached at line %d." + sqlite3_fprintf(stderr,"Input nesting limit (%d) reached at line %d." " Check recursion.\n", MAX_INPUT_NESTING, p->lineno); return 1; } @@ -11819,7 +12604,7 @@ static int process_input(ShellState *p){ zLine = one_input_line(p->in, zLine, nSql>0); if( zLine==0 ){ /* End of input */ - if( p->in==0 && stdin_is_interactive ) oputz("\n"); + if( p->in==0 && stdin_is_interactive ) sqlite3_fputs("\n", p->out); break; } if( seenInterrupt ){ @@ -12039,15 +12824,15 @@ static void process_sqliterc( shell_check_oom(zBuf); sqliterc = zBuf; } - p->in = fopen(sqliterc,"rb"); + p->in = sqlite3_fopen(sqliterc,"rb"); if( p->in ){ if( stdin_is_interactive ){ - eputf("-- Loading resources from %s\n", sqliterc); + sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc); } if( process_input(p) && bail_on_error ) exit(1); fclose(p->in); }else if( sqliterc_override!=0 ){ - eputf("cannot open: \"%s\"\n", sqliterc); + sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc); if( bail_on_error ) exit(1); } p->in = inSaved; @@ -12075,6 +12860,7 @@ static const char zOptions[] = " -deserialize open the database using sqlite3_deserialize()\n" #endif " -echo print inputs before execution\n" + " -escape T ctrl-char escape; T is one of: symbol, ascii, off\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) @@ -12116,19 +12902,17 @@ static const char zOptions[] = " -unsafe-testing allow unsafe commands and modes for testing\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_HAVE_ZLIB " -zip open the file as a ZIP Archive\n" #endif ; static void usage(int showDetail){ - eputf("Usage: %s [OPTIONS] [FILENAME [SQL]]\n" + sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL...]]\n" "FILENAME is the name of an SQLite database. A new database is created\n" "if the file does not previously exist. Defaults to :memory:.\n", Argv0); if( showDetail ){ - eputf("OPTIONS include:\n%s", zOptions); + sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions); }else{ eputz("Use the -help option for additional information\n"); } @@ -12153,6 +12937,9 @@ static void main_init(ShellState *data) { memset(data, 0, sizeof(*data)); data->normalMode = data->cMode = data->mode = MODE_List; data->autoExplain = 1; +#ifdef _WIN32 + data->crlfMode = 1; +#endif data->pAuxDb = &data->aAuxDb[0]; memcpy(data->colSeparator,SEP_Column, 2); memcpy(data->rowSeparator,SEP_Row, 2); @@ -12188,7 +12975,7 @@ static void printBold(const char *zText){ } #else static void printBold(const char *zText){ - sputf(stdout, "\033[1m%s\033[0m", zText); + sqlite3_fprintf(stdout, "\033[1m%s\033[0m", zText); } #endif @@ -12198,7 +12985,8 @@ static void printBold(const char *zText){ */ static char *cmdline_option_value(int argc, char **argv, int i){ if( i==argc ){ - eputf("%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); + sqlite3_fprintf(stderr, + "%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); exit(1); } return argv[i]; @@ -12208,6 +12996,15 @@ static void sayAbnormalExit(void){ if( seenInterrupt ) eputz("Program interrupted.\n"); } +/* Routine to output from vfstrace +*/ +static int vfstraceOut(const char *z, void *pArg){ + ShellState *p = (ShellState*)pArg; + sqlite3_fputs(z, p->out); + fflush(p->out); + return 1; +} + #ifndef SQLITE_SHELL_IS_UTF8 # if (defined(_WIN32) || defined(WIN32)) \ && (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__))) @@ -12235,7 +13032,6 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ # define data shellState #else ShellState data; - StreamsAreConsole consStreams = SAC_NoConsole; #endif const char *zInitFile = 0; int i; @@ -12244,6 +13040,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ int readStdin = 1; int nCmd = 0; int nOptsEnd = argc; + int bEnableVfstrace = 0; char **azCmd = 0; const char *zVfs = 0; /* Value of -vfs command-line option */ #if !SQLITE_SHELL_IS_UTF8 @@ -12257,10 +13054,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ stdout_is_console = 1; data.wasm.zDefaultDbName = "/fiddle.sqlite3"; #else - consStreams = consoleClassifySetup(stdin, stdout, stderr); - stdin_is_interactive = (consStreams & SAC_InConsole)!=0; - stdout_is_console = (consStreams & SAC_OutConsole)!=0; - atexit(consoleRestore); + stdin_is_interactive = isatty(0); + stdout_is_console = isatty(1); #endif atexit(sayAbnormalExit); #ifdef SQLITE_DEBUG @@ -12269,9 +13064,15 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ #if !defined(_WIN32_WCE) if( getenv("SQLITE_DEBUG_BREAK") ){ if( isatty(0) && isatty(2) ){ - eputf("attach debugger to process %d and press any key to continue.\n", + char zLine[100]; + sqlite3_fprintf(stderr, + "attach debugger to process %d and press ENTER to continue...", GETPID()); - fgetc(stdin); + if( sqlite3_fgets(zLine, sizeof(zLine), stdin)!=0 + && cli_strcmp(zLine,"stop")==0 + ){ + exit(1); + } }else{ #if defined(_WIN32) || defined(WIN32) #if SQLITE_OS_WINRT @@ -12296,7 +13097,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ #if USE_SYSTEM_SQLITE+0!=1 if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ - eputf("SQLite header and source version mismatch\n%s\n%s\n", + sqlite3_fprintf(stderr, + "SQLite header and source version mismatch\n%s\n%s\n", sqlite3_sourceid(), SQLITE_SOURCE_ID); exit(1); } @@ -12438,17 +13240,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break; default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break; } -#ifdef SQLITE_ENABLE_VFSTRACE }else if( cli_strcmp(z,"-vfstrace")==0 ){ - extern int vfstrace_register( - const char *zTraceName, - const char *zOldVfsName, - int (*xOut)(const char*,void*), - void *pOutArg, - int makeDefault - ); - vfstrace_register("trace",0,(int(*)(const char*,void*))fputs,stderr,1); -#endif + bEnableVfstrace = 1; #ifdef SQLITE_ENABLE_MULTIPLEX }else if( cli_strcmp(z,"-multiplex")==0 ){ extern int sqlite3_multiplex_initialize(const char*,int); @@ -12501,10 +13294,13 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ ShellSetFlag(&data,SHFLG_TestingMode); }else if( cli_strcmp(z,"-safe")==0 ){ /* no-op - catch this on the second pass */ + }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){ + /* skip over the argument */ + i++; } } #ifndef SQLITE_SHELL_FIDDLE - verify_uninitialized(); + if( !bEnableVfstrace ) verify_uninitialized(); #endif @@ -12528,7 +13324,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ if( pVfs ){ sqlite3_vfs_register(pVfs, 1); }else{ - eputf("no such VFS: \"%s\"\n", zVfs); + sqlite3_fprintf(stderr,"no such VFS: \"%s\"\n", zVfs); exit(1); } } @@ -12538,11 +13334,15 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ data.pAuxDb->zDbFilename = ":memory:"; warnInmemoryDb = argc==1; #else - eputf("%s: Error: no database filename specified\n", Argv0); + sqlite3_fprintf(stderr, + "%s: Error: no database filename specified\n", Argv0); return 1; #endif } data.out = stdout; + if( bEnableVfstrace ){ + vfstrace_register("trace",0,vfstraceOut, &data, 1); + } #ifndef SQLITE_SHELL_FIDDLE sqlite3_appendvfs_init(0,0,0); #endif @@ -12596,6 +13396,25 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ }else if( cli_strcmp(z,"-csv")==0 ){ data.mode = MODE_Csv; memcpy(data.colSeparator,",",2); + }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){ + /* See similar code at tag-20250224-1 */ + const char *zEsc = argv[++i]; + int k; + for(k=0; k<ArraySize(shell_EscModeNames); k++){ + if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){ + data.eEscMode = k; + break; + } + } + if( k>=ArraySize(shell_EscModeNames) ){ + sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\"" + " - choices:", zEsc); + for(k=0; k<ArraySize(shell_EscModeNames); k++){ + sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]); + } + sqlite3_fprintf(stderr, "\n"); + exit(1); + } #ifdef SQLITE_HAVE_ZLIB }else if( cli_strcmp(z,"-zip")==0 ){ data.openMode = SHELL_OPEN_ZIPFILE; @@ -12655,7 +13474,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ }else if( cli_strcmp(z,"-bail")==0 ){ /* No-op. The bail_on_error flag should already be set. */ }else if( cli_strcmp(z,"-version")==0 ){ - sputf(stdout, "%s %s (%d-bit)\n", + sqlite3_fprintf(stdout, "%s %s (%d-bit)\n", sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*)); return 0; }else if( cli_strcmp(z,"-interactive")==0 ){ @@ -12693,10 +13512,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ #endif }else if( cli_strcmp(z,"-vfs")==0 ){ i++; -#ifdef SQLITE_ENABLE_VFSTRACE }else if( cli_strcmp(z,"-vfstrace")==0 ){ i++; -#endif #ifdef SQLITE_ENABLE_MULTIPLEX }else if( cli_strcmp(z,"-multiplex")==0 ){ i++; @@ -12717,17 +13534,17 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ open_db(&data, 0); rc = shell_exec(&data, z, &zErrMsg); if( zErrMsg!=0 ){ - eputf("Error: %s\n", zErrMsg); + shellEmitError(zErrMsg); if( bail_on_error ) return rc!=0 ? rc : 1; }else if( rc!=0 ){ - eputf("Error: unable to process SQL \"%s\"\n", z); + sqlite3_fprintf(stderr,"Error: unable to process SQL \"%s\"\n", z); if( bail_on_error ) return rc; } } #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) }else if( cli_strncmp(z, "-A", 2)==0 ){ if( nCmd>0 ){ - eputf("Error: cannot mix regular SQL or dot-commands" + sqlite3_fprintf(stderr,"Error: cannot mix regular SQL or dot-commands" " with \"%s\"\n", z); return 1; } @@ -12746,7 +13563,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ /* Acted upon in first pass. */ }else{ - eputf("%s: Error: unknown option: %s\n", Argv0, z); + sqlite3_fprintf(stderr,"%s: Error: unknown option: %s\n", Argv0, z); eputz("Use -help for a list of options.\n"); return 1; } @@ -12759,6 +13576,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ ** the database filename. */ for(i=0; i<nCmd; i++){ + echo_group_input(&data, azCmd[i]); if( azCmd[i][0]=='.' ){ rc = do_meta_command(azCmd[i], &data); if( rc ){ @@ -12767,13 +13585,13 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ } }else{ open_db(&data, 0); - echo_group_input(&data, azCmd[i]); rc = shell_exec(&data, azCmd[i], &zErrMsg); if( zErrMsg || rc ){ if( zErrMsg!=0 ){ - eputf("Error: %s\n", zErrMsg); + shellEmitError(zErrMsg); }else{ - eputf("Error: unable to process SQL: %s\n", azCmd[i]); + sqlite3_fprintf(stderr, + "Error: unable to process SQL: %s\n", azCmd[i]); } sqlite3_free(zErrMsg); if( rc==0 ) rc = 1; @@ -12788,14 +13606,10 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ char *zHome; char *zHistory; int nHistory; -#if CIO_WIN_WC_XLATE -# define SHELL_CIO_CHAR_SET (stdout_is_console? " (UTF-16 console I/O)" : "") -#else -# define SHELL_CIO_CHAR_SET "" -#endif - sputf(stdout, "SQLite version %s %.19s%s\n" /*extra-version-info*/ + sqlite3_fprintf(stdout, + "SQLite version %s %.19s\n" /*extra-version-info*/ "Enter \".help\" for usage hints.\n", - sqlite3_libversion(), sqlite3_sourceid(), SHELL_CIO_CHAR_SET); + sqlite3_libversion(), sqlite3_sourceid()); if( warnInmemoryDb ){ sputz(stdout, "Connected to a "); printBold("transient in-memory database"); @@ -12812,9 +13626,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ } } if( zHistory ){ shell_read_history(zHistory); } -#if HAVE_READLINE || HAVE_EDITLINE +#if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION) rl_attempted_completion_function = readline_completion; -#elif HAVE_LINENOISE +#elif HAVE_LINENOISE==1 + linenoiseSetCompletionCallback(linenoise_completion); +#elif HAVE_LINENOISE==2 linenoiseSetCompletionCallback(linenoise_completion, NULL); #endif data.in = 0; @@ -12864,9 +13680,12 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ /* Clear the global data structure so that valgrind will detect memory ** leaks */ memset(&data, 0, sizeof(data)); + if( bEnableVfstrace ){ + vfstrace_unregister("trace"); + } #ifdef SQLITE_DEBUG if( sqlite3_memory_used()>mem_main_enter ){ - eputf("Memory leaked: %u bytes\n", + sqlite3_fprintf(stderr,"Memory leaked: %u bytes\n", (unsigned int)(sqlite3_memory_used()-mem_main_enter)); } #endif @@ -12906,7 +13725,7 @@ sqlite3_vfs * fiddle_db_vfs(const char *zDbName){ /* Only for emcc experimentation purposes. */ sqlite3 * fiddle_db_arg(sqlite3 *arg){ - oputf("fiddle_db_arg(%p)\n", (const void*)arg); + sqlite3_fprintf(stdout, "fiddle_db_arg(%p)\n", (const void*)arg); return arg; } @@ -12943,7 +13762,7 @@ void fiddle_reset_db(void){ ** Resolve problem reported in ** https://sqlite.org/forum/forumpost/0b41a25d65 */ - oputz("Rolling back in-progress transaction.\n"); + sqlite3_fputs("Rolling back in-progress transaction.\n", stdout); sqlite3_exec(globalDb,"ROLLBACK", 0, 0, 0); } rc = sqlite3_db_config(globalDb, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0); |