From: larrybr Date: Tue, 21 Nov 2023 18:26:06 +0000 (+0000) Subject: Merge console I/O changes for Windows CLI. X-Git-Tag: version-3.45.0~134 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d9f3d6d75340407dda404237520d4a571695a665;p=thirdparty%2Fsqlite.git Merge console I/O changes for Windows CLI. FossilOrigin-Name: 935a8a8ee76d0014df42c1480e044fd1c2dfc26e78abb587d99d861d2ae5eb27 --- d9f3d6d75340407dda404237520d4a571695a665 diff --cc Makefile.msc index 4339be891f,33a9746dd4..3a4d46b01f --- a/Makefile.msc +++ b/Makefile.msc @@@ -2261,8 -2261,12 +2261,10 @@@ keywordhash.h: $(TOP)\tool\mkkeywordhas # Source files that go into making shell.c SHELL_SRC = \ $(TOP)\src\shell.c.in \ + $(TOP)\ext\consio\console_io.c \ + $(TOP)\ext\consio\console_io.h \ $(TOP)\ext\misc\appendvfs.c \ $(TOP)\ext\misc\completion.c \ - $(TOP)\ext\consio\console_io.c \ - $(TOP)\ext\consio\console_io.h \ $(TOP)\ext\misc\base64.c \ $(TOP)\ext\misc\base85.c \ $(TOP)\ext\misc\decimal.c \ diff --cc ext/consio/console_io.c index 0000000000,7ed06ccd31..781ae7fc7e mode 000000,100644..100644 --- a/ext/consio/console_io.c +++ b/ext/consio/console_io.c @@@ -1,0 -1,677 +1,676 @@@ + /* + ** 2023 November 4 + ** + ** The author disclaims copyright to this source code. In place of + ** a legal notice, here is a blessing: + ** + ** May you do good and not evil. + ** May you find forgiveness for yourself and forgive others. + ** May you share freely, never taking more than you give. + ** + ******************************************************************************** + ** This file implements various interfaces used for console and stream I/O + ** by the SQLite project command-line tools, as explained in console_io.h . + ** Functions prefixed by "SQLITE_INTERNAL_LINKAGE" behave as described there. + */ + + #ifndef SQLITE_CDECL + # define SQLITE_CDECL + #endif + + #ifndef SHELL_NO_SYSINC + # include + # include + # include + # include + # include + # include "console_io.h" + # include "sqlite3.h" + #endif + + #ifndef SQLITE_CIO_NO_TRANSLATE + # if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT + # ifndef SHELL_NO_SYSINC + # include + # include + # undef WIN32_LEAN_AND_MEAN + # define WIN32_LEAN_AND_MEAN + # include + # endif + # define CIO_WIN_WC_XLATE 1 /* Use WCHAR Windows APIs for console I/O */ + # else + # ifndef SHELL_NO_SYSINC + # include + # endif + # define CIO_WIN_WC_XLATE 0 /* Use plain C library stream I/O at console */ + # endif + #else + # define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */ + #endif + + #if CIO_WIN_WC_XLATE + /* Character used to represent a known-incomplete UTF-8 char group (�) */ + static WCHAR cBadGroup = 0xfffd; + #endif + + #if CIO_WIN_WC_XLATE + static HANDLE handleOfFile(FILE *pf){ + int fileDesc = _fileno(pf); + union { intptr_t osfh; HANDLE fh; } fid = { + (fileDesc>=0)? _get_osfhandle(fileDesc) : (intptr_t)INVALID_HANDLE_VALUE + }; + return fid.fh; + } + #endif + + #ifndef SQLITE_CIO_NO_TRANSLATE + typedef struct PerStreamTags { + # if CIO_WIN_WC_XLATE + HANDLE hx; + DWORD consMode; + char acIncomplete[4]; + # else + short reachesConsole; + # endif + FILE *pf; + } PerStreamTags; + + /* Define NULL-like value for things which can validly be 0. */ + # define SHELL_INVALID_FILE_PTR ((FILE *)~0) + # if CIO_WIN_WC_XLATE + # define SHELL_INVALID_CONS_MODE 0xFFFF0000 + # endif + + # if CIO_WIN_WC_XLATE + # define PST_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \ + {0,0,0,0}, SHELL_INVALID_FILE_PTR } + # else + # define PST_INITIALIZER { 0, SHELL_INVALID_FILE_PTR } + # endif + + /* Quickly say whether a known output is going to the console. */ + # if CIO_WIN_WC_XLATE + static short pstReachesConsole(PerStreamTags *ppst){ + return (ppst->hx != INVALID_HANDLE_VALUE); + } + # else + # define pstReachesConsole(ppst) 0 + # endif + + # if CIO_WIN_WC_XLATE + static void restoreConsoleArb(PerStreamTags *ppst){ + if( pstReachesConsole(ppst) ) SetConsoleMode(ppst->hx, ppst->consMode); + } + # else + # define restoreConsoleArb(ppst) + # endif + + /* Say whether FILE* appears to be a console, collect associated info. */ + static short streamOfConsole(FILE *pf, /* out */ PerStreamTags *ppst){ + # if CIO_WIN_WC_XLATE + short rv = 0; + DWORD dwCM = SHELL_INVALID_CONS_MODE; + HANDLE fh = handleOfFile(pf); + ppst->pf = pf; + if( INVALID_HANDLE_VALUE != fh ){ + rv = (GetFileType(fh) == FILE_TYPE_CHAR && GetConsoleMode(fh,&dwCM)); + } + ppst->hx = (rv)? fh : INVALID_HANDLE_VALUE; + ppst->consMode = dwCM; + return rv; + # else + ppst->pf = pf; + ppst->reachesConsole = ( (short)isatty(fileno(pf)) ); + return ppst->reachesConsole; + # endif + } + + # if CIO_WIN_WC_XLATE + /* Define console modes for use with the Windows Console API. */ + # define SHELL_CONI_MODE \ + (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \ + | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT) + # define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \ + | ENABLE_VIRTUAL_TERMINAL_PROCESSING) + # endif + + typedef struct ConsoleInfo { + PerStreamTags pstSetup[3]; + PerStreamTags pstDesignated[3]; + StreamsAreConsole sacSetup; + } ConsoleInfo; + + static short isValidStreamInfo(PerStreamTags *ppst){ + return (ppst->pf != SHELL_INVALID_FILE_PTR); + } + + static ConsoleInfo consoleInfo = { + { /* pstSetup */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER }, + { /* pstDesignated[] */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER }, + SAC_NoConsole /* sacSetup */ + }; + + SQLITE_INTERNAL_LINKAGE FILE* invalidFileStream = (FILE *)~0; + + # if CIO_WIN_WC_XLATE + static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){ + if( pstReachesConsole(ppst) ){ + DWORD cm = odir? SHELL_CONO_MODE : SHELL_CONI_MODE; + SetConsoleMode(ppst->hx, cm); + } + } + # else + # define maybeSetupAsConsole(ppst,odir) + # endif + + SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void){ + # if CIO_WIN_WC_XLATE + int ix = 0; + while( ix < 6 ){ + PerStreamTags *ppst = (ix<3)? + &consoleInfo.pstSetup[ix] : &consoleInfo.pstDesignated[ix-3]; + maybeSetupAsConsole(ppst, (ix % 3)>0); + ++ix; + } + # endif + } + + SQLITE_INTERNAL_LINKAGE StreamsAreConsole + consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){ + StreamsAreConsole rv = SAC_NoConsole; + FILE* apf[3] = { pfIn, pfOut, pfErr }; + int ix; + for( ix = 2; ix >= 0; --ix ){ + PerStreamTags *ppst = &consoleInfo.pstSetup[ix]; + if( streamOfConsole(apf[ix], ppst) ){ + rv |= (SAC_InConsole< 0 ) fflush(apf[ix]); + } + consoleInfo.sacSetup = rv; + consoleRenewSetup(); + return rv; + } + + SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){ + # if CIO_WIN_WC_XLATE + static ConsoleInfo *pci = &consoleInfo; + if( pci->sacSetup ){ + int ix; + for( ix=0; ix<3; ++ix ){ + if( pci->sacSetup & (SAC_InConsole<pstSetup[ix]; + SetConsoleMode(ppst->hx, ppst->consMode); + } + } + } + # endif + } + #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ + + #ifdef SQLITE_CIO_INPUT_REDIR + /* Say whether given FILE* is among those known, via either + ** consoleClassifySetup() or set{Output,Error}Stream, as + ** readable, and return an associated PerStreamTags pointer + ** if so. Otherwise, return 0. + */ + static PerStreamTags * isKnownReadable(FILE *pf){ + static PerStreamTags *apst[] = { + &consoleInfo.pstDesignated[0], &consoleInfo.pstSetup[0], 0 + }; + int ix = 0; + do { + if( apst[ix]->pf == pf ) break; + } while( apst[++ix] != 0 ); + return apst[ix]; + } + #endif + + #ifndef SQLITE_CIO_NO_TRANSLATE + /* Say whether given FILE* is among those known, via either + ** consoleClassifySetup() or set{Output,Error}Stream, as + ** writable, and return an associated PerStreamTags pointer + ** if so. Otherwise, return 0. + */ + static PerStreamTags * isKnownWritable(FILE *pf){ + static PerStreamTags *apst[] = { + &consoleInfo.pstDesignated[1], &consoleInfo.pstDesignated[2], + &consoleInfo.pstSetup[1], &consoleInfo.pstSetup[2], 0 + }; + int ix = 0; + do { + if( apst[ix]->pf == pf ) break; + } while( apst[++ix] != 0 ); + return apst[ix]; + } + + static FILE *designateEmitStream(FILE *pf, unsigned chix){ + FILE *rv = consoleInfo.pstDesignated[chix].pf; + if( pf == invalidFileStream ) return rv; + else{ + /* Setting a possibly new output stream. */ + PerStreamTags *ppst = isKnownWritable(pf); + if( ppst != 0 ){ + PerStreamTags pst = *ppst; + consoleInfo.pstDesignated[chix] = pst; + }else streamOfConsole(pf, &consoleInfo.pstDesignated[chix]); + } + return rv; + } + + SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf){ + return designateEmitStream(pf, 1); + } + # ifdef CONSIO_SET_ERROR_STREAM + SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf){ + return designateEmitStream(pf, 2); + } + # endif + #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ + + #ifndef SQLITE_CIO_NO_SETMODE + # if CIO_WIN_WC_XLATE + static void setModeFlushQ(FILE *pf, short bFlush, int mode){ + if( bFlush ) fflush(pf); + _setmode(_fileno(pf), mode); + } + # else + # define setModeFlushQ(f, b, m) if(b) fflush(f) + # endif + + SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *pf, short bFlush){ + setModeFlushQ(pf, bFlush, _O_BINARY); + } + SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){ + setModeFlushQ(pf, bFlush, _O_TEXT); + } + # undef setModeFlushQ + + #else /* defined(SQLITE_CIO_NO_SETMODE) */ + # define setBinaryMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0) + # define setTextMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0) + #endif /* defined(SQLITE_CIO_NO_SETMODE) */ + + #ifndef SQLITE_CIO_NO_TRANSLATE + # if CIO_WIN_WC_XLATE + /* Write buffer cBuf as output to stream known to reach console, + ** limited to ncTake char's. Return ncTake on success, else 0. */ + static int conZstrEmit(PerStreamTags *ppst, const char *z, int ncTake){ + int rv = 0; + if( z!=NULL ){ + int nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, 0,0); + if( nwc > 0 ){ + WCHAR *zw = sqlite3_malloc64(nwc*sizeof(WCHAR)); + if( zw!=NULL ){ + nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, zw,nwc); + if( nwc > 0 ){ + /* Translation from UTF-8 to UTF-16, then WCHARs out. */ + if( WriteConsoleW(ppst->hx, zw,nwc, 0, NULL) ){ + rv = ncTake; + } + } + sqlite3_free(zw); + } + } + } + return rv; + } + + /* For {f,o,e}PrintfUtf8() when stream is known to reach console. */ + static int conioVmPrintf(PerStreamTags *ppst, const char *zFormat, va_list ap){ + char *z = sqlite3_vmprintf(zFormat, ap); + if( z ){ + int rv = conZstrEmit(ppst, z, (int)strlen(z)); + sqlite3_free(z); + return rv; + }else return 0; + } + # endif /* CIO_WIN_WC_XLATE */ + + # ifdef CONSIO_GET_EMIT_STREAM + static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix, + PerStreamTags *ppst){ + PerStreamTags *rv = isKnownWritable(pf); + short isValid = (rv!=0)? isValidStreamInfo(rv) : 0; + if( rv != 0 && isValid ) return rv; + streamOfConsole(pf, ppst); + return ppst; + } + # endif + + /* Get stream info, either for designated output or error stream when + ** chix equals 1 or 2, or for an arbitrary stream when chix == 0. + ** In either case, ppst references a caller-owned PerStreamTags + ** struct which may be filled in if none of the known writable + ** streams is being held by consoleInfo. The ppf parameter is an + ** output when chix!=0 and an input when chix==0. + */ + static PerStreamTags * + getEmitStreamInfo(unsigned chix, PerStreamTags *ppst, + /* in/out */ FILE **ppf){ + PerStreamTags *ppstTry; + FILE *pfEmit; + if( chix > 0 ){ + ppstTry = &consoleInfo.pstDesignated[chix]; + if( !isValidStreamInfo(ppstTry) ){ + ppstTry = &consoleInfo.pstSetup[chix]; + pfEmit = ppst->pf; + }else pfEmit = ppstTry->pf; + if( !isValidStreamInfo(ppst) ){ + pfEmit = (chix > 1)? stderr : stdout; + ppstTry = ppst; + streamOfConsole(pfEmit, ppstTry); + } + *ppf = pfEmit; + }else{ + ppstTry = isKnownWritable(*ppf); + if( ppstTry != 0 ) return ppstTry; + streamOfConsole(*ppf, ppst); + return ppst; + } + return ppstTry; + } + + SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...){ + va_list ap; + int rv; + FILE *pfOut; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ + # if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); + # else + getEmitStreamInfo(1, &pst, &pfOut); + # endif + assert(zFormat!=0); + va_start(ap, zFormat); + # if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ){ + rv = conioVmPrintf(ppst, zFormat, ap); + }else{ + # endif + rv = vfprintf(pfOut, zFormat, ap); + # if CIO_WIN_WC_XLATE + } + # endif + va_end(ap); + return rv; + } + + SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...){ + va_list ap; + int rv; + FILE *pfErr; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ + # if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); + # else + getEmitStreamInfo(2, &pst, &pfErr); + # endif + assert(zFormat!=0); + va_start(ap, zFormat); + # if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ){ + rv = conioVmPrintf(ppst, zFormat, ap); + }else{ + # endif + rv = vfprintf(pfErr, zFormat, ap); + # if CIO_WIN_WC_XLATE + } + # endif + va_end(ap); + return rv; + } + + SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...){ + va_list ap; + int rv; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ + # if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); + # else + getEmitStreamInfo(0, &pst, &pfO); + # endif + assert(zFormat!=0); + va_start(ap, zFormat); + # if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ){ + maybeSetupAsConsole(ppst, 1); + rv = conioVmPrintf(ppst, zFormat, ap); + if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); + }else{ + # endif + rv = vfprintf(pfO, zFormat, ap); + # if CIO_WIN_WC_XLATE + } + # endif + va_end(ap); + return rv; + } + + SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){ + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ + # if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); + # else + getEmitStreamInfo(0, &pst, &pfO); + # endif + assert(z!=0); + # if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ){ + int rv; + maybeSetupAsConsole(ppst, 1); + rv = conZstrEmit(ppst, z, (int)strlen(z)); + if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); + return rv; + }else { + # endif + return (fputs(z, pfO)<0)? 0 : (int)strlen(z); + # if CIO_WIN_WC_XLATE + } + # endif + } + + SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){ + FILE *pfErr; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ + # if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); + # else + getEmitStreamInfo(2, &pst, &pfErr); + # endif + assert(z!=0); + # if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); + else { + # endif + return (fputs(z, pfErr)<0)? 0 : (int)strlen(z); + # if CIO_WIN_WC_XLATE + } + # endif + } + + SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){ + FILE *pfOut; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ + # if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); + # else + getEmitStreamInfo(1, &pst, &pfOut); + # endif + assert(z!=0); + # if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z)); + else { + # endif + return (fputs(z, pfOut)<0)? 0 : (int)strlen(z); + # if CIO_WIN_WC_XLATE + } + # endif + } + + #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ + + #if !(defined(SQLITE_CIO_NO_UTF8SCAN) && defined(SQLITE_CIO_NO_TRANSLATE)) + /* 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<=0 => char count, nAccept<0 => character + */ + SQLITE_INTERNAL_LINKAGE 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 ) 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; + } + #endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/ + + #ifndef SQLITE_CIO_NO_TRANSLATE + SQLITE_INTERNAL_LINKAGE int + fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){ + assert(pfO!=0); + # if CIO_WIN_WC_XLATE + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ + PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO); + if( pstReachesConsole(ppst) ){ + int rv; + maybeSetupAsConsole(ppst, 1); + rv = conZstrEmit(ppst, cBuf, nAccept); + if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst); + return rv; + }else { + # endif + return (int)fwrite(cBuf, 1, nAccept, pfO); + # if CIO_WIN_WC_XLATE + } + # endif + } + + SQLITE_INTERNAL_LINKAGE int + oPutbUtf8(const char *cBuf, int nAccept){ + FILE *pfOut; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ + # if CIO_WIN_WC_XLATE + PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut); + # else + getEmitStreamInfo(1, &pst, &pfOut); + # endif + # if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ){ + return conZstrEmit(ppst, cBuf, nAccept); + }else { + # endif + return (int)fwrite(cBuf, 1, nAccept, pfOut); + # if CIO_WIN_WC_XLATE + } + # endif + } + + # ifdef CONSIO_EPUTB + SQLITE_INTERNAL_LINKAGE int + ePutbUtf8(const char *cBuf, int nAccept){ + FILE *pfErr; + PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */ + PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr); + # if CIO_WIN_WC_XLATE + if( pstReachesConsole(ppst) ){ + return conZstrEmit(ppst, cBuf, nAccept); + }else { + # endif + return (int)fwrite(cBuf, 1, nAccept, pfErr); + # if CIO_WIN_WC_XLATE + } + # endif + } + # endif /* defined(CONSIO_EPUTB) */ + + SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){ + if( pfIn==0 ) pfIn = stdin; + # if CIO_WIN_WC_XLATE + if( pfIn == consoleInfo.pstSetup[0].pf + && (consoleInfo.sacSetup & SAC_InConsole)!=0 ){ + # if CIO_WIN_WC_XLATE==1 + # define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */ + WCHAR wcBuf[SHELL_GULP+1]; + int lend = 0, noc = 0; + if( ncMax > 0 ) cBuf[0] = 0; + while( noc < ncMax-8-1 && !lend ){ + /* There is room for at least 2 more characters and a 0-terminator. */ + int na = (ncMax > SHELL_GULP*4+1 + noc)? SHELL_GULP : (ncMax-1 - noc)/4; + # undef SHELL_GULP + DWORD nbr = 0; + BOOL bRC = ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf, na, &nbr, 0); + if( bRC && nbr>0 && (wcBuf[nbr-1]&0xF800)==0xD800 ){ + /* Last WHAR read is first of a UTF-16 surrogate pair. Grab its mate. */ + DWORD nbrx; + bRC &= ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf+nbr, 1, &nbrx, 0); + if( bRC ) nbr += nbrx; + } + if( !bRC || (noc==0 && nbr==0) ) return 0; + if( nbr > 0 ){ + int nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,0,0,0,0); + if( nmb != 0 && noc+nmb <= ncMax ){ + int iseg = noc; + nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,cBuf+noc,nmb,0,0); + noc += nmb; + /* Fixup line-ends as coded by Windows for CR (or "Enter".) + ** This is done without regard for any setMode{Text,Binary}() + ** call that might have been done on the interactive input. + */ + if( noc > 0 ){ + if( cBuf[noc-1]=='\n' ){ + lend = 1; + if( noc > 1 && cBuf[noc-2]=='\r' ) cBuf[--noc-1] = '\n'; + } + } + /* Check for ^Z (anywhere in line) too, to act as EOF. */ + while( iseg < noc ){ + if( cBuf[iseg]=='\x1a' ){ + noc = iseg; /* Chop ^Z and anything following. */ + lend = 1; /* Counts as end of line too. */ + break; + } + ++iseg; + } + }else break; /* Drop apparent garbage in. (Could assert.) */ + }else break; + } + /* If got nothing, (after ^Z chop), must be at end-of-file. */ + if( noc > 0 ){ + cBuf[noc] = 0; + return cBuf; + }else return 0; + # endif + }else{ + # endif + return fgets(cBuf, ncMax, pfIn); + # if CIO_WIN_WC_XLATE + } + # endif + } + #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ + -#undef CIO_WIN_WC_XLATE + #undef SHELL_INVALID_FILE_PTR diff --cc ext/consio/console_io.h index 0000000000,4fb51d24d8..2c0e486cd1 mode 000000,100644..100644 --- a/ext/consio/console_io.h +++ b/ext/consio/console_io.h @@@ -1,0 -1,273 +1,278 @@@ + /* + ** 2023 November 1 + ** + ** The author disclaims copyright to this source code. In place of + ** a legal notice, here is a blessing: + ** + ** May you do good and not evil. + ** May you find forgiveness for yourself and forgive others. + ** May you share freely, never taking more than you give. + ** + ******************************************************************************** + ** This file exposes various interfaces used for console and other I/O + ** by the SQLite project command-line tools. These interfaces are used + ** at either source conglomeration time, compilation time, or run time. + ** This source provides for either inclusion into conglomerated, + ** "single-source" forms or separate compilation then linking. + ** + ** Platform dependencies are "hidden" here by various stratagems so + ** that, provided certain conditions are met, the programs using this + ** source or object code compiled from it need no explicit conditional + ** compilation in their source for their console and stream I/O. + ** + ** The symbols and functionality exposed here are not a public API. + ** This code may change in tandem with other project code as needed. ++** ++** When this .h file and its companion .c are directly incorporated into ++** a source conglomeration (such as shell.c), the preprocessor symbol ++** CIO_WIN_WC_XLATE is defined as 0 or 1, reflecting whether console I/O ++** translation for Windows is effected for the build. + */ + + #ifndef SQLITE_INTERNAL_LINKAGE + # define SQLITE_INTERNAL_LINKAGE extern /* external to translation unit */ + # include + #else + # define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */ + #endif + + #ifndef SQLITE3_H + # include "sqlite3.h" + #endif + + #ifndef SQLITE_CIO_NO_CLASSIFY + + /* Define enum for use with following function. */ + typedef enum StreamsAreConsole { + SAC_NoConsole = 0, + SAC_InConsole = 1, SAC_OutConsole = 2, SAC_ErrConsole = 4, + SAC_AnyConsole = 0x7 + } StreamsAreConsole; + + /* + ** Classify the three standard I/O streams according to whether + ** they are connected to a console attached to the process. + ** + ** Returns the bit-wise OR of SAC_{In,Out,Err}Console values, + ** or SAC_NoConsole if none of the streams reaches a console. + ** + ** This function should be called before any I/O is done with + ** the given streams. As a side-effect, the given inputs are + ** recorded so that later I/O operations on them may be done + ** differently than the C library FILE* I/O would be done, + ** iff the stream is used for the I/O functions that follow, + ** and to support the ones that use an implicit stream. + ** + ** On some platforms, stream or console mode alteration (aka + ** "Setup") may be made which is undone by consoleRestore(). + */ + SQLITE_INTERNAL_LINKAGE StreamsAreConsole + consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ); + /* A usual call for convenience: */ + #define SQLITE_STD_CONSOLE_INIT() consoleClassifySetup(stdin,stdout,stderr) + + /* + ** After an initial call to consoleClassifySetup(...), renew + ** the same setup it effected. (A call not after is an error.) + ** This will restore state altered by consoleRestore(); + ** + ** Applications which run an inferior (child) process which + ** inherits the same I/O streams may call this function after + ** such a process exits to guard against console mode changes. + */ + SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void); + + /* + ** Undo any side-effects left by consoleClassifySetup(...). + ** + ** This should be called after consoleClassifySetup() and + ** before the process terminates normally. It is suitable + ** for use with the atexit() C library procedure. After + ** this call, no console I/O should be done until one of + ** console{Classify or Renew}Setup(...) is called again. + ** + ** Applications which run an inferior (child) process that + ** inherits the same I/O streams might call this procedure + ** before so that said process will have a console setup + ** however users have configured it or come to expect. + */ + SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ); + + #else /* defined(SQLITE_CIO_NO_CLASSIFY) */ + # define consoleClassifySetup(i,o,e) + # define consoleRenewSetup() + # define consoleRestore() + #endif /* defined(SQLITE_CIO_NO_CLASSIFY) */ + + #ifndef SQLITE_CIO_NO_REDIRECT + /* + ** Set stream to be used for the functions below which write + ** to "the designated X stream", where X is Output or Error. + ** Returns the previous value. + ** + ** Alternatively, pass the special value, invalidFileStream, + ** to get the designated stream value without setting it. + ** + ** Before the designated streams are set, they default to + ** those passed to consoleClassifySetup(...), and before + ** that is called they default to stdout and stderr. + ** + ** It is error to close a stream so designated, then, without + ** designating another, use the corresponding {o,e}Emit(...). + */ + SQLITE_INTERNAL_LINKAGE FILE *invalidFileStream; + SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf); + # ifdef CONSIO_SET_ERROR_STREAM + SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf); + # endif + #else + # define setOutputStream(pf) + # define setErrorStream(pf) + #endif /* !defined(SQLITE_CIO_NO_REDIRECT) */ + + #ifndef SQLITE_CIO_NO_TRANSLATE + /* + ** Emit output like fprintf(). If the output is going to the + ** console and translation from UTF-8 is necessary, perform + ** the needed translation. Otherwise, write formatted output + ** to the provided stream almost as-is, possibly with newline + ** translation as specified by set{Binary,Text}Mode(). + */ + SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...); + /* Like fPrintfUtf8 except stream is always the designated output. */ + SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...); + /* Like fPrintfUtf8 except stream is always the designated error. */ + SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...); + + /* + ** Emit output like fputs(). If the output is going to the + ** console and translation from UTF-8 is necessary, perform + ** the needed translation. Otherwise, write given text to the + ** provided stream almost as-is, possibly with newline + ** translation as specified by set{Binary,Text}Mode(). + */ + SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO); + /* Like fPutsUtf8 except stream is always the designated output. */ + SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z); + /* Like fPutsUtf8 except stream is always the designated error. */ + SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z); + + /* + ** Emit output like fPutsUtf8(), except that the length of the + ** accepted char or character sequence is limited by nAccept. + ** + ** Returns the number of accepted char values. + */ + SQLITE_INTERNAL_LINKAGE int + fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept); + /* Like fPutbUtf8 except stream is always the designated output. */ + SQLITE_INTERNAL_LINKAGE int + oPutbUtf8(const char *cBuf, int nAccept); + /* Like fPutbUtf8 except stream is always the designated error. */ + #ifdef CONSIO_EPUTB + SQLITE_INTERNAL_LINKAGE int + ePutbUtf8(const char *cBuf, int nAccept); + #endif + + /* + ** Collect input like fgets(...) with special provisions for input + ** from the console on platforms that require same. Defers to the + ** C library fgets() when input is not from the console. Newline + ** translation may be done as set by set{Binary,Text}Mode(). As a + ** convenience, pfIn==NULL is treated as stdin. + */ + SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn); + /* Like fGetsUtf8 except stream is always the designated input. */ + /* SQLITE_INTERNAL_LINKAGE char* iGetsUtf8(char *cBuf, int ncMax); */ + + #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */ + + #ifndef SQLITE_CIO_NO_SETMODE + /* + ** Set given stream for binary mode, where newline translation is + ** not done, or for text mode where, for some platforms, newlines + ** are translated to the platform's conventional char sequence. + ** If bFlush true, flush the stream. + ** + ** An additional side-effect is that if the stream is one passed + ** to consoleClassifySetup() as an output, it is flushed first. + ** + ** Note that binary/text mode has no effect on console I/O + ** translation. On all platforms, newline to the console starts + ** a new line and CR,LF chars from the console become a newline. + */ + SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *, short bFlush); + SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *, short bFlush); + #endif + + #ifdef SQLITE_CIO_PROMPTED_IN + typedef struct Prompts { + int numPrompts; + const char **azPrompts; + } Prompts; + + /* + ** Macros for use of a line editor. + ** + ** The following macros define operations involving use of a + ** line-editing library or simple console interaction. + ** A "T" argument is a text (char *) buffer or filename. + ** A "N" argument is an integer. + ** + ** SHELL_ADD_HISTORY(T) // Record text as line(s) of history. + ** SHELL_READ_HISTORY(T) // Read history from file named by T. + ** SHELL_WRITE_HISTORY(T) // Write history to file named by T. + ** SHELL_STIFLE_HISTORY(N) // Limit history to N entries. + ** + ** A console program which does interactive console input is + ** expected to call: + ** SHELL_READ_HISTORY(T) before collecting such input; + ** SHELL_ADD_HISTORY(T) as record-worthy input is taken; + ** SHELL_STIFLE_HISTORY(N) after console input ceases; then + ** SHELL_WRITE_HISTORY(T) before the program exits. + */ + + /* + ** Retrieve a single line of input text from an input stream. + ** + ** If pfIn is the input stream passed to consoleClassifySetup(), + ** and azPrompt is not NULL, then a prompt is issued before the + ** line is collected, as selected by the isContinuation flag. + ** Array azPrompt[{0,1}] holds the {main,continuation} prompt. + ** + ** If zBufPrior is not NULL then it is a buffer from a prior + ** call to this routine that can be reused, or will be freed. + ** + ** The result is stored in space obtained from malloc() and + ** must either be freed by the caller or else passed back to + ** this function as zBufPrior for reuse. + ** + ** This function may call upon services of a line-editing + ** library to interactively collect line edited input. + */ + SQLITE_INTERNAL_LINKAGE char * + shellGetLine(FILE *pfIn, char *zBufPrior, int nLen, + short isContinuation, Prompts azPrompt); + #endif /* defined(SQLITE_CIO_PROMPTED_IN) */ + /* + ** TBD: Define an interface for application(s) to generate + ** completion candidates for use by the line-editor. + ** + ** This may be premature; the CLI is the only application + ** that does this. Yet, getting line-editing melded into + ** console I/O is desirable because a line-editing library + ** may have to establish console operating mode, possibly + ** in a way that interferes with the above functionality. + */ + + #if !(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE)) + /* 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<=0 => char count, nAccept<0 => character + */ + SQLITE_INTERNAL_LINKAGE const char* + zSkipValidUtf8(const char *z, int nAccept, long ccm); + + #endif diff --cc manifest index 721aac63bc,049a2be3b3..a6dac2dd9a --- a/manifest +++ b/manifest @@@ -1,11 -1,11 +1,11 @@@ - C Fix\sthe\strace3-4.4\stest\sto\sbe\smore\srebust\sagainst\stiming\squirks. - D 2023-11-21T12:02:04.720 -C Sync\sw/trunk\sas\spre-merge-to-trunk\ssanity\scheck. -D 2023-11-21T15:55:31.480 ++C Merge\sconsole\sI/O\schanges\sfor\sWindows\sCLI. ++D 2023-11-21T18:26:06.197 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 - F Makefile.in a0cf17b2a456ae24959030b979bc6f1ec5efc74a240fb834c12e63dd1127cd72 + F Makefile.in 890caf094636c308bc981032baf8f9208bf307755f9197ae4218a9fbcec2e449 F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6 - F Makefile.msc f0cf219350d9af4fba411b4f6306dce2adc897484e8f446de1fb4f40de674d00 -F Makefile.msc f90f75b3b62809e1d13e5362b84a5cce90bc9d6772e4092be52c2b9a6638a7ed ++F Makefile.msc 59bb36dba001f0b38212be0794fb838f25371008b180929bcf08aa799694c168 F README.md 963d30019abf0cc06b263cd2824bce022893f3f93a531758f6f04ff2194a16a8 F VERSION 73573d4545343f001bf5dc5461173a7c78c203dd046cabcf99153878cf25d3a6 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@@ -50,6 -50,8 +50,8 @@@ F ext/README.md fd5f78013b0a2bc6f0067af F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91 F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94 F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a -F ext/consio/console_io.c 48536ed2f2add0abd8be80f72cae46e19738fa671b69daa88197df53a55da7c4 -F ext/consio/console_io.h b8dce7fa926b51f19b5d1c516219ac91b5c1ccddd467b2510e2557c900830f7d ++F ext/consio/console_io.c 0d9e79742930dadc13f4819fa0fe602676ac32c825304d498171c5922a3863ce ++F ext/consio/console_io.h d9ba2de923f11348919cccc2ba161ccbf2e417b866d2b7d072329e6c702eb242 F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3 F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4 F ext/expert/expert1.test 0dd5cb096d66bed593e33053a3b364f6ef52ed72064bf5cf298364636dbf3cd6 @@@ -726,7 -729,7 +729,7 @@@ F src/random.c 606b00941a1d7dd09c381d32 F src/resolve.c d017bad7ba8e778617701a0e986fdeb393d67d6afa84fb28ef4e8b8ad2acf916 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 85857bedd2913d888aa571755b48c54cd2e6e7fcb0087e19b226ee0368cfda1e - F src/shell.c.in 297625a1ba6ea1c08bc2ea1b838b646cad309b62bf08df0e379355629404f140 -F src/shell.c.in 9bfdb6faaea31e2ff0f940f1234e39f505f263b068ba6736838c7b6ad9d7d8ad ++F src/shell.c.in 345be14456aeadfaaeced4149563a4a6d70942d8a38a1cd27b75d4e491757028 F src/sqlite.h.in d93a4821d2f792467a60f7dc81268d1bb8634f40c31694ef254cab4f9921f96a F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@@ -2140,8 -2143,8 +2143,8 @@@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a9 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 - P f5b3eb0fc8936ba274a7654ff6dfa7d4654bd8dbca7f3a5ec1134b0b5260d59d - R c3df342b9429d8c9fdce76751979c422 - U drh - Z 57a5e144d820d25acd5cfb831cc09e5a -P 39e30c5f9cc6dcac003255734e3ce1ac5b05349ea1a25e1c108b5f6d1d97612b 8936daa08243729d8538bb7288bbefb43f3bd842a0d4b2e8019092f5701c2926 -R 2d6da084016b6aec46129cec2ee38947 ++P 8936daa08243729d8538bb7288bbefb43f3bd842a0d4b2e8019092f5701c2926 448d6a1182d29940d5d34be2ce67df5601b688cd902dbbe97e95073f982a49ce ++R 764d6a65ddcbf1b62de463cc42b71475 + U larrybr -Z 345a9a9faa2545dfaaa7f274ef6f8330 ++Z 9d8c4f0e647c59f8c21d5587e8bd311b # Remove this line to create a well-formed Fossil manifest. diff --cc manifest.uuid index 73e608bdbe,79188a1745..094c6b93ac --- a/manifest.uuid +++ b/manifest.uuid @@@ -1,1 -1,1 +1,1 @@@ - 8936daa08243729d8538bb7288bbefb43f3bd842a0d4b2e8019092f5701c2926 -448d6a1182d29940d5d34be2ce67df5601b688cd902dbbe97e95073f982a49ce ++935a8a8ee76d0014df42c1480e044fd1c2dfc26e78abb587d99d861d2ae5eb27 diff --cc src/shell.c.in index 0e0dbd36c7,91d99d336c..6242074127 --- a/src/shell.c.in +++ b/src/shell.c.in @@@ -450,28 -479,9 +479,9 @@@ static int bail_on_error = 0 static int stdin_is_interactive = 1; /* - ** If build is for non-RT Windows, without 3rd-party line editing, - ** console input and output may be done in a UTF-8 compatible way, - ** if the OS is capable of it and the --no-utf8 option is not seen. - */ - #if (defined(_WIN32) || defined(WIN32)) && SHELL_USE_LOCAL_GETLINE \ - && !defined(SHELL_OMIT_WIN_UTF8) && !SQLITE_OS_WINRT - # define SHELL_WIN_UTF8_OPT 1 - /* Record whether to do UTF-8 console I/O translation per stream. */ - static int console_utf8_in = 0; - static int console_utf8_out = 0; - /* Record whether can do UTF-8 or --no-utf8 seen in invocation. */ - static int mbcs_opted = 1; /* Assume cannot do until shown otherwise. */ - #else - # define console_utf8_in 0 - # define console_utf8_out 0 - # define SHELL_WIN_UTF8_OPT 0 - #endif - - /* --** 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. ++** On Windows systems we need to know if standard output is a console ++** in order to show that UTF-16 translation is done in the sign-on ++** banner. The following variable is true if it is the console. */ static int stdout_is_console = 1; @@@ -10315,8 -10077,8 +10077,7 @@@ static int do_meta_command(char *zLine } if( pChng && fwrite(pChng, szChng, 1, out)!=1 ){ - raw_printf(stderr, "ERROR: Failed to write entire %d-byte output\n", - szChng); - eputf("ERROR: Failed to write entire %d-byte output\n", - szChng); ++ eputf("ERROR: Failed to write entire %d-byte output\n", szChng); } sqlite3_free(pChng); fclose(out); @@@ -12117,7 -11861,7 +11860,7 @@@ static void printBold(const char *zText FOREGROUND_RED|FOREGROUND_INTENSITY ); #endif - printf("%s", zText); - oputf("%s", zText); ++ oputz(zText); #if !SQLITE_OS_WINRT SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes); #endif @@@ -12134,8 -11878,8 +11877,7 @@@ static void printBold(const char *zText */ static char *cmdline_option_value(int argc, char **argv, int i){ if( i==argc ){ - utf8_printf(stderr, "%s: Error: missing argument to %s\n", - argv[0], argv[argc-1]); - eputf("%s: Error: missing argument to %s\n", - argv[0], argv[argc-1]); ++ eputf("%s: Error: missing argument to %s\n", argv[0], argv[argc-1]); exit(1); } return argv[i]; @@@ -12229,7 -11971,7 +11969,7 @@@ int SQLITE_CDECL wmain(int argc, wchar_ signal(SIGINT, interrupt_handler); #elif (defined(_WIN32) || defined(WIN32)) && !defined(_WIN32_WCE) if( !SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE) ){ - fprintf(stderr, "No ^C handler.\n"); - eputf("No ^C handler.\n"); ++ eputz("No ^C handler.\n"); } #endif @@@ -12737,26 -12463,21 +12461,20 @@@ if( stdin_is_interactive ){ char *zHome; char *zHistory; -- const char *zCharset = ""; int nHistory; - #if SHELL_WIN_UTF8_OPT - switch( console_utf8_in+2*console_utf8_out ){ - default: case 0: break; - case 1: zCharset = " (utf8 in)"; break; - case 2: zCharset = " (utf8 out)"; break; - case 3: zCharset = " (utf8 I/O)"; break; - } - #endif - printf( - "SQLite version %s %.19s%s\n" /*extra-version-info*/ - "Enter \".help\" for usage hints.\n", - sqlite3_libversion(), sqlite3_sourceid(), zCharset - ); -#if SHELL_CON_TRANSLATE==1 - zCharset = " (UTF-16 console I/O)"; -#elif SHELL_CON_TRANSLATE==2 - zCharset = " (MBCS console I/O)"; ++#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 + oputf("SQLite version %s %.19s%s\n" /*extra-version-info*/ + "Enter \".help\" for usage hints.\n", - sqlite3_libversion(), sqlite3_sourceid(), zCharset); ++ sqlite3_libversion(), sqlite3_sourceid(), SHELL_CIO_CHAR_SET); if( warnInmemoryDb ){ - printf("Connected to a "); + oputz("Connected to a "); printBold("transient in-memory database"); - printf(".\nUse \".open FILENAME\" to reopen on a " - "persistent database.\n"); + oputz(".\nUse \".open FILENAME\" to reopen on a" + " persistent database.\n"); } zHistory = getenv("SQLITE_HISTORY"); if( zHistory ){