Add display to shell script.

This commit is contained in:
anseki 2015-03-11 17:06:27 +09:00
parent e552f97db1
commit 16c0c476cd
4 changed files with 143 additions and 82 deletions

10
lib/decodedos.js Normal file
View file

@ -0,0 +1,10 @@
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);
});
}

View file

@ -1,42 +1,68 @@
@echo off
setlocal
setlocal ENABLEDELAYEDEXPANSION
if "%~1"=="noechoback" (
call :read_s
if ERRORLEVEL 1 exit /b 1
) else (
set /p INPUT=<CON >CON
)
set /p ="'%INPUT%'"<NUL
endlocal
exit /b 0
:: Silent Read
:read_s
:: where /q powershell
:: Win <Vista and <Server2008 don't have `where`.
powershell /? >NUL 2>&1
:: Win <7 and <Server2008R2 don't have PowerShell as default.
:: Win XP and Server2003 have `ScriptPW` (`scriptpw.dll`).
:: In the systems that don't have both, an error is thrown.
if ERRORLEVEL 1 (
set "EXECOMMAND=cscript //nologo "%~dp0read.cs.js""
) else (
set "EXECOMMAND=powershell -Command "$text = read-host -AsSecureString; ^
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR^($text^); ^
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto^($BSTR^)""
)
:: Can't get `ERRORLEVEL` from sub-shell (`for`).
:: 2 `%ERRCODE%` lines are returned if an error is thrown.
set ERRCODE=ERR
set "EXECOMMAND=%EXECOMMAND% ^& if ERRORLEVEL 1 ^(echo %ERRCODE%^& echo %ERRCODE%^)"
:: echo %EXECOMMAND%
for /f "usebackq delims=" %%i in (`%EXECOMMAND%`) do (
if "%%i"=="%ERRCODE%" if "!INPUT!"=="%ERRCODE%" exit /b 1
set "INPUT=%%i"
)
exit /b 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 >CON
)
set /p ="'%input%'"<NUL
endlocal
exit /b 0
:: Silent Read
:read_s
:: where /q powershell
:: Win <Vista and <Server2008 don't have `where`.
powershell /? >NUL 2>&1
:: Win <7 and <Server2008R2 don't have PowerShell as default.
:: Win XP and Server2003 have `ScriptPW` (`scriptpw.dll`).
:: In the systems that don't have both, an error is thrown.
if ERRORLEVEL 1 (
set "exec_line=cscript //nologo "%~dp0read.cs.js""
) else (
set "exec_line=powershell -Command "$text = read-host -AsSecureString; ^
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR^($text^); ^
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto^($BSTR^)""
)
:: Can't get `ERRORLEVEL` from sub-shell.
:: 2 `%ERRCODE%` lines are returned if an error is thrown.
set ERRCODE=ERR
set "exec_line=%exec_line% ^& if ERRORLEVEL 1 ^(echo %ERRCODE%^& echo %ERRCODE%^)"
:: echo %exec_line%
for /f "usebackq delims=" %%i in (`%exec_line%`) do (
if "%%i"=="%ERRCODE%" if "!input!"=="%ERRCODE%" exit /b 1
set "input=%%i"
)
exit /b 0

View file

@ -3,21 +3,36 @@ 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
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
}
if [ "$1" = "noechoback" ]; then
# getopt(s)
while [ $# -ge 1 ]; do
case "$1" in
"--noechoback") noechoback=1;;
"--keyin") keyin=1;;
"--display") shift; display=$1;;
esac
shift
done
if [ -n "$display" ]; then
printf '%s' "$display" >/dev/tty
fi
if [ "$noechoback" = "1" ]; then
# Try `-s` option. *ksh have it that not `--silent`. Therefore, don't try it.
if [ -n "$BASH_VERSION" ] || [ -n "$ZSH_VERSION" ]; then
IFS= read -rs INPUT </dev/tty 2>/dev/null && printf '\n' >/dev/tty || read_s
IFS= read -rs input </dev/tty 2>/dev/null && printf '\n' >/dev/tty || read_s
else
read_s
fi
else
IFS= read -r INPUT </dev/tty || exit 1
IFS= read -r input </dev/tty || exit 1
fi
printf '%s' "'$INPUT'"
printf '%s' "'$input'"
exit 0

View file

@ -10,7 +10,7 @@
var
IS_WIN = process.platform === 'win32',
SHELL_PATH = IS_WIN ? process.env.comspec || 'cmd.exe' : '/bin/sh',
SHELL_PATH = IS_WIN ? process.env.ComSpec || 'cmd.exe' : '/bin/sh',
SHELL_CMD = __dirname + (IS_WIN ? '\\read.bat' : '/read.sh'),
ALGORITHM_CIPHER = 'aes-256-cbc',
ALGORITHM_HASH = 'sha256',
@ -39,8 +39,6 @@ function _readlineSync(options) { // options.display is string
if (IS_WIN) { // r/w mode not supported
if (process.stdin.isTTY && process.stdout.isTTY) {
console.warn('STD TRY');
if (options.display !== '') {
// process.stdout.write(options.display, encoding);
fs.writeSync(process.stdout.fd, options.display);
@ -48,12 +46,9 @@ function _readlineSync(options) { // options.display is string
}
fd = process.stdin.fd;
isOpened = true;
console.warn('STD OK');
} else {
try {
console.warn('CON TRY');
if (options.display !== '') {
fd = fs.openSync('\\\\.\\CON', 'w');
fs.writeSync(fd, options.display);
@ -62,13 +57,10 @@ function _readlineSync(options) { // options.display is string
}
fd = fs.openSync('\\\\.\\CON', 'rs');
isOpened = true;
console.warn('CON OK');
} catch (e) {}
if (!isOpened || options.display !== '') { // Retry
try {
console.warn('CONINOUT TRY');
// For raw device path
// On XP, 2000, 7 (x86), it might fail.
// And, process.binding('fs') might be no good.
@ -84,24 +76,19 @@ function _readlineSync(options) { // options.display is string
fd = fsBind.open('CONIN$',
constBind.O_RDWR | constBind.O_SYNC, parseInt('0666', 8));
isOpened = true;
console.warn('CONINOUT OK');
} catch (e) {}
}
}
} else {
} else {
try {
console.warn('/dev/tty TRY');
fd = fs.openSync('/dev/tty', 'rs+');
isOpened = true;
if (options.display !== '') {
fs.writeSync(fd, options.display);
options.display = '';
}
console.warn('/dev/tty OK');
} catch (e) {}
}
if (isOpened && options.display === '') {
@ -148,12 +135,26 @@ function _readlineShell(options) {
encoding: encoding
};
if (options.noEchoBack) {
cmdArgs.push('noechoback');
// 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.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);
}
}
if (childProc.execFileSync) {
console.warn('execFileSync');
if (IS_WIN) {
process.env.Q = '"'; // The quote (") that isn't escaped.
execArgs = ['/V:ON', '/S', '/C',
@ -173,10 +174,10 @@ function _readlineShell(options) {
res.error.shellMessage = e.stderr.trim();
}
} else {
console.warn('_execSyncByFile');
res = _execSyncByFile(cmdArgs, execOptions);
}
if (!res.error) { res.input = res.input.replace(/^'|'$/g, ''); }
options.display = '';
return res;
}
@ -222,7 +223,7 @@ function _execSyncByFile(cmdArgs, execOptions) {
process.env.Q = '"'; // The quote (") that isn't escaped.
// `()` for ignore space by echo
execArgs = ['/V:ON', '/S', '/C',
'(' + SHELL_PATH + ' /V:ON /S /C %Q%%Q%' + SHELL_CMD + '%Q%' +
'(%Q%' + SHELL_PATH + '%Q% /V:ON /S /C %Q%%Q%' + SHELL_CMD + '%Q%' +
cmdArgs.map(function(arg) { return ' %Q%' + arg + '%Q%'; }).join('') +
' & (echo !ERRORLEVEL!)>%Q%' + pathStatus + '%Q%%Q%) 2>%Q%' + pathStderr + '%Q%' +
' |%Q%' + process.execPath + '%Q% %Q%' + __dirname + '\\encrypt.js%Q%' +
@ -232,8 +233,9 @@ function _execSyncByFile(cmdArgs, execOptions) {
} else {
execArgs = ['-c',
// Use `()`, not `{}` for `-c` (text param)
'(' + SHELL_PATH + ' "' + SHELL_CMD + '"' +
cmdArgs.map(function(arg) { return ' "' + arg + '"'; }).join('') +
'("' + SHELL_PATH + '" "' + SHELL_CMD + '"' +
cmdArgs.map(function(arg)
{ return ' "' + arg.replace(/[\\"`]/g, '\\\$&') + '"'; }).join('') +
'; echo $?>"' + pathStatus + '") 2>"' + pathStderr + '"' +
' |"' + process.execPath + '" "' + __dirname + '/encrypt.js"' +
' "' + ALGORITHM_CIPHER + '" "' + password + '"' +
@ -292,27 +294,35 @@ exports.setBufferSize = function(newBufSize) {
};
exports.prompt = function(options) {
options = options || {};
options.display = promptText + '';
options.keyIn = false;
return _readlineSync(options);
var readOptions = {
display: promptText + '',
noEchoBack: options.noEchoBack,
keyIn: false,
noTrim: options.noTrim
};
return _readlineSync(readOptions);
};
exports.question = function(query, options) {
options = options || {};
var readOptions = {
/* jshint eqnull:true */
options.display = query != null ? query + '' : '';
display: query != null ? query + '' : '',
/* jshint eqnull:false */
options.keyIn = false;
return _readlineSync(options);
noEchoBack: options.noEchoBack,
keyIn: false,
noTrim: options.noTrim
};
return _readlineSync(readOptions);
};
exports.keyIn = function(query, options) {
options = options || {};
var readOptions = {
/* jshint eqnull:true */
options.display = query != null ? query + '' : '';
display: query != null ? query + '' : '',
/* jshint eqnull:false */
options.keyIn = true;
options.noEchoBack = false;
return _readlineSync(options);
noEchoBack: false,
keyIn: true,
noTrim: true
};
return _readlineSync(readOptions);
};