From 4d2da3b2b36fc9a723e5c217734406ff08a2d1ed Mon Sep 17 00:00:00 2001 From: Thaddee Tyl Date: Wed, 11 Nov 2020 23:24:11 +0100 Subject: [PATCH] Use file descriptor 0 or /dev/tty to read stdin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Linux, when Node.js (at least, v15.2.0) does a readFileSync of /dev/tty, it hangs. The addition of /dev/tty comes from 9bcf45b1e384eb0983d023298e0e65bffafceb76, which converts to ESM. It also switches from reading /dev/stdin, to: 1. on Windows, reading file descriptor 0; 2. on other platforms, reading /dev/tty (which presumably works on macOS, but not Linux). However, based on the [Node.js documentation][fd0], reading file descriptor 0 always reads stdin on all platforms, thanks to a compatibility layer in Node.js. Subtly, for Linux specifically, using /dev/stdin instead of the zero file descriptor, enables the following use-case: just enter `commonmark` on the console, causing it to wait for prompt. You can then type text followed by Control-D to have that text be the input. | File | Linux | macOS | Windows | |============|=================|=================|===========| | | Pipe | ✓ | ✓ | ✓ | | 0 |--------|-----------------|-----------------|-----------| | | Prompt | EAGAIN | EAGAIN | TypeError | |~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~|~~~~~~~~~~~| | | ✓ | ✓ | ENOENT | | /dev/stdin |-----------------|-----------------|-----------| | | ✓ | EAGAIN | ENOENT | |~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~~|~~~~~~~~~~~| | | (input ignored) | (input ignored) | ENOENT | | /dev/tty |-----------------|-----------------|-----------| | | ✓ | ✓ | ENOENT | Thus we split the decision in a platform-independent way: 1. When in a TTY situation, use /dev/tty. Except on Windows, which does not have a PTY, so we manually read lines until we hit ^Z (which is EOF on Windows). 2. Otherwise, use file descriptor 0, which works across all platforms. [fd0]: https://nodejs.org/api/process.html#process_process_stdin_fd --- bin/commonmark | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/bin/commonmark b/bin/commonmark index 9f91f17e..63836622 100755 --- a/bin/commonmark +++ b/bin/commonmark @@ -4,6 +4,7 @@ import util from "util"; import fs from "fs"; import os from "os"; +import readline from "readline"; import * as commonmark from "../lib/index.js"; var version = require('../package.json').version; import parseArgs from "minimist"; @@ -77,11 +78,27 @@ if (format === 'html') { } if (files.length === 0) { - if (os.platform() === "win32") { - inps.push(fs.readFileSync(0, 'utf-8')); - } else { - inps.push(fs.readFileSync('/dev/tty', 'utf-8')); - } + if (process.stdin.isTTY) { + if (os.platform() === 'win32') { + // Windows does not have a PTY that Node relies on. + // Instead, we read lines explicitly, tracking ^Z for EOF. + (async () => { + const rl = readline.createInterface({ input: process.stdin }); + let inp = ''; + for await (const line of rl) { + if (line === '\x1a') { break; } + inp += line; + } + const doc = parser.parse(inp); + const rendered = renderer.render(doc); + if (!options.time) { process.stdout.write(rendered); } + })(); + } else { + inps.push(fs.readFileSync('/dev/tty', 'utf-8')); + } + } else { + inps.push(fs.readFileSync(0, 'utf-8')); + } } else { for (i = 0; i < files.length; i++) { var file = files[i];