--- /dev/null
-#undef CIO_WIN_WC_XLATE
+ /*
+ ** 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 <stdarg.h>
+ # include <string.h>
+ # include <stdlib.h>
+ # include <limits.h>
+ # include <assert.h>
+ # 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 <io.h>
+ # include <fcntl.h>
+ # undef WIN32_LEAN_AND_MEAN
+ # define WIN32_LEAN_AND_MEAN
+ # include <windows.h>
+ # endif
+ # define CIO_WIN_WC_XLATE 1 /* Use WCHAR Windows APIs for console I/O */
+ # else
+ # ifndef SHELL_NO_SYSINC
+ # include <unistd.h>
+ # 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<<ix);
+ }
+ consoleInfo.pstDesignated[ix] = *ppst;
+ if( ix > 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<<ix) ){
+ PerStreamTags *ppst = &pci->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<<cn) & ccm)!=0. On return, the
+ ** sequence z:return (inclusive:exclusive) is validated UTF-8.
+ ** Limit: nAccept>=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) : (ng-- != 0) ){
+ char c = *z;
+ if( (c & 0x80) == 0 ){
+ 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;
+ }
+ #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 SHELL_INVALID_FILE_PTR
--- /dev/null
+ /*
+ ** 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 <stdio.h>
+ #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<<cn) & ccm)!=0. On return, the
+ ** sequence z:return (inclusive:exclusive) is validated UTF-8.
+ ** Limit: nAccept>=0 => char count, nAccept<0 => character
+ */
+ SQLITE_INTERNAL_LINKAGE const char*
+ zSkipValidUtf8(const char *z, int nAccept, long ccm);
+
+ #endif
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;
}
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);
FOREGROUND_RED|FOREGROUND_INTENSITY
);
#endif
- printf("%s", zText);
- oputf("%s", zText);
++ oputz(zText);
#if !SQLITE_OS_WINRT
SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
#endif
*/
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];
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
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 ){