UNPKG

niim

Version:
178 lines (166 loc) 5.88 kB
#! /usr/bin/env node const process = require('process'); const fs = require('fs'); let leftover = false; let testObject = { hello: 'world' }; /** Read a line of input from stdin, one character at a time. Standard UNIX terminal line * discipline is emulated. Input characters may be ASCII or UTF-8. * * @param {string} echoChar The charater to echo when a key is pressed. When * undefined, nothing is echoed. When true, the input * character is (more or less) echoed back. More or * less because, rather than typical full-duplex terminal * emulation that echoes the exact input, in our case * the input is converted from UTF-8 to UTF-16 for output. * @param {number} bufSize An optional parameter to specify the buffer size. * A large (256-byte?) buffer will perform will when reading * large amounts of data, however the 1-byte default * will prevent this code from consuming extra data from * the stdin stream. * @returns {string} UTF-16 representation of input. Newline is discarded. Invalid * input could yield unpaired surrogates in the output. * * @note Known Bugs - when using echoChar = true, there will be display bugs for * erase and kill when dealing with codepoints outside of the BMP, as well as * unpaired surrogates. */ function raw_readln(echoChar, bufSize) { var chBuf = Buffer.alloc(bufSize || 1); var buf = ''; var utf8Buf; if (process.stdin.setRawMode) process.stdin.setRawMode(true); try { while(true) { let ch; try { if (!leftover) { nRead = fs.readSync(process.stdin.fd, chBuf, 0, chBuf.length); } else { leftover.copy(chBuf); nRead = leftover.length; leftover = false; } } catch (e) { switch(e.code) { case 'EAGAIN': nRead = 0 break case 'EOF': nRead = -1 break default: throw e } } if (nRead < 0) return null /* input stream is closed */ if (nRead == 0) { /* nothing to read - give up timeslice */ nap(); continue; } for (let pos=0; pos < nRead; pos++) { ch = chBuf[pos]; if (ch >= 0xc2) { /* start of utf-8 sequence */ if (ch >= 0xc2 && ch <= 0xdf) utf8Buf = Buffer.alloc(2); if (ch >= 0xe0 && ch <= 0xef) utf8Buf = Buffer.alloc(3); if (ch >= 0xf0 && ch <= 0xff) utf8Buf = Buffer.alloc(4); utf8Buf[0] = ch; utf8Pos = 1; continue; } if (ch >= 0x80 && ch <= 0xbf) { /* part of utf-8 sequence */ utf8Buf[utf8Pos++] = ch; } if (utf8Buf) { if (utf8Pos !== utf8Buf.length) { continue; } else { /* final byte of utf-8 sequence */ switch(utf8Buf.length) { case 4: codepoint = utf8Buf[0] & 0b00011111; break; case 3: codepoint = utf8Buf[0] & 0b00001111; break; case 2: codepoint = utf8Buf[0] & 0b00000111; break; } codepoint = codepoint << ((4 - utf8Buf.length) * 8); utf8Buf = false; //XXX console.log(codepoint.toString(16)); } } else { codepoint = ch; /* ascii */ } ch = undefined; /* From here down, we use the Unicode codepoint */ /* Emulate typical UNIX terminal behaviour wrt control codes */ switch(codepoint) { case 3: // process.kill(process.pid, 'SIGINT'); console.log('NOT DOING SIGINT'); continue; case 8: case 127: buf = buf.slice(0,-1); if (echoChar) process.stdout.write(Buffer.from([8,32,32,8,8])); continue; case 4: if (buf.length) break; case 10: case 13: if (nRead - pos != 0) { leftover = chBuf.slice(pos + 1) } return buf; case 28: process.kill(process.pid, 'SIGQUIT'); continue; case 21: for (let i=0; i < buf.length; i++) { if (buf.charCodeAt(i) >= 0xd800 && buf.charCodeAt(i) < 0xdc00) continue; process.stdout.write(Buffer.from([8,32,8])); } buf = ''; continue; default: if (codepoint < 32) continue; } buf += String.fromCodePoint(codepoint); if (echoChar) { if (echoChar === true) process.stdout.write(Buffer.from(String.fromCodePoint(codepoint))); else process.stdout.write(Buffer.from(echoChar)); } } } } finally { if (process.stdin.setRawMode) { process.stdout.write(Buffer.from([13])); if (echoChar) process.stdout.write(Buffer.from([10])); process.stdin.setRawMode(false); } } } /** Take a short nap - should cue the scheduler to take what's left of our timeslice.*/ function nap() { let sab = new SharedArrayBuffer(4); let int32 = new Int32Array(sab); Atomics.wait(int32, 0, 0, 125/2); /* 125ms = 100wpm */ } console.log('The next data will be a prompt with no newline'); process.stdout.write('please type your password: '); let password = raw_readln('*'); console.log('You typed:', password); process.stdout.write('please type your password again: '); password = raw_readln('*'); console.log('You typed:', password);