diff --git a/lib/read.cs.js b/lib/read.cs.js index 2a81a13..3fae749 100644 --- a/lib/read.cs.js +++ b/lib/read.cs.js @@ -44,17 +44,17 @@ var } return options; })({ - display: 'string', - noEchoBack: 'boolean', - mask: 'string', - keyIn: 'boolean', - encoded: 'boolean' + display: 'string', + keyIn: 'boolean', + noEchoBack: 'boolean', + mask: 'string', + encoded: 'boolean' }); if (!options.noEchoBack && !options.keyIn) { if (options.display !== '') { writeTTY(options.display); } input = readByFSO(); -} else if (options.noEchoBack && !options.keyIn && options.mask === '*') { +} else if (options.noEchoBack && !options.keyIn && options.mask === '') { if (options.display !== '') { writeTTY(options.display); } input = readByPW(); } else { diff --git a/lib/read.ps1 b/lib/read.ps1 index 2aebe75..a10863d 100644 --- a/lib/read.ps1 +++ b/lib/read.ps1 @@ -1,9 +1,11 @@ Param( [string] $display, + [switch] $keyIn, [switch] $noEchoBack, [string] $mask, - [switch] $keyIn, + [string] $exclude, + [switch] $cs, [switch] $encoded ) @@ -19,7 +21,7 @@ function decodeDOS ($arg) { } $options = @{} -foreach ($arg in @('display', 'noEchoBack', 'mask', 'keyIn', 'encoded')) { +foreach ($arg in @('display', 'keyIn', 'noEchoBack', 'mask', 'exclude', 'cs', 'encoded')) { $options.Add($arg, (Get-Variable $arg -ValueOnly)) } if ($options.encoded) { @@ -71,6 +73,8 @@ 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 '') { $isEol = $True } # NL or empty-text was input } else { diff --git a/lib/read.sh b/lib/read.sh index 56c6d91..e91f034 100644 --- a/lib/read.sh +++ b/lib/read.sh @@ -4,9 +4,11 @@ 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';; 'mask') shift; options_mask="$1";; - 'keyin') options_keyIn='true';; + 'exclude') shift; options_exclude="$1";; + 'cs') options_cs='true';; 'encoded') options_encoded='true';; esac shift diff --git a/lib/readline-sync.js b/lib/readline-sync.js index d238ddd..1f3eee4 100644 --- a/lib/readline-sync.js +++ b/lib/readline-sync.js @@ -31,13 +31,16 @@ var /* display: string + keyIn: boolean noEchoBack: boolean mask: string - keyIn: boolean + exclude: string (pattern, not RegExp) + cs: boolean noTrim: boolean */ function readlineSync(options) { - var input = '', displayTmp; + var input = '', displaySave = options.display, + silent = !options.display && options.keyIn && options.noEchoBack && !options.mask; function tryExt() { var res = readlineExt(options); @@ -45,7 +48,7 @@ function readlineSync(options) { return res.input; } - (function() { + (function() { // open TTY var fsB, constants; function getFsB() { @@ -109,19 +112,10 @@ function readlineSync(options) { } })(); - // Call before tryExt() - if (options.display !== '' && typeof print === 'function') - { print(options.display, encoding); } - (function() { // try read - var buffer, reqSize, readSize, chunk, isEol, isInputLine = false, line, + var buffer, reqSize, readSize, chunk, isEol, line, exclude, isCooked = !options.noEchoBack && !options.keyIn; - function writeTTY(text) { - fs.writeSync(fdW, text); - isInputLine = true; - } - // Node v0.10- returns an error if same mode is set. function setRawMode(mode) { if (mode === isRawMode) { return true; } @@ -130,14 +124,13 @@ function readlineSync(options) { return true; } - if (useExt || !ttyR || - typeof fdW !== 'number' && (options.display !== '' || !isCooked)) { + if (useExt || !ttyR || typeof fdW !== 'number' && (options.display || !isCooked)) { input = tryExt(); return; } - if (options.display !== '') { - writeTTY(options.display); + if (options.display) { + fs.writeSync(fdW, options.display); options.display = ''; } @@ -147,6 +140,10 @@ function readlineSync(options) { } buffer = new Buffer((reqSize = options.keyIn ? 1 : bufSize)); + if (options.exclude) { + exclude = new RegExp(options.exclude, 'g' + (options.cs ? '' : 'i')); + } + while (true) { readSize = 0; try { @@ -162,20 +159,20 @@ function readlineSync(options) { if (!isCooked) { chunk = chunk.replace(/[\r\n]/g, ''); - if (chunk === '') { isEol = true; } // NL or empty-text was input + 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, ''); + chunk = chunk.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ''); // other ctrl-chars + if (chunk && options.keyIn && exclude) { chunk = chunk.replace(exclude, ''); } - if (chunk !== '' && !isCooked) { + if (chunk && !isCooked) { if (!options.noEchoBack) { - writeTTY(chunk); - } else if (options.mask !== '') { - writeTTY((new Array(chunk.length + 1)).join(options.mask)); + fs.writeSync(fdW, chunk); + } else if (options.mask) { + fs.writeSync(fdW, (new Array(chunk.length + 1)).join(options.mask)); } } @@ -183,19 +180,16 @@ function readlineSync(options) { if (isEol || options.keyIn && input.length >= reqSize) { break; } } - if (!isCooked && !(options.keyIn && !isInputLine)) { writeTTY('\n'); } + if (!isCooked && !silent) { fs.writeSync(fdW, '\n'); } setRawMode(false); })(); - if (typeof print === 'function') { - displayTmp = input.replace(/[\r\n]/g, ''); - print((options.noEchoBack ? - (new Array(displayTmp.length + 1)).join(options.mask) : displayTmp) + - '\n', encoding); + if (typeof print === 'function' && !silent) { // must at least write '\n' + print(displaySave + (options.noEchoBack ? + (new Array(input.length + 1)).join(options.mask) : input) + '\n', encoding); } - return options.noTrim || options.keyIn ? - input.replace(/[\r\n]+$/, '') : input.trim(); + return options.noTrim || options.keyIn ? input : input.trim(); } function readlineExt(options) { @@ -225,6 +219,7 @@ function readlineExt(options) { if (childProc.execFileSync) { hostArgs = getHostArgs(options); + console.warn(''); try { res.input = childProc.execFileSync(extHostPath, hostArgs, execOptions); } catch (e) { // non-zero exit code @@ -238,9 +233,11 @@ 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 = ''; } @@ -359,7 +356,7 @@ function getHostArgs(options) { if (conf[key] === 'boolean') { if (options[key]) { args.push('--' + key); } } else if (conf[key] === 'string') { - if (options[key] !== '') { + if (options[key]) { args.push('--' + key, options.encoded ? encodeDOS(options[key]) : options[key]); } @@ -368,14 +365,29 @@ function getHostArgs(options) { } return args; })({ - display: 'string', - noEchoBack: 'boolean', - mask: 'string', - keyIn: 'boolean', - encoded: 'boolean' + display: 'string', + keyIn: 'boolean', + noEchoBack: 'boolean', + mask: 'string', + exclude: 'string', + cs: 'boolean', + encoded: 'boolean' })); } +function flattenArray(array, validate) { + var flatArray = []; + function parseArray(array) { +/* jshint eqnull:true */ + if (array == null) { return; } +/* jshint eqnull:false */ + else if (Array.isArray(array)) { array.forEach(parseArray); } + else if (!validate || validate(array)) { flatArray.push(array); } + } + parseArray(array); + return flatArray; +} + // for dev exports._useExtSet = function(use) { useExt = use; }; @@ -414,37 +426,46 @@ exports.setBufferSize = function(newBufSize) { exports.prompt = function(options) { var readOptions = { - display: promptText + '', - noEchoBack: !!(options && options.noEchoBack), - mask: mask, - keyIn: false, - noTrim: !!(options && options.noTrim) - }; + display: promptText + '', + keyIn: false, + noEchoBack: !!(options && options.noEchoBack), + mask: mask, + exclude: '', + cs: !!(options && options.caseSensitive), + noTrim: !!(options && options.noTrim) + }; return readlineSync(readOptions); }; exports.question = function(query, options) { var readOptions = { /* jshint eqnull:true */ - display: query != null ? query + '' : '', + display: query != null ? query + '' : '', /* jshint eqnull:false */ - noEchoBack: !!(options && options.noEchoBack), - mask: mask, - keyIn: false, - noTrim: !!(options && options.noTrim) - }; + keyIn: false, + noEchoBack: !!(options && options.noEchoBack), + mask: mask, + exclude: '', + cs: !!(options && options.caseSensitive), + noTrim: !!(options && options.noTrim) + }; return readlineSync(readOptions); }; exports.keyIn = function(query, options) { - var readOptions = { + var limit = options ? flattenArray(options.limit, function(value) { + return typeof value === 'string' || typeof value === 'number'; }) : [], + readOptions = { /* jshint eqnull:true */ - display: query != null ? query + '' : '', + display: query != null ? query + '' : '', /* jshint eqnull:false */ - noEchoBack: !!(options && options.noEchoBack), - mask: mask, - keyIn: true, - noTrim: true - }; + keyIn: true, + noEchoBack: !!(options && options.noEchoBack), + mask: mask, + exclude: limit.length ? + '[^' + limit.join('').replace(/\W/g, '\\$&') + ']' : '', + cs: !!(options && options.caseSensitive), + noTrim: true + }; return readlineSync(readOptions); };