CLI uses only lib-c for I/O on Windows. No calls to Win32. Works on Win11,

at least.  Reads and writes unicode to/from the console and UTF-8 to/from files.
Prototype code only - must testing and additional work required.

FossilOrigin-Name: 5c54530d5a0a4125a1ba44f22537c4f63d5e5708f347c43cbac3e1832c4335da
This commit is contained in:
drh 2024-09-23 20:23:43 +00:00
parent 5e419c1048
commit 1d9db01065
6 changed files with 122 additions and 97 deletions

View File

@ -1187,8 +1187,6 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c
# Source and header files that shell.c depends on
SHELL_DEP = \
$(TOP)/src/shell.c.in \
$(TOP)/ext/consio/console_io.c \
$(TOP)/ext/consio/console_io.h \
$(TOP)/ext/expert/sqlite3expert.c \
$(TOP)/ext/expert/sqlite3expert.h \
$(TOP)/ext/intck/sqlite3intck.c \

View File

@ -2316,8 +2316,6 @@ keywordhash.h: $(TOP)\tool\mkkeywordhash.c mkkeywordhash.exe
# Source and header files that shell.c depends on
SHELL_DEP = \
$(TOP)\src\shell.c.in \
$(TOP)\ext\consio\console_io.c \
$(TOP)\ext\consio\console_io.h \
$(TOP)\ext\expert\sqlite3expert.c \
$(TOP)\ext\expert\sqlite3expert.h \
$(TOP)\ext\intck\sqlite3intck.c \

View File

@ -763,8 +763,6 @@ keywordhash.h: $(TOP)/tool/mkkeywordhash.c
# Source and header files that shell.c depends on
SHELL_DEP = \
$(TOP)/src/shell.c.in \
$(TOP)/ext/consio/console_io.c \
$(TOP)/ext/consio/console_io.h \
$(TOP)/ext/expert/sqlite3expert.c \
$(TOP)/ext/expert/sqlite3expert.h \
$(TOP)/ext/intck/sqlite3intck.c \

View File

@ -1,11 +1,11 @@
C Add\sthe\srun-fuzzcheck\starget\sto\sthe\sMSVC\smakefile.
D 2024-09-21T17:27:47.017
C CLI\suses\sonly\slib-c\sfor\sI/O\son\sWindows.\s\sNo\scalls\sto\sWin32.\s\sWorks\son\sWin11,\nat\sleast.\s\sReads\sand\swrites\sunicode\sto/from\sthe\sconsole\sand\sUTF-8\sto/from\sfiles.\nPrototype\scode\sonly\s-\smust\stesting\sand\sadditional\swork\srequired.
D 2024-09-23T20:23:43.341
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F Makefile.in fa448c4c0567623fd140efebecb570ab58d955d766a5ea0fd8a94e9b5697007c
F Makefile.in aa594119c3c7f699e87a767fca6598452f77d4c32c41a6486c40d4f156d4efc1
F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6
F Makefile.msc e3c4723c27464acc31da4420b808c8d2690180ba2b915897bece0a9d5d2cecf6
F Makefile.msc add7e29bae33ad5b8c464daf6de84a1a01e31e6337d79fb0c1062e53fa7657da
F README.md c3c0f19532ce28f6297a71870f3c7b424729f0e6d9ab889616d3587dd2332159
F VERSION 0db40f92c04378404eb45bff93e9e42c148c7e54fd3da99469ed21e22411f5a6
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@ -687,7 +687,7 @@ F ext/wasm/wasmfs.make 8a4955882aaa0783b3f60a9484a1f0f3d8b6f775c0fcd17c082f31966
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
F main.mk 67e622f31d10fee8f0f62655b4f9b47cd97fe70a125674ca6754b3549d69cc0e
F main.mk b897586c0c7b77b7e39f0a0e9ed79fed7346b09af1ed35a08da745e02b795772
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
@ -768,7 +768,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c b2cd748488012312824508639b6af908461e45403037d5c4e19d9b0e8195507f
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 4b14337a2742f0c0beeba490e9a05507e9b4b12184b9cd12773501d08d48e3fe
F src/shell.c.in 375f8a183126be96ec73db4e42c57917ff10a0900846b1b722dd4f8cef537812
F src/shell.c.in 265c877932142ee8ef05a6aa5a0a5bff92905ffef97dc6f566062a27814274a1
F src/sqlite.h.in 77f55bd1978a04a14db211732f0a609077cf60ba4ccf9baf39988f508945419c
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
@ -2213,8 +2213,11 @@ F vsixtest/vsixtest.tcl 6195aba1f12a5e10efc2b8c0009532167be5e301abe5b31385638080
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 62e11a3a78edf9853b74d6495ccd8ae9ac1966c7d78eb3682cf2d5885e3740ec
R 44ce456c4e740a869984e636809287df
P 2e5194407a1b34dd0659c350ea8098bfef7b3f11aa5b2a07ecd2bce5582655a2
R 7c20c2fd1880ef1b062b32c88da2e8e4
T *branch * cli-stdlib
T *sym-cli-stdlib *
T -sym-trunk *
U drh
Z fb345da7ed81e5875e018c3667e473a3
Z cdf0bc25a30881c9fe4f62b1ff96af22
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
2e5194407a1b34dd0659c350ea8098bfef7b3f11aa5b2a07ecd2bce5582655a2
5c54530d5a0a4125a1ba44f22537c4f63d5e5708f347c43cbac3e1832c4335da

View File

@ -237,6 +237,57 @@ extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText);
#endif
#ifdef _WIN32
/* On Windows, we normally run with output mode of TEXT so that \n characters
** are automatically translated into \r\n. However, this behavior needs
** to be disabled in some cases (ex: when generating CSV output and when
** rendering quoted strings that contain \n characters). The following
** routines take care of that.
*/
static void setBinaryMode(FILE *file, int isOutput){
if( isOutput ) fflush(file);
_setmode(_fileno(file), _O_BINARY);
}
static void setTextMode(FILE *file, int isOutput){
if( isOutput ) fflush(file);
_setmode(_fileno(file), _O_TEXT);
}
#else
/* Unix equivalents to set*Mode() */
# define setBinaryMode(X,Y)
# define setTextMode(X,Y)
#endif
#ifdef _WIN32
/* fgets() for windows */
static char *cli_fgets(char *buf, int sz, FILE *in){
if( isatty(_fileno(in)) ){
/* When reading from the command-prompt in Windows, it is necessary
** to use _O_WTEXT input mode to read UTF-16 characters, then translate
** that into UTF-8. Otherwise, non-ASCII characters all get translated
** into '?'.
*/
wchar_t *b1 = malloc( sz*sizeof(wchar_t) );
if( b1==0 ) return 0;
_setmode(_fileno(in), _O_WTEXT);
if( fgetws(b1, sz/4, in)==0 ){
sqlite3_free(b1);
return 0;
}
WideCharToMultiByte(CP_UTF8, 0, b1, -1, buf, sz, 0, 0);
sqlite3_free(b1);
return buf;
}else{
/* Reading from a file or other input source, just read bytes without
** any translation. */
return fgets(buf, sz, in);
}
}
#else
/* library version works for everybody else */
# define cli_fgets fgets
#endif
/* Use console I/O package as a direct INCLUDE. */
#define SQLITE_INTERNAL_LINKAGE static
@ -248,57 +299,15 @@ extern LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText);
# define SQLITE_CIO_NO_SETMODE
# define SQLITE_CIO_NO_FLUSH
#endif
INCLUDE ../ext/consio/console_io.h
INCLUDE ../ext/consio/console_io.c
#ifndef SQLITE_SHELL_FIDDLE
/* From here onward, fgets() is redirected to the console_io library. */
# define fgets(b,n,f) fGetsUtf8(b,n,f)
/*
* Define macros for emitting output text in various ways:
* sputz(s, z) => emit 0-terminated string z to given stream s
* sputf(s, f, ...) => emit varargs per format f to given stream s
* oputz(z) => emit 0-terminated string z to default stream
* oputf(f, ...) => emit varargs per format f to default stream
* eputz(z) => emit 0-terminated string z to error stream
* eputf(f, ...) => emit varargs per format f to error stream
* oputb(b, n) => emit char buffer b[0..n-1] to default stream
*
* Note that the default stream is whatever has been last set via:
* setOutputStream(FILE *pf)
* This is normally the stream that CLI normal output goes to.
* For the stand-alone CLI, it is stdout with no .output redirect.
*
* The ?putz(z) forms are required for the Fiddle builds for string literal
* output, in aid of enforcing format string to argument correspondence.
*/
# define sputz(s,z) fPutsUtf8(z,s)
# define sputf fPrintfUtf8
# define oputz(z) oPutsUtf8(z)
# define oputf oPrintfUtf8
# define eputz(z) ePutsUtf8(z)
# define eputf ePrintfUtf8
# define oputb(buf,na) oPutbUtf8(buf,na)
# define fflush(s) fFlushBuffer(s);
#else
/* For Fiddle, all console handling and emit redirection is omitted. */
/* These next 3 macros are for emitting formatted output. When complaints
* from the WASM build are issued for non-formatted output, when a mere
* string literal is to be emitted, the ?putz(z) forms should be used.
* (This permits compile-time checking of format string / argument mismatch.)
*/
# define oputf(fmt, ...) printf(fmt,__VA_ARGS__)
# define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__)
# define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__)
#define oputf(fmt, ...) printf(fmt,__VA_ARGS__)
#define eputf(fmt, ...) fprintf(stderr,fmt,__VA_ARGS__)
#define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__)
/* These next 3 macros are for emitting simple string literals. */
# define oputz(z) fputs(z,stdout)
# define eputz(z) fputs(z,stderr)
# define sputz(fp,z) fputs(z,fp)
# define oputb(buf,na) fwrite(buf,1,na,stdout)
# undef fflush
#endif
#define oputz(z) fputs(z,stdout)
#define eputz(z) fputs(z,stderr)
#define sputz(fp,z) fputs(z,fp)
#define oputb(buf,na) fwrite(buf,1,na,stdout)
/* True if the timer is enabled */
static int enableTimer = 0;
@ -344,6 +353,7 @@ struct rusage {
#define getrusage(A,B) memset(B,0,sizeof(*B))
#endif
/* Saved resource information for the beginning of an operation */
static struct rusage sBegin; /* CPU time at start */
static sqlite3_int64 iBegin; /* Wall-clock time at start */
@ -802,7 +812,7 @@ static char *local_getline(char *zLine, FILE *in){
zLine = realloc(zLine, nLine);
shell_check_oom(zLine);
}
if( fgets(&zLine[n], nLine - n, in)==0 ){
if( cli_fgets(&zLine[n], nLine - n, in)==0 ){
if( n==0 ){
free(zLine);
return 0;
@ -1766,10 +1776,7 @@ static const char *unused_string(
static void output_quoted_string(const char *z){
int i;
char c;
#ifndef SQLITE_SHELL_FIDDLE
FILE *pfO = setOutputStream(invalidFileStream);
setBinaryMode(pfO, 1);
#endif
setBinaryMode(stdout, 1);
if( z==0 ) return;
for(i=0; (c = z[i])!=0 && c!='\''; i++){}
if( c==0 ){
@ -1794,11 +1801,7 @@ static void output_quoted_string(const char *z){
}
oputz("'");
}
#ifndef SQLITE_SHELL_FIDDLE
setTextMode(pfO, 1);
#else
setTextMode(stdout, 1);
#endif
}
/*
@ -1813,10 +1816,7 @@ static void output_quoted_string(const char *z){
static void output_quoted_escaped_string(const char *z){
int i;
char c;
#ifndef SQLITE_SHELL_FIDDLE
FILE *pfO = setOutputStream(invalidFileStream);
setBinaryMode(pfO, 1);
#endif
setBinaryMode(stdout, 1);
for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){}
if( c==0 ){
oputf("'%s'",z);
@ -1868,11 +1868,7 @@ static void output_quoted_escaped_string(const char *z){
oputf(",'%s',char(10))", zNL);
}
}
#ifndef SQLITE_SHELL_FIDDLE
setTextMode(pfO, 1);
#else
setTextMode(stdout, 1);
#endif
}
/*
@ -1892,6 +1888,42 @@ static const char *anyOfInStr(const char *s, const char *zAny, size_t ns){
}
return pcFirst;
}
/* Skip over as much z[] input char sequence as is valid UTF-8,
** limited per nAccept char's or whole characters and containing
** no char cn such that ((1<<cn) & ccm)!=0. On return, the
** sequence z:return (inclusive:exclusive) is validated UTF-8.
** Limit: nAccept>=0 => char count, nAccept<0 => character
*/
const char *zSkipValidUtf8(const char *z, int nAccept, long ccm){
int ng = (nAccept<0)? -nAccept : 0;
const char *pcLimit = (nAccept>=0)? z+nAccept : 0;
assert(z!=0);
while( (pcLimit)? (z<pcLimit) : (ng-- != 0) ){
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;
}
/*
** Output the given string as a quoted according to C or TCL quoting rules.
*/
@ -3027,7 +3059,7 @@ static void displayLinuxIoStats(void){
sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid());
in = fopen(z, "rb");
if( in==0 ) return;
while( fgets(z, sizeof(z), in)!=0 ){
while( cli_fgets(z, sizeof(z), in)!=0 ){
static const struct {
const char *zPattern;
const char *zDesc;
@ -5238,7 +5270,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
}
*pnData = 0;
nLine++;
if( fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error;
if( cli_fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error;
rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz);
if( rc!=2 ) goto readHexDb_error;
if( n<0 ) goto readHexDb_error;
@ -5251,7 +5283,7 @@ static unsigned char *readHexDb(ShellState *p, int *pnData){
eputz("invalid pagesize\n");
goto readHexDb_error;
}
for(nLine++; fgets(zLine, sizeof(zLine), in)!=0; nLine++){
for(nLine++; cli_fgets(zLine, sizeof(zLine), in)!=0; nLine++){
rc = sscanf(zLine, "| page %d offset %d", &j, &k);
if( rc==2 ){
iOffset = k;
@ -5283,7 +5315,7 @@ readHexDb_error:
if( in!=p->in ){
fclose(in);
}else{
while( fgets(zLine, sizeof(zLine), p->in)!=0 ){
while( cli_fgets(zLine, sizeof(zLine), p->in)!=0 ){
nLine++;
if(cli_strncmp(zLine, "| end ", 6)==0 ) break;
}
@ -6177,10 +6209,10 @@ static void tryToClone(ShellState *p, const char *zNewDb){
** Change the output stream (file or pipe or console) to something else.
*/
static void output_redir(ShellState *p, FILE *pfNew){
if( p->out != stdout ) eputz("Output already redirected.\n");
else{
if( p->out != stdout ){
eputz("Output already redirected.\n");
}else{
p->out = pfNew;
setOutputStream(pfNew);
}
}
@ -6226,7 +6258,6 @@ static void output_reset(ShellState *p){
}
p->outfile[0] = 0;
p->out = stdout;
setOutputStream(stdout);
}
#else
# define output_redir(SS,pfO)
@ -10719,9 +10750,9 @@ static int do_meta_command(char *zLine, ShellState *p){
zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"",
zCmd, azArg[i]);
}
consoleRestore();
/*consoleRestore();*/
x = zCmd!=0 ? system(zCmd) : 1;
consoleRenewSetup();
/*consoleRenewSetup();*/
sqlite3_free(zCmd);
if( x ) eputf("System command returns %d\n", x);
}else
@ -12374,7 +12405,6 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
# define data shellState
#else
ShellState data;
StreamsAreConsole consStreams = SAC_NoConsole;
#endif
const char *zInitFile = 0;
int i;
@ -12397,10 +12427,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
stdout_is_console = 1;
data.wasm.zDefaultDbName = "/fiddle.sqlite3";
#else
consStreams = consoleClassifySetup(stdin, stdout, stderr);
stdin_is_interactive = (consStreams & SAC_InConsole)!=0;
stdout_is_console = (consStreams & SAC_OutConsole)!=0;
atexit(consoleRestore);
stdin_is_interactive = isatty(0);
stdout_is_console = isatty(1);
#endif
atexit(sayAbnormalExit);
#ifdef SQLITE_DEBUG