dor send --stdin interprets backslash escapes in piped bytes, corrupting the documented use case
dor send --stdin reads standard input and forwards it as a text input:
if (flags.stdin === true) {
if (!readStdin) return { ok: false, message: 'stdin is not available' };
return { ok: true, value: [{ kind: 'text', text: await readStdin() }] };
}
(send.ts:183-186)
Text inputs then run through interpretTextEscapes unless --raw is set:
input += raw ? item.text : interpretTextEscapes(item.text);
(send.ts:227-228, converting \n \r \t \\ at send.ts:238-253)
Why this is a footgun for --stdin
The flagship stdin example in the help is:
cat script.sh | dor send surface:3 --stdin
(send.ts:115)
Bytes arriving on stdin are already literal — they are not a shell-authored string where a two-character \t stands in for a tab. Shell scripts routinely contain literal backslash sequences (printf 'a\tb', sed 's/\n/ /', grep -P '\d', Windows paths with \\). Piping such a file through --stdin silently rewrites every \n/\r/\t/\\, so the text typed into the target terminal is not the file's contents.
Repro: printf 'printf "a\\tb\\n"\n' | dor send surface:3 --stdin types a real TAB and newline into the middle of the line instead of the literal \t/\n the script source contains.
The escape interpretation is desirable for --text "echo hi\nthere" (a human types escapes on the command line), but for --stdin the input is already-real bytes.
Design question (why an issue, not a drive-by PR)
The current behavior is consistent with the documented contract — the help says "Text input interprets backslash escapes … unless --raw is set" (send.ts:97), and --stdin is documented as text input — so flipping the default is a contract change that needs a maintainer call. Options:
- Make
--stdin raw by default — treat piped bytes as literal; keep --text interpreting escapes. Most aligned with the cat script.sh | … example. Would need an opt-in flag if anyone wants escape interpretation on stdin.
- Keep current behavior, document it — call out at the
--stdin help that it interprets escapes and that --raw is needed for literal file contents.
I lean toward option 1, but it changes documented behavior, so I'm leaving the call to a maintainer. Happy to open the PR (including a regression test that pipes a script containing \t and asserts the bytes are preserved) once a direction is chosen.
Surfaced by the nightly code-quality survey.
dor send --stdininterprets backslash escapes in piped bytes, corrupting the documented use casedor send --stdinreads standard input and forwards it as a text input:(send.ts:183-186)
Text inputs then run through
interpretTextEscapesunless--rawis set:(send.ts:227-228, converting
\n \r \t \\at send.ts:238-253)Why this is a footgun for
--stdinThe flagship stdin example in the help is:
(send.ts:115)
Bytes arriving on stdin are already literal — they are not a shell-authored string where a two-character
\tstands in for a tab. Shell scripts routinely contain literal backslash sequences (printf 'a\tb',sed 's/\n/ /',grep -P '\d', Windows paths with\\). Piping such a file through--stdinsilently rewrites every\n/\r/\t/\\, so the text typed into the target terminal is not the file's contents.Repro:
printf 'printf "a\\tb\\n"\n' | dor send surface:3 --stdintypes a real TAB and newline into the middle of the line instead of the literal\t/\nthe script source contains.The escape interpretation is desirable for
--text "echo hi\nthere"(a human types escapes on the command line), but for--stdinthe input is already-real bytes.Design question (why an issue, not a drive-by PR)
The current behavior is consistent with the documented contract — the help says "Text input interprets backslash escapes … unless
--rawis set" (send.ts:97), and--stdinis documented as text input — so flipping the default is a contract change that needs a maintainer call. Options:--stdinraw by default — treat piped bytes as literal; keep--textinterpreting escapes. Most aligned with thecat script.sh | …example. Would need an opt-in flag if anyone wants escape interpretation on stdin.--stdinhelp that it interprets escapes and that--rawis needed for literal file contents.I lean toward option 1, but it changes documented behavior, so I'm leaving the call to a maintainer. Happy to open the PR (including a regression test that pipes a script containing
\tand asserts the bytes are preserved) once a direction is chosen.Surfaced by the nightly code-quality survey.