Now that we have established (two) ways to handle unicode input in Windows, the 
next matter to address is our inability to redirect unicode output to a file.

As a first step let's see how we can handle this in **C (GCC)** :

  * For unicode input to work, input has to be **UTF-16LE** encoded. So, for 
piping/redirection to work, unicode output also has to be **UTF-16LE** encoded 
and, additionally, **cmd** has to be started with the **/ u** switch (as " 
**cmd /u**").
  * In order to make **fgetws** and **fputws** correctly process **UTF-16LE** 
strings, **_setmode(_fileno(stdin, _O_U16TEXT))** and 
**_setmode(_fileno(stdout, _O_U16TEXT))** must be used.
  * **SetConsoleCP(65001)** and **SetConsoleOutputCP(65001)** are completely 
irrelevant and do not affect the result whether present or absent.
  * Output redirected to a file will have no **BOM** , so manually selecting 
its encoding may be necessary to correctly opening it e.g. using **Notepad**.



Now let's use these findings to work our way in **Nim** :
    
    
    #console.nim
    
    import strutils
    
    when defined(windows):
      import system/widestrs
      
      proc setMode(fd: int, mode: int): int
           {.importc: "_setmode", header: "io.h".}
      
      proc fgetws(str: WideCString, numChars: int, stream: File): bool
           {.importc, header: "stdio.h".}
      
      proc fputws(str: WideCString, stream: File): int
           {.importc, header: "stdio.h".}
      
      discard stdin.getFileHandle.setMode(0x20000)   #_O_U16TEXT
      discard stdout.getFileHandle.setMode(0x20000)  #_O_U16TEXT
      
      proc consoleReadLine*(line: var string): bool =
        let buffer = newWideCString("", 256)
        result = fgetws(buffer, 256, stdin)
        let length = buffer.len
        if length > 0 and buffer[length - 1].int == 10:  #discard '\n'
          buffer[length - 1] = Utf16Char(0)
          let buffer2 = newWideCString("", 2)  #discard extra '\n' - MinGW-w64
          discard fgetws(buffer2, 2, stdin)
        line = $buffer
      
      proc consoleReadLine*(): string =
        discard consoleReadLine(result)
      
      proc consoleEcho*(x: varargs[string, `$`]) =
        discard fputws(x.join.newWideCString, stdout)
    
    else:
      proc consoleReadLine*(line: var string): bool =
        result = stdin.readLine(line)
      
      proc consoleReadLine*(): string =
        result = stdin.readLine
      
      proc consoleEcho*(x: varargs[string, `$`]) =
        echo x.join
    
    Run

Reply via email to