diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-01 04:52:50 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-01 04:52:50 +0900 |
| commit | b8d46b0495d00815a699932ced0b43955c949ab9 (patch) | |
| tree | 17c340f51cbf259ea631ca7da7549714c91b916f /crates/shirabe-php-src/src/standard/exec.rs | |
| parent | 8e8a3c147aa388c4b0fc021a08b30113eb590727 (diff) | |
| download | php-shirabe-b8d46b0495d00815a699932ced0b43955c949ab9.tar.gz php-shirabe-b8d46b0495d00815a699932ced0b43955c949ab9.tar.zst php-shirabe-b8d46b0495d00815a699932ced0b43955c949ab9.zip | |
feat(php-src): add a BSD-licensed crate for php-src derived code
The audit in .ken/php-shim-copying.md judged 14 functions in
shirabe-php-shim (plus php_wordwrap in shirabe-external-packages) to be
line-by-line transcriptions or structural imitations of php-src. PHP's
relicensing to 3-clause BSD makes keeping them legal, but the boundary
between BSD-derived and MIT code was invisible in the source tree.
Moving them into their own crate puts the license into the build
metadata (so NOTICE generation follows the binary), makes a reverse
dependency a compile error, and encodes the origin in the module path,
which mirrors php-src's ext tree. Each function records its origin in a
fixed-format doc comment, and a new php_src_derivation_boundary linter
fails if `php-src` appears in any Rust source outside the crate.
Public paths under shirabe_php_shim:: are unchanged: functions that are
themselves derived are re-exported with `pub use`, and the wrappers that
only validate arguments stay on the MIT side.
This also resolves the duplicate wordwrap implementation.
shirabe_php_shim::wordwrap was todo!(), so SymfonyStyle::block panicked,
while shirabe-external-packages carried its own copy. Both now go
through the single port, verified against real PHP on 13 cases covering
multi-character breaks and cut.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-php-src/src/standard/exec.rs')
| -rw-r--r-- | crates/shirabe-php-src/src/standard/exec.rs | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/crates/shirabe-php-src/src/standard/exec.rs b/crates/shirabe-php-src/src/standard/exec.rs new file mode 100644 index 00000000..80ab26ae --- /dev/null +++ b/crates/shirabe-php-src/src/standard/exec.rs @@ -0,0 +1,40 @@ +/// php-src: ext/standard/exec.c `php_escape_shell_cmd` (PHP 8.5.2) +/// +/// Unix branch only. Shell metacharacters are backslash-escaped; quote characters are escaped +/// only when unpaired, paired quotes being left intact. The multibyte skip (`php_mblen`), the +/// command length check and the Windows branch are not ported. +pub fn escapeshellcmd(command: &str) -> String { + let bytes = command.as_bytes(); + let len = bytes.len(); + let mut out: Vec<u8> = Vec::with_capacity(len); + // Byte index of the matching closing quote while inside a paired quote run. + let mut paired: Option<usize> = None; + let mut x = 0; + while x < len { + let c = bytes[x]; + match c { + b'"' | b'\'' => { + if paired.is_none() { + if let Some(rel) = bytes[x + 1..].iter().position(|&b| b == c) { + paired = Some(x + 1 + rel); + } else { + out.push(b'\\'); + } + } else if paired == Some(x) { + paired = None; + } else { + out.push(b'\\'); + } + out.push(c); + } + b'#' | b'&' | b';' | b'`' | b'|' | b'*' | b'?' | b'~' | b'<' | b'>' | b'^' | b'(' + | b')' | b'[' | b']' | b'{' | b'}' | b'$' | b'\\' | 0x0A | 0xFF => { + out.push(b'\\'); + out.push(c); + } + _ => out.push(c), + } + x += 1; + } + String::from_utf8_lossy(&out).into_owned() +} |
