Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/bashkit/docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ for sandbox security reasons. See the compliance spec for details.
| `eval` | `command...` | POSIX construct and execute command |
| `readonly` | `VAR[=value]`, `-p` | POSIX mark variable read-only |
| `times` | - | POSIX display process times |
| `grep` | `-G/-E/-F/-P`, `-e`, `-f`, `-i/-y`, `--no-ignore-case`, `-v`, `-w`, `-x`, `-c`, `-l`, `-L`, `-o`, `-q`, `-s`, `-m`, `-n`, `-b`, `-H/-h`, `--label`, `-T`, `-Z`, `-z`, `-a`, `-I`, `--binary-files`, `-A/-B/-C/-NUM`, `--group-separator`, `--no-group-separator`, `-r/-R`, `-d`, `-D`, `--include/--exclude/--exclude-from/--exclude-dir`; long-option prefixes | GNU grep 3.11 output; BRE/ERE leftmost-longest, back-references |
| `grep` | `-G/-E/-F/-P`, `-e`, `-f`, `-i/-y`, `--no-ignore-case`, `-v`, `-w`, `-x`, `-c`, `-l`, `-L`, `-o`, `-q`, `-s`, `-m`, `-n`, `-b`, `-H/-h`, `--label`, `-T`, `-Z`, `-z`, `-a`, `-I`, `--binary-files`, `-A/-B/-C/-NUM`, `--group-separator`, `--no-group-separator`, `-r/-R`, `-d`, `-D`, `--include/--exclude/--exclude-from/--exclude-dir`; long-option prefixes | GNU grep 3.11 output; BRE/ERE leftmost-longest, back-references; recursive ancestor loops skipped with a warning, files scanned under shared resource budgets |
| `rg` | `-i/-S/-s`, `-n/-N`, `--column`, `-b/--byte-offset`, `--vimgrep`, basic `--json`, `--stats`, `--null`, `-c`, `--count-matches`, `--include-zero`, `-l`, `--files-with-matches`, `--files-without-match`, `--files`, `-F`, `-w`, `-x`, `-a/--text`, `--binary`, `-m`, `-M/--max-columns`, `--max-columns-preview`, `--max-depth`, `-A`, `-B`, `-C`, separator flags, `--heading/--no-heading`, `--sort/--sortr`, `--path-separator`, `-g/--glob`, `--ignore-file`, `.ignore`/`.gitignore`, `--no-ignore*`, `--require-git/--no-require-git`, `-u/--unrestricted`, `--messages/--no-messages`, `-t/--type`, `-T/--type-not`, `--type-add`, `--type-clear`, `--type-list`, `--hidden/--no-hidden`, `-o`, `-q`, `-e`, `-f`, `-E/--encoding` (`auto`, `none`, UTF-8, UTF-16LE/BE), `-r`, `--passthru`, `--trim`, `-H/-I`, `--no-config`, `--line-buffered`, `--block-buffered`, `--mmap/--no-mmap`, `-P/--pcre2`, `--no-pcre2`, `--engine`, `--auto-hybrid-regex`, `--no-auto-hybrid-regex`, `--help`, `--version` | Recursive ripgrep-style search |
| `sed` | `s///[gpiImMN w]`, `y`, `d`, `D`, `p`, `P`, `n`, `N`, `q[N]`, `Q[N]`, `=`, `l[N]`, `z`, `F`, `a`, `i`, `c`, `r`, `R`, `w`, `W`, `h/H/g/G/x`, `b`/`t`/`T`/`:label`, `{}`, `#`/`#n`; addresses `N`, `$`, `/re/[IM]`, `\cREc`, `first~step`, `a,b`, `a,+N`, `a,~N`, `0,/re/`, `!`; options `-n -e -f -i[SUF] -E/-r -s -z -l N -u --` and long forms | Stream editing; one stream across operands unless `-s`/`-i` |
| `awk` (also `gawk`, `mawk`, `nawk`) | `'{print}'`, `-F`, `-v`, loops, arrays, increment, ternary | Text processing |
Expand Down
51 changes: 1 addition & 50 deletions crates/bashkit/src/builtins/find/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ use crate::interpreter::ExecResult;

const HELP: &str = "Usage: find [-H] [-L] [-P] [PATH...] [EXPRESSION]\nSearch for files in a directory hierarchy.\n\nOperators: ( EXPR ), ! EXPR, -not EXPR, EXPR -a EXPR, EXPR -o EXPR, EXPR , EXPR\nOptions: -maxdepth N -mindepth N -depth -follow -daystart -regextype TYPE -xdev\nTests: -name -iname -path -ipath -lname -regex -iregex -type -xtype -size -empty\n -mtime -mmin -atime -amin -ctime -cmin -newer -newerXY -perm -readable\n -writable -executable -user -group -uid -gid -nouser -nogroup -true -false\nActions: -print -print0 -printf FMT -delete -prune -quit\n -exec CMD {} ; -exec CMD {} + -execdir CMD {} ; -execdir CMD {} +\n --help\tdisplay this help and exit\n --version\toutput version information and exit\n";

/// Symlink hops before ELOOP, matching Linux.
const MAX_SYMLINK_HOPS: usize = 40;
/// Paths passed per `-exec ... {} +` invocation before a new one starts.
const EXEC_BATCH_MAX: usize = 4096;
const OUTPUT_CAP_MSG: &str = "find: output size limit exceeded\n";
Expand Down Expand Up @@ -175,53 +173,6 @@ fn type_matches(meta: &Metadata, types: &[char]) -> bool {
types.contains(&c)
}

/// Resolve every symlink in `path` (realpath), with an ELOOP hop cap.
async fn canonicalize(fs: &dyn FileSystem, path: &Path) -> std::result::Result<PathBuf, String> {
let mut rest: VecDeque<String> = path
.components()
.filter_map(|c| match c {
std::path::Component::Normal(s) => Some(s.to_string_lossy().into_owned()),
std::path::Component::ParentDir => Some("..".to_string()),
_ => None,
})
.collect();
let mut out = PathBuf::from("/");
let mut hops = 0;
while let Some(comp) = rest.pop_front() {
if comp == ".." {
out.pop();
continue;
}
let candidate = out.join(&comp);
match fs.lstat(&candidate).await {
Ok(m) if m.file_type.is_symlink() => {
hops += 1;
if hops > MAX_SYMLINK_HOPS {
return Err("Too many levels of symbolic links".to_string());
}
let target = fs.read_link(&candidate).await.map_err(|e| e.to_string())?;
if target.is_absolute() {
out = PathBuf::from("/");
}
let mut parts: Vec<String> = target
.components()
.filter_map(|c| match c {
std::path::Component::Normal(s) => Some(s.to_string_lossy().into_owned()),
std::path::Component::ParentDir => Some("..".to_string()),
_ => None,
})
.collect();
while let Some(p) = parts.pop() {
rest.push_front(p);
}
}
Ok(_) => out = candidate,
Err(e) => return Err(e.to_string()),
}
}
Ok(out)
}

/// A directory on the current path, for `-L` loop detection.
struct Ancestor {
canon: PathBuf,
Expand Down Expand Up @@ -841,7 +792,7 @@ impl FindRun {
.await
.ok()
.map(|t| t.to_string_lossy().into_owned());
match canonicalize(fs.as_ref(), &abs).await {
match crate::fs::canonicalize(fs.as_ref(), &abs).await {
Ok(canon) => {
let m = fs.stat(&canon).await.ok();
(target, m, canon)
Expand Down
Loading
Loading