317 lines
9.3 KiB
Python
317 lines
9.3 KiB
Python
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)
|