SQLite

Check-in [b4fa233d3d]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Get CLI utf8_fgets() to not consume more input than it returns. Get console setup restoration to happen for all non-crash exits.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | cli-utf8
Files: files | file ages | folders
SHA3-256: b4fa233d3dda54fa83771844cf5156bf1275c687925340af17a7713a9400dfef
User & Date: larrybr 2023-04-13 14:14:27.434
Context
2023-04-14
19:56
Cure CLI double-prompting (by ditching gcc fgetws()), general cleanup. Work remaining is to avoid effect of -utf8 when a line editor is linked/used as part of CLI. (check-in: 73a5f54231 user: larrybr tags: cli-utf8)
2023-04-13
14:14
Get CLI utf8_fgets() to not consume more input than it returns. Get console setup restoration to happen for all non-crash exits. (check-in: b4fa233d3d user: larrybr tags: cli-utf8)
2023-04-12
17:54
WIP: CLI option to take control of console on Windows and make it support UTF-8 input pasting (or typing). Needs work to become robust per "ToDo:". (check-in: 824382393d user: larrybr tags: cli-utf8)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/shell.c.in.
1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
** 2001 September 15
**
** 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 contains code to implement the "sqlite" command line
** utility for accessing SQLite databases.

*/
#if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS)
/* This needs to come before any includes for MSVC compiler */
#define _CRT_SECURE_NO_WARNINGS
#endif
typedef unsigned int u32;
typedef unsigned short int u16;

/*
** Optionally #include a user-defined header, whereby compilation options
** may be set prior to where they take effect, but after platform setup.
** If SQLITE_CUSTOM_INCLUDE=? is defined, its value names the #include
** file. Note that this macro has a like effect on sqlite3.c compilation.
*/
# define SHELL_STRINGIFY_(f) #f
# define SHELL_STRINGIFY(f) SHELL_STRINGIFY_(f)
#ifdef SQLITE_CUSTOM_INCLUDE













>









|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
** 2001 September 15
**
** 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 contains code to implement the "sqlite" command line
** utility for accessing SQLite databases.
ToDo: The -utf8 option may not play well with line editing input libraries.
*/
#if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS)
/* This needs to come before any includes for MSVC compiler */
#define _CRT_SECURE_NO_WARNINGS
#endif
typedef unsigned int u32;
typedef unsigned short int u16;

/*
** optionally #include a user-defined header, whereby compilation options
** may be set prior to where they take effect, but after platform setup.
** If SQLITE_CUSTOM_INCLUDE=? is defined, its value names the #include
** file. Note that this macro has a like effect on sqlite3.c compilation.
*/
# define SHELL_STRINGIFY_(f) #f
# define SHELL_STRINGIFY(f) SHELL_STRINGIFY_(f)
#ifdef SQLITE_CUSTOM_INCLUDE
585
586
587
588
589
590
591
592
593
594

595
596
597
598
599
600
601
602
603
604
605
606
607
608
  }
  return dynPrompt.dynamicPrompt;
}
#endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */

#if (defined(_WIN32) || defined(WIN32)) && !defined(SQLITE_SHELL_FIDDLE)
static int infsMode;
static UINT codePage;
static HANDLE hConsoleIn;
static DWORD consoleMode;

/*
** Prepare input stream, (if known to be a WIN32 console), for UTF-8
** input (from either typing or suitable paste operations) and for
** UTF-8 rendering. This may "fail" with a message to stderr, where
** the preparation is not done and common "code page" issues occur.
*/
static void instream_prepare(){
  if( isatty(0) ){
    if( !IsValidCodePage(CP_UTF8) ){
      fprintf(stderr, "Cannot use UTF-8 code page.\n");
      console_utf8 = 0;
      return;
    }
    codePage = GetConsoleCP();







|


>






|







586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
  }
  return dynPrompt.dynamicPrompt;
}
#endif /* !defined(SQLITE_OMIT_DYNAPROMPT) */

#if (defined(_WIN32) || defined(WIN32)) && !defined(SQLITE_SHELL_FIDDLE)
static int infsMode;
static UINT codePage = 0;
static HANDLE hConsoleIn;
static DWORD consoleMode;

/*
** Prepare input stream, (if known to be a WIN32 console), for UTF-8
** input (from either typing or suitable paste operations) and for
** UTF-8 rendering. This may "fail" with a message to stderr, where
** the preparation is not done and common "code page" issues occur.
*/
static void instream_prepare(void){
  if( isatty(0) ){
    if( !IsValidCodePage(CP_UTF8) ){
      fprintf(stderr, "Cannot use UTF-8 code page.\n");
      console_utf8 = 0;
      return;
    }
    codePage = GetConsoleCP();
616
617
618
619
620
621
622
623
624
625
626
627

628
629
630
631
632
633
634
635
636
637
638
639
640

641
642
643
644
645






646

647
648


649

650
651
652
653
654
655
656
657
658
659
660
661
    console_utf8 = 0;
  }
}

/*
** Undo the effects of instream_prepare(), if any.
*/
static void instream_restore(){
  if( console_utf8 ){
    _setmode(_fileno(stdin), infsMode);
    SetConsoleCP(codePage);
    SetConsoleMode( hConsoleIn, consoleMode );

  }
}

/*
** Collect input like fgets(...) with special provisions for input
** from the Windows console to get around its strange coding issues.
** Defers to plain fgets() when input is not interactive or when the
** startup option, -utf8, has not been provided or taken effect.
** WIP: This routine needs work to consume only as many characters
** as can be fit into the provided input buffer. ToDo: That work.
** Avoiding this issue will require some refactoring of CLI input.
*/
static char* utf8_fgets(char *buf, int ncmax, FILE *fin){

  if( fin==stdin && stdin_is_interactive && console_utf8 ){
    wchar_t * wbuf = (wchar_t*) malloc(ncmax * sizeof(wchar_t));
    wchar_t * wz;
    int nmb;
    if( wbuf==0 ) return 0;






    if( 0 == (wz = fgetws(wbuf, ncmax, stdin))

        || 0 == (nmb = WideCharToMultiByte(CP_UTF8, 0, wz, -1, 0, 0, 0, 0))
        || nmb > ncmax ){


      buf = 0;

      goto bail;
    }
    WideCharToMultiByte(CP_UTF8, 0, wz, -1, buf, nmb, 0, 0);
  bail:
    free(wbuf);
    return buf;
  }else{
    return fgets(buf, ncmax, fin);
  }
}

# define fgets(b,n,f) utf8_fgets(b,n,f)







|
|



>








<
<
<


>

<
|
|
|
>
>
>
>
>
>
|
>
|
|
>
>
|
>
|

<
<
|







618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638



639
640
641
642

643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661


662
663
664
665
666
667
668
669
    console_utf8 = 0;
  }
}

/*
** Undo the effects of instream_prepare(), if any.
*/
static void SQLITE_CDECL instream_restore(void){
  if( console_utf8 && codePage!=0 ){
    _setmode(_fileno(stdin), infsMode);
    SetConsoleCP(codePage);
    SetConsoleMode( hConsoleIn, consoleMode );
    console_utf8 = 0; /* Avoid multiple calls. */
  }
}

/*
** Collect input like fgets(...) with special provisions for input
** from the Windows console to get around its strange coding issues.
** Defers to plain fgets() when input is not interactive or when the
** startup option, -utf8, has not been provided or taken effect.



*/
static char* utf8_fgets(char *buf, int ncmax, FILE *fin){
  #define SQLITE_IA_LINE_LEN 150
  if( fin==stdin && stdin_is_interactive && console_utf8 ){

    wchar_t wbuf[SQLITE_IA_LINE_LEN];
    int noc = 0;
    if( ncmax == 0 ) return 0;
    buf[0] = 0;
    while( noc < ncmax-7-1 ){
      /* There is room for at least 2 more characters and the 0-terminator. */
      int na = (ncmax-1 - noc)/4;
      wchar_t * wz;
      if( na > SQLITE_IA_LINE_LEN ) na = SQLITE_IA_LINE_LEN;
      wz = fgetws(wbuf, na, stdin);
      if( wz != 0 ){
        int nmb = WideCharToMultiByte(CP_UTF8, 0, wz, -1, 0, 0, 0, 0);
        if( nmb !=0 && noc+nmb <= ncmax ){
          WideCharToMultiByte(CP_UTF8, 0, wz, -1, buf+noc, nmb, 0, 0);
          noc += nmb-1; /* Strip 0-terminator added by above call. */
          if( buf[noc-1] == '\n' ) break;
        }else break; /* Drop apparent garbage in. (Could assert.) */
      }else break;
    }


    if( noc == 0 ) return 0;
    return buf;
  }else{
    return fgets(buf, ncmax, fin);
  }
}

# define fgets(b,n,f) utf8_fgets(b,n,f)
11785
11786
11787
11788
11789
11790
11791
11792
11793
11794
11795
11796
11797
11798
11799
#if SQLITE_SHELL_IS_UTF8
int SQLITE_CDECL main(int argc, char **argv){
#else
int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
  char **argv;
#endif
#ifdef SQLITE_DEBUG
  sqlite3_int64 mem_main_enter = sqlite3_memory_used();
#endif
  char *zErrMsg = 0;
#ifdef SQLITE_SHELL_FIDDLE
#  define data shellState
#else
  ShellState data;
#endif







|







11793
11794
11795
11796
11797
11798
11799
11800
11801
11802
11803
11804
11805
11806
11807
#if SQLITE_SHELL_IS_UTF8
int SQLITE_CDECL main(int argc, char **argv){
#else
int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
  char **argv;
#endif
#ifdef SQLITE_DEBUG
  sqlite3_int64 mem_main_enter = 0;
#endif
  char *zErrMsg = 0;
#ifdef SQLITE_SHELL_FIDDLE
#  define data shellState
#else
  ShellState data;
#endif
11817
11818
11819
11820
11821
11822
11823
11824





11825
11826
11827
11828
11829
11830
11831
  stdin_is_interactive = 0;
  stdout_is_console = 1;
  data.wasm.zDefaultDbName = "/fiddle.sqlite3";
#else
  stdin_is_interactive = isatty(0);
  stdout_is_console = isatty(1);
#endif






#if !defined(_WIN32_WCE)
  if( getenv("SQLITE_DEBUG_BREAK") ){
    if( isatty(0) && isatty(2) ){
      fprintf(stderr,
          "attach debugger to process %d and press any key to continue.\n",
          GETPID());
      fgetc(stdin);







|
>
>
>
>
>







11825
11826
11827
11828
11829
11830
11831
11832
11833
11834
11835
11836
11837
11838
11839
11840
11841
11842
11843
11844
  stdin_is_interactive = 0;
  stdout_is_console = 1;
  data.wasm.zDefaultDbName = "/fiddle.sqlite3";
#else
  stdin_is_interactive = isatty(0);
  stdout_is_console = isatty(1);
#endif
#if (defined(_WIN32) || defined(WIN32)) && !defined(SQLITE_SHELL_FIDDLE)
  atexit(instream_restore); /* Needs revision for CLI as library call */
#endif
#ifdef SQLITE_DEBUG
  mem_main_enter = sqlite3_memory_used();
#endif
#if !defined(_WIN32_WCE)
  if( getenv("SQLITE_DEBUG_BREAK") ){
    if( isatty(0) && isatty(2) ){
      fprintf(stderr,
          "attach debugger to process %d and press any key to continue.\n",
          GETPID());
      fgetc(stdin);
12366
12367
12368
12369
12370
12371
12372
12373
12374
12375
12376
12377
12378
12379
12380
12381
12382
12383
12384
        free(zHistory);
      }
    }else{
      data.in = stdin;
      rc = process_input(&data);
    }
  }
#if (defined(_WIN32) || defined(WIN32)) && !defined(SQLITE_SHELL_FIDDLE)
  if( console_utf8 ){
    instream_restore();
  }
#endif
#ifndef SQLITE_SHELL_FIDDLE
  /* In WASM mode we have to leave the db state in place so that
  ** client code can "push" SQL into it after this call returns. */
  free(azCmd);
  set_table_name(&data, 0);
  if( data.db ){
    session_close_all(&data, -1);







<
<
<
<
<







12379
12380
12381
12382
12383
12384
12385





12386
12387
12388
12389
12390
12391
12392
        free(zHistory);
      }
    }else{
      data.in = stdin;
      rc = process_input(&data);
    }
  }





#ifndef SQLITE_SHELL_FIDDLE
  /* In WASM mode we have to leave the db state in place so that
  ** client code can "push" SQL into it after this call returns. */
  free(azCmd);
  set_table_name(&data, 0);
  if( data.db ){
    session_close_all(&data, -1);