Merge trunk changes into this branch.
[sqlite.git] / ext / consio / console_io.c
blob3e2f556f52eb23b024d47b9a9f83040b3adac8f0
1 /*
2 ** 2023 November 4
3 **
4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
6 **
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 ********************************************************************************
12 ** This file implements various interfaces used for console and stream I/O
13 ** by the SQLite project command-line tools, as explained in console_io.h .
14 ** Functions prefixed by "SQLITE_INTERNAL_LINKAGE" behave as described there.
17 #ifndef SQLITE_CDECL
18 # define SQLITE_CDECL
19 #endif
21 #ifndef SHELL_NO_SYSINC
22 # include <stdarg.h>
23 # include <string.h>
24 # include <stdlib.h>
25 # include <limits.h>
26 # include <assert.h>
27 # include "sqlite3.h"
28 #endif
29 #ifndef HAVE_CONSOLE_IO_H
30 # include "console_io.h"
31 #endif
32 #if defined(_MSC_VER)
33 # pragma warning(disable : 4204)
34 #endif
36 #ifndef SQLITE_CIO_NO_TRANSLATE
37 # if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT
38 # ifndef SHELL_NO_SYSINC
39 # include <io.h>
40 # include <fcntl.h>
41 # undef WIN32_LEAN_AND_MEAN
42 # define WIN32_LEAN_AND_MEAN
43 # include <windows.h>
44 # endif
45 # define CIO_WIN_WC_XLATE 1 /* Use WCHAR Windows APIs for console I/O */
46 # else
47 # ifndef SHELL_NO_SYSINC
48 # include <unistd.h>
49 # endif
50 # define CIO_WIN_WC_XLATE 0 /* Use plain C library stream I/O at console */
51 # endif
52 #else
53 # define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */
54 #endif
56 #if CIO_WIN_WC_XLATE
57 /* Character used to represent a known-incomplete UTF-8 char group (�) */
58 static WCHAR cBadGroup = 0xfffd;
59 #endif
61 #if CIO_WIN_WC_XLATE
62 static HANDLE handleOfFile(FILE *pf){
63 int fileDesc = _fileno(pf);
64 union { intptr_t osfh; HANDLE fh; } fid = {
65 (fileDesc>=0)? _get_osfhandle(fileDesc) : (intptr_t)INVALID_HANDLE_VALUE
67 return fid.fh;
69 #endif
71 #ifndef SQLITE_CIO_NO_TRANSLATE
72 typedef struct PerStreamTags {
73 # if CIO_WIN_WC_XLATE
74 HANDLE hx;
75 DWORD consMode;
76 char acIncomplete[4];
77 # else
78 short reachesConsole;
79 # endif
80 FILE *pf;
81 } PerStreamTags;
83 /* Define NULL-like value for things which can validly be 0. */
84 # define SHELL_INVALID_FILE_PTR ((FILE *)~0)
85 # if CIO_WIN_WC_XLATE
86 # define SHELL_INVALID_CONS_MODE 0xFFFF0000
87 # endif
89 # if CIO_WIN_WC_XLATE
90 # define PST_INITIALIZER { INVALID_HANDLE_VALUE, SHELL_INVALID_CONS_MODE, \
91 {0,0,0,0}, SHELL_INVALID_FILE_PTR }
92 # else
93 # define PST_INITIALIZER { 0, SHELL_INVALID_FILE_PTR }
94 # endif
96 /* Quickly say whether a known output is going to the console. */
97 # if CIO_WIN_WC_XLATE
98 static short pstReachesConsole(PerStreamTags *ppst){
99 return (ppst->hx != INVALID_HANDLE_VALUE);
101 # else
102 # define pstReachesConsole(ppst) 0
103 # endif
105 # if CIO_WIN_WC_XLATE
106 static void restoreConsoleArb(PerStreamTags *ppst){
107 if( pstReachesConsole(ppst) ) SetConsoleMode(ppst->hx, ppst->consMode);
109 # else
110 # define restoreConsoleArb(ppst)
111 # endif
113 /* Say whether FILE* appears to be a console, collect associated info. */
114 static short streamOfConsole(FILE *pf, /* out */ PerStreamTags *ppst){
115 # if CIO_WIN_WC_XLATE
116 short rv = 0;
117 DWORD dwCM = SHELL_INVALID_CONS_MODE;
118 HANDLE fh = handleOfFile(pf);
119 ppst->pf = pf;
120 if( INVALID_HANDLE_VALUE != fh ){
121 rv = (GetFileType(fh) == FILE_TYPE_CHAR && GetConsoleMode(fh,&dwCM));
123 ppst->hx = (rv)? fh : INVALID_HANDLE_VALUE;
124 ppst->consMode = dwCM;
125 return rv;
126 # else
127 ppst->pf = pf;
128 ppst->reachesConsole = ( (short)isatty(fileno(pf)) );
129 return ppst->reachesConsole;
130 # endif
133 # ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
134 # define ENABLE_VIRTUAL_TERMINAL_PROCESSING (0x4)
135 # endif
137 # if CIO_WIN_WC_XLATE
138 /* Define console modes for use with the Windows Console API. */
139 # define SHELL_CONI_MODE \
140 (ENABLE_ECHO_INPUT | ENABLE_INSERT_MODE | ENABLE_LINE_INPUT | 0x80 \
141 | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_PROCESSED_INPUT)
142 # define SHELL_CONO_MODE (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT \
143 | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
144 # endif
146 typedef struct ConsoleInfo {
147 PerStreamTags pstSetup[3];
148 PerStreamTags pstDesignated[3];
149 StreamsAreConsole sacSetup;
150 } ConsoleInfo;
152 static short isValidStreamInfo(PerStreamTags *ppst){
153 return (ppst->pf != SHELL_INVALID_FILE_PTR);
156 static ConsoleInfo consoleInfo = {
157 { /* pstSetup */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER },
158 { /* pstDesignated[] */ PST_INITIALIZER, PST_INITIALIZER, PST_INITIALIZER },
159 SAC_NoConsole /* sacSetup */
162 SQLITE_INTERNAL_LINKAGE FILE* invalidFileStream = (FILE *)~0;
164 # if CIO_WIN_WC_XLATE
165 static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){
166 if( pstReachesConsole(ppst) ){
167 DWORD cm = odir? SHELL_CONO_MODE : SHELL_CONI_MODE;
168 SetConsoleMode(ppst->hx, cm);
171 # else
172 # define maybeSetupAsConsole(ppst,odir)
173 # endif
175 SQLITE_INTERNAL_LINKAGE void consoleRenewSetup(void){
176 # if CIO_WIN_WC_XLATE
177 int ix = 0;
178 while( ix < 6 ){
179 PerStreamTags *ppst = (ix<3)?
180 &consoleInfo.pstSetup[ix] : &consoleInfo.pstDesignated[ix-3];
181 maybeSetupAsConsole(ppst, (ix % 3)>0);
182 ++ix;
184 # endif
187 SQLITE_INTERNAL_LINKAGE StreamsAreConsole
188 consoleClassifySetup( FILE *pfIn, FILE *pfOut, FILE *pfErr ){
189 StreamsAreConsole rv = SAC_NoConsole;
190 FILE* apf[3] = { pfIn, pfOut, pfErr };
191 int ix;
192 for( ix = 2; ix >= 0; --ix ){
193 PerStreamTags *ppst = &consoleInfo.pstSetup[ix];
194 if( streamOfConsole(apf[ix], ppst) ){
195 rv |= (SAC_InConsole<<ix);
197 consoleInfo.pstDesignated[ix] = *ppst;
198 if( ix > 0 ) fflush(apf[ix]);
200 consoleInfo.sacSetup = rv;
201 consoleRenewSetup();
202 return rv;
205 SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void ){
206 # if CIO_WIN_WC_XLATE
207 static ConsoleInfo *pci = &consoleInfo;
208 if( pci->sacSetup ){
209 int ix;
210 for( ix=0; ix<3; ++ix ){
211 if( pci->sacSetup & (SAC_InConsole<<ix) ){
212 PerStreamTags *ppst = &pci->pstSetup[ix];
213 SetConsoleMode(ppst->hx, ppst->consMode);
217 # endif
219 #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
221 #ifdef SQLITE_CIO_INPUT_REDIR
222 /* Say whether given FILE* is among those known, via either
223 ** consoleClassifySetup() or set{Output,Error}Stream, as
224 ** readable, and return an associated PerStreamTags pointer
225 ** if so. Otherwise, return 0.
227 static PerStreamTags * isKnownReadable(FILE *pf){
228 static PerStreamTags *apst[] = {
229 &consoleInfo.pstDesignated[0], &consoleInfo.pstSetup[0], 0
231 int ix = 0;
232 do {
233 if( apst[ix]->pf == pf ) break;
234 } while( apst[++ix] != 0 );
235 return apst[ix];
237 #endif
239 #ifndef SQLITE_CIO_NO_TRANSLATE
240 /* Say whether given FILE* is among those known, via either
241 ** consoleClassifySetup() or set{Output,Error}Stream, as
242 ** writable, and return an associated PerStreamTags pointer
243 ** if so. Otherwise, return 0.
245 static PerStreamTags * isKnownWritable(FILE *pf){
246 static PerStreamTags *apst[] = {
247 &consoleInfo.pstDesignated[1], &consoleInfo.pstDesignated[2],
248 &consoleInfo.pstSetup[1], &consoleInfo.pstSetup[2], 0
250 int ix = 0;
251 do {
252 if( apst[ix]->pf == pf ) break;
253 } while( apst[++ix] != 0 );
254 return apst[ix];
257 static FILE *designateEmitStream(FILE *pf, unsigned chix){
258 FILE *rv = consoleInfo.pstDesignated[chix].pf;
259 if( pf == invalidFileStream ) return rv;
260 else{
261 /* Setting a possibly new output stream. */
262 PerStreamTags *ppst = isKnownWritable(pf);
263 if( ppst != 0 ){
264 PerStreamTags pst = *ppst;
265 consoleInfo.pstDesignated[chix] = pst;
266 }else streamOfConsole(pf, &consoleInfo.pstDesignated[chix]);
268 return rv;
271 SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf){
272 return designateEmitStream(pf, 1);
274 # ifdef CONSIO_SET_ERROR_STREAM
275 SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf){
276 return designateEmitStream(pf, 2);
278 # endif
279 #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
281 #ifndef SQLITE_CIO_NO_SETMODE
282 # if CIO_WIN_WC_XLATE
283 static void setModeFlushQ(FILE *pf, short bFlush, int mode){
284 if( bFlush ) fflush(pf);
285 _setmode(_fileno(pf), mode);
287 # else
288 # define setModeFlushQ(f, b, m) if(b) fflush(f)
289 # endif
291 SQLITE_INTERNAL_LINKAGE void setBinaryMode(FILE *pf, short bFlush){
292 setModeFlushQ(pf, bFlush, _O_BINARY);
294 SQLITE_INTERNAL_LINKAGE void setTextMode(FILE *pf, short bFlush){
295 setModeFlushQ(pf, bFlush, _O_TEXT);
297 # undef setModeFlushQ
299 #else /* defined(SQLITE_CIO_NO_SETMODE) */
300 # define setBinaryMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0)
301 # define setTextMode(f, bFlush) do{ if((bFlush)) fflush(f); }while(0)
302 #endif /* defined(SQLITE_CIO_NO_SETMODE) */
304 #ifndef SQLITE_CIO_NO_TRANSLATE
305 # if CIO_WIN_WC_XLATE
306 /* Write buffer cBuf as output to stream known to reach console,
307 ** limited to ncTake char's. Return ncTake on success, else 0. */
308 static int conZstrEmit(PerStreamTags *ppst, const char *z, int ncTake){
309 int rv = 0;
310 if( z!=NULL ){
311 int nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, 0,0);
312 if( nwc > 0 ){
313 WCHAR *zw = sqlite3_malloc64(nwc*sizeof(WCHAR));
314 if( zw!=NULL ){
315 nwc = MultiByteToWideChar(CP_UTF8,0, z,ncTake, zw,nwc);
316 if( nwc > 0 ){
317 /* Translation from UTF-8 to UTF-16, then WCHARs out. */
318 if( WriteConsoleW(ppst->hx, zw,nwc, 0, NULL) ){
319 rv = ncTake;
322 sqlite3_free(zw);
326 return rv;
329 /* For {f,o,e}PrintfUtf8() when stream is known to reach console. */
330 static int conioVmPrintf(PerStreamTags *ppst, const char *zFormat, va_list ap){
331 char *z = sqlite3_vmprintf(zFormat, ap);
332 if( z ){
333 int rv = conZstrEmit(ppst, z, (int)strlen(z));
334 sqlite3_free(z);
335 return rv;
336 }else return 0;
338 # endif /* CIO_WIN_WC_XLATE */
340 # ifdef CONSIO_GET_EMIT_STREAM
341 static PerStreamTags * getDesignatedEmitStream(FILE *pf, unsigned chix,
342 PerStreamTags *ppst){
343 PerStreamTags *rv = isKnownWritable(pf);
344 short isValid = (rv!=0)? isValidStreamInfo(rv) : 0;
345 if( rv != 0 && isValid ) return rv;
346 streamOfConsole(pf, ppst);
347 return ppst;
349 # endif
351 /* Get stream info, either for designated output or error stream when
352 ** chix equals 1 or 2, or for an arbitrary stream when chix == 0.
353 ** In either case, ppst references a caller-owned PerStreamTags
354 ** struct which may be filled in if none of the known writable
355 ** streams is being held by consoleInfo. The ppf parameter is a
356 ** byref output when chix!=0 and a byref input when chix==0.
358 static PerStreamTags *
359 getEmitStreamInfo(unsigned chix, PerStreamTags *ppst,
360 /* in/out */ FILE **ppf){
361 PerStreamTags *ppstTry;
362 FILE *pfEmit;
363 if( chix > 0 ){
364 ppstTry = &consoleInfo.pstDesignated[chix];
365 if( !isValidStreamInfo(ppstTry) ){
366 ppstTry = &consoleInfo.pstSetup[chix];
367 pfEmit = ppst->pf;
368 }else pfEmit = ppstTry->pf;
369 if( !isValidStreamInfo(ppstTry) ){
370 pfEmit = (chix > 1)? stderr : stdout;
371 ppstTry = ppst;
372 streamOfConsole(pfEmit, ppstTry);
374 *ppf = pfEmit;
375 }else{
376 ppstTry = isKnownWritable(*ppf);
377 if( ppstTry != 0 ) return ppstTry;
378 streamOfConsole(*ppf, ppst);
379 return ppst;
381 return ppstTry;
384 SQLITE_INTERNAL_LINKAGE int oPrintfUtf8(const char *zFormat, ...){
385 va_list ap;
386 int rv;
387 FILE *pfOut;
388 PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
389 # if CIO_WIN_WC_XLATE
390 PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
391 # else
392 getEmitStreamInfo(1, &pst, &pfOut);
393 # endif
394 assert(zFormat!=0);
395 va_start(ap, zFormat);
396 # if CIO_WIN_WC_XLATE
397 if( pstReachesConsole(ppst) ){
398 rv = conioVmPrintf(ppst, zFormat, ap);
399 }else{
400 # endif
401 rv = vfprintf(pfOut, zFormat, ap);
402 # if CIO_WIN_WC_XLATE
404 # endif
405 va_end(ap);
406 return rv;
409 SQLITE_INTERNAL_LINKAGE int ePrintfUtf8(const char *zFormat, ...){
410 va_list ap;
411 int rv;
412 FILE *pfErr;
413 PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
414 # if CIO_WIN_WC_XLATE
415 PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
416 # else
417 getEmitStreamInfo(2, &pst, &pfErr);
418 # endif
419 assert(zFormat!=0);
420 va_start(ap, zFormat);
421 # if CIO_WIN_WC_XLATE
422 if( pstReachesConsole(ppst) ){
423 rv = conioVmPrintf(ppst, zFormat, ap);
424 }else{
425 # endif
426 rv = vfprintf(pfErr, zFormat, ap);
427 # if CIO_WIN_WC_XLATE
429 # endif
430 va_end(ap);
431 return rv;
434 SQLITE_INTERNAL_LINKAGE int fPrintfUtf8(FILE *pfO, const char *zFormat, ...){
435 va_list ap;
436 int rv;
437 PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
438 # if CIO_WIN_WC_XLATE
439 PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
440 # else
441 getEmitStreamInfo(0, &pst, &pfO);
442 # endif
443 assert(zFormat!=0);
444 va_start(ap, zFormat);
445 # if CIO_WIN_WC_XLATE
446 if( pstReachesConsole(ppst) ){
447 maybeSetupAsConsole(ppst, 1);
448 rv = conioVmPrintf(ppst, zFormat, ap);
449 if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
450 }else{
451 # endif
452 rv = vfprintf(pfO, zFormat, ap);
453 # if CIO_WIN_WC_XLATE
455 # endif
456 va_end(ap);
457 return rv;
460 SQLITE_INTERNAL_LINKAGE int fPutsUtf8(const char *z, FILE *pfO){
461 PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
462 # if CIO_WIN_WC_XLATE
463 PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
464 # else
465 getEmitStreamInfo(0, &pst, &pfO);
466 # endif
467 assert(z!=0);
468 # if CIO_WIN_WC_XLATE
469 if( pstReachesConsole(ppst) ){
470 int rv;
471 maybeSetupAsConsole(ppst, 1);
472 rv = conZstrEmit(ppst, z, (int)strlen(z));
473 if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
474 return rv;
475 }else {
476 # endif
477 return (fputs(z, pfO)<0)? 0 : (int)strlen(z);
478 # if CIO_WIN_WC_XLATE
480 # endif
483 SQLITE_INTERNAL_LINKAGE int ePutsUtf8(const char *z){
484 FILE *pfErr;
485 PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
486 # if CIO_WIN_WC_XLATE
487 PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
488 # else
489 getEmitStreamInfo(2, &pst, &pfErr);
490 # endif
491 assert(z!=0);
492 # if CIO_WIN_WC_XLATE
493 if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z));
494 else {
495 # endif
496 return (fputs(z, pfErr)<0)? 0 : (int)strlen(z);
497 # if CIO_WIN_WC_XLATE
499 # endif
502 SQLITE_INTERNAL_LINKAGE int oPutsUtf8(const char *z){
503 FILE *pfOut;
504 PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
505 # if CIO_WIN_WC_XLATE
506 PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
507 # else
508 getEmitStreamInfo(1, &pst, &pfOut);
509 # endif
510 assert(z!=0);
511 # if CIO_WIN_WC_XLATE
512 if( pstReachesConsole(ppst) ) return conZstrEmit(ppst, z, (int)strlen(z));
513 else {
514 # endif
515 return (fputs(z, pfOut)<0)? 0 : (int)strlen(z);
516 # if CIO_WIN_WC_XLATE
518 # endif
521 #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
523 #if !(defined(SQLITE_CIO_NO_UTF8SCAN) && defined(SQLITE_CIO_NO_TRANSLATE))
524 /* Skip over as much z[] input char sequence as is valid UTF-8,
525 ** limited per nAccept char's or whole characters and containing
526 ** no char cn such that ((1<<cn) & ccm)!=0. On return, the
527 ** sequence z:return (inclusive:exclusive) is validated UTF-8.
528 ** Limit: nAccept>=0 => char count, nAccept<0 => character
530 SQLITE_INTERNAL_LINKAGE const char*
531 zSkipValidUtf8(const char *z, int nAccept, long ccm){
532 int ng = (nAccept<0)? -nAccept : 0;
533 const char *pcLimit = (nAccept>=0)? z+nAccept : 0;
534 assert(z!=0);
535 while( (pcLimit)? (z<pcLimit) : (ng-- != 0) ){
536 char c = *z;
537 if( (c & 0x80) == 0 ){
538 if( ccm != 0L && c < 0x20 && ((1L<<c) & ccm) != 0 ) return z;
539 ++z; /* ASCII */
540 }else if( (c & 0xC0) != 0xC0 ) return z; /* not a lead byte */
541 else{
542 const char *zt = z+1; /* Got lead byte, look at trail bytes.*/
544 if( pcLimit && zt >= pcLimit ) return z;
545 else{
546 char ct = *zt++;
547 if( ct==0 || (zt-z)>4 || (ct & 0xC0)!=0x80 ){
548 /* Trailing bytes are too few, too many, or invalid. */
549 return z;
552 } while( ((c <<= 1) & 0x40) == 0x40 ); /* Eat lead byte's count. */
553 z = zt;
556 return z;
558 #endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/
560 #ifndef SQLITE_CIO_NO_TRANSLATE
561 # ifdef CONSIO_SPUTB
562 SQLITE_INTERNAL_LINKAGE int
563 fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){
564 assert(pfO!=0);
565 # if CIO_WIN_WC_XLATE
566 PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
567 PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
568 if( pstReachesConsole(ppst) ){
569 int rv;
570 maybeSetupAsConsole(ppst, 1);
571 rv = conZstrEmit(ppst, cBuf, nAccept);
572 if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
573 return rv;
574 }else {
575 # endif
576 return (int)fwrite(cBuf, 1, nAccept, pfO);
577 # if CIO_WIN_WC_XLATE
579 # endif
581 # endif
583 SQLITE_INTERNAL_LINKAGE int
584 oPutbUtf8(const char *cBuf, int nAccept){
585 FILE *pfOut;
586 PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
587 # if CIO_WIN_WC_XLATE
588 PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
589 # else
590 getEmitStreamInfo(1, &pst, &pfOut);
591 # endif
592 # if CIO_WIN_WC_XLATE
593 if( pstReachesConsole(ppst) ){
594 return conZstrEmit(ppst, cBuf, nAccept);
595 }else {
596 # endif
597 return (int)fwrite(cBuf, 1, nAccept, pfOut);
598 # if CIO_WIN_WC_XLATE
600 # endif
603 # ifdef CONSIO_EPUTB
604 SQLITE_INTERNAL_LINKAGE int
605 ePutbUtf8(const char *cBuf, int nAccept){
606 FILE *pfErr;
607 PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
608 PerStreamTags *ppst = getEmitStreamInfo(2, &pst, &pfErr);
609 # if CIO_WIN_WC_XLATE
610 if( pstReachesConsole(ppst) ){
611 return conZstrEmit(ppst, cBuf, nAccept);
612 }else {
613 # endif
614 return (int)fwrite(cBuf, 1, nAccept, pfErr);
615 # if CIO_WIN_WC_XLATE
617 # endif
619 # endif /* defined(CONSIO_EPUTB) */
621 SQLITE_INTERNAL_LINKAGE char* fGetsUtf8(char *cBuf, int ncMax, FILE *pfIn){
622 if( pfIn==0 ) pfIn = stdin;
623 # if CIO_WIN_WC_XLATE
624 if( pfIn == consoleInfo.pstSetup[0].pf
625 && (consoleInfo.sacSetup & SAC_InConsole)!=0 ){
626 # if CIO_WIN_WC_XLATE==1
627 # define SHELL_GULP 150 /* Count of WCHARS to be gulped at a time */
628 WCHAR wcBuf[SHELL_GULP+1];
629 int lend = 0, noc = 0;
630 if( ncMax > 0 ) cBuf[0] = 0;
631 while( noc < ncMax-8-1 && !lend ){
632 /* There is room for at least 2 more characters and a 0-terminator. */
633 int na = (ncMax > SHELL_GULP*4+1 + noc)? SHELL_GULP : (ncMax-1 - noc)/4;
634 # undef SHELL_GULP
635 DWORD nbr = 0;
636 BOOL bRC = ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf, na, &nbr, 0);
637 if( bRC && nbr>0 && (wcBuf[nbr-1]&0xF800)==0xD800 ){
638 /* Last WHAR read is first of a UTF-16 surrogate pair. Grab its mate. */
639 DWORD nbrx;
640 bRC &= ReadConsoleW(consoleInfo.pstSetup[0].hx, wcBuf+nbr, 1, &nbrx, 0);
641 if( bRC ) nbr += nbrx;
643 if( !bRC || (noc==0 && nbr==0) ) return 0;
644 if( nbr > 0 ){
645 int nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,0,0,0,0);
646 if( nmb != 0 && noc+nmb <= ncMax ){
647 int iseg = noc;
648 nmb = WideCharToMultiByte(CP_UTF8, 0, wcBuf,nbr,cBuf+noc,nmb,0,0);
649 noc += nmb;
650 /* Fixup line-ends as coded by Windows for CR (or "Enter".)
651 ** This is done without regard for any setMode{Text,Binary}()
652 ** call that might have been done on the interactive input.
654 if( noc > 0 ){
655 if( cBuf[noc-1]=='\n' ){
656 lend = 1;
657 if( noc > 1 && cBuf[noc-2]=='\r' ) cBuf[--noc-1] = '\n';
660 /* Check for ^Z (anywhere in line) too, to act as EOF. */
661 while( iseg < noc ){
662 if( cBuf[iseg]=='\x1a' ){
663 noc = iseg; /* Chop ^Z and anything following. */
664 lend = 1; /* Counts as end of line too. */
665 break;
667 ++iseg;
669 }else break; /* Drop apparent garbage in. (Could assert.) */
670 }else break;
672 /* If got nothing, (after ^Z chop), must be at end-of-file. */
673 if( noc > 0 ){
674 cBuf[noc] = 0;
675 return cBuf;
676 }else return 0;
677 # endif
678 }else{
679 # endif
680 return fgets(cBuf, ncMax, pfIn);
681 # if CIO_WIN_WC_XLATE
683 # endif
685 #endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */
687 #if defined(_MSC_VER)
688 # pragma warning(default : 4204)
689 #endif
691 #undef SHELL_INVALID_FILE_PTR