Files

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)