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)