rewrite read.sh

This commit is contained in:
anseki 2015-03-29 23:45:55 +09:00
parent 16db4b4c1c
commit 5f2410132b
5 changed files with 114 additions and 54 deletions

View file

@ -101,7 +101,7 @@ readlineSync.setPrompt(chalk.red.bold('> '));
command = readlineSync.prompt(); 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 ```js
var readlineSync = require('readline-sync'); var readlineSync = require('readline-sync');

View file

@ -53,7 +53,7 @@ var
if (!options.noEchoBack && !options.keyIn) { if (!options.noEchoBack && !options.keyIn) {
if (options.display !== '') { writeTTY(options.display); } if (options.display !== '') { writeTTY(options.display); }
input = readByCS(); input = readByFSO();
} else if (options.noEchoBack && !options.keyIn && options.mask === '*') { } else if (options.noEchoBack && !options.keyIn && options.mask === '*') {
if (options.display !== '') { writeTTY(options.display); } if (options.display !== '') { writeTTY(options.display); }
input = readByPW(); input = readByPW();
@ -77,7 +77,7 @@ function writeTTY(text) {
} }
} }
function readByCS() { function readByFSO() {
var text; var text;
try { try {
text = getFso().OpenTextFile('CONIN$', FSO_ForReading).ReadLine(); text = getFso().OpenTextFile('CONIN$', FSO_ForReading).ReadLine();

View file

@ -33,7 +33,7 @@ if ($options.encoded) {
[string] $inputTTY = '' [string] $inputTTY = ''
[bool] $isInputLine = $False [bool] $isInputLine = $False
[bool] $isEditable = (-not $options.noEchoBack) -and (-not $options.keyIn) [bool] $isCooked = (-not $options.noEchoBack) -and (-not $options.keyIn)
function writeTTY ($text) { function writeTTY ($text) {
execWithTTY ('Write-Host ''' + ($text -replace '''', '''''') + ''' -NoNewline') 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 } if ($options.keyIn) { $reqSize = 1 }
else { $reqSize = 1024 } # dummy
while ($True) { while ($True) {
if ($isEditable) { if ($isCooked) {
$chunk = execWithTTY 'Read-Host' $True $chunk = execWithTTY 'Read-Host' $True
$chunk += "`n" $chunk += "`n"
} else { # raw } else { # raw
@ -82,7 +81,7 @@ while ($True) {
$chunk = $chunk -replace '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '' $chunk = $chunk -replace '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', ''
if ($chunk -eq '') { continue } if ($chunk -eq '') { continue }
if (-not $isEditable) { if (-not $isCooked) {
$displayTmp = $chunk -replace '[\r\n]', '' $displayTmp = $chunk -replace '[\r\n]', ''
if ($displayTmp -ne '') { if ($displayTmp -ne '') {
if ($options.noEchoBack) { if ($options.noEchoBack) {
@ -98,7 +97,7 @@ while ($True) {
($options.keyIn -and ($inputTTY.Length -ge $reqSize))) { break } ($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 { execWithTTY 'Write-Host ''''' } # new line
return '''' + $inputTTY + '''' return '''' + $inputTTY + ''''

View file

@ -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/tty || exit 1
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
}
# getopt(s) # getopt(s)
while [ $# -ge 1 ]; do while [ $# -ge 1 ]; do
case "$1" in arg="$(printf '%s' "$1" | grep -E '^-+[^-]+$' | tr '[A-Z]' '[a-z]' | tr -d '-')"
"--noechoback") noechoback=1;; case "$arg" in
"--keyin") keyin=1;; 'display') shift; options_display="$1";;
"--display") shift; display=$1;; 'noechoback') options_noEchoBack='true';;
'mask') shift; options_mask="$1";;
'keyin') options_keyIn='true';;
'encoded') options_encoded='true';;
esac esac
shift shift
done done
if [ -n "$display" ]; then reset_tty() {
printf '%s' "$display" >/dev/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 fi
if [ "$noechoback" = "1" ]; then if [ -n "$is_cooked" ]; then
# Try `-s` option. *ksh have it that not `--silent`. Therefore, don't try it. stty --file=/dev/tty cooked 2>/dev/null || \
if [ -n "$BASH_VERSION" ] || [ -n "$ZSH_VERSION" ]; then stty -F /dev/tty cooked 2>/dev/null || \
IFS= read -rs input </dev/tty 2>/dev/null && printf '\n' >/dev/tty || read_s stty -f /dev/tty cooked || exit $?
else
read_s
fi
else else
IFS= read -r input </dev/tty || exit 1 stty --file=/dev/tty raw -echo 2>/dev/null || \
stty -F /dev/tty raw -echo 2>/dev/null || \
stty -f /dev/tty raw -echo || exit $?
fi 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 </dev/tty || exit $?
chunk="$(printf '%s' "$chunk" | tr -d '\r\n')"
is_eol='true'
fi
# other ctrl-chars
# chunk="$(printf '%s' "$chunk" | tr -d '\00-\10\13\14\16-\37\177')"
# for System V
chunk="$(printf '%s' "$chunk" | tr -d '\00\01\02\03\04\05\06\07\10\13\14\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177')"
if [ -n "$chunk" ] && [ -z "$is_cooked" ]; then
if [ -z "$options_noEchoBack" ]; then
write_tty "$chunk"
elif [ -n "$options_mask" ]; then
write_tty "$(replace_allchars "$chunk" "$options_mask")"
fi
fi
input="$input$chunk"
if [ -n "$is_eol" ] || \
( [ -n "$options_keyIn" ] && [ ${#input} -ge $req_size ] ); then break; fi
done
if [ -z "$is_cooked" ] && ! ( [ -n "$options_keyIn" ] && [ -z "$is_inputline" ] ); then
write_tty '\r\n' 'true'
fi
printf "'%s'" "$input"
exit 0 exit 0

View file

@ -114,8 +114,8 @@ function readlineSync(options) {
{ print(options.display, encoding); } { print(options.display, encoding); }
(function() { // try read (function() { // try read
var buffer, reqSize, readSize, chunk, isInputLine = false, var buffer, reqSize, readSize, chunk, isEol, isInputLine = false, line,
isEditable = !options.noEchoBack && !options.keyIn; isCooked = !options.noEchoBack && !options.keyIn;
function writeTTY(text) { function writeTTY(text) {
fs.writeSync(fdW, text); fs.writeSync(fdW, text);
@ -131,7 +131,7 @@ function readlineSync(options) {
} }
if (useExt || !ttyR || if (useExt || !ttyR ||
typeof fdW !== 'number' && (options.display !== '' || !isEditable)) { typeof fdW !== 'number' && (options.display !== '' || !isCooked)) {
input = tryExt(); input = tryExt();
return; return;
} }
@ -141,7 +141,7 @@ function readlineSync(options) {
options.display = ''; options.display = '';
} }
if (!setRawMode(!isEditable)) { if (!setRawMode(!isCooked)) {
input = tryExt(); input = tryExt();
return; return;
} }
@ -149,36 +149,41 @@ function readlineSync(options) {
while (true) { while (true) {
readSize = 0; readSize = 0;
try { try {
readSize = fs.readSync(fdR, buffer, 0, reqSize); readSize = fs.readSync(fdR, buffer, 0, reqSize);
} catch (e) { } catch (e) {
if (e.code === 'EOF') { break; } if (e.code !== 'EOF') {
setRawMode(false); setRawMode(false);
input += tryExt(); input += tryExt();
return; return;
} }
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; }
if (!isEditable && (displayTmp = chunk.replace(/[\r\n]/g, '')) !== '') {
if (options.noEchoBack) {
displayTmp = options.mask === '' ? '' :
(new Array(displayTmp.length + 1)).join(options.mask);
} }
if (displayTmp !== '') { writeTTY(displayTmp); } chunk = readSize > 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;
}
// other ctrl-chars
chunk = chunk.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, '');
if (chunk !== '' && !isCooked) {
if (!options.noEchoBack) {
writeTTY(chunk);
} else if (options.mask !== '') {
writeTTY((new Array(chunk.length + 1)).join(options.mask));
}
} }
input += chunk; input += chunk;
if (/[\r\n]$/.test(input) || if (isEol || options.keyIn && input.length >= reqSize) { break; }
options.keyIn && input.length >= reqSize) { break; }
} }
if (!isEditable && !(options.keyIn && !isInputLine)) { writeTTY('\n'); } if (!isCooked && !(options.keyIn && !isInputLine)) { writeTTY('\n'); }
setRawMode(false); setRawMode(false);
})(); })();