193 lines
5.7 KiB
Plaintext
193 lines
5.7 KiB
Plaintext
from stdint import *
|
|
import posix
|
|
import memhub
|
|
import stdlib
|
|
|
|
|
|
# Maximum POSIX path length
|
|
define MAX_PATH_LEN = 4096
|
|
|
|
# Module-level global MBuddy pointer (initialized by the user in main)
|
|
_mbuddy: memhub.MemBuddy*
|
|
|
|
|
|
# =============================================================================
|
|
# File/Directory Attribute Query
|
|
# =============================================================================
|
|
|
|
def _stat(path: str) -> posix.Stat*:
|
|
"""Call stat and return a Stat struct pointer. Return None on failure.
|
|
Note: the Stat struct is allocated via _mbuddy; the caller must free it."""
|
|
if _mbuddy is None:
|
|
return None
|
|
# 不能在栈上构造后返回其地址:栈对象随本帧失效(栈地址逃逸)。
|
|
# 按本函数契约改为 _mbuddy 堆分配,由调用方 free。
|
|
st: posix.Stat* = _mbuddy.alloc(posix.Stat.__sizeof__())
|
|
if st is None:
|
|
return None
|
|
result: INT = posix.stat(path, st)
|
|
if result != 0:
|
|
_mbuddy.free(st)
|
|
return None
|
|
return st
|
|
|
|
|
|
def exists(path: str) -> bool:
|
|
"""Check whether the file/directory exists"""
|
|
result: INT = posix.access(path, posix.F_OK)
|
|
if result == 0:
|
|
return True
|
|
return False
|
|
|
|
|
|
def isdir(path: str) -> bool:
|
|
"""Check whether the path is a directory"""
|
|
st: posix.Stat = posix.Stat()
|
|
result: INT = posix.stat(path, &st)
|
|
if result != 0:
|
|
return False
|
|
if posix.S_ISDIR(st.st_mode):
|
|
return True
|
|
return False
|
|
|
|
|
|
def isfile(path: str) -> bool:
|
|
"""Check whether the path is a regular file"""
|
|
st: posix.Stat = posix.Stat()
|
|
result: INT = posix.stat(path, &st)
|
|
if result != 0:
|
|
return False
|
|
if posix.S_ISREG(st.st_mode):
|
|
return True
|
|
return False
|
|
|
|
|
|
def getsize(path: str) -> int64:
|
|
"""Return the file size in bytes. Return -1 on failure."""
|
|
st: posix.Stat = posix.Stat()
|
|
result: INT = posix.stat(path, &st)
|
|
if result != 0:
|
|
return -1
|
|
return st.st_size
|
|
|
|
|
|
# =============================================================================
|
|
# Working Directory
|
|
# =============================================================================
|
|
|
|
def getcwd_impl() -> str:
|
|
"""Return the current working directory (memory allocated via _mbuddy)"""
|
|
buf: bytes = _mbuddy.alloc(MAX_PATH_LEN)
|
|
if buf == None: return None
|
|
result: CHARPTR = posix.getcwd(buf, MAX_PATH_LEN)
|
|
if result == None:
|
|
return None
|
|
return str(buf)
|
|
|
|
|
|
def chdir_impl(path: str) -> int:
|
|
"""Switch the current working directory. Return 0 on success, -1 on failure."""
|
|
result: INT = posix.chdir(path)
|
|
if result != 0:
|
|
return -1
|
|
return 0
|
|
|
|
|
|
# =============================================================================
|
|
# File/Directory Operations
|
|
# =============================================================================
|
|
|
|
def mkdir_impl(path: str) -> int:
|
|
"""Create a directory. Return 0 on success, -1 on failure.
|
|
Default permissions are 0o755 (rwxr-xr-x)."""
|
|
result: INT = posix.mkdir(path, 0o755)
|
|
if result != 0:
|
|
return -1
|
|
return 0
|
|
|
|
|
|
def remove(path: str) -> int:
|
|
"""Delete a file. Return 0 on success, -1 on failure."""
|
|
result: INT = posix.unlink(path)
|
|
if result != 0:
|
|
return -1
|
|
return 0
|
|
|
|
|
|
def rmdir_impl(path: str) -> int:
|
|
"""Delete an empty directory. Return 0 on success, -1 on failure."""
|
|
result: INT = posix.rmdir(path)
|
|
if result != 0:
|
|
return -1
|
|
return 0
|
|
|
|
|
|
def rename_impl(old_path: str, new_path: str) -> int:
|
|
"""Rename/move a file or directory. Return 0 on success, -1 on failure."""
|
|
result: INT = posix.rename(old_path, new_path)
|
|
if result != 0:
|
|
return -1
|
|
return 0
|
|
|
|
|
|
def chmod_impl(path: str, mode: int) -> int:
|
|
"""Modify file permissions. Return 0 on success, -1 on failure.
|
|
mode is POSIX permission bits (e.g. 0o755)."""
|
|
result: INT = posix.chmod(path, posix.mode_t(mode))
|
|
if result != 0:
|
|
return -1
|
|
return 0
|
|
|
|
|
|
# =============================================================================
|
|
# Directory Listing (opendir / readdir / closedir)
|
|
# =============================================================================
|
|
|
|
def listdir(path: str, out_names: str*, max_count: ULONG) -> ULONG:
|
|
"""List file/subdirectory names under a directory.
|
|
out_names is a caller-provided char** array (each entry points to a filename allocated via _mbuddy); max_count is its capacity. Return the count, or 0 on failure."""
|
|
dirp: posix.DIR = posix.opendir(path)
|
|
if dirp == None:
|
|
return 0
|
|
|
|
count: ULONG = 0
|
|
while count < max_count:
|
|
entry: posix.Dirent* = posix.readdir(dirp)
|
|
if entry == None:
|
|
break
|
|
|
|
# Read the d_name field
|
|
name: str = entry.d_name
|
|
if name[0] == '.':
|
|
if name[1] == 0:
|
|
# "."
|
|
continue
|
|
if name[1] == '.' and name[2] == 0:
|
|
# ".."
|
|
continue
|
|
|
|
# Copy the filename into newly allocated memory
|
|
name_len: sizet = len(name)
|
|
name_buf: bytes = _mbuddy.alloc(name_len + 1)
|
|
if name_buf == None: break
|
|
i: sizet = 0
|
|
for ch in name:
|
|
name_buf[i] = ch
|
|
i += 1
|
|
name_buf[name_len] = 0
|
|
out_names[count] = str(name_buf)
|
|
count += 1
|
|
|
|
posix.closedir(dirp)
|
|
return count
|
|
|
|
|
|
# =============================================================================
|
|
# Command Execution
|
|
# =============================================================================
|
|
|
|
def system_impl(cmd: str) -> int:
|
|
"""Execute a command line command (via the C runtime system()).
|
|
Output goes to the child terminal (inherits parent stdout/stderr). Return the exit code (0 = success)."""
|
|
return stdlib.system(cmd)
|