Index: src/shell.c ================================================================== --- src/shell.c +++ src/shell.c @@ -327,10 +327,17 @@ ** Threat stdin as an interactive input if the following variable ** is true. Otherwise, assume stdin is connected to a file or pipe. */ static int stdin_is_interactive = 1; +/* +** On Windows systems we have to know if standard output is a console +** in order to translate UTF-8 into MBCS. The following variable is +** true if translation is required. +*/ +static int stdout_is_console = 1; + /* ** The following is the open SQLite database. We make a pointer ** to this database a static variable so that it can be accessed ** by the SIGINT handler to interrupt database processing. */ @@ -427,10 +434,20 @@ UNUSED_PARAMETER(argc); UNUSED_PARAMETER(argv); sqlite3_result_text(context, zShellStatic, -1, SQLITE_STATIC); } + +/* +** Compute a string length that is limited to what can be stored in +** lower 30 bits of a 32-bit signed integer. +*/ +static int strlen30(const char *z){ + const char *z2 = z; + while( *z2 ){ z2++; } + return 0x3fffffff & (int)(z2 - z); +} /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() @@ -463,10 +480,30 @@ if( n>0 && zLine[n-1]=='\r' ) n--; zLine[n] = 0; break; } } +#if defined(_WIN32) || defined(WIN32) + /* For interactive input on Windows systems, translate the + ** multi-byte characterset characters into UTF-8. */ + if( stdin_is_interactive ){ + extern char *sqlite3_win32_mbcs_to_utf8(const char*); + char *zTrans = sqlite3_win32_mbcs_to_utf8(zLine); + if( zTrans ){ + int nTrans = strlen30(zTrans)+1; + if( nTrans>nLine ){ + zLine = realloc(zLine, nTrans); + if( zLine==0 ){ + sqlite3_free(zTrans); + return 0; + } + } + memcpy(zLine, zTrans, nTrans); + sqlite3_free(zTrans); + } + } +#endif /* defined(_WIN32) || defined(WIN32) */ return zLine; } /* ** Retrieve a single line of input text. @@ -500,10 +537,35 @@ #endif } return zResult; } +/* +** Render output like fprintf(). Except, if the output is going to the +** console and if this is running on a Windows machine, translate the +** output from UTF-8 into MBCS. +*/ +#if defined(_WIN32) || defined(WIN32) +void utf8_printf(FILE *out, const char *zFormat, ...){ + va_list ap; + va_start(ap, zFormat); + if( stdout_is_console && out==stdout ){ + extern char *sqlite3_win32_utf8_to_mbcs(const char*); + char *z1 = sqlite3_vmprintf(zFormat, ap); + char *z2 = sqlite3_win32_utf8_to_mbcs(z1); + sqlite3_free(z1); + fputs(z2, out); + sqlite3_free(z2); + }else{ + vfprintf(out, zFormat, ap); + } + va_end(ap); +} +#else +# define utf8_printf fprintf +#endif + /* ** Shell output mode information from before ".explain on", ** saved so that it can be restored by ".explain off" */ typedef struct SavedModeInfo SavedModeInfo; @@ -605,20 +667,10 @@ /* ** Number of elements in an array */ #define ArraySize(X) (int)(sizeof(X)/sizeof(X[0])) -/* -** Compute a string length that is limited to what can be stored in -** lower 30 bits of a 32-bit signed integer. -*/ -static int strlen30(const char *z){ - const char *z2 = z; - while( *z2 ){ z2++; } - return 0x3fffffff & (int)(z2 - z); -} - /* ** A callback for the sqlite3_log() interface. */ static void shellLog(void *pArg, int iErrCode, const char *zMsg){ ShellState *p = (ShellState*)pArg; @@ -647,23 +699,23 @@ setBinaryMode(out); for(i=0; z[i]; i++){ if( z[i]=='\'' ) nSingle++; } if( nSingle==0 ){ - fprintf(out,"'%s'",z); + utf8_printf(out,"'%s'",z); }else{ fprintf(out,"'"); while( *z ){ for(i=0; z[i] && z[i]!='\''; i++){} if( i==0 ){ fprintf(out,"''"); z++; }else if( z[i]=='\'' ){ - fprintf(out,"%.*s''",i,z); + utf8_printf(out,"%.*s''",i,z); z += i+1; }else{ - fprintf(out,"%s",z); + utf8_printf(out,"%s",z); break; } } fprintf(out,"'"); } @@ -715,11 +767,11 @@ && z[i]!='>' && z[i]!='\"' && z[i]!='\''; i++){} if( i>0 ){ - fprintf(out,"%.*s",i,z); + utf8_printf(out,"%.*s",i,z); } if( z[i]=='<' ){ fprintf(out,"<"); }else if( z[i]=='&' ){ fprintf(out,"&"); @@ -766,11 +818,11 @@ ** is only issued if bSep is true. */ static void output_csv(ShellState *p, const char *z, int bSep){ FILE *out = p->out; if( z==0 ){ - fprintf(out,"%s",p->nullValue); + utf8_printf(out,"%s",p->nullValue); }else{ int i; int nSep = strlen30(p->colSeparator); for(i=0; z[i]; i++){ if( needCsvQuote[((unsigned char*)z)[i]] @@ -786,15 +838,15 @@ if( z[i]=='"' ) putc('"', out); putc(z[i], out); } putc('"', out); }else{ - fprintf(out, "%s", z); + utf8_printf(out, "%s", z); } } if( bSep ){ - fprintf(p->out, "%s", p->colSeparator); + utf8_printf(p->out, "%s", p->colSeparator); } } #ifdef SIGINT /* @@ -828,13 +880,13 @@ if( azArg==0 ) break; for(i=0; iw ) w = len; } - if( p->cnt++>0 ) fprintf(p->out, "%s", p->rowSeparator); + if( p->cnt++>0 ) utf8_printf(p->out, "%s", p->rowSeparator); for(i=0; iout,"%*s = %s%s", w, azCol[i], + utf8_printf(p->out,"%*s = %s%s", w, azCol[i], azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); } break; } case MODE_Explain: @@ -856,14 +908,14 @@ if( iactualWidth) ){ p->actualWidth[i] = w; } if( p->showHeader ){ if( w<0 ){ - fprintf(p->out,"%*.*s%s",-w,-w,azCol[i], + utf8_printf(p->out,"%*.*s%s",-w,-w,azCol[i], i==nArg-1 ? p->rowSeparator : " "); }else{ - fprintf(p->out,"%-*.*s%s",w,w,azCol[i], + utf8_printf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? p->rowSeparator : " "); } } } if( p->showHeader ){ @@ -873,11 +925,12 @@ w = p->actualWidth[i]; if( w<0 ) w = -w; }else{ w = 10; } - fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------" + fprintf(p->out,"%-*.*s%s",w,w, + "----------------------------------------------------------" "----------------------------------------------------------", i==nArg-1 ? p->rowSeparator : " "); } } } @@ -897,15 +950,15 @@ fprintf(p->out, "%*.s", p->aiIndent[p->iIndent], ""); } p->iIndent++; } if( w<0 ){ - fprintf(p->out,"%*.*s%s",-w,-w, + utf8_printf(p->out,"%*.*s%s",-w,-w, azArg[i] ? azArg[i] : p->nullValue, i==nArg-1 ? p->rowSeparator : " "); }else{ - fprintf(p->out,"%-*.*s%s",w,w, + utf8_printf(p->out,"%-*.*s%s",w,w, azArg[i] ? azArg[i] : p->nullValue, i==nArg-1 ? p->rowSeparator : " "); } } break; @@ -912,25 +965,25 @@ } case MODE_Semi: case MODE_List: { if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,"%s%s",azCol[i], + utf8_printf(p->out,"%s%s",azCol[i], i==nArg-1 ? p->rowSeparator : p->colSeparator); } } if( azArg==0 ) break; for(i=0; inullValue; - fprintf(p->out, "%s", z); + utf8_printf(p->out, "%s", z); if( iout, "%s", p->colSeparator); + utf8_printf(p->out, "%s", p->colSeparator); }else if( p->mode==MODE_Semi ){ - fprintf(p->out, ";%s", p->rowSeparator); + utf8_printf(p->out, ";%s", p->rowSeparator); }else{ - fprintf(p->out, "%s", p->rowSeparator); + utf8_printf(p->out, "%s", p->rowSeparator); } } break; } case MODE_Html: { @@ -955,48 +1008,48 @@ } case MODE_Tcl: { if( p->cnt++==0 && p->showHeader ){ for(i=0; iout,azCol[i] ? azCol[i] : ""); - if(iout, "%s", p->colSeparator); + if(iout, "%s", p->colSeparator); } - fprintf(p->out, "%s", p->rowSeparator); + utf8_printf(p->out, "%s", p->rowSeparator); } if( azArg==0 ) break; for(i=0; iout, azArg[i] ? azArg[i] : p->nullValue); - if(iout, "%s", p->colSeparator); + if(iout, "%s", p->colSeparator); } - fprintf(p->out, "%s", p->rowSeparator); + utf8_printf(p->out, "%s", p->rowSeparator); break; } case MODE_Csv: { setBinaryMode(p->out); if( p->cnt++==0 && p->showHeader ){ for(i=0; iout, "%s", p->rowSeparator); + utf8_printf(p->out, "%s", p->rowSeparator); } if( nArg>0 ){ for(i=0; iout, "%s", p->rowSeparator); + utf8_printf(p->out, "%s", p->rowSeparator); } setTextMode(p->out); break; } case MODE_Insert: { p->cnt++; if( azArg==0 ) break; - fprintf(p->out,"INSERT INTO %s",p->zDestTable); + utf8_printf(p->out,"INSERT INTO %s",p->zDestTable); if( p->showHeader ){ fprintf(p->out,"("); for(i=0; i0 ? ",": ""; - fprintf(p->out, "%s%s", zSep, azCol[i]); + utf8_printf(p->out, "%s%s", zSep, azCol[i]); } fprintf(p->out,")"); } fprintf(p->out," VALUES("); for(i=0; iout,"%s",zSep); output_quoted_string(p->out, azArg[i]); }else if( aiType && (aiType[i]==SQLITE_INTEGER || aiType[i]==SQLITE_FLOAT) ){ - fprintf(p->out,"%s%s",zSep, azArg[i]); + utf8_printf(p->out,"%s%s",zSep, azArg[i]); }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); if( zSep[0] ) fprintf(p->out,"%s",zSep); output_hex_blob(p->out, pBlob, nBlob); }else if( isNumber(azArg[i], 0) ){ - fprintf(p->out,"%s%s",zSep, azArg[i]); + utf8_printf(p->out,"%s%s",zSep, azArg[i]); }else{ if( zSep[0] ) fprintf(p->out,"%s",zSep); output_quoted_string(p->out, azArg[i]); } } @@ -1025,21 +1078,21 @@ break; } case MODE_Ascii: { if( p->cnt++==0 && p->showHeader ){ for(i=0; i0 ) fprintf(p->out, "%s", p->colSeparator); - fprintf(p->out,"%s",azCol[i] ? azCol[i] : ""); + if( i>0 ) utf8_printf(p->out, "%s", p->colSeparator); + utf8_printf(p->out,"%s",azCol[i] ? azCol[i] : ""); } - fprintf(p->out, "%s", p->rowSeparator); + utf8_printf(p->out, "%s", p->rowSeparator); } if( azArg==0 ) break; for(i=0; i0 ) fprintf(p->out, "%s", p->colSeparator); - fprintf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); + if( i>0 ) utf8_printf(p->out, "%s", p->colSeparator); + utf8_printf(p->out,"%s",azArg[i] ? azArg[i] : p->nullValue); } - fprintf(p->out, "%s", p->rowSeparator); + utf8_printf(p->out, "%s", p->rowSeparator); break; } } return 0; } @@ -1165,17 +1218,17 @@ } rc = sqlite3_step(pSelect); nResult = sqlite3_column_count(pSelect); while( rc==SQLITE_ROW ){ if( zFirstRow ){ - fprintf(p->out, "%s", zFirstRow); + utf8_printf(p->out, "%s", zFirstRow); zFirstRow = 0; } z = (const char*)sqlite3_column_text(pSelect, 0); - fprintf(p->out, "%s", z); + utf8_printf(p->out, "%s", z); for(i=1; iout, ",%s", sqlite3_column_text(pSelect, i)); + utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i)); } if( z==0 ) z = ""; while( z[0] && (z[0]!='-' || z[1]!='-') ) z++; if( z[0] ){ fprintf(p->out, "\n;\n"); @@ -1359,11 +1412,11 @@ } n++; sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_NVISIT, (void*)&nVisit); sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EST, (void*)&rEst); sqlite3_stmt_scanstatus(p, i, SQLITE_SCANSTAT_EXPLAIN, (void*)&zExplain); - fprintf(pArg->out, "Loop %2d: %s\n", n, zExplain); + utf8_printf(pArg->out, "Loop %2d: %s\n", n, zExplain); rEstLoop *= rEst; fprintf(pArg->out, " nLoop=%-8lld nRow=%-8lld estRow=%-8lld estRow/Loop=%-8g\n", nLoop, nVisit, (sqlite3_int64)(rEstLoop+0.5), rEst ); @@ -1520,11 +1573,11 @@ } /* echo the sql statement if echo on */ if( pArg && pArg->echoOn ){ const char *zStmtSql = sqlite3_sql(pStmt); - fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql); + utf8_printf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql); } /* Show the EXPLAIN QUERY PLAN if .eqp is on */ if( pArg && pArg->autoEQP ){ sqlite3_stmt *pExplain; @@ -1534,11 +1587,11 @@ if( rc==SQLITE_OK ){ while( sqlite3_step(pExplain)==SQLITE_ROW ){ fprintf(pArg->out,"--EQP-- %d,", sqlite3_column_int(pExplain, 0)); fprintf(pArg->out,"%d,", sqlite3_column_int(pExplain, 1)); fprintf(pArg->out,"%d,", sqlite3_column_int(pExplain, 2)); - fprintf(pArg->out,"%s\n", sqlite3_column_text(pExplain, 3)); + utf8_printf(pArg->out,"%s\n", sqlite3_column_text(pExplain, 3)); } } sqlite3_finalize(pExplain); sqlite3_free(zEQP); } @@ -1675,15 +1728,15 @@ } zIns = sqlite3_mprintf( "INSERT INTO sqlite_master(type,name,tbl_name,rootpage,sql)" "VALUES('table','%q','%q',0,'%q');", zTable, zTable, zSql); - fprintf(p->out, "%s\n", zIns); + utf8_printf(p->out, "%s\n", zIns); sqlite3_free(zIns); return 0; }else{ - fprintf(p->out, "%s;\n", zSql); + utf8_printf(p->out, "%s;\n", zSql); } if( strcmp(zType, "table")==0 ){ sqlite3_stmt *pTableInfo = 0; char *zSelect = 0; @@ -2124,11 +2177,11 @@ static void sql_trace_callback(void *pArg, const char *z){ FILE *f = (FILE*)pArg; if( f ){ int i = (int)strlen(z); while( i>0 && z[i-1]==';' ){ i--; } - fprintf(f, "%.*s;\n", i, z); + utf8_printf(f, "%.*s;\n", i, z); } } /* ** A no-op routine that runs with the ".breakpoint" doc-command. This is @@ -2607,11 +2660,11 @@ } for(i=0; iout, "%-20s %d\n", aQuery[i].zName, val); + utf8_printf(p->out, "%-20s %d\n", aQuery[i].zName, val); } sqlite3_free(zSchemaTab); return 0; } @@ -3447,11 +3500,11 @@ if( c=='p' && n>=3 && strncmp(azArg[0], "print", n)==0 ){ int i; for(i=1; i1 ) fprintf(p->out, " "); - fprintf(p->out, "%s", azArg[i]); + utf8_printf(p->out, "%s", azArg[i]); } fprintf(p->out, "\n"); }else if( c=='p' && strncmp(azArg[0], "prompt", n)==0 ){ @@ -3641,20 +3694,20 @@ if( c=='s' && n>=10 && strncmp(azArg[0], "selftest-", 9)==0 ){ if( strncmp(azArg[0]+9, "boolean", n-9)==0 ){ int i, v; for(i=1; iout, "%s: %d 0x%x\n", azArg[i], v, v); + utf8_printf(p->out, "%s: %d 0x%x\n", azArg[i], v, v); } } if( strncmp(azArg[0]+9, "integer", n-9)==0 ){ int i; sqlite3_int64 v; for(i=1; iout, "%s", zBuf); + utf8_printf(p->out, "%s", zBuf); } } }else #endif @@ -3824,11 +3877,12 @@ if( nPrintCol<1 ) nPrintCol = 1; nPrintRow = (nRow + nPrintCol - 1)/nPrintCol; for(i=0; iout, "%s%-*s", zSp, maxlen, azResult[j] ? azResult[j]:""); + utf8_printf(p->out, "%s%-*s", zSp, maxlen, + azResult[j] ? azResult[j]:""); } fprintf(p->out, "\n"); } } @@ -4545,10 +4599,11 @@ setBinaryMode(stdin); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ Argv0 = argv[0]; main_init(&data); stdin_is_interactive = isatty(0); + stdout_is_console = isatty(1); /* Make sure we have a valid signal handler early, before anything ** else is done. */ #ifdef SIGINT