summary refs log tree commit diff stats
path: root/lib/impure/rdstdin.nim
blob: f722a6b39a3d6e9bbb6072eab49eaf358585d04a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
generated by cgit-pink 1.4.1-2-gfad0 (git 2.36.2.497.gbbea4dcf42) at 2025-06-28 09:26:26 +0000
 


font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#
#
#            Nim's Runtime Library
#        (c) Copyright 2015 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## This module contains code for reading from `stdin`:idx:. On UNIX the
## linenoise library is wrapped and set up to provide default key bindings
## (e.g. you can navigate with the arrow keys). On Windows ``system.readLine``
## is used. This suffices because Windows' console already provides the
## wanted functionality.

{.deadCodeElim: on.}

when defined(Windows):
  proc readLineFromStdin*(prompt: string): TaintedString {.
                          tags: [ReadIOEffect, WriteIOEffect].} =
    ## Reads a line from stdin.
    stdout.write(prompt)
    result = readLine(stdin)

  proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {.
                          tags: [ReadIOEffect, WriteIOEffect].} =
    ## Reads a `line` from stdin. `line` must not be
    ## ``nil``! May throw an IO exception.
    ## A line of text may be delimited by ``CR``, ``LF`` or
    ## ``CRLF``. The newline character(s) are not part of the returned string.
    ## Returns ``false`` if the end of the file has been reached, ``true``
    ## otherwise. If ``false`` is returned `line` contains no new data.
    stdout.write(prompt)
    result = readLine(stdin, line)

  import winlean

  const
    VK_SHIFT* = 16
    VK_CONTROL* = 17
    VK_MENU* = 18
    KEY_EVENT* = 1

  type
    KEY_EVENT_RECORD = object
      bKeyDown: WinBool
      wRepeatCount: uint16
      wVirtualKeyCode: uint16
      wVirtualScanCode: uint16
      unicodeChar: uint16
      dwControlKeyState: uint32
    INPUT_RECORD = object
      eventType*: int16
      reserved*: int16
      event*: KEY_EVENT_RECORD
      safetyBuffer: array[0..5, DWORD]

  proc readConsoleInputW*(hConsoleInput: HANDLE, lpBuffer: var INPUTRECORD,
                          nLength: uint32,
                          lpNumberOfEventsRead: var uint32): WINBOOL{.
      stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".}

  proc getch(): uint16 =
    let hStdin = getStdHandle(STD_INPUT_HANDLE)
    var
      irInputRecord: INPUT_RECORD
      dwEventsRead: uint32

    while readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead) != 0:
      if irInputRecord.eventType == KEY_EVENT and
          irInputRecord.event.wVirtualKeyCode notin {VK_SHIFT, VK_MENU, VK_CONTROL}:
         result = irInputRecord.event.unicodeChar
         discard readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead)
         return result

  from unicode import toUTF8, Rune, runeLenAt

  proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
                              bool {.tags: [ReadIOEffect, WriteIOEffect].} =
    ## Reads a `password` from stdin without printing it. `password` must not
    ## be ``nil``! Returns ``false`` if the end of the file has been reached,
    ## ``true`` otherwise.
    password.setLen(0)
    stdout.write(prompt)
    while true:
      let c = getch()
      case c.char
      of '\r', chr(0xA):
        break
      of '\b':
        # ensure we delete the whole UTF-8 character:
        var i = 0
        var x = 1
        while i < password.len:
          x = runeLenAt(password, i)
          inc i, x
        password.setLen(max(password.len - x, 0))
      else:
        password.add(toUTF8(c.Rune))
    stdout.write "\n"

else:
  import linenoise, termios

  proc readLineFromStdin*(prompt: string): TaintedString {.
                          tags: [ReadIOEffect, WriteIOEffect].} =
    var buffer = linenoise.readLine(prompt)
    if isNil(buffer):
      raise newException(IOError, "Linenoise returned nil")
    result = TaintedString($buffer)
    if result.string.len > 0:
      historyAdd(buffer)
    linenoise.free(buffer)

  proc readLineFromStdin*(prompt: string, line: var TaintedString): bool {.
                          tags: [ReadIOEffect, WriteIOEffect].} =
    var buffer = linenoise.readLine(prompt)
    if isNil(buffer):
      raise newException(IOError, "Linenoise returned nil")
    line = TaintedString($buffer)
    if line.string.len > 0:
      historyAdd(buffer)
    linenoise.free(buffer)
    result = true

  proc readPasswordFromStdin*(prompt: string, password: var TaintedString):
                              bool {.tags: [ReadIOEffect, WriteIOEffect].} =
    password.setLen(0)
    let fd = stdin.getFileHandle()
    var cur, old: Termios
    discard fd.tcgetattr(cur.addr)
    old = cur
    cur.c_lflag = cur.c_lflag and not Cflag(ECHO)
    discard fd.tcsetattr(TCSADRAIN, cur.addr)
    stdout.write prompt
    result = stdin.readLine(password)
    stdout.write "\n"
    discard fd.tcsetattr(TCSADRAIN, old.addr)

proc readPasswordFromStdin*(prompt: string): TaintedString =
  ## Reads a password from stdin without printing it.
  result = TaintedString("")
  discard readPasswordFromStdin(prompt, result)