Rewrite code that call external programs.
This commit is contained in:
parent
e49c13d677
commit
67d20da312
3 changed files with 190 additions and 145 deletions
111
lib/read.cs.js
111
lib/read.cs.js
|
@ -2,43 +2,67 @@
|
|||
|
||||
var
|
||||
FSO_ForReading = 1, FSO_ForWriting = 2,
|
||||
PS_MSG = 'Microsoft Windows PowerShell is required.\n' +
|
||||
'https://technet.microsoft.com/en-us/library/hh847837.aspx',
|
||||
|
||||
fso, tty,
|
||||
args =// Array.prototype.slice.call(WScript.Arguments),
|
||||
input, fso, tty,
|
||||
options = (function(conf) {
|
||||
var options = {}, arg, args =// Array.prototype.slice.call(WScript.Arguments),
|
||||
(function() {
|
||||
var args = [], i, iLen;
|
||||
for (i = 0, iLen = WScript.Arguments.length; i < iLen; i++)
|
||||
{ args.push(WScript.Arguments(i)); }
|
||||
return args;
|
||||
})(),
|
||||
arg, options = {};
|
||||
confLc = {}, key;
|
||||
|
||||
function decodeDOS(arg) {
|
||||
return arg.replace(/#(\d+);/g, function(str, charCode) {
|
||||
return String.fromCharCode(+charCode);
|
||||
});
|
||||
}
|
||||
|
||||
for (key in conf) {
|
||||
if (conf.hasOwnProperty(key))
|
||||
{ confLc[key.toLowerCase()] = {key: key, type: conf[key]}; }
|
||||
}
|
||||
|
||||
while (typeof(arg = args.shift()) === 'string') {
|
||||
if (!(arg = (arg.match(/^\-+(.+)$/) || [])[1])) { continue; }
|
||||
arg = arg.toLowerCase();
|
||||
if (arg === '--display') {
|
||||
options.display = args.shift();
|
||||
} else if (arg === '--noechoback') {
|
||||
options.noEchoBack = true;
|
||||
} else if (arg === '--mask') {
|
||||
options.mask = args.shift();
|
||||
} else if (arg === '--keyin') {
|
||||
options.keyIn = true;
|
||||
} else if (arg === '--encoded') {
|
||||
options.encoded = true;
|
||||
if (confLc[arg]) {
|
||||
options[confLc[arg].key] =
|
||||
confLc[arg].type === 'boolean' ? true :
|
||||
confLc[arg].type === 'string' ? args.shift() : null;
|
||||
}
|
||||
}
|
||||
for (key in conf) {
|
||||
if (conf.hasOwnProperty(key) && conf[key] === 'string') {
|
||||
if (typeof options[key] !== 'string') { options[key] = ''; }
|
||||
else if (options.encoded) { options[key] = decodeDOS(options[key]); }
|
||||
}
|
||||
}
|
||||
return options;
|
||||
})({
|
||||
display: 'string',
|
||||
noEchoBack: 'boolean',
|
||||
mask: 'string',
|
||||
keyIn: 'boolean',
|
||||
encoded: 'boolean'
|
||||
});
|
||||
|
||||
if (options.encoded) {
|
||||
if (typeof options.display === 'string')
|
||||
{ options.display = decodeDOS(options.display); }
|
||||
if (typeof options.mask === 'string')
|
||||
{ options.mask = decodeDOS(options.mask); }
|
||||
if (!options.noEchoBack && !options.keyIn) {
|
||||
if (options.display !== '') { writeTTY(options.display); }
|
||||
input = readByCS();
|
||||
} else if (options.noEchoBack && !options.keyIn && options.mask === '*') {
|
||||
if (options.display !== '') { writeTTY(options.display); }
|
||||
input = readByPW();
|
||||
} else {
|
||||
WScript.StdErr.WriteLine(PS_MSG);
|
||||
WScript.Quit(1);
|
||||
}
|
||||
|
||||
if (typeof options.display === 'string' && options.display !== '')
|
||||
{ writeTTY(options.display); }
|
||||
|
||||
WScript.StdOut.Write("'" + readTTY() + "'");
|
||||
WScript.StdOut.Write('\'' + input + '\'');
|
||||
|
||||
WScript.Quit();
|
||||
|
||||
|
@ -48,50 +72,28 @@ function writeTTY(text) {
|
|||
tty.Write(text);
|
||||
} catch (e) {
|
||||
WScript.StdErr.WriteLine('TTY Write Error: ' + e.number +
|
||||
'\n' + e.description);
|
||||
WScript.Quit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function readTTY() {
|
||||
|
||||
// function psExists() {
|
||||
// var envPs = getShell().Environment('System')('PSModulePath');
|
||||
// return typeof envPs === 'string' && envPs !== '';
|
||||
// }
|
||||
|
||||
if (!options.noEchoBack && !options.keyIn) {
|
||||
return readByCS();
|
||||
// } else if (psExists()) {
|
||||
// return readByPS();
|
||||
} else if (options.noEchoBack && !options.keyIn && options.mask === '*') {
|
||||
return readByPW();
|
||||
} else {
|
||||
WScript.StdErr.WriteLine('Microsoft Windows PowerShell is required.\n' +
|
||||
'https://technet.microsoft.com/ja-jp/library/hh847837.aspx');
|
||||
'\n' + e.description + '\n' + PS_MSG);
|
||||
WScript.Quit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function readByCS() {
|
||||
WScript.StdErr.Write('<<readByCS>>'); //_DBG_
|
||||
var text;
|
||||
try {
|
||||
text = getFso().OpenTextFile('CONIN$', FSO_ForReading).ReadLine();
|
||||
} catch (e) {
|
||||
WScript.StdErr.WriteLine('TTY Read Error: ' + e.number +
|
||||
'\n' + e.description);
|
||||
'\n' + e.description + '\n' + PS_MSG);
|
||||
WScript.Quit(1);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
// TTY must be STDIN that is not redirected and not piped.
|
||||
function readByPW() {
|
||||
WScript.StdErr.Write('<<readByPW>>'); //_DBG_
|
||||
var pw;
|
||||
// exit-code is not returned even if an error is thrown.
|
||||
var text;
|
||||
try {
|
||||
pw = WScript.CreateObject('ScriptPW.Password').GetPassword()
|
||||
text = WScript.CreateObject('ScriptPW.Password').GetPassword()
|
||||
// Bug? Illegal data may be returned when user types before initializing.
|
||||
.replace(/[\u4000-\u40FF]/g, function(chr) {
|
||||
var charCode = chr.charCodeAt(0);
|
||||
|
@ -100,21 +102,14 @@ function readByPW() {
|
|||
});
|
||||
} catch (e) {
|
||||
WScript.StdErr.WriteLine('ScriptPW.Password Error: ' + e.number +
|
||||
'\n' + e.description);
|
||||
'\n' + e.description + '\n' + PS_MSG);
|
||||
WScript.Quit(1);
|
||||
}
|
||||
writeTTY('\n');
|
||||
return pw;
|
||||
return text;
|
||||
}
|
||||
|
||||
function getFso() {
|
||||
if (!fso) { fso = new ActiveXObject('Scripting.FileSystemObject'); }
|
||||
return fso;
|
||||
}
|
||||
|
||||
function decodeDOS(arg) {
|
||||
return arg.replace(/#(\d+);/g, function(str, charCode) {
|
||||
return String.fromCharCode(+charCode);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
67
lib/read.ps1
67
lib/read.ps1
|
@ -2,7 +2,7 @@
|
|||
Param(
|
||||
[string] $display,
|
||||
[switch] $noEchoBack,
|
||||
[string] $mask = '*',
|
||||
[string] $mask,
|
||||
[switch] $keyIn,
|
||||
[switch] $encoded
|
||||
)
|
||||
|
@ -18,20 +18,31 @@ function decodeDOS($arg) {
|
|||
[Regex]::Replace($arg, '#(\d+);', { [char][int] $args[0].Groups[1].Value })
|
||||
}
|
||||
|
||||
if ($encoded) {
|
||||
if ($display -ne '') { $display = decodeDOS $display }
|
||||
if ($mask -ne '') { $mask = decodeDOS $mask }
|
||||
$options = @{}
|
||||
foreach ($arg in @('display', 'noEchoBack', 'mask', 'keyIn', 'encoded')) {
|
||||
$options.Add($arg, (Get-Variable $arg -ValueOnly))
|
||||
}
|
||||
if ($options.encoded) {
|
||||
$argList = New-Object string[] $options.Keys.Count
|
||||
$options.Keys.CopyTo($argList, 0);
|
||||
foreach ($arg in $argList) {
|
||||
if (($options[$arg] -is [string]) -and ($options[$arg] -ne ''))
|
||||
{ $options[$arg] = decodeDOS $options[$arg] }
|
||||
}
|
||||
}
|
||||
|
||||
Write-Warning "[PS] display: <$display>" #_DBG
|
||||
Write-Warning "[PS] noEchoBack: $noEchoBack" #_DBG
|
||||
Write-Warning "[PS] mask: <$mask>" #_DBG
|
||||
Write-Warning "[PS] keyIn: $keyIn" #_DBG
|
||||
[string] $inputTTY = ''
|
||||
[bool] $isInputLine = $False
|
||||
[bool] $isEditable = (-not $options.noEchoBack) -and (-not $options.keyIn)
|
||||
|
||||
function writeTTY ($text) {
|
||||
execWithTTY ('Write-Host ''' + ($text -replace '''', '''''') + ''' -NoNewline')
|
||||
$script:isInputLine = $True
|
||||
}
|
||||
|
||||
# Instant method that opens TTY without CreateFile via P/Invoke in .NET Framework
|
||||
# **NOTE** Don't include special characters in $command when $getRes is True.
|
||||
# **NOTE** Don't include special characters of DOS in $command when $getRes is True.
|
||||
function execWithTTY ($command, $getRes = $False) {
|
||||
Write-Warning "[PS] command: <$command> getRes: $getRes" #_DBG
|
||||
if ($getRes) {
|
||||
$res = (cmd.exe /C "<CON powershell.exe -Command $command")
|
||||
if ($LastExitCode -ne 0) { exit 1 }
|
||||
|
@ -43,21 +54,19 @@ function execWithTTY ($command, $getRes = $False) {
|
|||
return $res
|
||||
}
|
||||
|
||||
function writeTTY ($text) {
|
||||
execWithTTY ('Write-Host ''' + ($text -replace '''', '''''') + ''' -NoNewline')
|
||||
if ($options.display -ne '') {
|
||||
writeTTY $options.display
|
||||
$options.display = ''
|
||||
}
|
||||
|
||||
[string] $inputTTY = ''
|
||||
|
||||
if ($noEchoBack -and (-not $keyIn) -and ($mask -eq '*')) {
|
||||
if ($options.noEchoBack -and (-not $options.keyIn) -and ($options.mask -eq '*')) {
|
||||
$inputTTY = execWithTTY ('$inputTTY = Read-Host -AsSecureString;' +
|
||||
'$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($inputTTY);' +
|
||||
'[System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)') $True
|
||||
return $inputTTY
|
||||
return '''' + $inputTTY + ''''
|
||||
}
|
||||
|
||||
$isEditable = (-not $noEchoBack) -and (-not $keyIn)
|
||||
if ($keyIn) { $reqSize = 1 }
|
||||
if ($options.keyIn) { $reqSize = 1 }
|
||||
else { $reqSize = 1024 } # dummy
|
||||
|
||||
while ($True) {
|
||||
|
@ -69,25 +78,27 @@ while ($True) {
|
|||
}
|
||||
|
||||
if ($chunk -eq '') { break }
|
||||
# other ctrl-chars
|
||||
$chunk = $chunk -replace '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]', ''
|
||||
if ($chunk -eq '') { continue }
|
||||
|
||||
if (-not $isEditable) {
|
||||
$displayInput = $chunk -replace '[\r\n]', ''
|
||||
if ($displayInput -ne '') {
|
||||
if ($noEchoBack) {
|
||||
if ($mask -eq '') { $displayInput = '' }
|
||||
else { $displayInput = $displayInput -replace '.', $mask }
|
||||
$displayTmp = $chunk -replace '[\r\n]', ''
|
||||
if ($displayTmp -ne '') {
|
||||
if ($options.noEchoBack) {
|
||||
if ($options.mask -eq '') { $displayTmp = '' }
|
||||
else { $displayTmp = $options.mask * $displayTmp.Length }
|
||||
}
|
||||
if ($displayInput -ne '') { writeTTY $displayInput }
|
||||
if ($displayTmp -ne '') { writeTTY $displayTmp }
|
||||
}
|
||||
}
|
||||
|
||||
$inputTTY += $chunk
|
||||
if (($inputTTY -match '[\r\n]$') -or ($keyIn -and ($inputTTY.Length -ge $reqSize)))
|
||||
{ break }
|
||||
if (($inputTTY -match '[\r\n]$') -or
|
||||
($options.keyIn -and ($inputTTY.Length -ge $reqSize))) { break }
|
||||
}
|
||||
|
||||
if (-not $isEditable) { writeTTY "`n" } # new-line
|
||||
if ((-not $isEditable) -and (-not ($options.keyIn -and (-not $isInputLine))))
|
||||
{ execWithTTY 'Write-Host ''''' } # new line
|
||||
|
||||
return '''' + $inputTTY + '''' #DBG
|
||||
return '''' + $inputTTY + ''''
|
||||
|
|
|
@ -27,9 +27,16 @@ var
|
|||
useExt = false,
|
||||
|
||||
fdR = 'none', fdW, ttyR, isRawMode = false,
|
||||
extHostPath, extScriptPath, tempdir, salt = 0;
|
||||
extHostPath, extHostArgs, tempdir, salt = 0;
|
||||
|
||||
function readlineSync(options) { // display, mask are string
|
||||
/*
|
||||
display: string
|
||||
noEchoBack: boolean
|
||||
mask: string
|
||||
keyIn: boolean
|
||||
noTrim: boolean
|
||||
*/
|
||||
function readlineSync(options) {
|
||||
var input = '', displayTmp;
|
||||
|
||||
function tryExt() {
|
||||
|
@ -187,48 +194,45 @@ function readlineSync(options) { // display, mask are string
|
|||
}
|
||||
|
||||
function readlineExt(options) {
|
||||
var cmdArgs = [], execArgs, res = {},
|
||||
execOptions = {
|
||||
env: process.env,
|
||||
var hostArgs, res = {},
|
||||
execOptions = {env: process.env, encoding: encoding};
|
||||
|
||||
if (!extHostPath) {
|
||||
if (IS_WIN) {
|
||||
if (process.env.PSModulePath) { // Windows PowerShell
|
||||
extHostPath = 'powershell.exe';
|
||||
extHostArgs = ['-ExecutionPolicy', 'Bypass', '-File', __dirname + '\\read.ps1'];
|
||||
} else { // Windows Script Host
|
||||
extHostPath = 'cscript.exe';
|
||||
extHostArgs = ['//nologo', __dirname + '\\read.cs.js'];
|
||||
}
|
||||
} else {
|
||||
extHostPath = '/bin/sh';
|
||||
extHostArgs = [__dirname + '/read.sh'];
|
||||
}
|
||||
}
|
||||
if (IS_WIN && !process.env.PSModulePath) { // Windows Script Host
|
||||
// ScriptPW (Win XP and Server2003) needs TTY stream as STDIN.
|
||||
// In this case, If STDIN isn't TTY, an error is thrown.
|
||||
stdio: [process.stdin],
|
||||
encoding: encoding
|
||||
};
|
||||
|
||||
extHostPath = extHostPath || (IS_WIN ? 'cscript.exe' : '/bin/sh');
|
||||
extScriptPath = extScriptPath || (__dirname + (IS_WIN ? '\\read.cs.js' : '/read.sh'));
|
||||
|
||||
// To send any text to crazy Windows shell safely.
|
||||
function encodeDOS(arg) {
|
||||
return arg.replace(/[^\w\u0080-\uFFFF]/g, function(chr) {
|
||||
return '#' + chr.charCodeAt(0) + ';';
|
||||
});
|
||||
}
|
||||
|
||||
if (options.noEchoBack) { cmdArgs.push('--noechoback'); }
|
||||
if (options.keyIn) { cmdArgs.push('--keyin'); }
|
||||
if (options.display !== '') {
|
||||
cmdArgs = cmdArgs.concat('--display', IS_WIN ?
|
||||
[encodeDOS(options.display), '--encoded'] : options.display);
|
||||
execOptions.stdio = [process.stdin];
|
||||
}
|
||||
|
||||
if (childProc.execFileSync) {
|
||||
execArgs = (IS_WIN ? ['//nologo', extScriptPath] : [extScriptPath]).concat(cmdArgs);
|
||||
hostArgs = getHostArgs(options);
|
||||
try {
|
||||
res.input = childProc.execFileSync(extHostPath, execArgs, execOptions);
|
||||
res.input = childProc.execFileSync(extHostPath, hostArgs, execOptions);
|
||||
} catch (e) { // non-zero exit code
|
||||
res.error = new Error(DEFAULT_ERR_MSG);
|
||||
res.error.method = 'execFileSync';
|
||||
res.error.command = extScriptPath;
|
||||
res.error.args = cmdArgs;
|
||||
res.error.ExtMessage = e.stderr.trim();
|
||||
res.error.program = extHostPath;
|
||||
res.error.args = hostArgs;
|
||||
res.error.extMessage = e.stderr.trim();
|
||||
res.error.exitCode = e.status;
|
||||
res.error.code = e.code;
|
||||
res.error.signal = e.signal;
|
||||
res.error.exitCode = e.status;
|
||||
}
|
||||
} else {
|
||||
res = _execFileSync(cmdArgs, execOptions);
|
||||
res = _execFileSync(options, execOptions);
|
||||
}
|
||||
if (!res.error) {
|
||||
res.input = res.input.replace(/^'|'$/g, '');
|
||||
|
@ -239,7 +243,7 @@ function readlineExt(options) {
|
|||
}
|
||||
|
||||
// piping via files (node v0.10-)
|
||||
function _execFileSync(cmdArgs, execOptions) {
|
||||
function _execFileSync(options, execOptions) {
|
||||
|
||||
function getTempfile(name) {
|
||||
var path = require('path'), filepath, suffix = '', fd;
|
||||
|
@ -263,7 +267,7 @@ function _execFileSync(cmdArgs, execOptions) {
|
|||
return filepath;
|
||||
}
|
||||
|
||||
var execArgs, interpreter, res = {}, exitCode,
|
||||
var hostArgs, shellPath, shellArgs, res = {}, exitCode,
|
||||
pathStdout = getTempfile('readline-sync.stdout'),
|
||||
pathStderr = getTempfile('readline-sync.stderr'),
|
||||
pathExit = getTempfile('readline-sync.exit'),
|
||||
|
@ -275,25 +279,27 @@ function _execFileSync(cmdArgs, execOptions) {
|
|||
password = shasum.digest('hex');
|
||||
decipher = crypto.createDecipher(ALGORITHM_CIPHER, password);
|
||||
|
||||
if (IS_WIN) { options.encoded = true; }
|
||||
hostArgs = getHostArgs(options);
|
||||
if (IS_WIN) {
|
||||
interpreter = process.env.ComSpec || 'cmd.exe';
|
||||
shellPath = process.env.ComSpec || 'cmd.exe';
|
||||
process.env.Q = '"'; // The quote (") that isn't escaped.
|
||||
// `()` for ignore space by echo
|
||||
execArgs = ['/V:ON', '/S', '/C',
|
||||
'(%Q%' + interpreter + '%Q% /V:ON /S /C %Q%' +
|
||||
'%Q%' + extHostPath + '%Q% //nologo %Q%' + extScriptPath + '%Q%' +
|
||||
cmdArgs.map(function(arg) { return ' %Q%' + arg + '%Q%'; }).join('') +
|
||||
shellArgs = ['/V:ON', '/S', '/C',
|
||||
'(%Q%' + shellPath + '%Q% /V:ON /S /C %Q%' +
|
||||
'%Q%' + extHostPath + '%Q%' +
|
||||
hostArgs.map(function(arg) { return ' %Q%' + arg + '%Q%'; }).join('') +
|
||||
' & (echo !ERRORLEVEL!)>%Q%' + pathExit + '%Q%%Q%) 2>%Q%' + pathStderr + '%Q%' +
|
||||
' |%Q%' + process.execPath + '%Q% %Q%' + __dirname + '\\encrypt.js%Q%' +
|
||||
' %Q%' + ALGORITHM_CIPHER + '%Q% %Q%' + password + '%Q%' +
|
||||
' >%Q%' + pathStdout + '%Q%' +
|
||||
' & (echo 1)>%Q%' + pathDone + '%Q%'];
|
||||
} else {
|
||||
interpreter = extHostPath;
|
||||
execArgs = ['-c',
|
||||
shellPath = extHostPath;
|
||||
shellArgs = ['-c',
|
||||
// Use `()`, not `{}` for `-c` (text param)
|
||||
'("' + extHostPath + '" "' + extScriptPath + '"' +
|
||||
cmdArgs.map(function(arg)
|
||||
'("' + extHostPath + '"' +
|
||||
hostArgs.map(function(arg)
|
||||
{ return " '" + arg.replace(/'/g, "'\\''") + "'"; }).join('') +
|
||||
'; echo $?>"' + pathExit + '") 2>"' + pathStderr + '"' +
|
||||
' |"' + process.execPath + '" "' + __dirname + '/encrypt.js"' +
|
||||
|
@ -302,11 +308,12 @@ function _execFileSync(cmdArgs, execOptions) {
|
|||
'; echo 1 >"' + pathDone + '"'];
|
||||
}
|
||||
try {
|
||||
childProc.spawn(interpreter, execArgs, execOptions);
|
||||
childProc.spawn(shellPath, shellArgs, execOptions);
|
||||
} catch (e) {
|
||||
res.error = new Error(e.message);
|
||||
res.error.method = '_execFileSync - spawn';
|
||||
res.error.interpreter = interpreter;
|
||||
res.error.program = shellPath;
|
||||
res.error.args = shellArgs;
|
||||
}
|
||||
|
||||
while (fs.readFileSync(pathDone, {encoding: encoding}).trim() !== '1') {}
|
||||
|
@ -317,9 +324,9 @@ function _execFileSync(cmdArgs, execOptions) {
|
|||
} else {
|
||||
res.error = new Error(DEFAULT_ERR_MSG);
|
||||
res.error.method = '_execFileSync';
|
||||
res.error.command = extScriptPath;
|
||||
res.error.args = cmdArgs;
|
||||
res.error.ExtMessage = fs.readFileSync(pathStderr, {encoding: encoding}).trim();
|
||||
res.error.program = shellPath;
|
||||
res.error.args = shellArgs;
|
||||
res.error.extMessage = fs.readFileSync(pathStderr, {encoding: encoding}).trim();
|
||||
res.error.exitCode = +exitCode;
|
||||
}
|
||||
|
||||
|
@ -331,6 +338,38 @@ function _execFileSync(cmdArgs, execOptions) {
|
|||
return res;
|
||||
}
|
||||
|
||||
function getHostArgs(options) {
|
||||
// To send any text to crazy Windows shell safely.
|
||||
function encodeDOS(arg) {
|
||||
return arg.replace(/[^\w\u0080-\uFFFF]/g, function(chr) {
|
||||
return '#' + chr.charCodeAt(0) + ';';
|
||||
});
|
||||
}
|
||||
|
||||
return extHostArgs.concat((function(conf) {
|
||||
var args = [], key;
|
||||
for (key in conf) {
|
||||
if (conf.hasOwnProperty(key)) {
|
||||
if (conf[key] === 'boolean') {
|
||||
if (options[key]) { args.push('--' + key); }
|
||||
} else if (conf[key] === 'string') {
|
||||
if (options[key] !== '') {
|
||||
args.push('--' + key,
|
||||
options.encoded ? encodeDOS(options[key]) : options[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return args;
|
||||
})({
|
||||
display: 'string',
|
||||
noEchoBack: 'boolean',
|
||||
mask: 'string',
|
||||
keyIn: 'boolean',
|
||||
encoded: 'boolean'
|
||||
}));
|
||||
}
|
||||
|
||||
// for dev
|
||||
exports._useExtSet = function(use) { useExt = use; };
|
||||
|
||||
|
|
Loading…
Reference in a new issue