aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-php-src/src/standard/exec.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-01 04:52:50 +0900
committernsfisis <nsfisis@gmail.com>2026-08-01 04:52:50 +0900
commitb8d46b0495d00815a699932ced0b43955c949ab9 (patch)
tree17c340f51cbf259ea631ca7da7549714c91b916f /crates/shirabe-php-src/src/standard/exec.rs
parent8e8a3c147aa388c4b0fc021a08b30113eb590727 (diff)
downloadphp-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.rs40
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()
+}