summary refs log tree commit diff stats
path: root/lib/system
diff options
context:
space:
mode:
authorktamp <ktamp@users.noreply.github.com>2020-06-23 23:31:41 +0300
committerGitHub <noreply@github.com>2020-06-23 23:31:41 +0300
commita3b95162499833c17699e0d22c44d3dd09a1ef54 (patch)
treeaa1097c47684ab581e3097a57a6a83e143b232c3 /lib/system
parent9ebbe000a81b735f0bb8350b0897c9d1b0d1f43f (diff)
downloadNim-a3b95162499833c17699e0d22c44d3dd09a1ef54.tar.gz
readLine: Unicode support for Windows console
When input is read from the Windows console, input encoding is UTF16. This is translated internally to UTF8.
Diffstat (limited to 'lib/system')
-rw-r--r--lib/system/io.nim36
1 files changed, 36 insertions, 0 deletions
diff --git a/lib/system/io.nim b/lib/system/io.nim
index 482057214..330b67dff 100644
--- a/lib/system/io.nim
+++ b/lib/system/io.nim
@@ -14,6 +14,11 @@ include inclrtl
 import std/private/since
 import formatfloat
 
+when defined(windows) and not defined(useWinAnsi):
+  import os
+  import system/widestrs
+  import terminal
+
 # ----------------- IO Part ------------------------------------------------
 type
   CFile {.importc: "FILE", header: "<stdio.h>",
@@ -352,6 +357,37 @@ proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect],
   proc c_memchr(s: pointer, c: cint, n: csize_t): pointer {.
     importc: "memchr", header: "<string.h>".}
 
+  when defined(windows) and not defined(useWinAnsi):
+    proc readConsole(hConsoleInput: FileHandle, lpBuffer: pointer,
+                     nNumberOfCharsToRead: uint32,
+                     lpNumberOfCharsRead: ptr uint32,
+                     pInputControl: pointer): int32 {.
+      stdcall, dynlib: "kernel32", importc: "ReadConsoleW".}
+
+    if f.isatty:
+      if c_feof(f) < 0:
+        return false
+      const numberOfCharsToRead = 2048
+      var numberOfCharsRead = 0'u32
+      var buffer = newWideCString("", numberOfCharsToRead)
+      if readConsole(getOsFileHandle(f), addr(buffer[0]),
+        numberOfCharsToRead, addr(numberOfCharsRead), nil) == 0:
+        var error = osLastError()
+        raiseEIO("error: " & $error & " `" & osErrorMsg(error) & "`")
+      # input always ends with "\r\n"
+      numberOfCharsRead -= 2
+      # handle Ctrl+Z as EOF
+      for i in 0'u32..<numberOfCharsRead:
+        if buffer[i].uint16 == 26:  #Ctrl+Z
+          close(f)  #has the same effect as setting EOF
+          if i == 0:
+            return false
+          numberOfCharsRead = i
+          break
+      buffer[numberOfCharsRead] = 0.Utf16Char
+      line = TaintedString($buffer)
+      return(true)
+
   var pos = 0
 
   # Use the currently reserved space for a first try