Browse Source

add os_path support

pikastech 2 years ago
parent
commit
34dedcde2d

+ 42 - 0
examples/os/os_path.py

@@ -0,0 +1,42 @@
+import os
+p = os.path
+assert p.join('dir', 'file.txt') == 'dir/file.txt'
+assert p.join('/home/user', 'dir', 'file.txt') == '/home/user/dir/file.txt'
+
+# Test split method
+assert p.split('dir/file.txt') == ('dir', 'file.txt')
+assert p.split('/home/user/dir/file.txt') == ('/home/user/dir', 'file.txt')
+    
+# Test splitext method
+assert p.splitext('file.txt') == ('file', '.txt')
+assert p.splitext('/home/user/file.tar.gz') == ('/home/user/file.tar', '.gz')
+    
+# Test basename method
+assert p.basename('dir/file.txt') == 'file.txt'
+assert p.basename('/home/user/dir/file.txt') == 'file.txt'
+
+# Test dirname method
+assert p.dirname('dir/file.txt') == 'dir'
+assert p.dirname('/home/user/dir/file.txt') == '/home/user/dir'
+
+# Test exists method
+assert p.exists('config/pika_config_void') == False
+assert p.exists('/root/go') == True
+
+# Test isdir method
+assert p.isdir('config/pika_config_void.h') == False
+assert p.isdir('config') == True
+
+# Test isfile method
+assert p.isfile('config') == False
+assert p.isfile('config/pika_config_void.h') == True
+
+# Test isabs method
+assert p.isabs('dir/file.txt') == False
+assert p.isabs('/home/user/file.txt') == True
+
+# Test abspath method
+assert p.abspath('config/pika_config_void.h') == "/root/pikascript/port/linux/config/pika_config_void.h"
+assert p.abspath('/root/go') == "/root/go"
+
+print("PASS")

+ 7 - 0
package/os/os.c

@@ -103,3 +103,10 @@ void os_remove(PikaObj* self, char* filename) {
         pika_platform_printf("remove error\r\n");
     }
 }
+
+void os_rename(PikaObj *self, char* old, char* new){
+    if (os_rename_platform(old, new) < 0) {
+        obj_setErrorCode(self, PIKA_RES_ERR_IO_ERROR);
+        pika_platform_printf("rename error\r\n");
+    }
+}

+ 41 - 5
package/os/os.pyi

@@ -5,11 +5,6 @@ O_APPEND: int
 O_CREAT: int
 
 
-class fileStat:
-    def st_size(self) -> int:
-        pass
-
-
 def __init__(self):
     pass
 
@@ -60,3 +55,44 @@ def fstat(self, fd: FILE) -> fileStat:
 
 def remove(self, filename: str):
     pass
+
+
+def rename(self, old: str, new: str):
+    pass
+
+
+class fileStat:
+    def st_size(self) -> int:
+        pass
+
+
+class path:
+    def join(self, *paths) -> str:
+        pass
+
+    def split(self, path: str) -> tuple:
+        pass
+
+    def splitext(self, path: str) -> tuple:
+        pass
+
+    def basename(self, path: str) -> str:
+        pass
+
+    def dirname(self, path: str) -> str:
+        pass
+
+    def exists(self, path: str) -> bool:
+        pass
+
+    def isdir(self, path: str) -> bool:
+        pass
+
+    def isfile(self, path: str) -> bool:
+        pass
+
+    def isabs(self, path: str) -> bool:
+        pass
+
+    def abspath(self, path: str) -> str:
+        pass

+ 390 - 0
package/os/os_path.c

@@ -0,0 +1,390 @@
+#include "os_path.h"
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/stat.h>
+#include <unistd.h>
+#include "PikaStdData_List.h"
+#include "PikaStdData_Tuple.h"
+#endif
+
+#ifdef _WIN32
+#define PATH_SEPARATOR '\\'
+#define PATH_SEPARATOR_STRING "\\"
+#else
+#define PATH_SEPARATOR '/'
+#define PATH_SEPARATOR_STRING "/"
+#endif
+
+#define IS_PATH_SEP(ch) ((ch) == '/' || (ch) == '\\')
+
+// 返回指定路径的绝对路径
+char* os_path_abspath(PikaObj* self, char* path) {
+    char* abs_path = NULL;
+
+#ifdef _WIN32
+    DWORD size = GetFullPathNameA(path, 0, NULL, NULL);
+    if (size == 0) {
+        // 获取绝对路径失败
+        return NULL;
+    }
+
+    abs_path = (char*)malloc(size * sizeof(char));
+    if (abs_path == NULL) {
+        // 内存分配失败
+        return NULL;
+    }
+
+    DWORD ret_size = GetFullPathNameA(path, size, abs_path, NULL);
+    if (ret_size == 0 || ret_size > size) {
+        // 获取绝对路径失败
+        free(abs_path);
+        return NULL;
+    }
+#else
+    char* cwd = getcwd(NULL, 0);
+    if (cwd == NULL) {
+        // 获取当前工作目录失败
+        return NULL;
+    }
+
+    abs_path = realpath(path, NULL);
+    if (abs_path == NULL) {
+        // 获取绝对路径失败
+        free(cwd);
+        return NULL;
+    }
+
+    // 如果路径不是绝对路径,则将其转换为绝对路径
+    if (abs_path[0] != '/') {
+        char* temp_path =
+            (char*)malloc((strlen(cwd) + strlen(abs_path) + 2) * sizeof(char));
+        if (temp_path == NULL) {
+            // 内存分配失败
+            free(cwd);
+            free(abs_path);
+            return NULL;
+        }
+
+        strcpy(temp_path, cwd);
+        strcat(temp_path, "/");
+        strcat(temp_path, abs_path);
+
+        free(abs_path);
+        abs_path = temp_path;
+    }
+
+    free(cwd);
+#endif
+    char* res = obj_cacheStr(self, abs_path);
+    free(abs_path);
+    return res;
+}
+
+// 判断指定路径是否存在
+PIKA_BOOL os_path_exists(PikaObj* self, char* path) {
+#ifdef _WIN32
+    DWORD attr = GetFileAttributesA(path);
+    if (attr == INVALID_FILE_ATTRIBUTES) {
+        // 获取文件属性失败
+        return PIKA_FALSE;
+    }
+
+    return PIKA_TRUE;
+#else
+    struct stat statbuf;
+    if (stat(path, &statbuf) == -1) {
+        // 获取文件状态失败
+        return PIKA_FALSE;
+    }
+
+    return PIKA_TRUE;
+#endif
+}
+
+// 判断指定路径是否为绝对路径
+PIKA_BOOL os_path_isabs(PikaObj* self, char* path) {
+#ifdef _WIN32
+    if (path[0] == '\\' || path[0] == '/') {
+        return PIKA_TRUE;
+    }
+
+    if (strlen(path) > 1 && path[1] == ':') {
+        return PIKA_TRUE;
+    }
+
+    return PIKA_FALSE;
+#else
+    if (path[0] == '/') {
+        return PIKA_TRUE;
+    }
+
+    return PIKA_FALSE;
+#endif
+}
+
+// Returns true if the given path is a directory, false otherwise.
+PIKA_BOOL os_path_isdir(PikaObj* self, char* path) {
+    PIKA_BOOL is_dir = PIKA_FALSE;
+#ifdef _WIN32
+    DWORD attrs = GetFileAttributes(path);
+    if (attrs != INVALID_FILE_ATTRIBUTES) {
+        is_dir =
+            (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0 ? PIKA_TRUE : PIKA_FALSE;
+    }
+#else
+    struct stat st;
+    if (stat(path, &st) == 0) {
+        is_dir = S_ISDIR(st.st_mode) ? PIKA_TRUE : PIKA_FALSE;
+    }
+#endif
+    return is_dir;
+}
+
+// Returns true if the given path is a regular file, false otherwise.
+PIKA_BOOL os_path_isfile(PikaObj* self, char* path) {
+    PIKA_BOOL is_file = PIKA_FALSE;
+#ifdef _WIN32
+    DWORD attrs = GetFileAttributes(path);
+    if (attrs != INVALID_FILE_ATTRIBUTES) {
+        is_file =
+            (attrs & FILE_ATTRIBUTE_DIRECTORY) == 0 ? PIKA_TRUE : PIKA_FALSE;
+    }
+#else
+    struct stat st;
+    if (stat(path, &st) == 0) {
+        is_file = S_ISREG(st.st_mode) ? PIKA_TRUE : PIKA_FALSE;
+    }
+#endif
+    return is_file;
+}
+
+char* os_path_join(PikaObj* self, PikaTuple* paths) {
+    size_t total_len = 1;  // Start with a single null terminator
+    int num_paths = pikaTuple_getSize(paths);
+    for (int i = 0; i < num_paths; i++) {
+        const char* path = pikaTuple_getStr(paths, i);
+        total_len += strlen(path);
+        if (i < num_paths - 1) {
+            if (!IS_PATH_SEP(path[strlen(path) - 1])) {
+                total_len++;
+            }
+        }
+    }
+    char* result = (char*)malloc(total_len);
+    if (!result) {
+        return NULL;
+    }
+    result[0] = '\0';
+    for (int i = 0; i < num_paths; i++) {
+        const char* path = pikaTuple_getStr(paths, i);
+        if (!path || !path[0]) {
+            continue;
+        }
+        if (i == 0) {
+            // First component
+            strncat(result, path, total_len);
+        } else {
+            // Subsequent components
+            if (!IS_PATH_SEP(result[strlen(result) - 1])) {
+                strncat(result, PATH_SEPARATOR_STRING, total_len);
+            }
+            strncat(result, path, total_len);
+        }
+    }
+    char* res = obj_cacheStr(self, result);
+    free(result);
+    return res;
+}
+
+char* os_path_basename(PikaObj* self, char* path) {
+    char* sep_pos = strrchr(path, PATH_SEPARATOR);
+    if (sep_pos == NULL) {
+        return obj_cacheStr(self, path);
+    } else {
+        return obj_cacheStr(self, sep_pos + 1);
+    }
+}
+
+char* os_path_dirname(PikaObj* self, char* path) {
+    char* sep_pos = strrchr(path, PATH_SEPARATOR);
+    if (sep_pos == NULL) {
+        return obj_cacheStr(self, ".");
+    } else if (sep_pos == path) {
+        return obj_cacheStr(self, PATH_SEPARATOR_STRING);
+    } else {
+        int dirname_len = sep_pos - path;
+        char* dirname = malloc(dirname_len + 1);
+        memcpy(dirname, path, dirname_len);
+        dirname[dirname_len] = '\0';
+        char* res = obj_cacheStr(self, dirname);
+        free (dirname);
+        return res;
+    }
+}
+
+int _os_path_split(char* path, char** folder, char** file) {
+    if (path == NULL || folder == NULL || file == NULL) {
+        return -1;
+    }
+    char* p = strrchr(path, PATH_SEPARATOR);
+    if (p) {
+        /* 字符串最后一个路径分隔符的位置 */
+        size_t idx = p - path;
+        /* 获取最后一个路径分隔符之前的路径 */
+        *folder = malloc(idx + 2);
+        if (*folder == NULL) {
+            return -1;
+        }
+        strncpy(*folder, path, idx + 1);
+        (*folder)[idx + 1] = '\0';
+        /* 获取最后一个路径分隔符之后的文件名 */
+        *file = strdup(p + 1);
+        if (*file == NULL) {
+            free(*folder);
+            return -1;
+        }
+        return 0;
+    } else {
+        /* 如果路径没有分隔符,则返回路径本身和空字符串 */
+        *folder = strdup(path);
+        if (*folder == NULL) {
+            return -1;
+        }
+        *file = strdup("");
+        if (*file == NULL) {
+            free(*folder);
+            return -1;
+        }
+        return 0;
+    }
+}
+
+int _os_path_splitext(char* path, char** file, char** ext) {
+    char* p = strrchr(path, '.');
+    if (p) {
+        /* 字符串最后一个点的位置 */
+        size_t idx = p - path;
+        /* 获取点之前的路径 */
+        *file = malloc(idx + 1);
+        if (!(*file)) {
+            /* 内存分配失败 */
+            return -1;
+        }
+        strncpy(*file, path, idx);
+        (*file)[idx] = '\0';
+        /* 获取点之后的扩展名 */
+        *ext = strdup(p);
+        if (!(*ext)) {
+            /* 内存分配失败 */
+            free(*file);
+            return -1;
+        }
+        return 0;
+    } else {
+        /* 如果没有扩展名,则返回路径本身和空字符串 */
+        *file = strdup(path);
+        if (!(*file)) {
+            /* 内存分配失败 */
+            return -1;
+        }
+        *ext = strdup("");
+        if (!(*ext)) {
+            /* 内存分配失败 */
+            free(*file);
+            return -1;
+        }
+        return 0;
+    }
+}
+
+PikaObj* os_path_split(PikaObj* self, char* path) {
+    char* folder = NULL;
+    char* file = NULL;
+    PikaObj* tuple = NULL;
+    Arg* aFolder = NULL;
+    Arg* aFile = NULL;
+
+    if (0 != _os_path_split(path, &folder, &file)) {
+        goto __exit;  // 发生错误,跳转到 __exit 处做资源回收
+    }
+
+    tuple = newNormalObj(New_PikaStdData_Tuple);
+    PikaStdData_Tuple___init__(tuple);
+
+    aFolder = arg_newStr(folder);
+    aFile = arg_newStr(file);
+
+    PikaStdData_List_append(tuple, aFolder);
+    PikaStdData_List_append(tuple, aFile);
+
+    arg_deinit(aFolder);
+    arg_deinit(aFile);
+    free(folder);
+    free(file);
+
+    return tuple;
+__exit:
+    if (aFolder) {
+        arg_deinit(aFolder);
+    }
+    if (aFile) {
+        arg_deinit(aFile);
+    }
+    if (tuple) {
+        obj_deinit(tuple);
+    }
+    if (folder) {
+        free(folder);
+    }
+    if (file) {
+        free(file);
+    }
+    return NULL;
+}
+
+PikaObj* os_path_splitext(PikaObj* self, char* path) {
+    char* file = NULL;
+    char* ext = NULL;
+    PikaObj* tuple = NULL;
+    Arg* aFile = NULL;
+    Arg* aExt = NULL;
+
+    if (0 != _os_path_splitext(path, &file, &ext)) {
+        goto __exit;  // 发生错误,跳转到 __exit 处做资源回收
+    }
+
+    tuple = newNormalObj(New_PikaStdData_Tuple);
+    PikaStdData_Tuple___init__(tuple);
+    aFile = arg_newStr(file);
+    aExt = arg_newStr(ext);
+    PikaStdData_List_append(tuple, aFile);
+    PikaStdData_List_append(tuple, aExt);
+
+    arg_deinit(aFile);
+    arg_deinit(aExt);
+    free(file);
+    free(ext);
+
+    return tuple;
+
+__exit:
+    if (aFile) {
+        arg_deinit(aFile);
+    }
+    if (aExt) {
+        arg_deinit(aExt);
+    }
+    if (tuple) {
+        obj_deinit(tuple);
+    }
+    if (file) {
+        free(file);
+    }
+    if (ext) {
+        free(ext);
+    }
+    return NULL;
+}

+ 10 - 0
package/os/os_platform.c

@@ -201,3 +201,13 @@ int os_remove_platform(char* filename) {
     ret = remove(dirpath);
     return ret;
 }
+
+int os_rename_platform(char* old, char* new) {
+    if (NULL == old || NULL == new) {
+        return -1;
+    }
+    if (0 != rename(old, new)) {
+        return -1;
+    }
+    return 0;
+}

+ 1 - 0
package/os/os_platform.h

@@ -35,5 +35,6 @@ int os_chdir_platform(char* path);
 int os_rmdir_platform(char* path);
 int os_remove_platform(char* filename);
 int os_getFileSize(PikaObj* fd);
+int os_rename_platform(char* old, char* new);
 
 #endif

+ 2 - 1
port/linux/.vscode/launch.json

@@ -13,7 +13,8 @@
             "args": [
                 // "--gtest_filter=pikaui.*"
                 // "--gtest_filter=doc.*"
-                "--gtest_filter=packtool.*"
+                // "--gtest_filter=packtool.*"
+                "--gtest_filter=os.path"
             ],
             "stopAtEntry": false,
             "cwd": "${workspaceFolder}",

+ 41 - 5
port/linux/package/pikascript/os.pyi

@@ -5,11 +5,6 @@ O_APPEND: int
 O_CREAT: int
 
 
-class fileStat:
-    def st_size(self) -> int:
-        pass
-
-
 def __init__(self):
     pass
 
@@ -60,3 +55,44 @@ def fstat(self, fd: FILE) -> fileStat:
 
 def remove(self, filename: str):
     pass
+
+
+def rename(self, old: str, new: str):
+    pass
+
+
+class fileStat:
+    def st_size(self) -> int:
+        pass
+
+
+class path:
+    def join(self, *paths) -> str:
+        pass
+
+    def split(self, path: str) -> tuple:
+        pass
+
+    def splitext(self, path: str) -> tuple:
+        pass
+
+    def basename(self, path: str) -> str:
+        pass
+
+    def dirname(self, path: str) -> str:
+        pass
+
+    def exists(self, path: str) -> bool:
+        pass
+
+    def isdir(self, path: str) -> bool:
+        pass
+
+    def isfile(self, path: str) -> bool:
+        pass
+
+    def isabs(self, path: str) -> bool:
+        pass
+
+    def abspath(self, path: str) -> str:
+        pass

+ 7 - 0
port/linux/package/pikascript/pikascript-lib/os/os.c

@@ -103,3 +103,10 @@ void os_remove(PikaObj* self, char* filename) {
         pika_platform_printf("remove error\r\n");
     }
 }
+
+void os_rename(PikaObj *self, char* old, char* new){
+    if (os_rename_platform(old, new) < 0) {
+        obj_setErrorCode(self, PIKA_RES_ERR_IO_ERROR);
+        pika_platform_printf("rename error\r\n");
+    }
+}

+ 390 - 0
port/linux/package/pikascript/pikascript-lib/os/os_path.c

@@ -0,0 +1,390 @@
+#include "os_path.h"
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/stat.h>
+#include <unistd.h>
+#include "PikaStdData_List.h"
+#include "PikaStdData_Tuple.h"
+#endif
+
+#ifdef _WIN32
+#define PATH_SEPARATOR '\\'
+#define PATH_SEPARATOR_STRING "\\"
+#else
+#define PATH_SEPARATOR '/'
+#define PATH_SEPARATOR_STRING "/"
+#endif
+
+#define IS_PATH_SEP(ch) ((ch) == '/' || (ch) == '\\')
+
+// 返回指定路径的绝对路径
+char* os_path_abspath(PikaObj* self, char* path) {
+    char* abs_path = NULL;
+
+#ifdef _WIN32
+    DWORD size = GetFullPathNameA(path, 0, NULL, NULL);
+    if (size == 0) {
+        // 获取绝对路径失败
+        return NULL;
+    }
+
+    abs_path = (char*)malloc(size * sizeof(char));
+    if (abs_path == NULL) {
+        // 内存分配失败
+        return NULL;
+    }
+
+    DWORD ret_size = GetFullPathNameA(path, size, abs_path, NULL);
+    if (ret_size == 0 || ret_size > size) {
+        // 获取绝对路径失败
+        free(abs_path);
+        return NULL;
+    }
+#else
+    char* cwd = getcwd(NULL, 0);
+    if (cwd == NULL) {
+        // 获取当前工作目录失败
+        return NULL;
+    }
+
+    abs_path = realpath(path, NULL);
+    if (abs_path == NULL) {
+        // 获取绝对路径失败
+        free(cwd);
+        return NULL;
+    }
+
+    // 如果路径不是绝对路径,则将其转换为绝对路径
+    if (abs_path[0] != '/') {
+        char* temp_path =
+            (char*)malloc((strlen(cwd) + strlen(abs_path) + 2) * sizeof(char));
+        if (temp_path == NULL) {
+            // 内存分配失败
+            free(cwd);
+            free(abs_path);
+            return NULL;
+        }
+
+        strcpy(temp_path, cwd);
+        strcat(temp_path, "/");
+        strcat(temp_path, abs_path);
+
+        free(abs_path);
+        abs_path = temp_path;
+    }
+
+    free(cwd);
+#endif
+    char* res = obj_cacheStr(self, abs_path);
+    free(abs_path);
+    return res;
+}
+
+// 判断指定路径是否存在
+PIKA_BOOL os_path_exists(PikaObj* self, char* path) {
+#ifdef _WIN32
+    DWORD attr = GetFileAttributesA(path);
+    if (attr == INVALID_FILE_ATTRIBUTES) {
+        // 获取文件属性失败
+        return PIKA_FALSE;
+    }
+
+    return PIKA_TRUE;
+#else
+    struct stat statbuf;
+    if (stat(path, &statbuf) == -1) {
+        // 获取文件状态失败
+        return PIKA_FALSE;
+    }
+
+    return PIKA_TRUE;
+#endif
+}
+
+// 判断指定路径是否为绝对路径
+PIKA_BOOL os_path_isabs(PikaObj* self, char* path) {
+#ifdef _WIN32
+    if (path[0] == '\\' || path[0] == '/') {
+        return PIKA_TRUE;
+    }
+
+    if (strlen(path) > 1 && path[1] == ':') {
+        return PIKA_TRUE;
+    }
+
+    return PIKA_FALSE;
+#else
+    if (path[0] == '/') {
+        return PIKA_TRUE;
+    }
+
+    return PIKA_FALSE;
+#endif
+}
+
+// Returns true if the given path is a directory, false otherwise.
+PIKA_BOOL os_path_isdir(PikaObj* self, char* path) {
+    PIKA_BOOL is_dir = PIKA_FALSE;
+#ifdef _WIN32
+    DWORD attrs = GetFileAttributes(path);
+    if (attrs != INVALID_FILE_ATTRIBUTES) {
+        is_dir =
+            (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0 ? PIKA_TRUE : PIKA_FALSE;
+    }
+#else
+    struct stat st;
+    if (stat(path, &st) == 0) {
+        is_dir = S_ISDIR(st.st_mode) ? PIKA_TRUE : PIKA_FALSE;
+    }
+#endif
+    return is_dir;
+}
+
+// Returns true if the given path is a regular file, false otherwise.
+PIKA_BOOL os_path_isfile(PikaObj* self, char* path) {
+    PIKA_BOOL is_file = PIKA_FALSE;
+#ifdef _WIN32
+    DWORD attrs = GetFileAttributes(path);
+    if (attrs != INVALID_FILE_ATTRIBUTES) {
+        is_file =
+            (attrs & FILE_ATTRIBUTE_DIRECTORY) == 0 ? PIKA_TRUE : PIKA_FALSE;
+    }
+#else
+    struct stat st;
+    if (stat(path, &st) == 0) {
+        is_file = S_ISREG(st.st_mode) ? PIKA_TRUE : PIKA_FALSE;
+    }
+#endif
+    return is_file;
+}
+
+char* os_path_join(PikaObj* self, PikaTuple* paths) {
+    size_t total_len = 1;  // Start with a single null terminator
+    int num_paths = pikaTuple_getSize(paths);
+    for (int i = 0; i < num_paths; i++) {
+        const char* path = pikaTuple_getStr(paths, i);
+        total_len += strlen(path);
+        if (i < num_paths - 1) {
+            if (!IS_PATH_SEP(path[strlen(path) - 1])) {
+                total_len++;
+            }
+        }
+    }
+    char* result = (char*)malloc(total_len);
+    if (!result) {
+        return NULL;
+    }
+    result[0] = '\0';
+    for (int i = 0; i < num_paths; i++) {
+        const char* path = pikaTuple_getStr(paths, i);
+        if (!path || !path[0]) {
+            continue;
+        }
+        if (i == 0) {
+            // First component
+            strncat(result, path, total_len);
+        } else {
+            // Subsequent components
+            if (!IS_PATH_SEP(result[strlen(result) - 1])) {
+                strncat(result, PATH_SEPARATOR_STRING, total_len);
+            }
+            strncat(result, path, total_len);
+        }
+    }
+    char* res = obj_cacheStr(self, result);
+    free(result);
+    return res;
+}
+
+char* os_path_basename(PikaObj* self, char* path) {
+    char* sep_pos = strrchr(path, PATH_SEPARATOR);
+    if (sep_pos == NULL) {
+        return obj_cacheStr(self, path);
+    } else {
+        return obj_cacheStr(self, sep_pos + 1);
+    }
+}
+
+char* os_path_dirname(PikaObj* self, char* path) {
+    char* sep_pos = strrchr(path, PATH_SEPARATOR);
+    if (sep_pos == NULL) {
+        return obj_cacheStr(self, ".");
+    } else if (sep_pos == path) {
+        return obj_cacheStr(self, PATH_SEPARATOR_STRING);
+    } else {
+        int dirname_len = sep_pos - path;
+        char* dirname = malloc(dirname_len + 1);
+        memcpy(dirname, path, dirname_len);
+        dirname[dirname_len] = '\0';
+        char* res = obj_cacheStr(self, dirname);
+        free (dirname);
+        return res;
+    }
+}
+
+int _os_path_split(char* path, char** folder, char** file) {
+    if (path == NULL || folder == NULL || file == NULL) {
+        return -1;
+    }
+    char* p = strrchr(path, PATH_SEPARATOR);
+    if (p) {
+        /* 字符串最后一个路径分隔符的位置 */
+        size_t idx = p - path;
+        /* 获取最后一个路径分隔符之前的路径 */
+        *folder = malloc(idx + 2);
+        if (*folder == NULL) {
+            return -1;
+        }
+        strncpy(*folder, path, idx + 1);
+        (*folder)[idx + 1] = '\0';
+        /* 获取最后一个路径分隔符之后的文件名 */
+        *file = strdup(p + 1);
+        if (*file == NULL) {
+            free(*folder);
+            return -1;
+        }
+        return 0;
+    } else {
+        /* 如果路径没有分隔符,则返回路径本身和空字符串 */
+        *folder = strdup(path);
+        if (*folder == NULL) {
+            return -1;
+        }
+        *file = strdup("");
+        if (*file == NULL) {
+            free(*folder);
+            return -1;
+        }
+        return 0;
+    }
+}
+
+int _os_path_splitext(char* path, char** file, char** ext) {
+    char* p = strrchr(path, '.');
+    if (p) {
+        /* 字符串最后一个点的位置 */
+        size_t idx = p - path;
+        /* 获取点之前的路径 */
+        *file = malloc(idx + 1);
+        if (!(*file)) {
+            /* 内存分配失败 */
+            return -1;
+        }
+        strncpy(*file, path, idx);
+        (*file)[idx] = '\0';
+        /* 获取点之后的扩展名 */
+        *ext = strdup(p);
+        if (!(*ext)) {
+            /* 内存分配失败 */
+            free(*file);
+            return -1;
+        }
+        return 0;
+    } else {
+        /* 如果没有扩展名,则返回路径本身和空字符串 */
+        *file = strdup(path);
+        if (!(*file)) {
+            /* 内存分配失败 */
+            return -1;
+        }
+        *ext = strdup("");
+        if (!(*ext)) {
+            /* 内存分配失败 */
+            free(*file);
+            return -1;
+        }
+        return 0;
+    }
+}
+
+PikaObj* os_path_split(PikaObj* self, char* path) {
+    char* folder = NULL;
+    char* file = NULL;
+    PikaObj* tuple = NULL;
+    Arg* aFolder = NULL;
+    Arg* aFile = NULL;
+
+    if (0 != _os_path_split(path, &folder, &file)) {
+        goto __exit;  // 发生错误,跳转到 __exit 处做资源回收
+    }
+
+    tuple = newNormalObj(New_PikaStdData_Tuple);
+    PikaStdData_Tuple___init__(tuple);
+
+    aFolder = arg_newStr(folder);
+    aFile = arg_newStr(file);
+
+    PikaStdData_List_append(tuple, aFolder);
+    PikaStdData_List_append(tuple, aFile);
+
+    arg_deinit(aFolder);
+    arg_deinit(aFile);
+    free(folder);
+    free(file);
+
+    return tuple;
+__exit:
+    if (aFolder) {
+        arg_deinit(aFolder);
+    }
+    if (aFile) {
+        arg_deinit(aFile);
+    }
+    if (tuple) {
+        obj_deinit(tuple);
+    }
+    if (folder) {
+        free(folder);
+    }
+    if (file) {
+        free(file);
+    }
+    return NULL;
+}
+
+PikaObj* os_path_splitext(PikaObj* self, char* path) {
+    char* file = NULL;
+    char* ext = NULL;
+    PikaObj* tuple = NULL;
+    Arg* aFile = NULL;
+    Arg* aExt = NULL;
+
+    if (0 != _os_path_splitext(path, &file, &ext)) {
+        goto __exit;  // 发生错误,跳转到 __exit 处做资源回收
+    }
+
+    tuple = newNormalObj(New_PikaStdData_Tuple);
+    PikaStdData_Tuple___init__(tuple);
+    aFile = arg_newStr(file);
+    aExt = arg_newStr(ext);
+    PikaStdData_List_append(tuple, aFile);
+    PikaStdData_List_append(tuple, aExt);
+
+    arg_deinit(aFile);
+    arg_deinit(aExt);
+    free(file);
+    free(ext);
+
+    return tuple;
+
+__exit:
+    if (aFile) {
+        arg_deinit(aFile);
+    }
+    if (aExt) {
+        arg_deinit(aExt);
+    }
+    if (tuple) {
+        obj_deinit(tuple);
+    }
+    if (file) {
+        free(file);
+    }
+    if (ext) {
+        free(ext);
+    }
+    return NULL;
+}

+ 10 - 0
port/linux/package/pikascript/pikascript-lib/os/os_platform.c

@@ -201,3 +201,13 @@ int os_remove_platform(char* filename) {
     ret = remove(dirpath);
     return ret;
 }
+
+int os_rename_platform(char* old, char* new) {
+    if (NULL == old || NULL == new) {
+        return -1;
+    }
+    if (0 != rename(old, new)) {
+        return -1;
+    }
+    return 0;
+}

+ 1 - 0
port/linux/package/pikascript/pikascript-lib/os/os_platform.h

@@ -35,5 +35,6 @@ int os_chdir_platform(char* path);
 int os_rmdir_platform(char* path);
 int os_remove_platform(char* filename);
 int os_getFileSize(PikaObj* fd);
+int os_rename_platform(char* old, char* new);
 
 #endif

+ 2 - 0
test/os-test.cpp

@@ -18,5 +18,7 @@ TEST(os, test1) {
     EXPECT_EQ(pikaMemNow(), 0);
 }
 
+TEST_SINGLE_FILE(os, path, "test/python/os/os_path.py")
+
 #endif
 TEST_END

+ 42 - 0
test/python/os/os_path.py

@@ -0,0 +1,42 @@
+import os
+p = os.path
+assert p.join('dir', 'file.txt') == 'dir/file.txt'
+assert p.join('/home/user', 'dir', 'file.txt') == '/home/user/dir/file.txt'
+
+# Test split method
+assert p.split('dir/file.txt') == ('dir', 'file.txt')
+assert p.split('/home/user/dir/file.txt') == ('/home/user/dir', 'file.txt')
+    
+# Test splitext method
+assert p.splitext('file.txt') == ('file', '.txt')
+assert p.splitext('/home/user/file.tar.gz') == ('/home/user/file.tar', '.gz')
+    
+# Test basename method
+assert p.basename('dir/file.txt') == 'file.txt'
+assert p.basename('/home/user/dir/file.txt') == 'file.txt'
+
+# Test dirname method
+assert p.dirname('dir/file.txt') == 'dir'
+assert p.dirname('/home/user/dir/file.txt') == '/home/user/dir'
+
+# Test exists method
+assert p.exists('config/pika_config_void') == False
+assert p.exists('/root/go') == True
+
+# Test isdir method
+assert p.isdir('config/pika_config_void.h') == False
+assert p.isdir('config') == True
+
+# Test isfile method
+assert p.isfile('config') == False
+assert p.isfile('config/pika_config_void.h') == True
+
+# Test isabs method
+assert p.isabs('dir/file.txt') == False
+assert p.isabs('/home/user/file.txt') == True
+
+# Test abspath method
+assert p.abspath('config/pika_config_void.h') == "/root/pikascript/port/linux/config/pika_config_void.h"
+assert p.abspath('/root/go') == "/root/go"
+
+print("PASS")

+ 35 - 3
test/test_common.h

@@ -2,12 +2,10 @@
 #define __OOC_DEBUG__
 
 extern "C" {
-#include <stdio.h>
+#include "BaseObj.h"
 #include "PikaCompiler.h"
 #include "PikaMain.h"
 #include "PikaMath_Operator.h"
-#include "BaseObj.h"
-#include "pika_hal.h"
 #include "PikaParser.h"
 #include "PikaStdLib_MemChecker.h"
 #include "PikaStdLib_SysObj.h"
@@ -18,11 +16,45 @@ extern "C" {
 #include "dataStrs.h"
 #include "pikaScript.h"
 #include "pika_config_gtest.h"
+#include "pika_hal.h"
+#include <stdio.h>
 extern PikaMemInfo g_PikaMemInfo;
 /* the log_buff of printf */
 extern char log_buff[LOG_BUFF_MAX][LOG_SIZE];
 }
 
+#define TEST_SINGLE_FILE(_test_suite_, _test_name_, _file_name_)               \
+  TEST(_test_suite_, _test_name_) {                                            \
+    g_PikaMemInfo.heapUsedMax = 0;                                             \
+    PikaObj *pikaMain = newRootObj("pikaMain", New_PikaMain);                  \
+    extern unsigned char pikaModules_py_a[];                                   \
+    obj_linkLibrary(pikaMain, pikaModules_py_a);                               \
+    /* run */                                                                  \
+    __platform_printf("BEGIN\r\n");                                            \
+    pikaVM_runSingleFile(pikaMain, _file_name_);                               \
+    /* assert */                                                               \
+    /* deinit */                                                               \
+    obj_deinit(pikaMain);                                                      \
+    EXPECT_EQ(pikaMemNow(), 0);                                                \
+  }
+
+#define TEST_SINGLE_FILE_PASS(_test_suite_, _test_name_, _file_name_)          \
+  TEST(_test_suite_, _test_name_) {                                            \
+    g_PikaMemInfo.heapUsedMax = 0;                                             \
+    PikaObj *pikaMain = newRootObj("pikaMain", New_PikaMain);                  \
+    extern unsigned char pikaModules_py_a[];                                   \
+    obj_linkLibrary(pikaMain, pikaModules_py_a);                               \
+    /* run */                                                                  \
+    __platform_printf("BEGIN\r\n");                                            \
+    pikaVM_runSingleFile(pikaMain, _file_name_);                               \
+    /* assert */                                                               \
+    EXPECT_STREQ(log_buff[0], "PASS\r\n");                                     \
+    EXPECT_STREQ(log_buff[1], "BEGIN\r\n");                                    \
+    /* deinit */                                                               \
+    obj_deinit(pikaMain);                                                      \
+    EXPECT_EQ(pikaMemNow(), 0);                                                \
+  }
+
 #if USE_GOOGLE_TEST
 #include "gtest/gtest.h"
 #define TEST_START