From 8e141597ba8c4b9c65e80b7b5c66ff0980ec32e7 Mon Sep 17 00:00:00 2001 From: anseki Date: Fri, 13 Mar 2015 18:34:41 +0900 Subject: [PATCH] fixed #14 Replace BAT with WSH --- lib/decodedos.js | 10 --- lib/read-s.cs.js | 20 ------ lib/read.bat | 68 -------------------- lib/read.cs.js | 148 +++++++++++++++++++++++++++++++++++++++++++ lib/readline-sync.js | 73 +++++++++++---------- 5 files changed, 184 insertions(+), 135 deletions(-) delete mode 100644 lib/decodedos.js delete mode 100644 lib/read-s.cs.js delete mode 100644 lib/read.bat create mode 100644 lib/read.cs.js diff --git a/lib/decodedos.js b/lib/decodedos.js deleted file mode 100644 index e7aec8a..0000000 --- a/lib/decodedos.js +++ /dev/null @@ -1,10 +0,0 @@ -process.stdout.write(decodeDOS(process.argv[2] /*text*/ || ''), - process.env.RLS_ENCODING || 'binary', function() { - process.exit(0); -}); - -function decodeDOS(arg) { - return arg.replace(/#(\d+);/g, function(str, charCode) { - return String.fromCharCode(+charCode); - }); -} diff --git a/lib/read-s.cs.js b/lib/read-s.cs.js deleted file mode 100644 index 1340a93..0000000 --- a/lib/read-s.cs.js +++ /dev/null @@ -1,20 +0,0 @@ -/*jshint wsh:true */ - -var oExec; - -// exit-code is not returned even if an error is thrown. -try { - WScript.StdOut.Write(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); - return charCode >= 0x4020 && charCode <= 0x407F ? - String.fromCharCode(charCode - 0x4000) : ''; - })); -} catch (e) { - WScript.StdErr.Write(e.description); - WScript.Quit(1); -} - -oExec = WScript.CreateObject('WScript.Shell').Exec('cmd /c echo; >CON'); -while (oExec.Status === 0) { WScript.Sleep(100); } diff --git a/lib/read.bat b/lib/read.bat deleted file mode 100644 index 638c0f8..0000000 --- a/lib/read.bat +++ /dev/null @@ -1,68 +0,0 @@ -@echo off -setlocal ENABLEDELAYEDEXPANSION - -:args_loop -if "%~1"=="" ( - goto args_end - -) else if "%~1"=="--noechoback" ( - set noechoback=1 - -) else if "%~1"=="--keyin" ( - set keyin=1 - -) else if "%~1"=="--display" ( - set "display=%~2" - shift /1 - -) -shift /1 -goto args_loop -:args_end - -:: type tmpfile.txt >CON -if "%display%" NEQ "" if "%NODE_EXEC_PATH%" NEQ "" ( - "%NODE_EXEC_PATH%" "%~dp0decodedos.js" "%display%" >CON - if ERRORLEVEL 1 exit /b 1 -) - -if "%noechoback%"=="1" ( - call :read_s - if ERRORLEVEL 1 exit /b 1 -) else ( - set /p input=CON -) -set /p ="'%input%'"NUL 2>&1 -:: Win <7 and = 0x4020 && charCode <= 0x407F ? + String.fromCharCode(charCode - 0x4000) : ''; + }); + } catch (e) { + WScript.StdErr.WriteLine('ScriptPW.Password Error: ' + e.number + + '\n' + e.description); + WScript.Quit(1); + } + ttyWrite('\n'); + return pw; +} + +function shellExec(cmd, callback) { // callback(exitCode, stdout, stderr, error) + var wsExec, stdout = '', stderr = '', noOutput; + + function getShell() { + if (!shell) { shell = WScript.CreateObject('WScript.Shell'); } + return shell; + } + + try { + wsExec = getShell().Exec(cmd); + } catch (e) { + callback(e.number, stdout, stderr, e); + return wsExec; + } + + while (true) { + noOutput = true; + if (!wsExec.StdOut.AtEndOfStream) { + stdout += wsExec.StdOut.ReadAll(); + noOutput = false; + } + if (!wsExec.StdErr.AtEndOfStream) { + stderr += wsExec.StdErr.ReadAll(); + noOutput = false; + } + if (noOutput) { + if (wsExec.Status === 1 /*WshFinished*/) { break; } + WScript.Sleep(100); + } + } + + callback(wsExec.ExitCode, stdout, stderr); + return wsExec; +} + +function decodeDOS(arg) { + return arg.replace(/#(\d+);/g, function(str, charCode) { + return String.fromCharCode(+charCode); + }); +} diff --git a/lib/readline-sync.js b/lib/readline-sync.js index 9222eff..1ee40c5 100644 --- a/lib/readline-sync.js +++ b/lib/readline-sync.js @@ -10,8 +10,8 @@ var IS_WIN = process.platform === 'win32', - SHELL_PATH = IS_WIN ? process.env.ComSpec || 'cmd.exe' : '/bin/sh', - SHELL_CMD = __dirname + (IS_WIN ? '\\read.bat' : '/read.sh'), + SHELL_PATH = IS_WIN ? 'cscript.exe' : '/bin/sh', + SHELL_CMD = __dirname + (IS_WIN ? '\\read.cs.js' : '/read.sh'), ALGORITHM_CIPHER = 'aes-256-cbc', ALGORITHM_HASH = 'sha256', DEFAULT_ERR_MSG = 'The platform doesn\'t support interactive reading', @@ -31,7 +31,7 @@ function _readlineSync(options) { // options.display is string if (options.display !== '' && typeof print === 'function') { print(options.display, encoding); } - if (useShell || options.noEchoBack) { + if (useShell || options.noEchoBack || options.keyIn) { res = _readlineShell(options); if (res.error) { throw res.error; } input = res.input; @@ -129,7 +129,7 @@ function _readlineShell(options) { var cmdArgs = [], execArgs, res = {}, execOptions = { env: process.env, - // ScriptPW (Win XP and Server2003) for `noEchoBack` needs TTY stream. + // 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 @@ -142,28 +142,15 @@ function _readlineShell(options) { }); } + if (options.noEchoBack) { cmdArgs.push('--noechoback'); } if (options.keyIn) { cmdArgs.push('--keyin'); } - else if (options.noEchoBack) { cmdArgs.push('--noechoback'); } if (options.display !== '') { - if (IS_WIN) { - cmdArgs.push('--display', encodeDOS(options.display)); - process.env.NODE_EXEC_PATH = process.execPath; - process.env.RLS_ENCODING = encoding; - } else { - cmdArgs.push('--display', options.display); - } + cmdArgs = cmdArgs.concat('--display', IS_WIN ? + [encodeDOS(options.display), '--encoded'] : options.display); } if (childProc.execFileSync) { - if (IS_WIN) { - process.env.Q = '"'; // The quote (") that isn't escaped. - execArgs = ['/V:ON', '/S', '/C', - '%Q%' + SHELL_CMD + '%Q%' + - cmdArgs.map(function(arg) { return ' %Q%' + arg + '%Q%'; }).join('') + - ' & exit !ERRORLEVEL!']; - } else { - execArgs = [SHELL_CMD].concat(cmdArgs); - } + execArgs = (IS_WIN ? ['//nologo', SHELL_CMD] : [SHELL_CMD]).concat(cmdArgs); try { res.input = childProc.execFileSync(SHELL_PATH, execArgs, execOptions); } catch (e) { // non-zero exit code @@ -176,8 +163,10 @@ function _readlineShell(options) { } else { res = _execSyncByFile(cmdArgs, execOptions); } - if (!res.error) { res.input = res.input.replace(/^'|'$/g, ''); } - options.display = ''; + if (!res.error) { + res.input = res.input.replace(/^'|'$/g, ''); + options.display = ''; + } return res; } @@ -207,11 +196,11 @@ function _execSyncByFile(cmdArgs, execOptions) { return filepath; } - var execArgs, res = {}, - pathStdout = getTempfile('readline-sync.stdout'), - pathStderr = getTempfile('readline-sync.stderr'), - pathStatus = getTempfile('readline-sync.status'), - pathDone = getTempfile('readline-sync.done'), + var execArgs, interpreter, res = {}, + pathStdout = getTempfile('readline-sync.stdout'), + pathStderr = getTempfile('readline-sync.stderr'), + pathExit = getTempfile('readline-sync.exit'), + pathDone = getTempfile('readline-sync.done'), crypto = require('crypto'), shasum, decipher, password; shasum = crypto.createHash(ALGORITHM_HASH); @@ -220,32 +209,41 @@ function _execSyncByFile(cmdArgs, execOptions) { decipher = crypto.createDecipher(ALGORITHM_CIPHER, password); if (IS_WIN) { + interpreter = 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%' + SHELL_PATH + '%Q% /V:ON /S /C %Q%%Q%' + SHELL_CMD + '%Q%' + + '(%Q%' + interpreter + '%Q% /V:ON /S /C %Q%' + + '%Q%' + SHELL_PATH + '%Q% //nologo %Q%' + SHELL_CMD + '%Q%' + cmdArgs.map(function(arg) { return ' %Q%' + arg + '%Q%'; }).join('') + - ' & (echo !ERRORLEVEL!)>%Q%' + pathStatus + '%Q%%Q%) 2>%Q%' + pathStderr + '%Q%' + + ' & (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 = SHELL_PATH; execArgs = ['-c', // Use `()`, not `{}` for `-c` (text param) '("' + SHELL_PATH + '" "' + SHELL_CMD + '"' + cmdArgs.map(function(arg) - { return ' "' + arg.replace(/[\\"`]/g, '\\\$&') + '"'; }).join('') + - '; echo $?>"' + pathStatus + '") 2>"' + pathStderr + '"' + + { return " '" + arg.replace(/'/g, "'\\''") + "'"; }).join('') + + '; echo $?>"' + pathExit + '") 2>"' + pathStderr + '"' + ' |"' + process.execPath + '" "' + __dirname + '/encrypt.js"' + ' "' + ALGORITHM_CIPHER + '" "' + password + '"' + ' >"' + pathStdout + '"' + '; echo 1 >"' + pathDone + '"']; } - childProc.spawn(SHELL_PATH, execArgs, execOptions); + try { + childProc.spawn(interpreter, execArgs, execOptions); + } catch (e) { + res.error = new Error(e.message); + res.error.method = '_execSyncByFile - spawn'; + res.error.interpreter = interpreter; + } while (fs.readFileSync(pathDone, {encoding: encoding}).trim() !== '1') {} - if (fs.readFileSync(pathStatus, {encoding: encoding}).trim() === '0') { + if (fs.readFileSync(pathExit, {encoding: encoding}).trim() === '0') { res.input = decipher.update(fs.readFileSync(pathStdout, {encoding: 'binary'}), 'hex', encoding) + decipher.final(encoding); @@ -259,7 +257,7 @@ function _execSyncByFile(cmdArgs, execOptions) { fs.unlinkSync(pathStdout); fs.unlinkSync(pathStderr); - fs.unlinkSync(pathStatus); + fs.unlinkSync(pathExit); fs.unlinkSync(pathDone); return res; @@ -287,7 +285,8 @@ exports.setEncoding = function(newEncoding) { }; exports.setBufferSize = function(newBufSize) { - if (typeof newBufSize === 'number') { + newBufSize = parseInt(newBufSize, 10); + if (!isNaN(newBufSize) && typeof newBufSize === 'number') { bufSize = newBufSize; } return bufSize; @@ -320,7 +319,7 @@ exports.keyIn = function(query, options) { /* jshint eqnull:true */ display: query != null ? query + '' : '', /* jshint eqnull:false */ - noEchoBack: false, + noEchoBack: !!(options && options.noEchoBack), keyIn: true, noTrim: true };