Commit 41954a49 by Russell Belfer

Switch search paths to classic delimited strings

This switches the APIs for setting and getting the global/system
search paths from using git_strarray to using a simple string with
GIT_PATH_LIST_SEPARATOR delimited paths, just as the environment
PATH variable would contain.  This makes it simpler to get and set
the value.

I also added code to expand "$PATH" when setting a new value to
embed the old value of the path.  This means that I no longer
require separate actions to PREPEND to the value.
parent 5540d947
......@@ -131,7 +131,6 @@ enum {
GIT_OPT_SET_MWINDOW_MAPPED_LIMIT,
GIT_OPT_GET_SEARCH_PATH,
GIT_OPT_SET_SEARCH_PATH,
GIT_OPT_PREPEND_SEARCH_PATH,
};
/**
......@@ -152,25 +151,21 @@ enum {
* Set the maximum amount of memory that can be mapped at any time
* by the library
*
* opts(GIT_OPT_GET_SEARCH_PATH, const git_strarray **, int level)
* Get a strarray of the search path for a given level of config
* data. "level" must be one of GIT_CONFIG_LEVEL_SYSTEM,
* GIT_CONFIG_LEVEL_GLOBAL, or GIT_CONFIG_LEVEL_XDG. The search
* path applies to shared attributes and ignore files, too.
* opts(GIT_OPT_GET_SEARCH_PATH, int level, char *out, size_t len)
* Get the search path for a given level of config data. "level" must
* be one of GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL, or
* GIT_CONFIG_LEVEL_XDG. The search path is written to the `out`
* buffer up to size `len`. Returns GIT_EBUFS if buffer is too small.
*
* opts(GIT_OPT_SET_SEARCH_PATH, int level, const git_strarray *)
* Set the search path for a given level of config data. Passing
* NULL for the git_strarray pointer resets the search path to the
* default (which is generally based on environment variables).
* "level" must be one of GIT_CONFIG_LEVEL_SYSTEM,
* GIT_CONFIG_LEVEL_GLOBAL, or GIT_CONFIG_LEVEL_XDG. The search
* path applies to shared attributes and ignore files, too.
*
* opts(GIT_OPT_PREPEND_SEARCH_PATH, int level, const git_strarray *)
* Prepend new directories to the search path for a given level of
* config data. "level" must be one of GIT_CONFIG_LEVEL_SYSTEM,
* GIT_CONFIG_LEVEL_GLOBAL, or GIT_CONFIG_LEVEL_XDG. The search
* path applies to shared attributes and ignore files, too.
* opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path)
* Set the search path for a level of config data. The search path
* applied to shared attributes and ignore files, too.
* - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR.
* Pass NULL to reset to the default (generally based on environment
* variables). Use magic path `$PATH` to include the old value
* of the path (if you want to prepend or append, for instance).
* - `level` must be GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL,
* or GIT_CONFIG_LEVEL_XDG.
*
* @param option Option key
* @param ... value to set the option
......
......@@ -521,7 +521,7 @@ static int git_config__find_file_to_path(
if (path.size >= outlen) {
giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path");
error = -1;
error = GIT_EBUFS;
goto done;
}
......
......@@ -559,48 +559,46 @@ clean_up:
}
static int git_futils_guess_system_dirs(git_strarray *out)
static int git_futils_guess_system_dirs(git_buf *out)
{
#ifdef GIT_WIN32
return win32_find_system_dirs(out);
#else
return git_strarray_set(out, 1, "/etc");
return git_buf_sets(out, "/etc");
#endif
}
static int git_futils_guess_global_dirs(git_strarray *out)
static int git_futils_guess_global_dirs(git_buf *out)
{
#ifdef GIT_WIN32
return win32_find_global_dirs(out);
#else
return git_strarray_set(out, 1, getenv("HOME"));
return git_buf_sets(out, getenv("HOME"));
#endif
}
static int git_futils_guess_xdg_dirs(git_strarray *out)
static int git_futils_guess_xdg_dirs(git_buf *out)
{
#ifdef GIT_WIN32
return win32_find_xdg_dirs(out);
#else
int error = 0;
git_buf xdg = GIT_BUF_INIT;
const char *env = NULL;
if ((env = getenv("XDG_CONFIG_HOME")) != NULL)
git_buf_joinpath(&xdg, env, "git");
return git_buf_joinpath(out, env, "git");
else if ((env = getenv("HOME")) != NULL)
git_buf_joinpath(&xdg, env, ".config/git");
error = git_strarray_set(out, 1, xdg.ptr);
git_buf_free(&xdg);
return git_buf_joinpath(out, env, ".config/git");
return error;
git_buf_clear(out);
return 0;
#endif
}
typedef int (*git_futils_dirs_guess_cb)(git_strarray *out);
typedef int (*git_futils_dirs_guess_cb)(git_buf *out);
static git_buf git_futils__dirs[GIT_FUTILS_DIR__MAX] =
{ GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
static git_strarray git_futils__dirs[GIT_FUTILS_DIR__MAX];
static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = {
git_futils_guess_system_dirs,
git_futils_guess_global_dirs,
......@@ -615,53 +613,105 @@ static int git_futils_check_selector(git_futils_dir_t which)
return -1;
}
int git_futils_dirs_get(const git_strarray **out, git_futils_dir_t which)
int git_futils_dirs_get(const git_buf **out, git_futils_dir_t which)
{
if (!out) {
giterr_set(GITERR_INVALID, "Output git_strarray not provided");
return -1;
}
assert(out);
*out = NULL;
GITERR_CHECK_ERROR(git_futils_check_selector(which));
if (!git_futils__dirs[which].count) {
int error = git_futils__dir_guess[which](&git_futils__dirs[which]);
if (error < 0)
return error;
}
if (!git_buf_len(&git_futils__dirs[which]))
GITERR_CHECK_ERROR(
git_futils__dir_guess[which](&git_futils__dirs[which]));
*out = &git_futils__dirs[which];
return 0;
}
int git_futils_dirs_set(
git_futils_dir_t which, const git_strarray *dirs, bool replace)
int git_futils_dirs_get_str(char *out, size_t outlen, git_futils_dir_t which)
{
const git_buf *path = NULL;
GITERR_CHECK_ERROR(git_futils_check_selector(which));
GITERR_CHECK_ERROR(git_futils_dirs_get(&path, which));
if (!out || path->size >= outlen) {
giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path");
return GIT_EBUFS;
}
git_buf_copy_cstr(out, outlen, path);
return 0;
}
#define PATH_MAGIC "$PATH"
int git_futils_dirs_set(git_futils_dir_t which, const char *search_path)
{
const char *expand_path = NULL;
git_buf merge = GIT_BUF_INIT;
GITERR_CHECK_ERROR(git_futils_check_selector(which));
if (replace)
git_strarray_free(&git_futils__dirs[which]);
if (search_path != NULL)
expand_path = strstr(search_path, PATH_MAGIC);
/* init with defaults if it hasn't been done yet, but ignore error */
else if (!git_futils__dirs[which].count)
/* init with default if not yet done and needed (ignoring error) */
if ((!search_path || expand_path) &&
!git_buf_len(&git_futils__dirs[which]))
git_futils__dir_guess[which](&git_futils__dirs[which]);
return git_strarray_prepend(&git_futils__dirs[which], dirs);
/* if $PATH is not referenced, then just set the path */
if (!expand_path)
return git_buf_sets(&git_futils__dirs[which], search_path);
/* otherwise set to join(before $PATH, old value, after $PATH) */
if (expand_path > search_path)
git_buf_set(&merge, search_path, expand_path - search_path);
if (git_buf_len(&git_futils__dirs[which]))
git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR,
merge.ptr, git_futils__dirs[which].ptr);
expand_path += strlen(PATH_MAGIC);
if (*expand_path)
git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path);
git_buf_swap(&git_futils__dirs[which], &merge);
git_buf_free(&merge);
return git_buf_oom(&git_futils__dirs[which]) ? -1 : 0;
}
void git_futils_dirs_free(void)
{
int i;
for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
git_buf_free(&git_futils__dirs[i]);
}
static int git_futils_find_in_dirlist(
git_buf *path, const char *name, git_futils_dir_t which, const char *label)
{
size_t i;
const git_strarray *syspaths;
size_t len;
const char *scan, *next = NULL;
const git_buf *syspath;
GITERR_CHECK_ERROR(git_futils_dirs_get(&syspaths, which));
GITERR_CHECK_ERROR(git_futils_dirs_get(&syspath, which));
for (i = 0; i < syspaths->count; ++i) {
GITERR_CHECK_ERROR(
git_buf_joinpath(path, syspaths->strings[i], name));
for (scan = git_buf_cstr(syspath); scan; scan = next) {
for (next = strchr(scan, GIT_PATH_LIST_SEPARATOR);
next && next > scan && next[-1] == '\\';
next = strchr(next + 1, GIT_PATH_LIST_SEPARATOR))
/* find unescaped separator or end of string */;
len = next ? (size_t)(next++ - scan) : strlen(scan);
if (!len)
continue;
GITERR_CHECK_ERROR(git_buf_set(path, scan, len));
GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name));
if (git_path_exists(path->ptr))
return 0;
......
......@@ -306,25 +306,42 @@ typedef enum {
} git_futils_dir_t;
/**
* Get the strarray of search paths for global/system files
* Get the search path for global/system/xdg files
*
* @param out git_strarray of search paths
* @param out pointer to git_buf containing search path
* @param which which list of paths to return
* @return 0 on success, <0 on failure (allocation error)
* @return 0 on success, <0 on failure
*/
extern int git_futils_dirs_get(const git_buf **out, git_futils_dir_t which);
/**
* Get search path into a preallocated buffer
*
* @param out String buffer to write into
* @param outlen Size of string buffer
* @param which Which search path to return
* @return 0 on success, GIT_EBUFS if out is too small, <0 on other failure
*/
extern int git_futils_dirs_get(
const git_strarray **out, git_futils_dir_t which);
extern int git_futils_dirs_get_str(
char *out, size_t outlen, git_futils_dir_t which);
/**
* Set or prepend strarray of search paths for global/system files
* Set search paths for global/system/xdg files
*
* The first occurrence of the magic string "$PATH" in the new value will
* be replaced with the old value of the search path.
*
* @param which which list of paths to modify
* @param dirs new list of search paths
* @param replace true to replace old, false to prepend to old
* @param which Which search path to modify
* @param paths New search path (separated by GIT_PATH_LIST_SEPARATOR)
* @return 0 on success, <0 on failure (allocation error)
*/
extern int git_futils_dirs_set(
git_futils_dir_t which, const git_strarray *dirs, bool replace);
extern int git_futils_dirs_set(git_futils_dir_t which, const char *paths);
/**
* Release / reset all search paths
*/
extern void git_futils_dirs_free(void);
/**
* Create a "fake" symlink (text file containing the target path).
......
......@@ -7,7 +7,8 @@
#include "common.h"
#include "global.h"
#include "hash.h"
#include "git2/threads.h"
#include "fileops.h"
#include "git2/threads.h"
#include "thread-utils.h"
......@@ -82,6 +83,7 @@ void git_threads_shutdown(void)
/* Shut down any subsystems that have global state */
git_hash_global_shutdown();
git_futils_dirs_free();
}
git_global_st *git__global_state(void)
......@@ -139,6 +141,7 @@ void git_threads_shutdown(void)
/* Shut down any subsystems that have global state */
git_hash_global_shutdown();
git_futils_dirs_free();
}
git_global_st *git__global_state(void)
......@@ -171,7 +174,9 @@ int git_threads_init(void)
void git_threads_shutdown(void)
{
/* noop */
/* Shut down any subsystems that have global state */
git_hash_global_shutdown();
git_futils_dirs_free();
}
git_global_st *git__global_state(void)
......
......@@ -39,7 +39,7 @@ int git_libgit2_capabilities()
extern size_t git_mwindow__window_size;
extern size_t git_mwindow__mapped_limit;
static int convert_config_level_to_futils_dir(int config_level)
static int config_level_to_futils_dir(int config_level)
{
int val = -1;
......@@ -80,24 +80,18 @@ int git_libgit2_opts(int key, ...)
break;
case GIT_OPT_GET_SEARCH_PATH:
{
const git_strarray **out = va_arg(ap, const git_strarray **);
int which = convert_config_level_to_futils_dir(va_arg(ap, int));
if ((error = config_level_to_futils_dir(va_arg(ap, int))) >= 0) {
char *out = va_arg(ap, char *);
size_t outlen = va_arg(ap, size_t);
error = (which < 0) ? which : git_futils_dirs_get(out, which);
break;
error = git_futils_dirs_get_str(out, outlen, error);
}
break;
case GIT_OPT_SET_SEARCH_PATH:
case GIT_OPT_PREPEND_SEARCH_PATH:
{
int which = convert_config_level_to_futils_dir(va_arg(ap, int));
const git_strarray *dirs = va_arg(ap, git_strarray *);
error = (which < 0) ? which : git_futils_dirs_set(
which, dirs, key == GIT_OPT_SET_SEARCH_PATH);
break;
}
if ((error = config_level_to_futils_dir(va_arg(ap, int))) >= 0)
error = git_futils_dirs_set(error, va_arg(ap, const char *));
break;
}
va_end(ap);
......
......@@ -155,75 +155,59 @@ static int win32_find_git_in_registry(
return path16.len ? 0 : GIT_ENOTFOUND;
}
static int win32_copy_to_strarray(
git_strarray *out, size_t count, char **strings)
{
size_t i, realcount;
if (!count)
return 0;
for (i = 0, realcount = 0; i < count; ++i)
if (strings[i]) realcount++;
out->strings = git__calloc(realcount, sizeof(char *));
GITERR_CHECK_ALLOC(out->strings);
for (i = 0, out->count = 0; i < count; ++i)
if (strings[i])
out->strings[out->count++] = strings[i];
return 0;
}
static int win32_find_existing_dirs(
git_strarray *out, const wchar_t *tmpl[], char *temp[])
git_buf *out, const wchar_t *tmpl[], char *temp[])
{
struct win32_path path16;
git_buf buf = GIT_BUF_INIT;
size_t count;
for (count = 0; *tmpl != NULL; tmpl++) {
git_buf_clear(out);
for (; *tmpl != NULL; tmpl++) {
if (!win32_expand_path(&path16, *tmpl) &&
path16.path[0] != L'%' &&
!_waccess(path16.path, F_OK))
{
win32_path_utf16_to_8(&buf, path16.path);
temp[count++] = git_buf_detach(&buf);
if (buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
}
}
return win32_copy_to_strarray(out, count, temp);
git_buf_free(&buf);
return (git_buf_oom(out) ? -1 : 0);
}
int win32_find_system_dirs(git_strarray *out)
int win32_find_system_dirs(git_buf *out)
{
char *strings[4];
size_t count = 0;
git_buf buf = GIT_BUF_INIT;
memset(out, 0, sizeof(*out));
/* directories where git.exe & git.cmd are found */
if (!win32_find_git_in_path(&buf, L"git.exe"))
strings[count++] = git_buf_detach(&buf);
if (!win32_find_git_in_path(&buf, L"git.exe") && buf.size)
git_buf_set(out, buf.ptr, buf.size);
else
git_buf_clear(out);
if (!win32_find_git_in_path(&buf, L"git.cmd"))
strings[count++] = git_buf_detach(&buf);
if (!win32_find_git_in_path(&buf, L"git.cmd") && buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
/* directories where git is installed according to registry */
if (!win32_find_git_in_registry(
&buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL))
strings[count++] = git_buf_detach(&buf);
&buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL) && buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
if (!win32_find_git_in_registry(
&buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL))
strings[count++] = git_buf_detach(&buf);
&buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL) && buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
git_buf_free(&buf);
return win32_copy_to_strarray(out, count, strings);
return (git_buf_oom(out) ? -1 : 0);
}
int win32_find_global_dirs(git_strarray *out)
int win32_find_global_dirs(git_buf *out)
{
char *temp[3];
static const wchar_t *global_tmpls[4] = {
......@@ -236,7 +220,7 @@ int win32_find_global_dirs(git_strarray *out)
return win32_find_existing_dirs(out, global_tmpls, temp);
}
int win32_find_xdg_dirs(git_strarray *out)
int win32_find_xdg_dirs(git_buf *out)
{
char *temp[6];
static const wchar_t *global_tmpls[7] = {
......
......@@ -18,9 +18,9 @@ extern int win32_expand_path(struct win32_path *s_root, const wchar_t *templ);
extern int win32_find_file(
git_buf *path, const struct win32_path *root, const char *filename);
extern int win32_find_system_dirs(git_strarray *out);
extern int win32_find_global_dirs(git_strarray *out);
extern int win32_find_xdg_dirs(git_strarray *out);
extern int win32_find_system_dirs(git_buf *out);
extern int win32_find_global_dirs(git_buf *out);
extern int win32_find_xdg_dirs(git_buf *out);
#endif
......@@ -136,7 +136,7 @@ int cl_rename(const char *source, const char *dest)
#include <stdlib.h>
char *cl_getenv(const char *name)
{
return getenv(name);
return getenv(name);
}
int cl_setenv(const char *name, const char *value)
......
......@@ -29,8 +29,24 @@ static char *home_values[] = {
void test_core_env__initialize(void)
{
int i;
for (i = 0; i < NUM_VARS; ++i)
env_save[i] = cl_getenv(env_vars[i]);
for (i = 0; i < NUM_VARS; ++i) {
const char *original = cl_getenv(env_vars[i]);
#ifdef GIT_WIN32
env_save[i] = original;
#else
env_save[i] = original ? git__strdup(original) : NULL;
#endif
}
}
static void reset_global_search_path(void)
{
cl_git_pass(git_futils_dirs_set(GIT_FUTILS_DIR_GLOBAL, NULL));
}
static void reset_system_search_path(void)
{
cl_git_pass(git_futils_dirs_set(GIT_FUTILS_DIR_SYSTEM, NULL));
}
void test_core_env__cleanup(void)
......@@ -40,9 +56,7 @@ void test_core_env__cleanup(void)
for (i = 0; i < NUM_VARS; ++i) {
cl_setenv(env_vars[i], env_save[i]);
#ifdef GIT_WIN32
git__free(env_save[i]);
#endif
env_save[i] = NULL;
}
......@@ -55,8 +69,8 @@ void test_core_env__cleanup(void)
}
/* reset search paths to default */
git_futils_dirs_set(GIT_FUTILS_DIR_GLOBAL, NULL, true);
git_futils_dirs_set(GIT_FUTILS_DIR_SYSTEM, NULL, true);
reset_global_search_path();
reset_system_search_path();
}
static void setenv_and_check(const char *name, const char *value)
......@@ -64,21 +78,10 @@ static void setenv_and_check(const char *name, const char *value)
char *check;
cl_git_pass(cl_setenv(name, value));
check = cl_getenv(name);
cl_assert_equal_s(value, check);
#ifdef GIT_WIN32
git__free(check);
#endif
}
static void reset_global_search_path(void)
{
cl_git_pass(git_futils_dirs_set(GIT_FUTILS_DIR_GLOBAL, NULL, true));
}
static void reset_system_search_path(void)
{
cl_git_pass(git_futils_dirs_set(GIT_FUTILS_DIR_SYSTEM, NULL, true));
}
void test_core_env__0(void)
......@@ -216,7 +219,7 @@ void test_core_env__2(void)
char **val;
const char *testname = "alternate";
size_t testlen = strlen(testname);
git_strarray arr;
char out[GIT_PATH_MAX];
strncpy(testfile, testname, sizeof(testfile));
cl_assert_equal_s(testname, testfile);
......@@ -227,7 +230,7 @@ void test_core_env__2(void)
* we are on a filesystem that doesn't support the
* characters in question and skip this test...
*/
if (p_mkdir(*val, 0777) != 0) {
if (p_mkdir(*val, 0777) != 0 && errno != EEXIST) {
*val = ""; /* mark as not created */
continue;
}
......@@ -243,31 +246,67 @@ void test_core_env__2(void)
cl_git_mkfile(path.ptr, "find me");
git_buf_rtruncate_at_char(&path, '/');
arr.count = 1;
arr.strings = &path.ptr;
/* default should be NOTFOUND */
cl_assert_equal_i(
GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile));
/* set search path */
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &arr));
GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, out, sizeof(out)));
cl_assert_equal_s(out, path.ptr);
cl_git_pass(git_futils_find_global_file(&found, testfile));
/* reset */
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL));
cl_assert_equal_i(
GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile));
/* try prepend behavior */
cl_git_pass(git_buf_putc(&path, GIT_PATH_LIST_SEPARATOR));
cl_git_pass(git_buf_puts(&path, "$PATH"));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
git_buf_rtruncate_at_char(&path, GIT_PATH_LIST_SEPARATOR);
cl_git_pass(git_libgit2_opts(
GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, out, sizeof(out)));
cl_assert(git__prefixcmp(out, path.ptr) == 0);
cl_git_pass(git_futils_find_global_file(&found, testfile));
/* reset */
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL));
cl_assert_equal_i(
GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile));
/* try append behavior */
cl_git_pass(git_buf_join(
&found, GIT_PATH_LIST_SEPARATOR, "$PATH", path.ptr));
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, found.ptr));
cl_git_pass(git_libgit2_opts(
GIT_OPT_PREPEND_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &arr));
GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, out, sizeof(out)));
cl_assert(git__suffixcmp(out, path.ptr) == 0);
cl_git_pass(git_futils_find_global_file(&found, testfile));
/* reset */
cl_git_pass(git_libgit2_opts(
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL));
cl_git_pass(git_buf_joinpath(&path, path.ptr, testfile));
(void)p_unlink(path.ptr);
(void)p_rmdir(*val);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment