From 90f173ad92d8d296a410a9b5b19df306a6ccc970 Mon Sep 17 00:00:00 2001 From: anseki Date: Fri, 28 Oct 2016 15:16:59 +0900 Subject: [PATCH] Add getRawInput method, fix #36 --- README.md | 17 +++++++++++++++-- lib/readline-sync.js | 39 ++++++++++++++++++++++++--------------- package.json | 2 +- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 53184c4..071ea12 100644 --- a/README.md +++ b/README.md @@ -479,7 +479,7 @@ _For `question*` and `prompt*` methods only_ *Type:* number *Default:* `1024` -When readlineSync reads from a console directly (without external program), use a size `bufferSize` buffer. +When readlineSync reads from a console directly (without [external program](#note-reading_by_external_program)), use a size `bufferSize` buffer. Even if the input by user exceeds it, it's usually no problem, because the buffer is used repeatedly. But some platforms's (e.g. Windows) console might not accept input that exceeds it. And set an enough size. Note that this might be limited by [version of Node.js](https://nodejs.org/api/buffer.html#buffer_class_method_buffer_alloc_size_fill_encoding) and environment running your script (Big buffer size is usually not required). (See also: [issue](https://github.com/nodejs/node/issues/4660), [PR](https://github.com/nodejs/node/pull/4682)) @@ -1728,6 +1728,19 @@ password = readlineSync.questionNewPassword('PASSWORD: ', {charlist: '$'}); See also [`limit` option for `keyIn*` method](#basic_options-limit-for_keyin_method). +## Special method `getRawInput` + +```js +rawInput = readlineSync.getRawInput() +``` + +Return a raw input data of last method. +When the input was terminated with no data, a `NULL` is inserted to the data. + +This might contain control-codes (e.g. `LF`, `CR`, `EOF`, etc.), therefore, it might be used to get `^D` that was input. But you should understand each environments for that. Or, **you should not use this** if your script is used in multiple environments. +For example, when the user input `EOF` (`^D` in Unix like system, `^Z` in Windows), `x1A` (`EOF`) is returned in Windows, and `x00` (`NULL`) is returned in Unix like system. And `x04` (`EOT`) is returned in Unix like system with raw-mode. And also, when [external program](#note-reading_by_external_program) is used, nothing is returned. See also [Control characters](#note-control_characters). +You may examine each environment and you must test your script very much, if you want to handle the raw input data. + ## With Task Runner The easy way to control a flow of the task runner by the input from the user: @@ -1790,7 +1803,7 @@ try { } ``` -### Control characters +### Control characters TTY interfaces are different by the platforms. In some environments, ANSI escape sequences might be ignored. For example, in non-POSIX TTY such as Windows CMD does not support it (that of Windows 8 especially has problems). Since readlineSync does not use Node.js library that emulates POSIX TTY (but that is still incomplete), those characters may be not parsed. Then, using ANSI escape sequences is not recommended if you will support more environments. Also, control characters user input might be not accepted or parsed. That behavior differs depending on the environment. And current Node.js does not support controlling a readline system library. diff --git a/lib/readline-sync.js b/lib/readline-sync.js index 34fba42..18b3fcd 100644 --- a/lib/readline-sync.js +++ b/lib/readline-sync.js @@ -44,7 +44,7 @@ var fdR = 'none', fdW, ttyR, isRawMode = false, extHostPath, extHostArgs, tempdir, salt = 0, - lastInput = '', inputHistory = [], + lastInput = '', inputHistory = [], rawInput, _DBG_useExt = false, _DBG_checkOptions = false, _DBG_checkMethod = false; function getHostArgs(options) { @@ -337,6 +337,7 @@ function _readlineSync(options) { var atEol, limit, isCooked = !options.hideEchoBack && !options.keyIn, buffer, reqSize, readSize, chunk, line; + rawInput = ''; // Node.js v0.10- returns an error if same mode is set. function setRawMode(mode) { @@ -387,7 +388,13 @@ function _readlineSync(options) { return; } } - chunk = readSize > 0 ? buffer.toString(options.encoding, 0, readSize) : '\n'; + if (readSize > 0) { + chunk = buffer.toString(options.encoding, 0, readSize); + rawInput += chunk; + } else { + chunk = '\n'; + rawInput += String.fromCharCode(0); + } if (chunk && typeof (line = (chunk.match(/^(.*?)[\r\n]/) || [])[1]) === 'string') { chunk = line; @@ -432,7 +439,7 @@ function _readlineSync(options) { function flattenArray(array, validator) { var flatArray = []; function _flattenArray(array) { - if (array == null) { // eslint-disable-line eqeqeq + if (array == null) { return; } else if (Array.isArray(array)) { array.forEach(_flattenArray); @@ -465,7 +472,7 @@ function margeOptions() { } return optionsList.reduce(function(options, optionsPart) { - if (optionsPart == null) { return options; } // eslint-disable-line eqeqeq + if (optionsPart == null) { return options; } // ======== DEPRECATED ======== if (optionsPart.hasOwnProperty('noEchoBack') && @@ -492,7 +499,7 @@ function margeOptions() { case 'limitMessage': // * case 'defaultInput': // * case 'encoding': // * * - value = value != null ? value + '' : ''; // eslint-disable-line eqeqeq + value = value != null ? value + '' : ''; if (value && optionName !== 'limitMessage') { value = value.replace(/[\r\n]/g, ''); } options[optionName] = value; break; @@ -533,7 +540,7 @@ function margeOptions() { // ================ other case 'prompt': // * case 'display': // * - options[optionName] = value != null ? value : ''; // eslint-disable-line eqeqeq + options[optionName] = value != null ? value : ''; break; // no default } @@ -877,7 +884,7 @@ exports.keyIn = function(query, options) { // ------------------------------------ exports.questionEMail = function(query, options) { - if (query == null) { query = 'Input e-mail address: '; } // eslint-disable-line eqeqeq + if (query == null) { query = 'Input e-mail address: '; } /* eslint-disable key-spacing */ return exports.question(query, margeOptions({ // -------- default @@ -932,13 +939,13 @@ exports.questionNewPassword = function(query, options) { resCharlist = array2charlist([charlist], readOptions.caseSensitive, true); resCharlist.text = joinChunks(resCharlist.values, resCharlist.suppressed); - confirmMessage = options.confirmMessage != null ? options.confirmMessage : // eslint-disable-line eqeqeq + confirmMessage = options.confirmMessage != null ? options.confirmMessage : 'Reinput a same one to confirm it: '; - unmatchMessage = options.unmatchMessage != null ? options.unmatchMessage : // eslint-disable-line eqeqeq + unmatchMessage = options.unmatchMessage != null ? options.unmatchMessage : 'It differs from first one.' + ' Hit only the Enter key if you want to retry from first one.'; - if (query == null) { query = 'Input new password: '; } // eslint-disable-line eqeqeq + if (query == null) { query = 'Input new password: '; } limitMessage = readOptions.limitMessage; while (!res2) { @@ -1067,7 +1074,7 @@ exports.questionPath = function(query, options) { /* eslint-enable key-spacing */ options = options || {}; - if (query == null) { query = 'Input path (you can "cd" and "pwd"): '; } // eslint-disable-line eqeqeq + if (query == null) { query = 'Input path (you can "cd" and "pwd"): '; } exports.question(query, readOptions); return validPath; @@ -1192,7 +1199,7 @@ exports.promptSimShell = function(options) { function _keyInYN(query, options, limit) { var res; - if (query == null) { query = 'Are you sure? '; } // eslint-disable-line eqeqeq + if (query == null) { query = 'Are you sure? '; } if ((!options || options.guide !== false) && (query += '')) { query = query.replace(/\s*:?\s*$/, '') + ' [y/n]: '; } @@ -1214,7 +1221,7 @@ exports.keyInYN = function(query, options) { return _keyInYN(query, options); }; exports.keyInYNStrict = function(query, options) { return _keyInYN(query, options, 'yn'); }; exports.keyInPause = function(query, options) { - if (query == null) { query = 'Continue...'; } // eslint-disable-line eqeqeq + if (query == null) { query = 'Continue...'; } if ((!options || options.guide !== false) && (query += '')) { query = query.replace(/\s+$/, '') + ' (Hit any key)'; } @@ -1267,13 +1274,13 @@ exports.keyInSelect = function(items, query, options) { keylist += '0'; key2i['0'] = -1; display += '[0] ' + - (options && options.cancel != null && typeof options.cancel !== 'boolean' ? // eslint-disable-line eqeqeq + (options && options.cancel != null && typeof options.cancel !== 'boolean' ? (options.cancel + '').trim() : 'CANCEL') + '\n'; } readOptions.limit = keylist; display += '\n'; - if (query == null) { query = 'Choose one from list: '; } // eslint-disable-line eqeqeq + if (query == null) { query = 'Choose one from list: '; } if ((query += '')) { if (!options || options.guide !== false) { query = query.replace(/\s*:?\s*$/, '') + ' [$]: '; @@ -1284,6 +1291,8 @@ exports.keyInSelect = function(items, query, options) { return key2i[exports.keyIn(display, readOptions).toLowerCase()]; }; +exports.getRawInput = function() { return rawInput; }; + // ======== DEPRECATED ======== function _setOption(optionName, args) { var options; diff --git a/package.json b/package.json index 6092eb2..0f66e91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "readline-sync", - "version": "1.4.4", + "version": "1.4.5", "title": "readlineSync", "description": "Synchronous Readline for interactively running to have a conversation with the user via a console(TTY).", "keywords": [