tb_void_t tb_directory_walk(tb_char_t const* path, tb_long_t recursion, tb_bool_t prefix, tb_directory_walk_func_t func, tb_cpointer_t priv) { // check tb_assert_and_check_return(path && func); // walk it directly if rootdir is relative path tb_file_info_t info = {0}; if (!tb_path_is_absolute(path) && tb_file_info(path, &info) && info.type == TB_FILE_TYPE_DIRECTORY) { tb_wchar_t path_w[TB_PATH_MAXN]; if (tb_atow(path_w, path, tb_arrayn(path_w)) != -1) tb_directory_walk_impl(path_w, recursion, prefix, func, priv); } else { // the absolute path (translate "~/") tb_wchar_t full_w[TB_PATH_MAXN]; if (tb_path_absolute_w(path, full_w, TB_PATH_MAXN)) tb_directory_walk_impl(full_w, recursion, prefix, func, priv); } }
tb_char_t const* tb_path_relative_to(tb_char_t const* root, tb_char_t const* path, tb_char_t* data, tb_size_t maxn) { // check tb_assert_and_check_return_val(path && data && maxn, tb_null); // trace tb_trace_d("path: %s", path); // the root is the current and the path is absolute? return path directly if (!root && !tb_path_is_absolute(path)) { // copy it tb_strlcpy(data, path, maxn); // translate it return tb_path_translate(data, 0, maxn)? data : tb_null; } // get the absolute path tb_size_t path_size = 0; tb_char_t path_absolute[TB_PATH_MAXN]; tb_size_t path_maxn = sizeof(path_absolute); path = tb_path_absolute(path, path_absolute, path_maxn); path_size = tb_strlen(path); tb_assert_and_check_return_val(path && path_size && path_size < path_maxn, tb_null); // trace tb_trace_d("path_absolute: %s", path); // get the absolute root tb_size_t root_size = 0; tb_char_t root_absolute[TB_PATH_MAXN]; tb_size_t root_maxn = sizeof(root_absolute); if (root) { // get the absolute root root = tb_path_absolute(root, root_absolute, root_maxn); root_size = tb_strlen(root); } else { // get the current directory if (!(root_size = tb_directory_current(root_absolute, root_maxn))) return tb_null; // translate it if (!(root_size = tb_path_translate(root_absolute, root_size, root_maxn))) return tb_null; root = root_absolute; } tb_assert_and_check_return_val(root && root_size && root_size < root_maxn, tb_null); // trace tb_trace_d("root_absolute: %s", root); // same directory? return "." if (path_size == root_size && !tb_strncmp(path, root, root_size)) { // check tb_assert_and_check_return_val(maxn >= 2, "."); // return "." data[0] = '.'; data[1] = '\0'; return data; } // append separator if (path_size + 1 < path_maxn) { path_absolute[path_size++] = TB_PATH_SEPARATOR; path_absolute[path_size] = '\0'; } if (root_size + 1 < root_maxn) { root_absolute[root_size++] = TB_PATH_SEPARATOR; root_absolute[root_size] = '\0'; } // find the common leading directory tb_char_t const* p = path; tb_char_t const* q = root; tb_long_t last = -1; for (; *p && *q && *p == *q; q++, p++) { // save the last separator if (*p == TB_PATH_SEPARATOR) last = q - root; } // is different directory or outside the windows drive root? using the absolute path if (last <= 0 || (last == 2 && root[1] == ':')) { // the path size tb_size_t size = tb_min(path_size - 1, maxn); // copy it tb_strncpy(data, path, size); data[size] = '\0'; } // exists same root? else { // count the remaining levels in root tb_size_t count = 0; tb_char_t const* l = root + last + 1; for (; *l; l++) { if (*l == TB_PATH_SEPARATOR) count++; } // append "../" or "..\\" tb_char_t* d = data; tb_char_t* e = data + maxn; while (count--) { if (d + 3 < e) { d[0] = '.'; d[1] = '.'; d[2] = TB_PATH_SEPARATOR; d += 3; } } // append the left path l = path + last + 1; while (*l && d < e) *d++ = *l++; // remove the last separator if (d > data) d--; // end *d = '\0'; } // trace tb_trace_d("relative: %s", data); // ok? return data; }
tb_char_t const* tb_path_absolute_to(tb_char_t const* root, tb_char_t const* path, tb_char_t* data, tb_size_t maxn) { // check tb_assert_and_check_return_val(path && data && maxn, tb_null); // trace tb_trace_d("path: %s", path); // the path is absolute? if (tb_path_is_absolute(path)) { // copy it tb_strlcpy(data, path, maxn); // translate it return tb_path_translate(data, 0, maxn)? data : tb_null; } // get the root directory tb_size_t size = 0; if (root) { // copy it size = tb_strlcpy(data, root, maxn); tb_assert_and_check_return_val(size < maxn, tb_null); } else { // get the current directory if (!(size = tb_directory_current(data, maxn))) return tb_null; } // translate the root directory size = tb_path_translate(data, size, maxn); // trace tb_trace_d("root: %s, size: %lu", data, size); // is windows path? skip the drive prefix tb_char_t* absolute = data; if (size > 2 && tb_isalpha(absolute[0]) && absolute[1] == ':' && absolute[2] == TB_PATH_SEPARATOR) { // skip it absolute += 2; size -= 2; } // path => data tb_char_t const* p = path; tb_char_t const* t = p; tb_char_t* q = absolute + size; tb_char_t const* e = absolute + maxn - 1; while (1) { if (tb_path_is_separator(*p) || !*p) { // the item size tb_size_t n = p - t; // ..? remove item if (n == 2 && t[0] == '.' && t[1] == '.') { // find the last separator for (; q > absolute && *q != TB_PATH_SEPARATOR; q--) ; // strip it *q = '\0'; } // .? continue it else if (n == 1 && t[0] == '.') ; // append item else if (n && q + 1 + n < e) { *q++ = TB_PATH_SEPARATOR; tb_strncpy(q, t, n); q += n; } // empty item? remove repeat else if (!n) ; // too small? else { // trace tb_trace_e("the data path is too small for %s", path); return tb_null; } // break tb_check_break(*p); // next t = p + 1; } // next p++; } // end if (q > absolute) *q = '\0'; // root? else { *q++ = TB_PATH_SEPARATOR; *q = '\0'; } // trace tb_trace_d("absolute: %s", data); // ok? return data; }