snapshot before regression test
This commit is contained in:
133
includes/os/__init__.py
Normal file
133
includes/os/__init__.py
Normal file
@@ -0,0 +1,133 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import platmacro
|
||||
import memhub
|
||||
import os.path
|
||||
import os._win32
|
||||
import os._posix
|
||||
|
||||
|
||||
# os 包入口
|
||||
# 根据 platmacro.IS_WINDOWS 分派到 _win32 或 _posix 后端
|
||||
# os.path 为跨平台纯字符串操作,直接可用
|
||||
|
||||
# 模块级全局 MBuddy 指针(由用户在 main 中赋值初始化)
|
||||
_mbuddy: memhub.MemBuddy | t.CPtr
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 文件/目录属性查询
|
||||
# =============================================================================
|
||||
|
||||
def exists(path: str) -> bool:
|
||||
"""检查文件/目录是否存在"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.exists(path)
|
||||
return os._posix.exists(path)
|
||||
|
||||
|
||||
def isdir(path: str) -> bool:
|
||||
"""检查路径是否为目录"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.isdir(path)
|
||||
return os._posix.isdir(path)
|
||||
|
||||
|
||||
def isfile(path: str) -> bool:
|
||||
"""检查路径是否为普通文件"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.isfile(path)
|
||||
return os._posix.isfile(path)
|
||||
|
||||
|
||||
def getsize(path: str) -> t.CInt64T:
|
||||
"""返回文件大小(字节)。失败返回 -1。"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.getsize(path)
|
||||
return os._posix.getsize(path)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 工作目录
|
||||
# =============================================================================
|
||||
|
||||
def getcwd() -> str:
|
||||
"""返回当前工作目录(通过 _mbuddy 分配内存)"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.getcwd()
|
||||
return os._posix.getcwd_impl()
|
||||
|
||||
|
||||
def chdir(path: str) -> int:
|
||||
"""切换当前工作目录。成功返回 0,失败返回 -1。"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.chdir(path)
|
||||
return os._posix.chdir_impl(path)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 文件/目录操作
|
||||
# =============================================================================
|
||||
|
||||
def mkdir(path: str) -> int:
|
||||
"""创建目录。成功返回 0,失败返回 -1。"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.mkdir(path)
|
||||
return os._posix.mkdir_impl(path)
|
||||
|
||||
|
||||
def remove(path: str) -> int:
|
||||
"""删除文件。成功返回 0,失败返回 -1。"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.remove(path)
|
||||
return os._posix.remove(path)
|
||||
|
||||
|
||||
def rmdir(path: str) -> int:
|
||||
"""删除空目录。成功返回 0,失败返回 -1。"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.rmdir(path)
|
||||
return os._posix.rmdir_impl(path)
|
||||
|
||||
|
||||
def rename(old_path: str, new_path: str) -> int:
|
||||
"""重命名/移动文件或目录。成功返回 0,失败返回 -1。"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.rename(old_path, new_path)
|
||||
return os._posix.rename_impl(old_path, new_path)
|
||||
|
||||
|
||||
def chmod(path: str, mode: int) -> int:
|
||||
"""修改文件权限。成功返回 0,失败返回 -1。
|
||||
Windows 上只处理只读位(S_IWUSR);POSIX 上使用完整权限位。"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.chmod(path, mode)
|
||||
return os._posix.chmod_impl(path, mode)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 目录列举
|
||||
# =============================================================================
|
||||
|
||||
def listdir(path: str, out_names: str, max_count: ULONG) -> ULONG:
|
||||
"""列举目录下的文件/子目录名。
|
||||
out_names 为调用者提供的字符串指针数组(char**),每个元素指向一个文件名。
|
||||
文件名通过 _mbuddy 分配内存。
|
||||
max_count 为 out_names 数组最大容量。
|
||||
返回实际列举的条目数。失败返回 0。"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.listdir(path, out_names, max_count)
|
||||
return os._posix.listdir(path, out_names, max_count)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 命令执行
|
||||
# =============================================================================
|
||||
|
||||
def system(cmd: str) -> int:
|
||||
"""执行命令行命令(通过 C 运行时 system())。
|
||||
命令输出直接到终端(继承父进程 stdout/stderr)。
|
||||
返回命令的退出码(0 表示成功)。"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return os._win32.system(cmd)
|
||||
return os._posix.system_impl(cmd)
|
||||
190
includes/os/_posix.py
Normal file
190
includes/os/_posix.py
Normal file
@@ -0,0 +1,190 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import posix
|
||||
import memhub
|
||||
import stdlib
|
||||
|
||||
|
||||
# POSIX 路径最大长度
|
||||
MAX_PATH_LEN: t.CDefine = 4096
|
||||
|
||||
# 模块级全局 MBuddy 指针(由用户在 main 中赋值初始化)
|
||||
_mbuddy: memhub.MemBuddy | t.CPtr
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 文件/目录属性查询
|
||||
# =============================================================================
|
||||
|
||||
def _stat(path: str) -> posix.Stat | t.CPtr:
|
||||
"""调用 stat 并返回 Stat 结构体指针。失败返回 None。
|
||||
注意:Stat 结构体通过 _mbuddy 分配,调用者负责释放。"""
|
||||
st: posix.Stat = posix.Stat()
|
||||
result: INT = posix.stat(path, t.CPtr(c.Addr(st)))
|
||||
if result != 0:
|
||||
return None
|
||||
return t.CPtr(c.Addr(st))
|
||||
|
||||
|
||||
def exists(path: str) -> bool:
|
||||
"""检查文件/目录是否存在"""
|
||||
result: INT = posix.access(path, posix.F_OK)
|
||||
if result == 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isdir(path: str) -> bool:
|
||||
"""检查路径是否为目录"""
|
||||
st: posix.Stat = posix.Stat()
|
||||
result: INT = posix.stat(path, t.CPtr(c.Addr(st)))
|
||||
if result != 0:
|
||||
return False
|
||||
if posix.S_ISDIR(st.st_mode):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isfile(path: str) -> bool:
|
||||
"""检查路径是否为普通文件"""
|
||||
st: posix.Stat = posix.Stat()
|
||||
result: INT = posix.stat(path, t.CPtr(c.Addr(st)))
|
||||
if result != 0:
|
||||
return False
|
||||
if posix.S_ISREG(st.st_mode):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def getsize(path: str) -> t.CInt64T:
|
||||
"""返回文件大小(字节)。失败返回 -1。"""
|
||||
st: posix.Stat = posix.Stat()
|
||||
result: INT = posix.stat(path, t.CPtr(c.Addr(st)))
|
||||
if result != 0:
|
||||
return -1
|
||||
return st.st_size
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 工作目录
|
||||
# =============================================================================
|
||||
|
||||
def getcwd_impl() -> str:
|
||||
"""返回当前工作目录(通过 _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:
|
||||
"""切换当前工作目录。成功返回 0,失败返回 -1。"""
|
||||
result: INT = posix.chdir(path)
|
||||
if result != 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 文件/目录操作
|
||||
# =============================================================================
|
||||
|
||||
def mkdir_impl(path: str) -> int:
|
||||
"""创建目录。成功返回 0,失败返回 -1。
|
||||
默认权限 0o755(rwxr-xr-x)。"""
|
||||
result: INT = posix.mkdir(path, 0o755)
|
||||
if result != 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
def remove(path: str) -> int:
|
||||
"""删除文件。成功返回 0,失败返回 -1。"""
|
||||
result: INT = posix.unlink(path)
|
||||
if result != 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
def rmdir_impl(path: str) -> int:
|
||||
"""删除空目录。成功返回 0,失败返回 -1。"""
|
||||
result: INT = posix.rmdir(path)
|
||||
if result != 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
def rename_impl(old_path: str, new_path: str) -> int:
|
||||
"""重命名/移动文件或目录。成功返回 0,失败返回 -1。"""
|
||||
result: INT = posix.rename(old_path, new_path)
|
||||
if result != 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
def chmod_impl(path: str, mode: int) -> int:
|
||||
"""修改文件权限。成功返回 0,失败返回 -1。
|
||||
mode 为 POSIX 权限位(如 0o755)。"""
|
||||
result: INT = posix.chmod(path, posix.mode_t(mode))
|
||||
if result != 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 目录列举(opendir / readdir / closedir)
|
||||
# =============================================================================
|
||||
|
||||
def listdir(path: str, out_names: str, max_count: ULONG) -> ULONG:
|
||||
"""列举目录下的文件/子目录名。
|
||||
out_names 为调用者提供的字符串指针数组(char**),每个元素指向一个文件名。
|
||||
文件名通过 _mbuddy 分配内存。
|
||||
max_count 为 out_names 数组最大容量。
|
||||
返回实际列举的条目数。失败返回 0。"""
|
||||
dirp: posix.DIR | t.CPtr = posix.opendir(path)
|
||||
if dirp == None:
|
||||
return 0
|
||||
|
||||
count: ULONG = 0
|
||||
while count < max_count:
|
||||
entry: posix.Dirent | t.CPtr = posix.readdir(dirp)
|
||||
if entry == None:
|
||||
break
|
||||
|
||||
# 读取 d_name 字段
|
||||
name: str = entry.d_name
|
||||
if name[0] == '.':
|
||||
if name[1] == 0:
|
||||
# "."
|
||||
continue
|
||||
if name[1] == '.' and name[2] == 0:
|
||||
# ".."
|
||||
continue
|
||||
|
||||
# 复制文件名到新分配的内存
|
||||
name_len: t.CSizeT = len(name)
|
||||
name_buf: bytes = _mbuddy.alloc(name_len + 1)
|
||||
if name_buf == None: break
|
||||
i: t.CSizeT = 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
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 命令执行
|
||||
# =============================================================================
|
||||
|
||||
def system_impl(cmd: str) -> int:
|
||||
"""执行命令行命令(通过 C 运行时 system())。
|
||||
命令输出直接到子进程的终端(继承父进程 stdout/stderr)。
|
||||
返回命令的退出码(0 表示成功)。"""
|
||||
return stdlib.system(cmd)
|
||||
234
includes/os/_win32.py
Normal file
234
includes/os/_win32.py
Normal file
@@ -0,0 +1,234 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import w32.win32base
|
||||
import w32.win32file
|
||||
import memhub
|
||||
import stdlib
|
||||
|
||||
|
||||
# Windows 路径最大长度
|
||||
MAX_PATH_LEN: t.CDefine = 260
|
||||
|
||||
# INVALID_FILE_ATTRIBUTES
|
||||
INVALID_FILE_ATTRIBUTES: t.CDefine = 0xFFFFFFFF
|
||||
|
||||
# 模块级全局 MBuddy 指针(由用户在 main 中赋值初始化)
|
||||
_mbuddy: memhub.MemBuddy | t.CPtr
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 文件/目录属性查询
|
||||
# =============================================================================
|
||||
|
||||
def _get_attrs(path: str) -> ULONG:
|
||||
"""获取文件属性。失败返回 INVALID_FILE_ATTRIBUTES。"""
|
||||
return w32.win32file.GetFileAttributesA(path)
|
||||
|
||||
|
||||
def exists(path: str) -> bool:
|
||||
"""检查文件/目录是否存在"""
|
||||
attrs: ULONG = _get_attrs(path)
|
||||
if attrs == INVALID_FILE_ATTRIBUTES:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def isdir(path: str) -> bool:
|
||||
"""检查路径是否为目录"""
|
||||
attrs: ULONG = _get_attrs(path)
|
||||
if attrs == INVALID_FILE_ATTRIBUTES:
|
||||
return False
|
||||
if (attrs & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isfile(path: str) -> bool:
|
||||
"""检查路径是否为普通文件"""
|
||||
attrs: ULONG = _get_attrs(path)
|
||||
if attrs == INVALID_FILE_ATTRIBUTES:
|
||||
return False
|
||||
if (attrs & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def getsize(path: str) -> t.CInt64T:
|
||||
"""返回文件大小(字节)。失败返回 -1。"""
|
||||
handle: w32.win32base.HANDLE = w32.win32file.CreateFileA(
|
||||
path, w32.win32file.GENERIC_READ, w32.win32file.FILE_SHARE_READ,
|
||||
t.CPtr(0), w32.win32file.OPEN_EXISTING, w32.win32file.FILE_ATTRIBUTE_NORMAL, t.CPtr(0)
|
||||
)
|
||||
if handle == w32.win32base.INVALID_HANDLE_VALUE:
|
||||
return -1
|
||||
li: w32.win32base.LARGE_INTEGER = w32.win32base.LARGE_INTEGER()
|
||||
result: BOOL = w32.win32file.GetFileSizeEx(handle, t.CPtr(c.Addr(li)))
|
||||
w32.win32base.CloseHandle(handle)
|
||||
if result == 0:
|
||||
return -1
|
||||
return li.QuadPart
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 工作目录
|
||||
# =============================================================================
|
||||
|
||||
def getcwd() -> str:
|
||||
"""返回当前工作目录(通过 _mbuddy 分配内存)"""
|
||||
buf: bytes = _mbuddy.alloc(MAX_PATH_LEN + 1)
|
||||
if buf == None: return None
|
||||
length: ULONG = w32.win32file.GetCurrentDirectoryA(MAX_PATH_LEN + 1, buf)
|
||||
if length == 0:
|
||||
return None
|
||||
if length > MAX_PATH_LEN:
|
||||
# 缓冲区不够,重新分配
|
||||
buf2: bytes = _mbuddy.alloc(length + 1)
|
||||
if buf2 == None: return None
|
||||
w32.win32file.GetCurrentDirectoryA(length + 1, buf2)
|
||||
buf2[length] = 0
|
||||
return str(buf2)
|
||||
buf[length] = 0
|
||||
return str(buf)
|
||||
|
||||
|
||||
def chdir(path: str) -> int:
|
||||
"""切换当前工作目录。成功返回 0,失败返回 -1。"""
|
||||
result: BOOL = w32.win32file.SetCurrentDirectoryA(path)
|
||||
if result == 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 文件/目录操作
|
||||
# =============================================================================
|
||||
|
||||
def mkdir(path: str) -> int:
|
||||
"""创建目录。成功返回 0,失败返回 -1。"""
|
||||
result: BOOL = w32.win32file.CreateDirectoryA(path, t.CPtr(0))
|
||||
if result == 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
def remove(path: str) -> int:
|
||||
"""删除文件。成功返回 0,失败返回 -1。"""
|
||||
result: BOOL = w32.win32file.DeleteFileA(path)
|
||||
if result == 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
def rmdir(path: str) -> int:
|
||||
"""删除空目录。成功返回 0,失败返回 -1。"""
|
||||
result: BOOL = w32.win32file.RemoveDirectoryA(path)
|
||||
if result == 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
def rename(old_path: str, new_path: str) -> int:
|
||||
"""重命名/移动文件或目录。成功返回 0,失败返回 -1。"""
|
||||
result: BOOL = w32.win32file.MoveFileExA(
|
||||
old_path, new_path,
|
||||
w32.win32file.MOVEFILE_REPLACE_EXISTING | w32.win32file.MOVEFILE_COPY_ALLOWED
|
||||
)
|
||||
if result == 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
def chmod(path: str, mode: int) -> int:
|
||||
"""修改文件属性(Windows 简化版:只处理只读位)。
|
||||
mode 的 S_IWUSR 位(0o200)为 0 时设为只读,为 1 时设为可写。
|
||||
成功返回 0,失败返回 -1。"""
|
||||
if (mode & 0o200) != 0:
|
||||
# 可写:清除只读属性
|
||||
result: BOOL = w32.win32file.SetFileAttributesA(
|
||||
path, w32.win32file.FILE_ATTRIBUTE_NORMAL
|
||||
)
|
||||
else:
|
||||
# 只读:设置只读属性
|
||||
result: BOOL = w32.win32file.SetFileAttributesA(
|
||||
path, w32.win32file.FILE_ATTRIBUTE_READONLY
|
||||
)
|
||||
if result == 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 目录列举(FindFirstFile / FindNextFile)
|
||||
# =============================================================================
|
||||
|
||||
def listdir(path: str, out_names: str, max_count: ULONG) -> ULONG:
|
||||
"""列举目录下的文件/子目录名。
|
||||
out_names 为调用者提供的字符串指针数组(char**),每个元素指向一个文件名。
|
||||
文件名通过 _mbuddy 分配内存。
|
||||
max_count 为 out_names 数组最大容量。
|
||||
返回实际列举的条目数。失败返回 0。"""
|
||||
# 构造搜索模式:path\*
|
||||
path_len: t.CSizeT = len(path)
|
||||
pattern: bytes = _mbuddy.alloc(path_len + 3)
|
||||
if pattern == None: return 0
|
||||
i: t.CSizeT = 0
|
||||
for ch in path:
|
||||
pattern[i] = ch
|
||||
i += 1
|
||||
# 添加 \* 后缀
|
||||
if path[path_len - 1] != '\\' and path[path_len - 1] != '/':
|
||||
pattern[i] = '\\'
|
||||
i += 1
|
||||
pattern[i] = '*'
|
||||
i += 1
|
||||
pattern[i] = 0
|
||||
|
||||
find_data: w32.win32file.WIN32_FIND_DATAA = w32.win32file.WIN32_FIND_DATAA()
|
||||
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, t.CPtr(c.Addr(find_data)))
|
||||
if handle == w32.win32base.INVALID_HANDLE_VALUE:
|
||||
return 0
|
||||
|
||||
count: ULONG = 0
|
||||
while count < max_count:
|
||||
# 跳过 . 和 ..
|
||||
name: str = find_data.cFileName
|
||||
if name[0] == '.':
|
||||
if name[1] == 0:
|
||||
# "."
|
||||
if w32.win32file.FindNextFileA(handle, t.CPtr(c.Addr(find_data))) == 0:
|
||||
break
|
||||
continue
|
||||
if name[1] == '.' and name[2] == 0:
|
||||
# ".."
|
||||
if w32.win32file.FindNextFileA(handle, t.CPtr(c.Addr(find_data))) == 0:
|
||||
break
|
||||
continue
|
||||
|
||||
# 复制文件名到新分配的内存
|
||||
name_len: t.CSizeT = len(name)
|
||||
name_buf: bytes = _mbuddy.alloc(name_len + 1)
|
||||
if name_buf == None: break
|
||||
j: t.CSizeT = 0
|
||||
for ch in name:
|
||||
name_buf[j] = ch
|
||||
j += 1
|
||||
name_buf[name_len] = 0
|
||||
out_names[count] = str(name_buf)
|
||||
count += 1
|
||||
|
||||
if w32.win32file.FindNextFileA(handle, t.CPtr(c.Addr(find_data))) == 0:
|
||||
break
|
||||
|
||||
w32.win32file.FindClose(handle)
|
||||
return count
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 命令执行
|
||||
# =============================================================================
|
||||
|
||||
def system(cmd: str) -> int:
|
||||
"""执行命令行命令(通过 C 运行时 system())。
|
||||
命令输出直接到子进程的终端(继承父进程 stdout/stderr)。
|
||||
返回命令的退出码(0 表示成功)。"""
|
||||
return stdlib.system(cmd)
|
||||
316
includes/os/path.py
Normal file
316
includes/os/path.py
Normal file
@@ -0,0 +1,316 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import string
|
||||
import memhub
|
||||
import platmacro
|
||||
|
||||
|
||||
# 模块级全局 MBuddy 指针(由用户在 main 中赋值初始化)
|
||||
_mbuddy: memhub.MemBuddy | t.CPtr
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 路径分隔符
|
||||
# =============================================================================
|
||||
|
||||
def sep() -> t.CChar:
|
||||
"""返回当前平台的路径分隔符(Windows: \\,POSIX: /)"""
|
||||
if platmacro.IS_WINDOWS:
|
||||
return '\\'
|
||||
return '/'
|
||||
|
||||
|
||||
def _is_sep(ch: t.CChar) -> bool:
|
||||
"""判断字符是否为路径分隔符(/ 或 \\,跨平台识别)"""
|
||||
return ch == '/' or ch == '\\'
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 内部辅助函数
|
||||
# =============================================================================
|
||||
|
||||
def _dup_str(s: str) -> str:
|
||||
"""复制字符串(通过全局 MBuddy 分配内存)"""
|
||||
if s == None: return None
|
||||
length: t.CSizeT = len(s)
|
||||
buf: bytes = _mbuddy.alloc(length + 1)
|
||||
if buf == None: return None
|
||||
i: t.CSizeT = 0
|
||||
for ch in s:
|
||||
buf[i] = ch
|
||||
i += 1
|
||||
buf[length] = 0
|
||||
return str(buf)
|
||||
|
||||
|
||||
def _substr(s: str, start: t.CSizeT, end: t.CSizeT) -> str:
|
||||
"""提取子串 s[start:end](通过全局 MBuddy 分配内存)"""
|
||||
if s == None: return None
|
||||
if start >= end: return _dup_str("")
|
||||
result_len: t.CSizeT = end - start
|
||||
buf: bytes = _mbuddy.alloc(result_len + 1)
|
||||
if buf == None: return None
|
||||
i: t.CSizeT = 0
|
||||
k: t.CSizeT = start
|
||||
while k < end:
|
||||
buf[i] = s[k]
|
||||
i += 1
|
||||
k += 1
|
||||
buf[i] = 0
|
||||
return str(buf)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 路径判断
|
||||
# =============================================================================
|
||||
|
||||
def isabs(path: str) -> bool:
|
||||
"""判断路径是否为绝对路径。
|
||||
POSIX: 以 / 开头。
|
||||
Windows: 以 \\ 开头(UNC)或 X:\\ / X:/ 开头(盘符)。"""
|
||||
if path == None: return False
|
||||
if path[0] == 0: return False
|
||||
if path[0] == '/' or path[0] == '\\': return True
|
||||
if platmacro.IS_WINDOWS:
|
||||
# 检查 X:\ 或 X:/ 模式
|
||||
c0: t.CChar = path[0]
|
||||
if (c0 >= 'A' and c0 <= 'Z') or (c0 >= 'a' and c0 <= 'z'):
|
||||
if path[1] == ':':
|
||||
if path[2] == '\\' or path[2] == '/':
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 路径拼接与分割
|
||||
# =============================================================================
|
||||
|
||||
def join(path1: str, path2: str) -> str:
|
||||
"""拼接两个路径。如果 path2 是绝对路径,返回 path2 的副本。"""
|
||||
if path1 == None or path1[0] == 0:
|
||||
return _dup_str(path2)
|
||||
if path2 == None or path2[0] == 0:
|
||||
return _dup_str(path1)
|
||||
if isabs(path2):
|
||||
return _dup_str(path2)
|
||||
|
||||
len1: t.CSizeT = len(path1)
|
||||
len2: t.CSizeT = len(path2)
|
||||
|
||||
# 检查 path1 是否以分隔符结尾
|
||||
need_sep: bool = True
|
||||
if _is_sep(path1[len1 - 1]):
|
||||
need_sep = False
|
||||
|
||||
total: t.CSizeT = len1 + len2 + 1
|
||||
if need_sep:
|
||||
total += 1
|
||||
|
||||
buf: bytes = _mbuddy.alloc(total)
|
||||
if buf == None: return None
|
||||
|
||||
# 复制 path1
|
||||
i: t.CSizeT = 0
|
||||
for ch in path1:
|
||||
buf[i] = ch
|
||||
i += 1
|
||||
|
||||
# 添加分隔符
|
||||
if need_sep:
|
||||
buf[i] = sep()
|
||||
i += 1
|
||||
|
||||
# 复制 path2
|
||||
for ch in path2:
|
||||
buf[i] = ch
|
||||
i += 1
|
||||
|
||||
buf[i] = 0
|
||||
return str(buf)
|
||||
|
||||
|
||||
def basename(path: str) -> str:
|
||||
"""返回路径的文件名部分(最后一个分隔符之后的内容)"""
|
||||
if path == None: return None
|
||||
length: t.CSizeT = len(path)
|
||||
if length == 0: return _dup_str("")
|
||||
|
||||
# 从后向前找分隔符
|
||||
i: t.CSizeT = length
|
||||
while i > 0:
|
||||
i -= 1
|
||||
if _is_sep(path[i]):
|
||||
return _substr(path, i + 1, length)
|
||||
|
||||
# 没有分隔符,返回整个路径的副本
|
||||
return _dup_str(path)
|
||||
|
||||
|
||||
def dirname(path: str) -> str:
|
||||
"""返回路径的目录部分(最后一个分隔符之前的内容)"""
|
||||
if path == None: return None
|
||||
length: t.CSizeT = len(path)
|
||||
if length == 0: return _dup_str("")
|
||||
|
||||
# 从后向前找分隔符
|
||||
i: t.CSizeT = length
|
||||
while i > 0:
|
||||
i -= 1
|
||||
if _is_sep(path[i]):
|
||||
if i == 0:
|
||||
# 路径以分隔符开头,返回根目录
|
||||
return _dup_str("/")
|
||||
return _substr(path, 0, i)
|
||||
|
||||
# 没有分隔符,返回空字符串
|
||||
return _dup_str("")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 扩展名处理
|
||||
# =============================================================================
|
||||
|
||||
def splitext_name(path: str) -> str:
|
||||
"""返回路径去除扩展名后的主名部分。
|
||||
扩展名从最后一个 '.' 开始(该 '.' 不能是路径最后一个组件的第一个字符)。"""
|
||||
if path == None: return None
|
||||
length: t.CSizeT = len(path)
|
||||
if length == 0: return _dup_str("")
|
||||
|
||||
# 从后向前找 '.',遇到分隔符停止
|
||||
i: t.CSizeT = length
|
||||
while i > 0:
|
||||
i -= 1
|
||||
if path[i] == '.':
|
||||
return _substr(path, 0, i)
|
||||
if _is_sep(path[i]):
|
||||
break
|
||||
|
||||
# 没有扩展名,返回整个路径的副本
|
||||
return _dup_str(path)
|
||||
|
||||
|
||||
def splitext_ext(path: str) -> str:
|
||||
"""返回路径的扩展名部分(包含 '.')。无扩展名时返回空字符串。"""
|
||||
if path == None: return None
|
||||
length: t.CSizeT = len(path)
|
||||
if length == 0: return _dup_str("")
|
||||
|
||||
# 从后向前找 '.',遇到分隔符停止
|
||||
i: t.CSizeT = length
|
||||
while i > 0:
|
||||
i -= 1
|
||||
if path[i] == '.':
|
||||
return _substr(path, i, length)
|
||||
if _is_sep(path[i]):
|
||||
break
|
||||
|
||||
# 没有扩展名,返回空字符串
|
||||
return _dup_str("")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 路径规范化
|
||||
# =============================================================================
|
||||
|
||||
def normpath(path: str) -> str:
|
||||
"""规范化路径:统一分隔符、消除多余的 . 和 .. 组件、合并连续分隔符。
|
||||
注意:不解析符号链接,不调用系统 API。"""
|
||||
if path == None: return None
|
||||
length: t.CSizeT = len(path)
|
||||
if length == 0: return _dup_str(".")
|
||||
|
||||
# 分配输出缓冲区(最坏情况:长度不变 + 前缀 /)
|
||||
buf: bytes = _mbuddy.alloc(length + 2)
|
||||
if buf == None: return None
|
||||
|
||||
out_pos: t.CSizeT = 0
|
||||
i: t.CSizeT = 0
|
||||
s: str = sep()
|
||||
|
||||
# 处理绝对路径前缀
|
||||
is_absolute: bool = False
|
||||
if _is_sep(path[0]):
|
||||
is_absolute = True
|
||||
buf[out_pos] = s
|
||||
out_pos += 1
|
||||
i = 1
|
||||
|
||||
# Windows 盘符前缀(X:\ 或 X:/)
|
||||
drive_prefix_len: t.CSizeT = 0
|
||||
if platmacro.IS_WINDOWS:
|
||||
if length >= 3:
|
||||
c0: t.CChar = path[0]
|
||||
if (c0 >= 'A' and c0 <= 'Z') or (c0 >= 'a' and c0 <= 'z'):
|
||||
if path[1] == ':':
|
||||
if _is_sep(path[2]):
|
||||
buf[out_pos] = path[0]
|
||||
out_pos += 1
|
||||
buf[out_pos] = ':'
|
||||
out_pos += 1
|
||||
buf[out_pos] = s
|
||||
out_pos += 1
|
||||
drive_prefix_len = 3
|
||||
i = 3
|
||||
is_absolute = True
|
||||
|
||||
# 逐组件处理
|
||||
while i < length:
|
||||
# 跳过连续分隔符
|
||||
while i < length and _is_sep(path[i]):
|
||||
i += 1
|
||||
if i >= length:
|
||||
break
|
||||
|
||||
# 提取组件
|
||||
comp_start: t.CSizeT = i
|
||||
while i < length and not _is_sep(path[i]):
|
||||
i += 1
|
||||
comp_len: t.CSizeT = i - comp_start
|
||||
|
||||
# 处理 "." 组件:跳过
|
||||
if comp_len == 1 and path[comp_start] == '.':
|
||||
continue
|
||||
|
||||
# 处理 ".." 组件:回退一级
|
||||
if comp_len == 2 and path[comp_start] == '.' and path[comp_start + 1] == '.':
|
||||
if out_pos > drive_prefix_len:
|
||||
# 回退到上一个分隔符前
|
||||
out_pos -= 1
|
||||
while out_pos > drive_prefix_len and not _is_sep(buf[out_pos - 1]):
|
||||
out_pos -= 1
|
||||
# 如果回退到了根目录前缀,不删除前缀
|
||||
if out_pos == drive_prefix_len and is_absolute:
|
||||
out_pos = drive_prefix_len
|
||||
if drive_prefix_len == 0:
|
||||
# POSIX 根:保留 /
|
||||
buf[out_pos] = s
|
||||
out_pos += 1
|
||||
continue
|
||||
|
||||
# 普通组件:复制到输出
|
||||
if out_pos > drive_prefix_len and not (drive_prefix_len == 0 and out_pos == 1 and is_absolute):
|
||||
buf[out_pos] = s
|
||||
out_pos += 1
|
||||
elif out_pos == 0 and not is_absolute:
|
||||
pass # 第一个组件,不需要前导分隔符
|
||||
elif is_absolute and out_pos == drive_prefix_len:
|
||||
# 已经有前导分隔符,不重复添加
|
||||
pass
|
||||
else:
|
||||
buf[out_pos] = s
|
||||
out_pos += 1
|
||||
|
||||
j: t.CSizeT = comp_start
|
||||
while j < comp_start + comp_len:
|
||||
buf[out_pos] = path[j]
|
||||
out_pos += 1
|
||||
j += 1
|
||||
|
||||
# 处理空结果
|
||||
if out_pos == 0:
|
||||
buf[out_pos] = '.'
|
||||
out_pos += 1
|
||||
|
||||
buf[out_pos] = 0
|
||||
return str(buf)
|
||||
Reference in New Issue
Block a user