Files
TransPyC/includes/os/path.py
2026-07-18 19:25:40 +08:00

317 lines
9.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)