diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-25 17:49:35 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-26 00:20:05 +0900 |
| commit | d4ec1dcb6fc04ff029ded35cc2fcc14f3bbc02ad (patch) | |
| tree | 0a85bd085961fcdfd5570cafc3b44ba5eb8cb152 /crates/shirabe-php-shim/src | |
| parent | 22d0b327368e5f39de6f381046c081d08efdba15 (diff) | |
| download | php-shirabe-d4ec1dcb6fc04ff029ded35cc2fcc14f3bbc02ad.tar.gz php-shirabe-d4ec1dcb6fc04ff029ded35cc2fcc14f3bbc02ad.tar.zst php-shirabe-d4ec1dcb6fc04ff029ded35cc2fcc14f3bbc02ad.zip | |
test: port 32 command/repository/downloader tests
Add create_installed_json/create_composer_lock test helpers. Port command (8),
repository path/forgejo/perforce/vcs (11), and fossil/hg/download_manager (13)
tests. Fix production porting bugs: root_package_loader/forgejo_url/version_bumper
regex delimiters, repository_manager create_repository_by_class, array_loader
isset, licenses_command RefCell borrow; implement disk_free_space and
touch2/touch3 via libc.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-php-shim/src')
| -rw-r--r-- | crates/shirabe-php-shim/src/datetime.rs | 1 | ||||
| -rw-r--r-- | crates/shirabe-php-shim/src/fs.rs | 86 |
2 files changed, 77 insertions, 10 deletions
diff --git a/crates/shirabe-php-shim/src/datetime.rs b/crates/shirabe-php-shim/src/datetime.rs index f44bb7b..8d1062c 100644 --- a/crates/shirabe-php-shim/src/datetime.rs +++ b/crates/shirabe-php-shim/src/datetime.rs @@ -63,6 +63,7 @@ pub const DATE_ATOM: &str = DATE_RFC3339; pub fn date_format_to_strftime(format: &str) -> &'static str { match format { "Y-m-d H:i:s" => "%Y-%m-%d %H:%M:%S", + "Y/m/d H:i:s" => "%Y/%m/%d %H:%M:%S", "Y-m-d Hi" => "%Y-%m-%d %H%M", "Y-m-d" => "%Y-%m-%d", "Ymd" => "%Y%m%d", diff --git a/crates/shirabe-php-shim/src/fs.rs b/crates/shirabe-php-shim/src/fs.rs index 2fb0dc5..a557d26 100644 --- a/crates/shirabe-php-shim/src/fs.rs +++ b/crates/shirabe-php-shim/src/fs.rs @@ -672,15 +672,43 @@ pub fn fwrite_resource(resource: &PhpResource, data: &str) { fwrite(resource, data, None); } -pub fn touch2(_path: &str, _mtime: i64) -> bool { - // TODO(phase-d): setting an explicit mtime needs utimensat(2), not exposed by std (no - // libc/filetime crate available). - todo!() +// libc is already linked into every binary, so `utime` can be declared directly without an extra +// crate (mirrors the `fcntl`/`statvfs` declarations elsewhere). PHP's `touch($path, $mtime, $atime)` +// passes whole seconds, which matches `struct utimbuf`'s `time_t` fields. +#[repr(C)] +struct Utimbuf { + actime: std::os::raw::c_long, + modtime: std::os::raw::c_long, } -pub fn touch3(_path: &str, _mtime: i64, _atime: i64) -> bool { - // TODO(phase-d): setting explicit mtime/atime needs utimensat(2); see touch2. - todo!() +unsafe extern "C" { + fn utime(path: *const std::os::raw::c_char, times: *const Utimbuf) -> std::os::raw::c_int; +} + +fn touch_impl(path: &str, mtime: i64, atime: i64) -> bool { + let Ok(c_path) = std::ffi::CString::new(path) else { + return false; + }; + // PHP's `touch` creates the file first if it does not exist. + if !std::path::Path::new(path).exists() && !touch(path) { + return false; + } + let times = Utimbuf { + actime: atime as std::os::raw::c_long, + modtime: mtime as std::os::raw::c_long, + }; + unsafe { utime(c_path.as_ptr(), ×) == 0 } +} + +/// PHP `touch($path, $mtime)`: sets the modification time (and access time, per PHP, to the same +/// value). Returns `false` (PHP failure) on error. +pub fn touch2(path: &str, mtime: i64) -> bool { + touch_impl(path, mtime, mtime) +} + +/// PHP `touch($path, $mtime, $atime)`. +pub fn touch3(path: &str, mtime: i64, atime: i64) -> bool { + touch_impl(path, mtime, atime) } pub fn chmod(_path: &str, _mode: u32) -> bool { @@ -1077,10 +1105,48 @@ pub fn clearstatcache2(_clear_realpath_cache: bool, _filename: &str) { // cache to invalidate. } +// libc is already linked into every binary, so `statvfs` can be declared directly without an extra +// crate (mirrors the `fcntl`/`select` declarations in stream.rs). The layout below matches Linux's +// `struct statvfs`. +#[cfg(target_os = "linux")] +#[repr(C)] +struct Statvfs { + f_bsize: std::os::raw::c_ulong, + f_frsize: std::os::raw::c_ulong, + f_blocks: u64, + f_bfree: u64, + f_bavail: u64, + f_files: u64, + f_ffree: u64, + f_favail: u64, + f_fsid: std::os::raw::c_ulong, + f_flag: std::os::raw::c_ulong, + f_namemax: std::os::raw::c_ulong, + f_spare: [std::os::raw::c_int; 6], +} + +#[cfg(target_os = "linux")] +unsafe extern "C" { + fn statvfs(path: *const std::os::raw::c_char, buf: *mut Statvfs) -> std::os::raw::c_int; +} + +/// PHP `disk_free_space()`: the number of available bytes on the filesystem containing `directory`, +/// computed via `statvfs(3)` (`f_bavail * f_frsize`). Returns `None` (PHP `false`) on failure. +#[cfg(target_os = "linux")] +pub fn disk_free_space(directory: &str) -> Option<f64> { + let c_path = std::ffi::CString::new(directory).ok()?; + let mut buf = std::mem::MaybeUninit::<Statvfs>::uninit(); + let rc = unsafe { statvfs(c_path.as_ptr(), buf.as_mut_ptr()) }; + if rc != 0 { + return None; + } + let buf = unsafe { buf.assume_init() }; + Some(buf.f_bavail as f64 * buf.f_frsize as f64) +} + +#[cfg(not(target_os = "linux"))] pub fn disk_free_space(_directory: &str) -> Option<f64> { - // TODO(phase-d): reading free space for an arbitrary path requires statvfs(3); std exposes no - // equivalent and no /proc file gives per-path free space (no libc/syscall crate available). - todo!() + None } pub const GLOB_MARK: i64 = 8; |
