feat: use bstr::BString for shell values to fix UTF-8 lossy conversion#1116
Open
lu-zero wants to merge 5 commits into
Open
feat: use bstr::BString for shell values to fix UTF-8 lossy conversion#1116lu-zero wants to merge 5 commits into
lu-zero wants to merge 5 commits into
Conversation
Performance Benchmark Report
Code Coverage Report: Only Changed Files listed
Minimum allowed coverage is Test Summary: bash-completion test suite
|
Elsie19
suggested changes
May 6, 2026
| let mut s; | ||
| if self.interpret_backslash_escapes { | ||
| s = String::new(); | ||
| s = Vec::new(); |
Contributor
There was a problem hiding this comment.
Suggested change
| s = Vec::new(); | |
| s = Vec::with_capacity(self.args.len()); |
If you do this you can avoid a couple reallocations.
Convert ShellValue, ShellValueLiteral, and ArrayLiteral from String to
bstr::BString so arbitrary bytes are preserved throughout the shell
variable system. This fixes silent data corruption in:
- echo -e with non-UTF-8 escape sequences (e.g. \x80)
- mapfile reading non-UTF-8 input
- ANSI-C quoting ($'\x80') producing non-UTF-8 bytes
- ${var@E} parameter transform
- PWD/OLDPWD for directories with non-UTF-8 paths (Unix)
- From<OsString> for ShellValue (uses into_vec() on Unix)
Closes: reubeno#1115
Co-Authored-by: opencode <opencode@anomaly.co>
Assisted-by: glm-5.1
Co-Authored-by: opencode <opencode@anomaly.co> Assisted-by: glm-5.1
- Convert ExpansionPiece to use BString, preserving raw bytes through the expansion pipeline including tilde expansion - Fix glob expansion to use byte-level path handling (patterns.rs) - Fix pwd builtin to output raw bytes - Fix external command execution to pass Path directly to exec - Fix wellknownvars: PWD init, DIRSTACK, HISTFILE, HOSTNAME, SHELL defaults all use path_to_bstring() - Fix pathcache to use path_to_bstring() - Fix completion candidates to use path_to_bstring() - Re-export path_to_bstring from crate root for external use Remaining to_string_lossy calls are display-only (prompts, error messages, debug logging) or explicit fallbacks that try lossless conversion first. Co-Authored-by: opencode <opencode@anomaly.co> Assisted-by: glm-5.1
Switch glob pattern matching to use is_match_bytes() so filenames with non-UTF-8 bytes are matched correctly on Unix. Update fancy-regex dependency to fork with bytes API. Co-Authored-by: opencode <opencode@anomaly.co> Assisted-by: glm-5.1
Use Vec::with_capacity to avoid reallocations.
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.
Summary
ShellValue,ShellValueLiteral, andArrayLiteralfromStringtobstr::BStringso arbitrary bytes are preserved throughout the shell variable systemecho -e,mapfile, ANSI-C quoting ($'...'),${var@E}, PWD/OLDPWD, andFrom<OsString>forShellValueCloses #1115
Details
Bash treats all data (filenames, variable values, command output) as raw byte sequences. Rust's
Stringrequires valid UTF-8, so everyfrom_utf8_lossyorto_string_lossycall was a potential silent data corruption point.The core change replaces
Stringwithbstr::BString(backed byVec<u8>) inShellValue::String,AssociativeArray, andIndexedArray, plus all related types. Key fixes:echo -e '\x80': now writes raw bytes to stdout instead of lossy-convertingmapfile: stores raw bytes asBString$'\x80'): preserves non-UTF-8 bytes${var@E}: same as aboveOsStrExt::as_bytes()on Unix to preserve raw path bytesFrom<OsString>: usesinto_vec()on Unix instead ofto_string_lossy()24 files changed, all tests passing (49 unit + 224 parser + 25 builtins + 1696 compat).
Co-Authored-by: opencode opencode@anomaly.co
Assisted-by: glm-5.1