Repository navigation
馃悰 fix(csh): preserve literals across quoting modes - #3414
Merged
Merged
Conversation
gaborbernat
force-pushed
the
fix/cshell-quote-modes
branch
2 times, most recently
from
October 16, 2026 14:25
3cca319 to
5cd8d80
Compare
for more information, see https://pre-commit.ci
Reported by Anthropic's OSS Scanner: ANT-2026-I4L9S9FD, ANT-2026-YYQQ0SWO.
Reported by Anthropic's OSS Scanner: ANT-2026-ZZZSMHXA, ANT-2026-BZW697LP.
Linux CI had no tcsh and no GBK locale, so every tcsh test in this module was skipped there; install both as the parent PR does. Fold the percent, caret and GBK prompt tests into one interactive rendering test, since the caret escape was only checked through the generated text and the GBK caret case passes on unfixed code. Build the disabled-prompt and bsd-csh environments through the shared fixture.
gaborbernat
force-pushed
the
fix/cshell-quote-modes
branch
from
October 17, 2026 09:01
836b407 to
0129973
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
With tcsh's
backslash_quoteoption set, activation storedVIRTUAL_ENV_PROMPTandVIRTUAL_ENVwith fewer backslashes than the values given, and a value ending in a backslash stopped the script withUnmatched '''. On bsd-csh, thecshof Debian and Ubuntu, every backslash in the prompt drew twice. 馃悰 After this PR the generated literals mean the same in both quoting modes, and each shell draws one backslash per backslash in the value. This builds on #3404.Backslashes inside single quotes change meaning with
backslash_quoteWithout the option, a backslash inside single quotes is a literal character (Lexical structure). With
backslash_quoteset, a backslash always quotes\,'and", inside single quotes too (tcsh manual,backslash_quote).quote()left the value's backslashes inside the quotesshlex.quotewrote (old lines, kept by #3404). On tcsh 6.24,'a\\b'is read asa\\bin one mode and asa\bin the other, and'a\'has no closing quote once\'quotes the apostrophe, so the shell stops sourcing withUnmatched '''afterVIRTUAL_ENVandPATHare already changed.quote()now moves every backslash outside the quotes as'\\'(new lines), where a backslash followed by a backslash gives one backslash in both modes._prompt_literalhas kept its backslashes outside the quotes since #3404; its apostrophe now uses the'"'"'formshlex.quoteemits, so both helpers write the same source for the same character (new lines). A test sources the script in tcsh with and withoutset backslash_quotefor a backslash, a trailing backslash, an apostrophe and a protected history character, and comparesVIRTUAL_ENV,VIRTUAL_ENV_PROMPTandprompt(test).flowchart LR V["value a\\b"] --> O["old source 'a\\b'"] O -- "backslash_quote unset" --> O1["a\\b"] O -- "backslash_quote set" --> O2["a\b"] V --> N["new source 'a'\\'\\'b'"] N -- "either mode" --> N1["a\\b"] classDef bad fill:#ffd6d6,stroke:#c1121f,color:#5c0000; classDef ok fill:#d8f3dc,stroke:#2d6a4f,color:#1b4332; class O2 bad; class N1 ok;Prompt text is assigned before the conditional
The prompt block from #1454, with the tcsh arm added in #3263, embedded the display literal in both arms of the
$?tcshselection (old lines). Whichever arm the shell took, the other one still carried caller-controlled quoting through the shell's line scanner, and the whole block was scanned without being run whenVIRTUAL_ENV_DISABLE_PROMPTwas set. Both display strings are now assigned once, beforedo_promptis tested, to_virtualenv_prompt_tcshand_virtualenv_prompt_plain(new lines). The arms reference them as"$_virtualenv_prompt_tcsh:q"and"$_virtualenv_prompt_plain:q"(new lines), and the two variables are unset after the block (new lines). No text from the caller remains inside a skipped arm. A test sources the script in csh withVIRTUAL_ENV_DISABLE_PROMPTset and a prompt containing an apostrophe, and checks that the prompt is untouched (test).flowchart LR A["set _virtualenv_prompt_tcsh = '...'<br>set _virtualenv_prompt_plain = '...'"] --> B{"do_prompt == 1"} B -- no --> X["block scanned, not run"] B -- yes --> C{"$?tcsh"} C -- yes --> T["set prompt = '('#quot;$_virtualenv_prompt_tcsh:q#quot;') '"] C -- no --> P["set prompt = '('#quot;$_virtualenv_prompt_plain:q#quot;') '"] classDef ok fill:#d8f3dc,stroke:#2d6a4f,color:#1b4332; class A ok;bsd-csh draws the prompt without decoding backslashes
tcsh parses
\cwhen it draws the prompt, so a literal backslash has to reachpromptdoubled (tcsh manual,prompt). bsd-csh prints the string as stored and replaces only!(csh manual,prompt). Since #3263_prompt_literaldoubled the backslash for both shells (old lines), so bsd-csh drew(a\\b)for a prompt ofa\b. The plain literal now writes one backslash per backslash and the tcsh literal keeps two (new lines). A test that skips unlesscshis bsd-csh sources the script interactively and checks the drawn prompt (test).flowchart LR V["prompt a\b"] --> T["tcsh literal: prompt holds a\\b"] T -- "\c decoded on draw" --> T1["(a\b)"] V --> O["old plain literal: prompt holds a\\b"] O -- "printed as stored" --> O1["(a\\b)"] V --> N["new plain literal: prompt holds a\b"] N -- "printed as stored" --> N1["(a\b)"] classDef bad fill:#ffd6d6,stroke:#c1121f,color:#5c0000; classDef ok fill:#d8f3dc,stroke:#2d6a4f,color:#1b4332; class O1 bad; class T1,N1 ok;Behavior changes
With
backslash_quoteset,VIRTUAL_ENV,VIRTUAL_ENV_PROMPTandprompthold the same values as without it, and a value that ends in a backslash activates. bsd-csh draws one backslash per backslash in the prompt; tcsh draws the same prompt as before. Activation sets two shell variables,_virtualenv_prompt_tcshand_virtualenv_prompt_plain, and unsets them before it returns. An apostrophe in a generated prompt literal is written as'"'"'rather than'\''.Reported by Anthropic's OSS Scanner: ANT-2026-I4L9S9FD, ANT-2026-YYQQ0SWO.