Add: options.limit, options.caseSensitive

This commit is contained in:
anseki 2015-04-04 01:14:55 +09:00
parent b4bdf19667
commit 63e5ad583e
4 changed files with 133 additions and 103 deletions

View file

@ -60,10 +60,10 @@ var
});
if (!options.noEchoBack && !options.keyIn) {
if (options.display !== '') { writeTTY(options.display); }
if (options.display) { writeTTY(options.display); }
input = readByFSO();
} else if (options.noEchoBack && !options.keyIn && options.mask === '') {
if (options.display !== '') { writeTTY(options.display); }
} else if (options.noEchoBack && !options.keyIn && !options.mask) {
if (options.display) { writeTTY(options.display); }
input = readByPW();
} else {
WScript.StdErr.WriteLine(PS_MSG);

View file

@ -9,7 +9,7 @@ Param(
[switch] $keyIn,
[switch] $noEchoBack,
[string] $mask,
[string] $exclude,
[string] $limit,
[switch] $cs,
[switch] $encoded
)
@ -26,23 +26,22 @@ function decodeDOS ($arg) {
}
$options = @{}
foreach ($arg in @('display', 'keyIn', 'noEchoBack', 'mask', 'exclude', 'cs', 'encoded')) {
foreach ($arg in @('display', 'keyIn', 'noEchoBack', 'mask', 'limit', 'cs', 'encoded')) {
$options.Add($arg, (Get-Variable $arg -ValueOnly))
}
if ($options.encoded) {
$argList = New-Object string[] $options.Keys.Count
$options.Keys.CopyTo($argList, 0);
$options.Keys.CopyTo($argList, 0)
foreach ($arg in $argList) {
if (($options[$arg] -is [string]) -and ($options[$arg] -ne ''))
if ($options[$arg] -is [string] -and $options[$arg])
{ $options[$arg] = decodeDOS $options[$arg] }
}
}
[string] $inputTTY = ''
[string] $displaySave = $options.display
[bool] $silent = (-not $options.display) -and
$options.keyIn -and $options.noEchoBack -and (-not $options.mask)
[bool] $isCooked = (-not $options.noEchoBack) -and (-not $options.keyIn)
[bool] $silent = -not $options.display -and
$options.keyIn -and $options.noEchoBack -and -not $options.mask
[bool] $isCooked = -not $options.noEchoBack -and -not $options.keyIn
# Instant method that opens TTY without CreateFile via P/Invoke in .NET Framework
# **NOTE** Don't include special characters of DOS in $command when $getRes is True.
@ -63,49 +62,58 @@ function writeTTY ($text) {
execWithTTY ('Write-Host ''' + ($text -replace '''', '''''') + ''' -NoNewline')
}
if ($options.display -ne '') {
if ($options.display) {
writeTTY $options.display
$options.display = ''
}
if ((-not $options.keyIn) -and $options.noEchoBack -and ($options.mask -eq '*')) {
$inputTTY = execWithTTY ('$inputTTY = Read-Host -AsSecureString;' +
'$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($inputTTY);' +
'[System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)') $True
if (-not $options.keyIn -and $options.noEchoBack -and $options.mask -eq '*') {
$inputTTY = execWithTTY ('$text = Read-Host -AsSecureString;' +
'$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($text);' +
'[Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)') $True
return '''' + $inputTTY + ''''
}
if ($options.keyIn) { $reqSize = 1 }
while ($True) {
if (-not $isCooked) {
$chunk = execWithTTY '[System.Console]::ReadKey($True).KeyChar' $True
# ReadKey() may returns [System.Array], then don't cast data.
if ($chunk -isnot [string]) { $chunk = '' }
$chunk = $chunk -replace '[\r\n]', ''
if ($chunk -eq '') { $atEol = $True } # NL or empty-text was input
} else {
$chunk = execWithTTY 'Read-Host' $True
$chunk = $chunk -replace '[\r\n]', ''
$atEol = $True
if ($options.keyIn -and $options.limit) {
$limitPtn = '[^' + $options.limit + ']'
}
# other ctrl-chars
$chunk = $chunk -replace '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', ''
while ($True) {
if (-not $isCooked) {
$chunk = [char][int] (execWithTTY '[int] [Console]::ReadKey($True).KeyChar' $True)
} else {
$chunk = execWithTTY 'Read-Host' $True
$chunk += "`n"
}
if ($chunk -ne '' -and (-not $isCooked)) {
if ($chunk -and $chunk -match '^(.*?)[\r\n]') {
$chunk = $Matches[1]
$atEol = $True
} else { $atEol = $False }
# other ctrl-chars
if ($chunk) { $chunk = $chunk -replace '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', '' }
if ($chunk -and $limitPtn) {
if ($options.cs) { $chunk = $chunk -creplace $limitPtn, '' }
else { $chunk = $chunk -ireplace $limitPtn, '' }
}
if ($chunk) {
if (-not $isCooked) {
if (-not $options.noEchoBack) {
writeTTY $chunk
} elseif ($options.mask -ne '') {
} elseif ($options.mask) {
writeTTY ($options.mask * $chunk.Length)
}
}
$inputTTY += $chunk
if ($atEol -or ($options.keyIn -and ($inputTTY.Length -ge $reqSize))) { break }
}
if ((-not $isCooked) -and (-not ($options.keyIn -and (-not $isInputLine))))
{ execWithTTY 'Write-Host ''''' } # new line
if ((-not $options.keyIn -and $atEol) -or
($options.keyIn -and $inputTTY.Length -ge $reqSize)) { break }
}
return '''' + $inputTTY + ''''
if (-not $isCooked -and -not $silent) { execWithTTY 'Write-Host ''''' } # new line
return "'$inputTTY'"

View file

@ -9,29 +9,32 @@ while [ $# -ge 1 ]; do
arg="$(printf '%s' "$1" | grep -E '^-+[^-]+$' | tr '[A-Z]' '[a-z]' | tr -d '-')"
case "$arg" in
'display') shift; options_display="$1";;
'keyin') options_keyIn='true';;
'noechoback') options_noEchoBack='true';;
'keyin') options_keyIn=true;;
'noechoback') options_noEchoBack=true;;
'mask') shift; options_mask="$1";;
'exclude') shift; options_exclude="$1";;
'cs') options_cs='true';;
'encoded') options_encoded='true';;
'limit') shift; options_limit="$1";;
'cs') options_cs=true;;
'encoded') options_encoded=true;;
esac
shift
done
reset_tty() {
if [ -n "$save_tty" ]; then stty "$save_tty"; fi
if [ -n "$save_tty" ]; then
stty --file=/dev/tty "$save_tty" 2>/dev/null || \
stty -F /dev/tty "$save_tty" 2>/dev/null || \
stty -f /dev/tty "$save_tty" || exit $?
fi
}
trap 'reset_tty' EXIT
save_tty="$(stty -g)"
save_tty="$(stty --file=/dev/tty -g 2>/dev/null || stty -F /dev/tty -g 2>/dev/null || stty -f /dev/tty -g || exit $?)"
write_tty() { # 2nd arg: enable escape sequence
if [ -n "$2" ]; then
if [ "$2" = true ]; then
printf '%b' "$1" >/dev/tty
else
printf '%s' "$1" >/dev/tty
fi
is_inputline='true'
}
replace_allchars() { (
@ -43,14 +46,15 @@ replace_allchars() { (
printf '%s' "$text"
) }
[ -z "$options_noEchoBack" ] && [ -z "$options_keyIn" ] && is_cooked='true'
[ -z "$options_display" ] && [ "$options_keyIn" = true ] && \
[ "$options_noEchoBack" = true ] && [ -z "$options_mask" ] && silent=true
[ "$options_noEchoBack" != true ] && [ "$options_keyIn" != true ] && is_cooked=true
if [ -n "$options_display" ]; then
write_tty "$options_display"
options_display=''
fi
if [ -n "$is_cooked" ]; then
if [ "$is_cooked" = true ]; then
stty --file=/dev/tty cooked 2>/dev/null || \
stty -F /dev/tty cooked 2>/dev/null || \
stty -f /dev/tty cooked || exit $?
@ -60,41 +64,58 @@ else
stty -f /dev/tty raw -echo || exit $?
fi
[ -n "$options_keyIn" ] && req_size=1
[ "$options_keyIn" = true ] && req_size=1
if [ "$options_keyIn" = true ] && [ -n "$options_limit" ]; then
if [ "$options_cs" = true ]; then
limit_ptn="$options_limit"
else
# Safe list
limit_ptn="$(printf '%s' "$options_limit" | sed 's/\([a-z]\)/\L\1\U\1/ig')"
fi
fi
while :
do
if [ -z "$is_cooked" ]; then
if [ "$is_cooked" != true ]; then
chunk="$(dd if=/dev/tty bs=1 count=1 2>/dev/null)"
chunk="$(printf '%s' "$chunk" | tr -d '\r\n')"
[ -z "$chunk" ] && at_eol='true' # NL or empty-text was input
else
IFS= read -r chunk </dev/tty || exit $?
chunk="$(printf '%s' "$chunk" | tr -d '\r\n')"
at_eol='true'
chunk="$(printf '%s\n_NL_' "$chunk")"
fi
# Don't assign '\n' to chunk.
if [ -n "$chunk" ] && [ "$(printf '%s' "$chunk" | tr '\r' '\n' | wc -l)" != "0" ]; then
chunk="$(printf '%s' "$chunk" | tr '\r' '\n' | head -n 1)"
at_eol=true
fi
# other ctrl-chars
if [ -n "$chunk" ]; then
# 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')"
fi
if [ -n "$chunk" ] && [ -n "$limit_ptn" ]; then
chunk="$(printf '%s' "$chunk" | tr -cd "$limit_ptn")"
fi
if [ -n "$chunk" ] && [ -z "$is_cooked" ]; then
if [ -z "$options_noEchoBack" ]; then
if [ -n "$chunk" ]; then
if [ "$is_cooked" != true ]; then
if [ "$options_noEchoBack" != true ]; then
write_tty "$chunk"
elif [ -n "$options_mask" ]; then
write_tty "$(replace_allchars "$chunk" "$options_mask")"
fi
fi
input="$input$chunk"
if [ -n "$at_eol" ] || \
( [ -n "$options_keyIn" ] && [ ${#input} -ge $req_size ] ); then break; fi
fi
if ( [ "$options_keyIn" != true ] && [ "$at_eol" = true ] ) || \
( [ "$options_keyIn" = true ] && [ ${#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
if [ "$is_cooked" != true ] && [ "$silent" != true ]; then write_tty '\r\n' true; fi
printf "'%s'" "$input"

View file

@ -34,7 +34,7 @@ var
keyIn: boolean
noEchoBack: boolean
mask: string
exclude: string (pattern, not RegExp)
limit: string (pattern)
cs: boolean
noTrim: boolean
*/
@ -113,7 +113,7 @@ function readlineSync(options) {
})();
(function() { // try read
var atEol, exclude,
var atEol, limit,
isCooked = !options.noEchoBack && !options.keyIn,
buffer, reqSize, readSize, chunk, line;
@ -141,8 +141,8 @@ function readlineSync(options) {
}
buffer = new Buffer((reqSize = options.keyIn ? 1 : bufSize));
if (options.keyIn && options.exclude) {
exclude = new RegExp(options.exclude, 'g' + (options.cs ? '' : 'i'));
if (options.keyIn && options.limit) {
limit = new RegExp('[^' + options.limit + ']', 'g' + (options.cs ? '' : 'i'));
}
while (true) {
@ -158,23 +158,26 @@ function readlineSync(options) {
}
chunk = readSize > 0 ? buffer.toString(encoding, 0, readSize) : '\n';
if (typeof(line = (chunk.match(/^(.*?)[\r\n]/) || [])[1]) === 'string') {
if (chunk && typeof(line = (chunk.match(/^(.*?)[\r\n]/) || [])[1]) === 'string') {
chunk = line;
atEol = true;
}
chunk = chunk.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ''); // other ctrl-chars
if (chunk && exclude) { chunk = chunk.replace(exclude, ''); }
// other ctrl-chars
if (chunk) { chunk = chunk.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ''); }
if (chunk && limit) { chunk = chunk.replace(limit, ''); }
if (chunk && !isCooked) {
if (chunk) {
if (!isCooked) {
if (!options.noEchoBack) {
fs.writeSync(fdW, chunk);
} else if (options.mask) {
fs.writeSync(fdW, (new Array(chunk.length + 1)).join(options.mask));
}
}
input += chunk;
}
if (!options.keyIn && atEol ||
options.keyIn && input.length >= reqSize) { break; }
}
@ -218,7 +221,6 @@ function readlineExt(options) {
if (childProc.execFileSync) {
hostArgs = getHostArgs(options);
console.warn('<childProc.execFileSync>');
try {
res.input = childProc.execFileSync(extHostPath, hostArgs, execOptions);
} catch (e) { // non-zero exit code
@ -232,11 +234,9 @@ function readlineExt(options) {
res.error.signal = e.signal;
}
} else {
console.warn('<_execFileSync>');
res = _execFileSync(options, execOptions);
}
if (!res.error) {
console.warn('ROW-RES:<'+res.input+'>');
res.input = res.input.replace(/^\s*'|'\s*$/g, '');
options.display = '';
}
@ -368,7 +368,7 @@ function getHostArgs(options) {
keyIn: 'boolean',
noEchoBack: 'boolean',
mask: 'string',
exclude: 'string',
limit: 'string',
cs: 'boolean',
encoded: 'boolean'
}));
@ -429,7 +429,7 @@ exports.prompt = function(options) {
keyIn: false,
noEchoBack: !!(options && options.noEchoBack),
mask: mask,
exclude: '',
limit: '',
cs: !!(options && options.caseSensitive),
noTrim: !!(options && options.noTrim)
};
@ -444,7 +444,7 @@ exports.question = function(query, options) {
keyIn: false,
noEchoBack: !!(options && options.noEchoBack),
mask: mask,
exclude: '',
limit: '',
cs: !!(options && options.caseSensitive),
noTrim: !!(options && options.noTrim)
};
@ -452,8 +452,10 @@ exports.question = function(query, options) {
};
exports.keyIn = function(query, options) {
var limit = options ? flattenArray(options.limit, function(value) {
return typeof value === 'string' || typeof value === 'number'; }).join('') : '',
var limit = options ?
flattenArray(options.limit, function(value)
{ return typeof value === 'string' || typeof value === 'number'; })
.join('').replace(/\n/g, '').replace(/[^A-Za-z0-9_ ]/g, '\\$&') : '',
readOptions = {
/* jshint eqnull:true */
display: query != null ? query + '' : '',
@ -461,8 +463,7 @@ exports.keyIn = function(query, options) {
keyIn: true,
noEchoBack: !!(options && options.noEchoBack),
mask: mask,
exclude: limit ? '[^' +
limit.replace(/\n/g, '').replace(/\W/g, '\\$&') + ']' : '',
limit: limit,
cs: !!(options && options.caseSensitive),
noTrim: true
};