@@ -0,0 +1,1329 @@
1+/*
2+ * readlineSync
3+ * https://github.com/anseki/readline-sync
4+ *
5+ * Copyright (c) 2019 anseki
6+ * Licensed under the MIT license.
7+ */
8+
9+'use strict';
10+
11+var
12+ IS_WIN = process.platform === 'win32',
13+
14+ ALGORITHM_CIPHER = 'aes-256-cbc',
15+ ALGORITHM_HASH = 'sha256',
16+ DEFAULT_ERR_MSG = 'The current environment doesn\'t support interactive reading from TTY.',
17+
18+ fs = require('fs'),
19+ TTY = process.binding('tty_wrap').TTY,
20+ childProc = require('child_process'),
21+ pathUtil = require('path'),
22+
23+ defaultOptions = {
24+ /* eslint-disable key-spacing */
25+ prompt: '> ',
26+ hideEchoBack: false,
27+ mask: '*',
28+ limit: [],
29+ limitMessage: 'Input another, please.$<( [)limit(])>',
30+ defaultInput: '',
31+ trueValue: [],
32+ falseValue: [],
33+ caseSensitive: false,
34+ keepWhitespace: false,
35+ encoding: 'utf8',
36+ bufferSize: 1024,
37+ print: void 0,
38+ history: true,
39+ cd: false,
40+ phContent: void 0,
41+ preCheck: void 0
42+ /* eslint-enable key-spacing */
43+ },
44+
45+ fdR = 'none',
46+ isRawMode = false,
47+ salt = 0,
48+ lastInput = '',
49+ inputHistory = [],
50+ _DBG_useExt = false,
51+ _DBG_checkOptions = false,
52+ _DBG_checkMethod = false,
53+ fdW, ttyR, extHostPath, extHostArgs, tempdir, rawInput;
54+
55+function getHostArgs(options) {
56+ // Send any text to crazy Windows shell safely.
57+ function encodeArg(arg) {
58+ return arg.replace(/[^\w\u0080-\uFFFF]/g, function(chr) {
59+ return '#' + chr.charCodeAt(0) + ';';
60+ });
61+ }
62+
63+ return extHostArgs.concat((function(conf) {
64+ var args = [];
65+ Object.keys(conf).forEach(function(optionName) {
66+ if (conf[optionName] === 'boolean') {
67+ if (options[optionName]) { args.push('--' + optionName); }
68+ } else if (conf[optionName] === 'string') {
69+ if (options[optionName]) {
70+ args.push('--' + optionName, encodeArg(options[optionName]));
71+ }
72+ }
73+ });
74+ return args;
75+ })({
76+ /* eslint-disable key-spacing */
77+ display: 'string',
78+ displayOnly: 'boolean',
79+ keyIn: 'boolean',
80+ hideEchoBack: 'boolean',
81+ mask: 'string',
82+ limit: 'string',
83+ caseSensitive: 'boolean'
84+ /* eslint-enable key-spacing */
85+ }));
86+}
87+
88+// piping via files (for Node.js v0.10-)
89+function _execFileSync(options, execOptions) {
90+
91+ function getTempfile(name) {
92+ var suffix = '',
93+ filepath, fd;
94+ tempdir = tempdir || require('os').tmpdir();
95+
96+ while (true) {
97+ filepath = pathUtil.join(tempdir, name + suffix);
98+ try {
99+ fd = fs.openSync(filepath, 'wx');
100+ } catch (e) {
101+ if (e.code === 'EEXIST') {
102+ suffix++;
103+ continue;
104+ } else {
105+ throw e;
106+ }
107+ }
108+ fs.closeSync(fd);
109+ break;
110+ }
111+ return filepath;
112+ }
113+
114+ var res = {},
115+ pathStdout = getTempfile('readline-sync.stdout'),
116+ pathStderr = getTempfile('readline-sync.stderr'),
117+ pathExit = getTempfile('readline-sync.exit'),
118+ pathDone = getTempfile('readline-sync.done'),
119+ crypto = require('crypto'),
120+ hostArgs, shellPath, shellArgs, exitCode, extMessage, shasum, decipher, password;
121+
122+ shasum = crypto.createHash(ALGORITHM_HASH);
123+ shasum.update('' + process.pid + (salt++) + Math.random());
124+ password = shasum.digest('hex');
125+ decipher = crypto.createDecipher(ALGORITHM_CIPHER, password);
126+
127+ hostArgs = getHostArgs(options);
128+ if (IS_WIN) {
129+ shellPath = process.env.ComSpec || 'cmd.exe';
130+ process.env.Q = '"'; // The quote (") that isn't escaped.
131+ // `()` for ignore space by echo
132+ shellArgs = ['/V:ON', '/S', '/C',
133+ '(%Q%' + shellPath + '%Q% /V:ON /S /C %Q%' + /* ESLint bug? */ // eslint-disable-line no-path-concat
134+ '%Q%' + extHostPath + '%Q%' +
135+ hostArgs.map(function(arg) { return ' %Q%' + arg + '%Q%'; }).join('') +
136+ ' & (echo !ERRORLEVEL!)>%Q%' + pathExit + '%Q%%Q%) 2>%Q%' + pathStderr + '%Q%' +
137+ ' |%Q%' + process.execPath + '%Q% %Q%' + __dirname + '\\encrypt.js%Q%' +
138+ ' %Q%' + ALGORITHM_CIPHER + '%Q% %Q%' + password + '%Q%' +
139+ ' >%Q%' + pathStdout + '%Q%' +
140+ ' & (echo 1)>%Q%' + pathDone + '%Q%'];
141+ } else {
142+ shellPath = '/bin/sh';
143+ shellArgs = ['-c',
144+ // Use `()`, not `{}` for `-c` (text param)
145+ '("' + extHostPath + '"' + /* ESLint bug? */ // eslint-disable-line no-path-concat
146+ hostArgs.map(function(arg) { return " '" + arg.replace(/'/g, "'\\''") + "'"; }).join('') +
147+ '; echo $?>"' + pathExit + '") 2>"' + pathStderr + '"' +
148+ ' |"' + process.execPath + '" "' + __dirname + '/encrypt.js"' +
149+ ' "' + ALGORITHM_CIPHER + '" "' + password + '"' +
150+ ' >"' + pathStdout + '"' +
151+ '; echo 1 >"' + pathDone + '"'];
152+ }
153+ if (_DBG_checkMethod) { _DBG_checkMethod('_execFileSync', hostArgs); }
154+ try {
155+ childProc.spawn(shellPath, shellArgs, execOptions);
156+ } catch (e) {
157+ res.error = new Error(e.message);
158+ res.error.method = '_execFileSync - spawn';
159+ res.error.program = shellPath;
160+ res.error.args = shellArgs;
161+ }
162+
163+ while (fs.readFileSync(pathDone, {encoding: options.encoding}).trim() !== '1') {} // eslint-disable-line no-empty
164+ if ((exitCode =
165+ fs.readFileSync(pathExit, {encoding: options.encoding}).trim()) === '0') {
166+ res.input =
167+ decipher.update(fs.readFileSync(pathStdout, {encoding: 'binary'}),
168+ 'hex', options.encoding) +
169+ decipher.final(options.encoding);
170+ } else {
171+ extMessage = fs.readFileSync(pathStderr, {encoding: options.encoding}).trim();
172+ res.error = new Error(DEFAULT_ERR_MSG + (extMessage ? '\n' + extMessage : ''));
173+ res.error.method = '_execFileSync';
174+ res.error.program = shellPath;
175+ res.error.args = shellArgs;
176+ res.error.extMessage = extMessage;
177+ res.error.exitCode = +exitCode;
178+ }
179+
180+ fs.unlinkSync(pathStdout);
181+ fs.unlinkSync(pathStderr);
182+ fs.unlinkSync(pathExit);
183+ fs.unlinkSync(pathDone);
184+
185+ return res;
186+}
187+
188+function readlineExt(options) {
189+ var res = {},
190+ execOptions = {env: process.env, encoding: options.encoding},
191+ hostArgs, extMessage;
192+
193+ if (!extHostPath) {
194+ if (IS_WIN) {
195+ if (process.env.PSModulePath) { // Windows PowerShell
196+ extHostPath = 'powershell.exe';
197+ extHostArgs = ['-ExecutionPolicy', 'Bypass',
198+ '-File', __dirname + '\\read.ps1']; // eslint-disable-line no-path-concat
199+ } else { // Windows Script Host
200+ extHostPath = 'cscript.exe';
201+ extHostArgs = ['//nologo', __dirname + '\\read.cs.js']; // eslint-disable-line no-path-concat
202+ }
203+ } else {
204+ extHostPath = '/bin/sh';
205+ extHostArgs = [__dirname + '/read.sh']; // eslint-disable-line no-path-concat
206+ }
207+ }
208+ if (IS_WIN && !process.env.PSModulePath) { // Windows Script Host
209+ // ScriptPW (Win XP and Server2003) needs TTY stream as STDIN.
210+ // In this case, If STDIN isn't TTY, an error is thrown.
211+ execOptions.stdio = [process.stdin];
212+ }
213+
214+ if (childProc.execFileSync) {
215+ hostArgs = getHostArgs(options);
216+ if (_DBG_checkMethod) { _DBG_checkMethod('execFileSync', hostArgs); }
217+ try {
218+ res.input = childProc.execFileSync(extHostPath, hostArgs, execOptions);
219+ } catch (e) { // non-zero exit code
220+ extMessage = e.stderr ? (e.stderr + '').trim() : '';
221+ res.error = new Error(DEFAULT_ERR_MSG + (extMessage ? '\n' + extMessage : ''));
222+ res.error.method = 'execFileSync';
223+ res.error.program = extHostPath;
224+ res.error.args = hostArgs;
225+ res.error.extMessage = extMessage;
226+ res.error.exitCode = e.status;
227+ res.error.code = e.code;
228+ res.error.signal = e.signal;
229+ }
230+ } else {
231+ res = _execFileSync(options, execOptions);
232+ }
233+ if (!res.error) {
234+ res.input = res.input.replace(/^\s*'|'\s*$/g, '');
235+ options.display = '';
236+ }
237+
238+ return res;
239+}
240+
241+/*
242+ display: string
243+ displayOnly: boolean
244+ keyIn: boolean
245+ hideEchoBack: boolean
246+ mask: string
247+ limit: string (pattern)
248+ caseSensitive: boolean
249+ keepWhitespace: boolean
250+ encoding, bufferSize, print
251+*/
252+function _readlineSync(options) {
253+ var input = '',
254+ displaySave = options.display,
255+ silent = !options.display && options.keyIn && options.hideEchoBack && !options.mask;
256+
257+ function tryExt() {
258+ var res = readlineExt(options);
259+ if (res.error) { throw res.error; }
260+ return res.input;
261+ }
262+
263+ if (_DBG_checkOptions) { _DBG_checkOptions(options); }
264+
265+ (function() { // open TTY
266+ var fsB, constants, verNum;
267+
268+ function getFsB() {
269+ if (!fsB) {
270+ fsB = process.binding('fs'); // For raw device path
271+ constants = process.binding('constants');
272+ // for v6.3.0+
273+ constants = constants && constants.fs && typeof constants.fs.O_RDWR === 'number'
274+ ? constants.fs : constants;
275+ }
276+ return fsB;
277+ }
278+
279+ if (typeof fdR !== 'string') { return; }
280+ fdR = null;
281+
282+ if (IS_WIN) {
283+ // iojs-v2.3.2+ input stream can't read first line. (#18)
284+ // ** Don't get process.stdin before check! **
285+ // Fixed v5.1.0
286+ // Fixed v4.2.4
287+ // It regressed again in v5.6.0, it is fixed in v6.2.0.
288+ verNum = (function(ver) { // getVerNum
289+ var nums = ver.replace(/^\D+/, '').split('.');
290+ var verNum = 0;
291+ if ((nums[0] = +nums[0])) { verNum += nums[0] * 10000; }
292+ if ((nums[1] = +nums[1])) { verNum += nums[1] * 100; }
293+ if ((nums[2] = +nums[2])) { verNum += nums[2]; }
294+ return verNum;
295+ })(process.version);
296+ if (!(verNum >= 20302 && verNum < 40204 || verNum >= 50000 && verNum < 50100 || verNum >= 50600 && verNum < 60200) &&
297+ process.stdin.isTTY) {
298+ process.stdin.pause();
299+ fdR = process.stdin.fd;
300+ ttyR = process.stdin._handle;
301+ } else {
302+ try {
303+ // The stream by fs.openSync('\\\\.\\CON', 'r') can't switch to raw mode.
304+ // 'CONIN$' might fail on XP, 2000, 7 (x86).
305+ fdR = getFsB().open('CONIN$', constants.O_RDWR, parseInt('0666', 8));
306+ ttyR = new TTY(fdR, true);
307+ } catch (e) { /* ignore */ }
308+ }
309+
310+ if (process.stdout.isTTY) {
311+ fdW = process.stdout.fd;
312+ } else {
313+ try {
314+ fdW = fs.openSync('\\\\.\\CON', 'w');
315+ } catch (e) { /* ignore */ }
316+ if (typeof fdW !== 'number') { // Retry
317+ try {
318+ fdW = getFsB().open('CONOUT$', constants.O_RDWR, parseInt('0666', 8));
319+ } catch (e) { /* ignore */ }
320+ }
321+ }
322+
323+ } else {
324+ if (process.stdin.isTTY) {
325+ process.stdin.pause();
326+ try {
327+ fdR = fs.openSync('/dev/tty', 'r'); // device file, not process.stdin
328+ ttyR = process.stdin._handle;
329+ } catch (e) { /* ignore */ }
330+ } else {
331+ // Node.js v0.12 read() fails.
332+ try {
333+ fdR = fs.openSync('/dev/tty', 'r');
334+ ttyR = new TTY(fdR, false);
335+ } catch (e) { /* ignore */ }
336+ }
337+
338+ if (process.stdout.isTTY) {
339+ fdW = process.stdout.fd;
340+ } else {
341+ try {
342+ fdW = fs.openSync('/dev/tty', 'w');
343+ } catch (e) { /* ignore */ }
344+ }
345+ }
346+ })();
347+
348+ (function() { // try read
349+ var isCooked = !options.hideEchoBack && !options.keyIn,
350+ atEol, limit, buffer, reqSize, readSize, chunk, line;
351+ rawInput = '';
352+
353+ // Node.js v0.10- returns an error if same mode is set.
354+ function setRawMode(mode) {
355+ if (mode === isRawMode) { return true; }
356+ if (ttyR.setRawMode(mode) !== 0) { return false; }
357+ isRawMode = mode;
358+ return true;
359+ }
360+
361+ if (_DBG_useExt || !ttyR ||
362+ typeof fdW !== 'number' && (options.display || !isCooked)) {
363+ input = tryExt();
364+ return;
365+ }
366+
367+ if (options.display) {
368+ fs.writeSync(fdW, options.display);
369+ options.display = '';
370+ }
371+ if (options.displayOnly) { return; }
372+
373+ if (!setRawMode(!isCooked)) {
374+ input = tryExt();
375+ return;
376+ }
377+
378+ reqSize = options.keyIn ? 1 : options.bufferSize;
379+ // Check `allocUnsafe` to make sure of the new API.
380+ buffer = Buffer.allocUnsafe && Buffer.alloc ? Buffer.alloc(reqSize) : new Buffer(reqSize);
381+
382+ if (options.keyIn && options.limit) {
383+ limit = new RegExp('[^' + options.limit + ']',
384+ 'g' + (options.caseSensitive ? '' : 'i'));
385+ }
386+
387+ while (true) {
388+ readSize = 0;
389+ try {
390+ readSize = fs.readSync(fdR, buffer, 0, reqSize);
391+ } catch (e) {
392+ if (e.code !== 'EOF') {
393+ setRawMode(false);
394+ input += tryExt();
395+ return;
396+ }
397+ }
398+ if (readSize > 0) {
399+ chunk = buffer.toString(options.encoding, 0, readSize);
400+ rawInput += chunk;
401+ } else {
402+ chunk = '\n';
403+ rawInput += String.fromCharCode(0);
404+ }
405+
406+ if (chunk && typeof (line = (chunk.match(/^(.*?)[\r\n]/) || [])[1]) === 'string') {
407+ chunk = line;
408+ atEol = true;
409+ }
410+
411+ // other ctrl-chars
412+ // eslint-disable-next-line no-control-regex
413+ if (chunk) { chunk = chunk.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ''); }
414+ if (chunk && limit) { chunk = chunk.replace(limit, ''); }
415+
416+ if (chunk) {
417+ if (!isCooked) {
418+ if (!options.hideEchoBack) {
419+ fs.writeSync(fdW, chunk);
420+ } else if (options.mask) {
421+ fs.writeSync(fdW, (new Array(chunk.length + 1)).join(options.mask));
422+ }
423+ }
424+ input += chunk;
425+ }
426+
427+ if (!options.keyIn && atEol ||
428+ options.keyIn && input.length >= reqSize) { break; }
429+ }
430+
431+ if (!isCooked && !silent) { fs.writeSync(fdW, '\n'); }
432+ setRawMode(false);
433+ })();
434+
435+ if (options.print && !silent) {
436+ options.print(
437+ displaySave + (
438+ options.displayOnly ? '' : (
439+ options.hideEchoBack ? (new Array(input.length + 1)).join(options.mask) : input
440+ ) + '\n' // must at least write '\n'
441+ ),
442+ options.encoding);
443+ }
444+
445+ return options.displayOnly ? '' :
446+ (lastInput = options.keepWhitespace || options.keyIn ? input : input.trim());
447+}
448+
449+function flattenArray(array, validator) {
450+ var flatArray = [];
451+ function _flattenArray(array) {
452+ if (array == null) { return; }
453+ if (Array.isArray(array)) {
454+ array.forEach(_flattenArray);
455+ } else if (!validator || validator(array)) {
456+ flatArray.push(array);
457+ }
458+ }
459+ _flattenArray(array);
460+ return flatArray;
461+}
462+
463+function escapePattern(pattern) {
464+ return pattern.replace(/[\x00-\x7f]/g, // eslint-disable-line no-control-regex
465+ function(s) { return '\\x' + ('00' + s.charCodeAt().toString(16)).substr(-2); });
466+}
467+
468+// margeOptions(options1, options2 ... )
469+// margeOptions(true, options1, options2 ... )
470+// arg1=true : Start from defaultOptions and pick elements of that.
471+function margeOptions() {
472+ var optionsList = Array.prototype.slice.call(arguments),
473+ optionNames, fromDefault;
474+
475+ if (optionsList.length && typeof optionsList[0] === 'boolean') {
476+ fromDefault = optionsList.shift();
477+ if (fromDefault) {
478+ optionNames = Object.keys(defaultOptions);
479+ optionsList.unshift(defaultOptions);
480+ }
481+ }
482+
483+ return optionsList.reduce(function(options, optionsPart) {
484+ if (optionsPart == null) { return options; }
485+
486+ // ======== DEPRECATED ========
487+ if (optionsPart.hasOwnProperty('noEchoBack') &&
488+ !optionsPart.hasOwnProperty('hideEchoBack')) {
489+ optionsPart.hideEchoBack = optionsPart.noEchoBack;
490+ delete optionsPart.noEchoBack;
491+ }
492+ if (optionsPart.hasOwnProperty('noTrim') &&
493+ !optionsPart.hasOwnProperty('keepWhitespace')) {
494+ optionsPart.keepWhitespace = optionsPart.noTrim;
495+ delete optionsPart.noTrim;
496+ }
497+ // ======== /DEPRECATED ========
498+
499+ if (!fromDefault) { optionNames = Object.keys(optionsPart); }
500+ optionNames.forEach(function(optionName) {
501+ var value;
502+ if (!optionsPart.hasOwnProperty(optionName)) { return; }
503+ value = optionsPart[optionName];
504+ /* eslint-disable no-multi-spaces */
505+ switch (optionName) {
506+ // _readlineSync <- * * -> defaultOptions
507+ // ================ string
508+ case 'mask': // * *
509+ case 'limitMessage': // *
510+ case 'defaultInput': // *
511+ case 'encoding': // * *
512+ value = value != null ? value + '' : '';
513+ if (value && optionName !== 'limitMessage') { value = value.replace(/[\r\n]/g, ''); }
514+ options[optionName] = value;
515+ break;
516+ // ================ number(int)
517+ case 'bufferSize': // * *
518+ if (!isNaN(value = parseInt(value, 10)) && typeof value === 'number') {
519+ options[optionName] = value; // limited updating (number is needed)
520+ }
521+ break;
522+ // ================ boolean
523+ case 'displayOnly': // *
524+ case 'keyIn': // *
525+ case 'hideEchoBack': // * *
526+ case 'caseSensitive': // * *
527+ case 'keepWhitespace': // * *
528+ case 'history': // *
529+ case 'cd': // *
530+ options[optionName] = !!value;
531+ break;
532+ // ================ array
533+ case 'limit': // * * to string for readlineExt
534+ case 'trueValue': // *
535+ case 'falseValue': // *
536+ options[optionName] = flattenArray(value, function(value) {
537+ var type = typeof value;
538+ return type === 'string' || type === 'number' ||
539+ type === 'function' || value instanceof RegExp;
540+ }).map(function(value) {
541+ return typeof value === 'string' ? value.replace(/[\r\n]/g, '') : value;
542+ });
543+ break;
544+ // ================ function
545+ case 'print': // * *
546+ case 'phContent': // *
547+ case 'preCheck': // *
548+ options[optionName] = typeof value === 'function' ? value : void 0;
549+ break;
550+ // ================ other
551+ case 'prompt': // *
552+ case 'display': // *
553+ options[optionName] = value != null ? value : '';
554+ break;
555+ // no default
556+ }
557+ /* eslint-enable no-multi-spaces */
558+ });
559+ return options;
560+ }, {});
561+}
562+
563+function isMatched(res, comps, caseSensitive) {
564+ return comps.some(function(comp) {
565+ var type = typeof comp;
566+ return type === 'string'
567+ ? (caseSensitive ? res === comp : res.toLowerCase() === comp.toLowerCase()) :
568+ type === 'number' ? parseFloat(res) === comp :
569+ type === 'function' ? comp(res) :
570+ comp instanceof RegExp ? comp.test(res) : false;
571+ });
572+}
573+
574+function replaceHomePath(path, expand) {
575+ var homePath = pathUtil.normalize(
576+ IS_WIN ? (process.env.HOMEDRIVE || '') + (process.env.HOMEPATH || '') :
577+ process.env.HOME || '').replace(/[/\\]+$/, '');
578+ path = pathUtil.normalize(path);
579+ return expand ? path.replace(/^~(?=\/|\\|$)/, homePath) :
580+ path.replace(new RegExp('^' + escapePattern(homePath) +
581+ '(?=\\/|\\\\|$)', IS_WIN ? 'i' : ''), '~');
582+}
583+
584+function replacePlaceholder(text, generator) {
585+ var PTN_INNER = '(?:\\(([\\s\\S]*?)\\))?(\\w+|.-.)(?:\\(([\\s\\S]*?)\\))?',
586+ rePlaceholder = new RegExp('(\\$)?(\\$<' + PTN_INNER + '>)', 'g'),
587+ rePlaceholderCompat = new RegExp('(\\$)?(\\$\\{' + PTN_INNER + '\\})', 'g');
588+
589+ function getPlaceholderText(s, escape, placeholder, pre, param, post) {
590+ var text;
591+ return escape || typeof (text = generator(param)) !== 'string' ? placeholder :
592+ text ? (pre || '') + text + (post || '') : '';
593+ }
594+
595+ return text.replace(rePlaceholder, getPlaceholderText)
596+ .replace(rePlaceholderCompat, getPlaceholderText);
597+}
598+
599+function array2charlist(array, caseSensitive, collectSymbols) {
600+ var group = [],
601+ groupClass = -1,
602+ charCode = 0,
603+ symbols = '',
604+ values, suppressed;
605+ function addGroup(groups, group) {
606+ if (group.length > 3) { // ellipsis
607+ groups.push(group[0] + '...' + group[group.length - 1]);
608+ suppressed = true;
609+ } else if (group.length) {
610+ groups = groups.concat(group);
611+ }
612+ return groups;
613+ }
614+
615+ values = array.reduce(function(chars, value) {
616+ return chars.concat((value + '').split(''));
617+ }, []).reduce(function(groups, curChar) {
618+ var curGroupClass, curCharCode;
619+ if (!caseSensitive) { curChar = curChar.toLowerCase(); }
620+ curGroupClass = /^\d$/.test(curChar) ? 1 :
621+ /^[A-Z]$/.test(curChar) ? 2 : /^[a-z]$/.test(curChar) ? 3 : 0;
622+ if (collectSymbols && curGroupClass === 0) {
623+ symbols += curChar;
624+ } else {
625+ curCharCode = curChar.charCodeAt(0);
626+ if (curGroupClass && curGroupClass === groupClass &&
627+ curCharCode === charCode + 1) {
628+ group.push(curChar);
629+ } else {
630+ groups = addGroup(groups, group);
631+ group = [curChar];
632+ groupClass = curGroupClass;
633+ }
634+ charCode = curCharCode;
635+ }
636+ return groups;
637+ }, []);
638+ values = addGroup(values, group); // last group
639+ if (symbols) { values.push(symbols); suppressed = true; }
640+ return {values: values, suppressed: suppressed};
641+}
642+
643+function joinChunks(chunks, suppressed) {
644+ return chunks.join(chunks.length > 2 ? ', ' : suppressed ? ' / ' : '/');
645+}
646+
647+function getPhContent(param, options) {
648+ var resCharlist = {},
649+ text, values, arg;
650+ if (options.phContent) {
651+ text = options.phContent(param, options);
652+ }
653+ if (typeof text !== 'string') {
654+ switch (param) {
655+ case 'hideEchoBack':
656+ case 'mask':
657+ case 'defaultInput':
658+ case 'caseSensitive':
659+ case 'keepWhitespace':
660+ case 'encoding':
661+ case 'bufferSize':
662+ case 'history':
663+ case 'cd':
664+ text = !options.hasOwnProperty(param) ? '' :
665+ typeof options[param] === 'boolean' ? (options[param] ? 'on' : 'off') :
666+ options[param] + '';
667+ break;
668+ // case 'prompt':
669+ // case 'query':
670+ // case 'display':
671+ // text = options.hasOwnProperty('displaySrc') ? options.displaySrc + '' : '';
672+ // break;
673+ case 'limit':
674+ case 'trueValue':
675+ case 'falseValue':
676+ values = options[options.hasOwnProperty(param + 'Src') ? param + 'Src' : param];
677+ if (options.keyIn) { // suppress
678+ resCharlist = array2charlist(values, options.caseSensitive);
679+ values = resCharlist.values;
680+ } else {
681+ values = values.filter(function(value) {
682+ var type = typeof value;
683+ return type === 'string' || type === 'number';
684+ });
685+ }
686+ text = joinChunks(values, resCharlist.suppressed);
687+ break;
688+ case 'limitCount':
689+ case 'limitCountNotZero':
690+ text = options[options.hasOwnProperty('limitSrc') ? 'limitSrc' : 'limit'].length;
691+ text = text || param !== 'limitCountNotZero' ? text + '' : '';
692+ break;
693+ case 'lastInput':
694+ text = lastInput;
695+ break;
696+ case 'cwd':
697+ case 'CWD':
698+ case 'cwdHome':
699+ text = process.cwd();
700+ if (param === 'CWD') {
701+ text = pathUtil.basename(text);
702+ } else if (param === 'cwdHome') {
703+ text = replaceHomePath(text);
704+ }
705+ break;
706+ case 'date':
707+ case 'time':
708+ case 'localeDate':
709+ case 'localeTime':
710+ text = (new Date())['to' +
711+ param.replace(/^./, function(str) { return str.toUpperCase(); }) +
712+ 'String']();
713+ break;
714+ default: // with arg
715+ if (typeof (arg = (param.match(/^history_m(\d+)$/) || [])[1]) === 'string') {
716+ text = inputHistory[inputHistory.length - arg] || '';
717+ }
718+ }
719+ }
720+ return text;
721+}
722+
723+function getPhCharlist(param) {
724+ var matches = /^(.)-(.)$/.exec(param),
725+ text = '',
726+ from, to, code, step;
727+ if (!matches) { return null; }
728+ from = matches[1].charCodeAt(0);
729+ to = matches[2].charCodeAt(0);
730+ step = from < to ? 1 : -1;
731+ for (code = from; code !== to + step; code += step) { text += String.fromCharCode(code); }
732+ return text;
733+}
734+
735+// cmd "arg" " a r g " "" 'a"r"g' "a""rg" "arg
736+function parseCl(cl) {
737+ var reToken = new RegExp(/(\s*)(?:("|')(.*?)(?:\2|$)|(\S+))/g),
738+ taken = '',
739+ args = [],
740+ matches, part;
741+ cl = cl.trim();
742+ while ((matches = reToken.exec(cl))) {
743+ part = matches[3] || matches[4] || '';
744+ if (matches[1]) {
745+ args.push(taken);
746+ taken = '';
747+ }
748+ taken += part;
749+ }
750+ if (taken) { args.push(taken); }
751+ return args;
752+}
753+
754+function toBool(res, options) {
755+ return (
756+ (options.trueValue.length &&
757+ isMatched(res, options.trueValue, options.caseSensitive)) ? true :
758+ (options.falseValue.length &&
759+ isMatched(res, options.falseValue, options.caseSensitive)) ? false : res);
760+}
761+
762+function getValidLine(options) {
763+ var res, forceNext, limitMessage,
764+ matches, histInput, args, resCheck;
765+
766+ function _getPhContent(param) { return getPhContent(param, options); }
767+ function addDisplay(text) { options.display += (/[^\r\n]$/.test(options.display) ? '\n' : '') + text; }
768+
769+ options.limitSrc = options.limit;
770+ options.displaySrc = options.display;
771+ options.limit = ''; // for readlineExt
772+ options.display = replacePlaceholder(options.display + '', _getPhContent);
773+
774+ while (true) {
775+ res = _readlineSync(options);
776+ forceNext = false;
777+ limitMessage = '';
778+
779+ if (options.defaultInput && !res) { res = options.defaultInput; }
780+
781+ if (options.history) {
782+ if ((matches = /^\s*!(?:!|-1)(:p)?\s*$/.exec(res))) { // `!!` `!-1` +`:p`
783+ histInput = inputHistory[0] || '';
784+ if (matches[1]) { // only display
785+ forceNext = true;
786+ } else { // replace input
787+ res = histInput;
788+ }
789+ // Show it even if it is empty (NL only).
790+ addDisplay(histInput + '\n');
791+ if (!forceNext) { // Loop may break
792+ options.displayOnly = true;
793+ _readlineSync(options);
794+ options.displayOnly = false;
795+ }
796+ } else if (res && res !== inputHistory[inputHistory.length - 1]) {
797+ inputHistory = [res];
798+ }
799+ }
800+
801+ if (!forceNext && options.cd && res) {
802+ args = parseCl(res);
803+ switch (args[0].toLowerCase()) {
804+ case 'cd':
805+ if (args[1]) {
806+ try {
807+ process.chdir(replaceHomePath(args[1], true));
808+ } catch (e) {
809+ addDisplay(e + '');
810+ }
811+ }
812+ forceNext = true;
813+ break;
814+ case 'pwd':
815+ addDisplay(process.cwd());
816+ forceNext = true;
817+ break;
818+ // no default
819+ }
820+ }
821+
822+ if (!forceNext && options.preCheck) {
823+ resCheck = options.preCheck(res, options);
824+ res = resCheck.res;
825+ if (resCheck.forceNext) { forceNext = true; } // Don't switch to false.
826+ }
827+
828+ if (!forceNext) {
829+ if (!options.limitSrc.length ||
830+ isMatched(res, options.limitSrc, options.caseSensitive)) { break; }
831+ if (options.limitMessage) {
832+ limitMessage = replacePlaceholder(options.limitMessage, _getPhContent);
833+ }
834+ }
835+
836+ addDisplay((limitMessage ? limitMessage + '\n' : '') +
837+ replacePlaceholder(options.displaySrc + '', _getPhContent));
838+ }
839+ return toBool(res, options);
840+}
841+
842+// for dev
843+exports._DBG_set_useExt = function(val) { _DBG_useExt = val; };
844+exports._DBG_set_checkOptions = function(val) { _DBG_checkOptions = val; };
845+exports._DBG_set_checkMethod = function(val) { _DBG_checkMethod = val; };
846+exports._DBG_clearHistory = function() { lastInput = ''; inputHistory = []; };
847+
848+// ------------------------------------
849+
850+exports.setDefaultOptions = function(options) {
851+ defaultOptions = margeOptions(true, options);
852+ return margeOptions(true); // copy
853+};
854+
855+exports.question = function(query, options) {
856+ /* eslint-disable key-spacing */
857+ return getValidLine(margeOptions(margeOptions(true, options), {
858+ display: query
859+ }));
860+ /* eslint-enable key-spacing */
861+};
862+
863+exports.prompt = function(options) {
864+ var readOptions = margeOptions(true, options);
865+ readOptions.display = readOptions.prompt;
866+ return getValidLine(readOptions);
867+};
868+
869+exports.keyIn = function(query, options) {
870+ /* eslint-disable key-spacing */
871+ var readOptions = margeOptions(margeOptions(true, options), {
872+ display: query,
873+ keyIn: true,
874+ keepWhitespace: true
875+ });
876+ /* eslint-enable key-spacing */
877+
878+ // char list
879+ readOptions.limitSrc = readOptions.limit.filter(function(value) {
880+ var type = typeof value;
881+ return type === 'string' || type === 'number';
882+ }).map(function(text) {
883+ return replacePlaceholder(text + '', getPhCharlist);
884+ });
885+ // pattern
886+ readOptions.limit = escapePattern(readOptions.limitSrc.join(''));
887+
888+ ['trueValue', 'falseValue'].forEach(function(optionName) {
889+ readOptions[optionName] = readOptions[optionName].reduce(function(comps, comp) {
890+ var type = typeof comp;
891+ if (type === 'string' || type === 'number') {
892+ comps = comps.concat((comp + '').split(''));
893+ } else { comps.push(comp); }
894+ return comps;
895+ }, []);
896+ });
897+
898+ readOptions.display = replacePlaceholder(readOptions.display + '',
899+ function(param) { return getPhContent(param, readOptions); });
900+
901+ return toBool(_readlineSync(readOptions), readOptions);
902+};
903+
904+// ------------------------------------
905+
906+exports.questionEMail = function(query, options) {
907+ if (query == null) { query = 'Input e-mail address: '; }
908+ /* eslint-disable key-spacing */
909+ return exports.question(query, margeOptions({
910+ // -------- default
911+ hideEchoBack: false,
912+ // http://www.w3.org/TR/html5/forms.html#valid-e-mail-address
913+ limit: /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,
914+ limitMessage: 'Input valid e-mail address, please.',
915+ trueValue: null,
916+ falseValue: null
917+ }, options, {
918+ // -------- forced
919+ keepWhitespace: false,
920+ cd: false
921+ }));
922+ /* eslint-enable key-spacing */
923+};
924+
925+exports.questionNewPassword = function(query, options) {
926+ /* eslint-disable key-spacing */
927+ var resCharlist, min, max,
928+ readOptions = margeOptions({
929+ // -------- default
930+ hideEchoBack: true,
931+ mask: '*',
932+ limitMessage: 'It can include: $<charlist>\n' +
933+ 'And the length must be: $<length>',
934+ trueValue: null,
935+ falseValue: null,
936+ caseSensitive: true
937+ }, options, {
938+ // -------- forced
939+ history: false,
940+ cd: false,
941+ // limit (by charlist etc.),
942+ phContent: function(param) {
943+ return param === 'charlist' ? resCharlist.text :
944+ param === 'length' ? min + '...' + max : null;
945+ }
946+ }),
947+ // added: charlist, min, max, confirmMessage, unmatchMessage
948+ charlist, confirmMessage, unmatchMessage,
949+ limit, limitMessage, res1, res2;
950+ /* eslint-enable key-spacing */
951+ options = options || {};
952+
953+ charlist = replacePlaceholder(
954+ options.charlist ? options.charlist + '' : '$<!-~>', getPhCharlist);
955+ if (isNaN(min = parseInt(options.min, 10)) || typeof min !== 'number') { min = 12; }
956+ if (isNaN(max = parseInt(options.max, 10)) || typeof max !== 'number') { max = 24; }
957+ limit = new RegExp('^[' + escapePattern(charlist) +
958+ ']{' + min + ',' + max + '}$');
959+ resCharlist = array2charlist([charlist], readOptions.caseSensitive, true);
960+ resCharlist.text = joinChunks(resCharlist.values, resCharlist.suppressed);
961+
962+ confirmMessage = options.confirmMessage != null ? options.confirmMessage :
963+ 'Reinput a same one to confirm it: ';
964+ unmatchMessage = options.unmatchMessage != null ? options.unmatchMessage :
965+ 'It differs from first one.' +
966+ ' Hit only the Enter key if you want to retry from first one.';
967+
968+ if (query == null) { query = 'Input new password: '; }
969+
970+ limitMessage = readOptions.limitMessage;
971+ while (!res2) {
972+ readOptions.limit = limit;
973+ readOptions.limitMessage = limitMessage;
974+ res1 = exports.question(query, readOptions);
975+
976+ readOptions.limit = [res1, ''];
977+ readOptions.limitMessage = unmatchMessage;
978+ res2 = exports.question(confirmMessage, readOptions);
979+ }
980+
981+ return res1;
982+};
983+
984+function _questionNum(query, options, parser) {
985+ var validValue;
986+ function getValidValue(value) {
987+ validValue = parser(value);
988+ return !isNaN(validValue) && typeof validValue === 'number';
989+ }
990+ /* eslint-disable key-spacing */
991+ exports.question(query, margeOptions({
992+ // -------- default
993+ limitMessage: 'Input valid number, please.'
994+ }, options, {
995+ // -------- forced
996+ limit: getValidValue,
997+ cd: false
998+ // trueValue, falseValue, caseSensitive, keepWhitespace don't work.
999+ }));
1000+ /* eslint-enable key-spacing */
1001+ return validValue;
1002+}
1003+exports.questionInt = function(query, options) {
1004+ return _questionNum(query, options, function(value) { return parseInt(value, 10); });
1005+};
1006+exports.questionFloat = function(query, options) {
1007+ return _questionNum(query, options, parseFloat);
1008+};
1009+
1010+exports.questionPath = function(query, options) {
1011+ /* eslint-disable key-spacing */
1012+ var error = '',
1013+ validPath, // before readOptions
1014+ readOptions = margeOptions({
1015+ // -------- default
1016+ hideEchoBack: false,
1017+ limitMessage: '$<error(\n)>Input valid path, please.' +
1018+ '$<( Min:)min>$<( Max:)max>',
1019+ history: true,
1020+ cd: true
1021+ }, options, {
1022+ // -------- forced
1023+ keepWhitespace: false,
1024+ limit: function(value) {
1025+ var exists, stat, res;
1026+ value = replaceHomePath(value, true);
1027+ error = ''; // for validate
1028+ // mkdir -p
1029+ function mkdirParents(dirPath) {
1030+ dirPath.split(/\/|\\/).reduce(function(parents, dir) {
1031+ var path = pathUtil.resolve((parents += dir + pathUtil.sep));
1032+ if (!fs.existsSync(path)) {
1033+ fs.mkdirSync(path);
1034+ } else if (!fs.statSync(path).isDirectory()) {
1035+ throw new Error('Non directory already exists: ' + path);
1036+ }
1037+ return parents;
1038+ }, '');
1039+ }
1040+
1041+ try {
1042+ exists = fs.existsSync(value);
1043+ validPath = exists ? fs.realpathSync(value) : pathUtil.resolve(value);
1044+ // options.exists default: true, not-bool: no-check
1045+ if (!options.hasOwnProperty('exists') && !exists ||
1046+ typeof options.exists === 'boolean' && options.exists !== exists) {
1047+ error = (exists ? 'Already exists' : 'No such file or directory') +
1048+ ': ' + validPath;
1049+ return false;
1050+ }
1051+ if (!exists && options.create) {
1052+ if (options.isDirectory) {
1053+ mkdirParents(validPath);
1054+ } else {
1055+ mkdirParents(pathUtil.dirname(validPath));
1056+ fs.closeSync(fs.openSync(validPath, 'w')); // touch
1057+ }
1058+ validPath = fs.realpathSync(validPath);
1059+ }
1060+ if (exists && (options.min || options.max ||
1061+ options.isFile || options.isDirectory)) {
1062+ stat = fs.statSync(validPath);
1063+ // type check first (directory has zero size)
1064+ if (options.isFile && !stat.isFile()) {
1065+ error = 'Not file: ' + validPath;
1066+ return false;
1067+ } else if (options.isDirectory && !stat.isDirectory()) {
1068+ error = 'Not directory: ' + validPath;
1069+ return false;
1070+ } else if (options.min && stat.size < +options.min ||
1071+ options.max && stat.size > +options.max) {
1072+ error = 'Size ' + stat.size + ' is out of range: ' + validPath;
1073+ return false;
1074+ }
1075+ }
1076+ if (typeof options.validate === 'function' &&
1077+ (res = options.validate(validPath)) !== true) {
1078+ if (typeof res === 'string') { error = res; }
1079+ return false;
1080+ }
1081+ } catch (e) {
1082+ error = e + '';
1083+ return false;
1084+ }
1085+ return true;
1086+ },
1087+ // trueValue, falseValue, caseSensitive don't work.
1088+ phContent: function(param) {
1089+ return param === 'error' ? error :
1090+ param !== 'min' && param !== 'max' ? null :
1091+ options.hasOwnProperty(param) ? options[param] + '' : '';
1092+ }
1093+ });
1094+ // added: exists, create, min, max, isFile, isDirectory, validate
1095+ /* eslint-enable key-spacing */
1096+ options = options || {};
1097+
1098+ if (query == null) { query = 'Input path (you can "cd" and "pwd"): '; }
1099+
1100+ exports.question(query, readOptions);
1101+ return validPath;
1102+};
1103+
1104+// props: preCheck, args, hRes, limit
1105+function getClHandler(commandHandler, options) {
1106+ var clHandler = {},
1107+ hIndex = {};
1108+ if (typeof commandHandler === 'object') {
1109+ Object.keys(commandHandler).forEach(function(cmd) {
1110+ if (typeof commandHandler[cmd] === 'function') {
1111+ hIndex[options.caseSensitive ? cmd : cmd.toLowerCase()] = commandHandler[cmd];
1112+ }
1113+ });
1114+ clHandler.preCheck = function(res) {
1115+ var cmdKey;
1116+ clHandler.args = parseCl(res);
1117+ cmdKey = clHandler.args[0] || '';
1118+ if (!options.caseSensitive) { cmdKey = cmdKey.toLowerCase(); }
1119+ clHandler.hRes =
1120+ cmdKey !== '_' && hIndex.hasOwnProperty(cmdKey)
1121+ ? hIndex[cmdKey].apply(res, clHandler.args.slice(1)) :
1122+ hIndex.hasOwnProperty('_') ? hIndex._.apply(res, clHandler.args) : null;
1123+ return {res: res, forceNext: false};
1124+ };
1125+ if (!hIndex.hasOwnProperty('_')) {
1126+ clHandler.limit = function() { // It's called after preCheck.
1127+ var cmdKey = clHandler.args[0] || '';
1128+ if (!options.caseSensitive) { cmdKey = cmdKey.toLowerCase(); }
1129+ return hIndex.hasOwnProperty(cmdKey);
1130+ };
1131+ }
1132+ } else {
1133+ clHandler.preCheck = function(res) {
1134+ clHandler.args = parseCl(res);
1135+ clHandler.hRes = typeof commandHandler === 'function'
1136+ ? commandHandler.apply(res, clHandler.args) : true; // true for break loop
1137+ return {res: res, forceNext: false};
1138+ };
1139+ }
1140+ return clHandler;
1141+}
1142+
1143+exports.promptCL = function(commandHandler, options) {
1144+ /* eslint-disable key-spacing */
1145+ var readOptions = margeOptions({
1146+ // -------- default
1147+ hideEchoBack: false,
1148+ limitMessage: 'Requested command is not available.',
1149+ caseSensitive: false,
1150+ history: true
1151+ }, options),
1152+ // -------- forced
1153+ // trueValue, falseValue, keepWhitespace don't work.
1154+ // preCheck, limit (by clHandler)
1155+ clHandler = getClHandler(commandHandler, readOptions);
1156+ /* eslint-enable key-spacing */
1157+ readOptions.limit = clHandler.limit;
1158+ readOptions.preCheck = clHandler.preCheck;
1159+ exports.prompt(readOptions);
1160+ return clHandler.args;
1161+};
1162+
1163+exports.promptLoop = function(inputHandler, options) {
1164+ /* eslint-disable key-spacing */
1165+ var readOptions = margeOptions({
1166+ // -------- default
1167+ hideEchoBack: false,
1168+ trueValue: null,
1169+ falseValue: null,
1170+ caseSensitive: false,
1171+ history: true
1172+ }, options);
1173+ /* eslint-enable key-spacing */
1174+ while (true) { if (inputHandler(exports.prompt(readOptions))) { break; } }
1175+ // return; // nothing is returned
1176+};
1177+
1178+exports.promptCLLoop = function(commandHandler, options) {
1179+ /* eslint-disable key-spacing */
1180+ var readOptions = margeOptions({
1181+ // -------- default
1182+ hideEchoBack: false,
1183+ limitMessage: 'Requested command is not available.',
1184+ caseSensitive: false,
1185+ history: true
1186+ }, options),
1187+ // -------- forced
1188+ // trueValue, falseValue, keepWhitespace don't work.
1189+ // preCheck, limit (by clHandler)
1190+ clHandler = getClHandler(commandHandler, readOptions);
1191+ /* eslint-enable key-spacing */
1192+ readOptions.limit = clHandler.limit;
1193+ readOptions.preCheck = clHandler.preCheck;
1194+ while (true) {
1195+ exports.prompt(readOptions);
1196+ if (clHandler.hRes) { break; }
1197+ }
1198+ // return; // nothing is returned
1199+};
1200+
1201+exports.promptSimShell = function(options) {
1202+ /* eslint-disable key-spacing */
1203+ return exports.prompt(margeOptions({
1204+ // -------- default
1205+ hideEchoBack: false,
1206+ history: true
1207+ }, options, {
1208+ // -------- forced
1209+ prompt: (function() {
1210+ return IS_WIN ? '$<cwd>>' :
1211+ // 'user@host:cwd$ '
1212+ (process.env.USER || '') +
1213+ (process.env.HOSTNAME ? '@' + process.env.HOSTNAME.replace(/\..*$/, '') : '') +
1214+ ':$<cwdHome>$ ';
1215+ })()
1216+ }));
1217+ /* eslint-enable key-spacing */
1218+};
1219+
1220+function _keyInYN(query, options, limit) {
1221+ var res;
1222+ if (query == null) { query = 'Are you sure? '; }
1223+ if ((!options || options.guide !== false) && (query += '')) {
1224+ query = query.replace(/\s*:?\s*$/, '') + ' [y/n]: ';
1225+ }
1226+ /* eslint-disable key-spacing */
1227+ res = exports.keyIn(query, margeOptions(options, {
1228+ // -------- forced
1229+ hideEchoBack: false,
1230+ limit: limit,
1231+ trueValue: 'y',
1232+ falseValue: 'n',
1233+ caseSensitive: false
1234+ // mask doesn't work.
1235+ }));
1236+ // added: guide
1237+ /* eslint-enable key-spacing */
1238+ return typeof res === 'boolean' ? res : '';
1239+}
1240+exports.keyInYN = function(query, options) { return _keyInYN(query, options); };
1241+exports.keyInYNStrict = function(query, options) { return _keyInYN(query, options, 'yn'); };
1242+
1243+exports.keyInPause = function(query, options) {
1244+ if (query == null) { query = 'Continue...'; }
1245+ if ((!options || options.guide !== false) && (query += '')) {
1246+ query = query.replace(/\s+$/, '') + ' (Hit any key)';
1247+ }
1248+ /* eslint-disable key-spacing */
1249+ exports.keyIn(query, margeOptions({
1250+ // -------- default
1251+ limit: null
1252+ }, options, {
1253+ // -------- forced
1254+ hideEchoBack: true,
1255+ mask: ''
1256+ }));
1257+ // added: guide
1258+ /* eslint-enable key-spacing */
1259+ // return; // nothing is returned
1260+};
1261+
1262+exports.keyInSelect = function(items, query, options) {
1263+ /* eslint-disable key-spacing */
1264+ var readOptions = margeOptions({
1265+ // -------- default
1266+ hideEchoBack: false
1267+ }, options, {
1268+ // -------- forced
1269+ trueValue: null,
1270+ falseValue: null,
1271+ caseSensitive: false,
1272+ // limit (by items),
1273+ phContent: function(param) {
1274+ return param === 'itemsCount' ? items.length + '' :
1275+ param === 'firstItem' ? (items[0] + '').trim() :
1276+ param === 'lastItem' ? (items[items.length - 1] + '').trim() : null;
1277+ }
1278+ }),
1279+ // added: guide, cancel
1280+ keylist = '',
1281+ key2i = {},
1282+ charCode = 49 /* '1' */,
1283+ display = '\n';
1284+ /* eslint-enable key-spacing */
1285+ if (!Array.isArray(items) || !items.length || items.length > 35) {
1286+ throw '`items` must be Array (max length: 35).';
1287+ }
1288+
1289+ items.forEach(function(item, i) {
1290+ var key = String.fromCharCode(charCode);
1291+ keylist += key;
1292+ key2i[key] = i;
1293+ display += '[' + key + '] ' + (item + '').trim() + '\n';
1294+ charCode = charCode === 57 /* '9' */ ? 97 /* 'a' */ : charCode + 1;
1295+ });
1296+ if (!options || options.cancel !== false) {
1297+ keylist += '0';
1298+ key2i['0'] = -1;
1299+ display += '[0] ' +
1300+ (options && options.cancel != null && typeof options.cancel !== 'boolean'
1301+ ? (options.cancel + '').trim() : 'CANCEL') + '\n';
1302+ }
1303+ readOptions.limit = keylist;
1304+ display += '\n';
1305+
1306+ if (query == null) { query = 'Choose one from list: '; }
1307+ if ((query += '')) {
1308+ if (!options || options.guide !== false) {
1309+ query = query.replace(/\s*:?\s*$/, '') + ' [$<limit>]: ';
1310+ }
1311+ display += query;
1312+ }
1313+
1314+ return key2i[exports.keyIn(display, readOptions).toLowerCase()];
1315+};
1316+
1317+exports.getRawInput = function() { return rawInput; };
1318+
1319+// ======== DEPRECATED ========
1320+function _setOption(optionName, args) {
1321+ var options;
1322+ if (args.length) { options = {}; options[optionName] = args[0]; }
1323+ return exports.setDefaultOptions(options)[optionName];
1324+}
1325+exports.setPrint = function() { return _setOption('print', arguments); };
1326+exports.setPrompt = function() { return _setOption('prompt', arguments); };
1327+exports.setEncoding = function() { return _setOption('encoding', arguments); };
1328+exports.setMask = function() { return _setOption('mask', arguments); };
1329+exports.setBufferSize = function() { return _setOption('bufferSize', arguments); };