From 5f2410132bdd2051c78186c3ea69aad2cac704cd Mon Sep 17 00:00:00 2001 From: anseki Date: Sun, 29 Mar 2015 23:45:55 +0900 Subject: [PATCH] rewrite read.sh --- README.md | 2 +- lib/read.cs.js | 4 +- lib/read.ps1 | 9 ++-- lib/read.sh | 106 +++++++++++++++++++++++++++++++++---------- lib/readline-sync.js | 47 ++++++++++--------- 5 files changed, 114 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index c216bc6..002798a 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ readlineSync.setPrompt(chalk.red.bold('> ')); command = readlineSync.prompt(); ``` -* When you do the redirecting that like `node yourscript.js > foo.log` to record into a file, this is used to output conversation to the file. That is, the conversation isn't outputted to `foo.log` without this code. +* When you do the redirecting that like `your-script > foo.log` to record into a file, this is used to output conversation to the file. That is, the conversation isn't outputted to `foo.log` without this code. ```js var readlineSync = require('readline-sync'); diff --git a/lib/read.cs.js b/lib/read.cs.js index 11113d6..2a81a13 100644 --- a/lib/read.cs.js +++ b/lib/read.cs.js @@ -53,7 +53,7 @@ var if (!options.noEchoBack && !options.keyIn) { if (options.display !== '') { writeTTY(options.display); } - input = readByCS(); + input = readByFSO(); } else if (options.noEchoBack && !options.keyIn && options.mask === '*') { if (options.display !== '') { writeTTY(options.display); } input = readByPW(); @@ -77,7 +77,7 @@ function writeTTY(text) { } } -function readByCS() { +function readByFSO() { var text; try { text = getFso().OpenTextFile('CONIN$', FSO_ForReading).ReadLine(); diff --git a/lib/read.ps1 b/lib/read.ps1 index 09babe8..78d4831 100644 --- a/lib/read.ps1 +++ b/lib/read.ps1 @@ -33,7 +33,7 @@ if ($options.encoded) { [string] $inputTTY = '' [bool] $isInputLine = $False -[bool] $isEditable = (-not $options.noEchoBack) -and (-not $options.keyIn) +[bool] $isCooked = (-not $options.noEchoBack) -and (-not $options.keyIn) function writeTTY ($text) { execWithTTY ('Write-Host ''' + ($text -replace '''', '''''') + ''' -NoNewline') @@ -67,10 +67,9 @@ if ($options.noEchoBack -and (-not $options.keyIn) -and ($options.mask -eq '*')) } if ($options.keyIn) { $reqSize = 1 } -else { $reqSize = 1024 } # dummy while ($True) { - if ($isEditable) { + if ($isCooked) { $chunk = execWithTTY 'Read-Host' $True $chunk += "`n" } else { # raw @@ -82,7 +81,7 @@ while ($True) { $chunk = $chunk -replace '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '' if ($chunk -eq '') { continue } - if (-not $isEditable) { + if (-not $isCooked) { $displayTmp = $chunk -replace '[\r\n]', '' if ($displayTmp -ne '') { if ($options.noEchoBack) { @@ -98,7 +97,7 @@ while ($True) { ($options.keyIn -and ($inputTTY.Length -ge $reqSize))) { break } } -if ((-not $isEditable) -and (-not ($options.keyIn -and (-not $isInputLine)))) +if ((-not $isCooked) -and (-not ($options.keyIn -and (-not $isInputLine)))) { execWithTTY 'Write-Host ''''' } # new line return '''' + $inputTTY + '''' diff --git a/lib/read.sh b/lib/read.sh index 3092b78..56c6d91 100644 --- a/lib/read.sh +++ b/lib/read.sh @@ -1,38 +1,94 @@ -# Silent Read : emulate `read -s` of bash/zsh -read_s() { - stty --file=/dev/tty -echo echonl 2>/dev/null || \ - stty -F /dev/tty -echo echonl 2>/dev/null || \ - stty -f /dev/tty -echo echonl || exit 1 - IFS= read -r input /dev/null || \ - stty -F /dev/tty echo -echonl 2>/dev/null || \ - stty -f /dev/tty echo -echonl || exit 1 -} # getopt(s) while [ $# -ge 1 ]; do - case "$1" in - "--noechoback") noechoback=1;; - "--keyin") keyin=1;; - "--display") shift; display=$1;; + arg="$(printf '%s' "$1" | grep -E '^-+[^-]+$' | tr '[A-Z]' '[a-z]' | tr -d '-')" + case "$arg" in + 'display') shift; options_display="$1";; + 'noechoback') options_noEchoBack='true';; + 'mask') shift; options_mask="$1";; + 'keyin') options_keyIn='true';; + 'encoded') options_encoded='true';; esac shift done -if [ -n "$display" ]; then - printf '%s' "$display" >/dev/tty +reset_tty() { + if [ -n "$save_tty" ]; then stty "$save_tty"; fi +} +trap 'reset_tty' EXIT +save_tty="$(stty -g)" + +write_tty() { # 2nd arg: enable escape sequence + if [ -n "$2" ]; then + printf '%b' "$1" >/dev/tty + else + printf '%s' "$1" >/dev/tty + fi + is_inputline='true' +} + +replace_allchars() { ( + text='' + for i in $(seq 1 ${#1}) + do + text="$text$2" + done + printf '%s' "$text" +) } + +[ -z "$options_noEchoBack" ] && [ -z "$options_keyIn" ] && is_cooked='true' + +if [ -n "$options_display" ]; then + write_tty "$options_display" + options_display='' fi -if [ "$noechoback" = "1" ]; then - # Try `-s` option. *ksh have it that not `--silent`. Therefore, don't try it. - if [ -n "$BASH_VERSION" ] || [ -n "$ZSH_VERSION" ]; then - IFS= read -rs input /dev/null && printf '\n' >/dev/tty || read_s - else - read_s - fi +if [ -n "$is_cooked" ]; then + stty --file=/dev/tty cooked 2>/dev/null || \ + stty -F /dev/tty cooked 2>/dev/null || \ + stty -f /dev/tty cooked || exit $? else - IFS= read -r input /dev/null || \ + stty -F /dev/tty raw -echo 2>/dev/null || \ + stty -f /dev/tty raw -echo || exit $? fi -printf '%s' "'$input'" + +[ -n "$options_keyIn" ] && req_size=1 + +while : +do + if [ -z "$is_cooked" ]; then + chunk="$(dd if=/dev/tty bs=1 count=1 2>/dev/null)" + chunk="$(printf '%s' "$chunk" | tr -d '\r\n')" + [ -z "$chunk" ] && is_eol='true' # NL or empty-text was input + else + IFS= read -r chunk 0 ? buffer.toString(encoding, 0, readSize) : '\n'; + + if (!isCooked) { + chunk = chunk.replace(/[\r\n]/g, ''); + if (chunk === '') { isEol = true; } // NL or empty-text was input + } else if (typeof(line = (chunk.match(/^(.*?)[\r\n]/) || [])[1]) === 'string') { + chunk = line; + isEol = true; } - if (readSize === 0) { break; } - chunk = buffer.toString(encoding, 0, readSize); // other ctrl-chars - if ((chunk = chunk.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '')) === '') - { continue; } + chunk = chunk.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ''); - if (!isEditable && (displayTmp = chunk.replace(/[\r\n]/g, '')) !== '') { - if (options.noEchoBack) { - displayTmp = options.mask === '' ? '' : - (new Array(displayTmp.length + 1)).join(options.mask); + if (chunk !== '' && !isCooked) { + if (!options.noEchoBack) { + writeTTY(chunk); + } else if (options.mask !== '') { + writeTTY((new Array(chunk.length + 1)).join(options.mask)); } - if (displayTmp !== '') { writeTTY(displayTmp); } } input += chunk; - if (/[\r\n]$/.test(input) || - options.keyIn && input.length >= reqSize) { break; } + if (isEol || options.keyIn && input.length >= reqSize) { break; } } - if (!isEditable && !(options.keyIn && !isInputLine)) { writeTTY('\n'); } + if (!isCooked && !(options.keyIn && !isInputLine)) { writeTTY('\n'); } setRawMode(false); })();