Commit 892f59bd authored by Rusty Russell's avatar Rusty Russell

tal/path: new module

Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>


Header from folded patch 'path-talloc-take.patch':

tal/path: accept take() for arguments.
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 1322fd33
......@@ -72,6 +72,7 @@ MODS_NORMAL_WITH_SRC := antithread \
str_talloc \
take \
tal \
tal/path \
tal/str \
talloc \
talloc_link \
......
../../../licenses/BSD-MIT
\ No newline at end of file
#include <string.h>
#include "config.h"
/**
* tal/path - routines to manipulate paths
*
* This code helps manage paths.
*
* License: BSD-MIT
* Author: Rusty Russell <rusty@rustcorp.com.au>
*
* Example:
* // Program to print out full path names, recursively.
* #include <ccan/tal/path/path.h>
* #include <sys/types.h>
* #include <dirent.h>
* #include <stdio.h>
* #include <ccan/err/err.h>
*
* static void dump(const char *dir)
* {
* struct dirent *di;
* DIR *d = opendir(dir);
* if (!d) {
* warn("Failed to open %s", dir);
* return;
* }
* printf("%s\n", dir);
* while ((di = readdir(d)) != NULL) {
* char *path;
* if (streq(di->d_name, ".") || streq(di->d_name, ".."))
* continue;
* path = path_join(NULL, dir, di->d_name);
* if (path_is_dir(path))
* dump(path);
* tal_free(path);
* }
* closedir(d);
* }
*
* int main(void)
* {
* dump(path_cwd(NULL));
* return 0;
* }
*/
int main(int argc, char *argv[])
{
/* Expect exactly one argument */
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/str\n");
printf("ccan/take\n");
printf("ccan/tal\n");
printf("ccan/tal/str\n");
return 0;
}
return 1;
}
/* Licensed under BSD-MIT - see LICENSE file for details */
#include <ccan/tal/path/path.h>
#include <ccan/str/str.h>
#include <ccan/take/take.h>
#include <ccan/tal/str/str.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#define PATH_SEP_STR "/"
#define PATH_SEP (PATH_SEP_STR[0])
char *path_cwd(const tal_t *ctx)
{
size_t len = 64;
char *cwd;
/* *This* is why people hate C. */
cwd = tal_arr(ctx, char, len);
while (cwd && !getcwd(cwd, len)) {
if (errno != ERANGE || !tal_resize(&cwd, len *= 2))
cwd = tal_free(cwd);
}
return cwd;
}
char *path_join(const tal_t *ctx, const char *base, const char *a)
{
char *ret = NULL;
size_t len;
if (unlikely(!a) && taken(a)) {
if (taken(base))
tal_free(base);
return NULL;
}
if (a[0] == PATH_SEP) {
if (taken(base))
tal_free(base);
return tal_strdup(ctx, a);
}
if (unlikely(!base) && taken(base))
goto out;
len = strlen(base);
ret = tal_dup(ctx, char, base, len, 1 + strlen(a) + 1);
if (!ret)
goto out;
if (ret[len-1] != PATH_SEP)
ret[len++] = PATH_SEP;
strcpy(ret + len, a);
out:
if (taken(a))
tal_free(a);
return ret;
}
#if HAVE_FCHDIR
struct path_pushd {
int fd;
};
static void pushd_destroy(struct path_pushd *pushd)
{
close(pushd->fd);
}
struct path_pushd *path_pushd(const tal_t *ctx, const char *dir)
{
struct path_pushd *old = tal(ctx, struct path_pushd);
if (!old)
return NULL;
if (unlikely(!dir) && taken(dir))
return tal_free(old);
if (!tal_add_destructor(old, pushd_destroy))
old = tal_free(old);
else {
old->fd = open(".", O_RDONLY);
if (old->fd < 0)
old = tal_free(old);
else if (chdir(dir) != 0)
old = tal_free(old);
}
if (taken(dir))
tal_free(dir);
return old;
}
bool path_popd(struct path_pushd *olddir)
{
bool ok = (fchdir(olddir->fd) == 0);
tal_free(olddir);
return ok;
}
#else
struct path_pushd {
const char *olddir;
};
struct path_pushd *path_pushd(const tal_t *ctx, const char *dir)
{
struct path_pushd *old = tal(ctx, struct path_pushd);
if (!old)
return NULL;
old->olddir = path_cwd(old);
if (unlikely(!old->olddir))
old = tal_free(old);
else if (unlikely(!dir) && is_taken(dir))
old = tal_free(old);
else if (chdir(dir) != 0)
old = tal_free(old);
if (taken(dir))
tal_free(dir);
return old;
}
bool path_popd(struct path_pushd *olddir)
{
bool ok = (chdir(olddir->olddir) == 0);
tal_free(olddir);
return ok;
}
#endif /* !HAVE_FCHDIR */
char *path_canon(const tal_t *ctx, const char *a)
{
#if 0
char *oldcwd, *path, *p;
void *tmpctx;
size_t len;
struct path_pushd *olddir;
/* A good guess as to size. */
len = strlen(a) + 1;
if (a[0] != PATH_SEP) {
tmpctx = oldcwd = path_cwd(ctx);
if (!oldcwd)
return NULL;
len += strlen(oldcwd) + strlen(PATH_SEP_STR);
path = tal_array(tmpctx, char, len);
if (!path)
goto out;
len = strlen(oldcwd);
memcpy(path, oldcwd, len);
path[len++] = PATH_SEP;
} else {
tmpctx = path = tal_array(ctx, char, len);
if (!path)
return NULL;
len = 0;
}
strcpy(path + len, a);
p = strrchr(path, PATH_SEP);
*p = '\0';
olddir = path_pushd(tmpctx, path);
if (!olddir)
goto out;
/* Make OS canonicalize path for us. */
path = path_cwd(tmpctx);
if (!path)
goto out;
/* Append rest of old path. */
len = strlen(p+1);
if (len) {
size_t oldlen = tal_array_length(path);
if (path[oldlen-1] != PATH_SEP) {
/* Include / to append. */
*p = PATH_SEP;
p--;
len++;
}
path = tal_realloc(NULL, path, char, oldlen+len+1);
if (!path)
goto out;
memcpy(path + oldlen, p, len+1);
}
path = tal_steal(ctx, path);
out:
/* This can happen if old cwd is deleted. */
if (!path_popd(olddir))
path = tal_free(path);
tal_free(tmpctx);
return path;
#else
char *path;
if (unlikely(!a) && is_taken(a))
path = NULL;
else {
path = tal_arr(ctx, char, PATH_MAX);
if (path && !realpath(a, path))
path = tal_free(path);
}
if (taken(a))
tal_free(a);
return path;
#endif
}
/* Symlinks make this hard! */
char *path_rel(const tal_t *ctx, const char *from, const char *to)
{
char *cfrom, *cto, *ret, *p;
tal_t *tmpctx;
size_t common, num_back, i, postlen;
/* This frees from if we're supposed to take it. */
tmpctx = cfrom = path_canon(ctx, from);
if (!cfrom)
goto fail_take_to;
/* From is a directory, so we append / to it. */
if (!streq(cfrom, PATH_SEP_STR)) {
if (!tal_resize(&cfrom, strlen(cfrom)+2))
goto fail_take_to;
tmpctx = cfrom;
strcat(cfrom, PATH_SEP_STR);
}
/* This frees to if we're supposed to take it. */
cto = path_canon(tmpctx, to);
if (!cto)
goto out;
/* How much is in common? */
for (common = i = 0; cfrom[i] && cto[i]; i++) {
if (cfrom[i] != cto[i])
break;
if (cfrom[i] == PATH_SEP)
common = i + 1;
}
/* Skip over / if matches end of other path. */
if (!cfrom[i] && cto[i] == PATH_SEP) {
cto++;
common = i;
} else if (!cto[i] && cfrom[i] == PATH_SEP) {
cfrom++;
common = i;
}
/* Normalize so strings point past common area. */
cfrom += common;
cto += common;
/* One .. for every path element remaining in 'from', to get
* back to common prefix. Then the rest of 'to'. */
num_back = strcount(cfrom, PATH_SEP_STR);
postlen = strlen(cto) + 1;
/* Nothing left? That's ".". */
if (num_back == 0 && postlen == 1) {
ret = tal_strdup(ctx, ".");
goto out;
}
ret = tal_arr(ctx, char,
strlen(".." PATH_SEP_STR) * num_back + postlen);
if (!ret)
goto out;
for (i = 0, p = ret; i < num_back; i++, p += strlen(".." PATH_SEP_STR))
memcpy(p, ".." PATH_SEP_STR, strlen(".." PATH_SEP_STR));
/* Nothing to append? Trim the final / */
if (postlen == 1)
p--;
memcpy(p, cto, postlen);
out:
tal_free(tmpctx);
return ret;
fail_take_to:
if (taken(to))
tal_free(to);
ret = NULL;
goto out;
}
char *path_readlink(const tal_t *ctx, const char *linkname)
{
ssize_t len, maxlen = 64; /* good first guess. */
char *ret = NULL;
if (unlikely(!linkname) && is_taken(linkname))
goto fail;
ret = tal_arr(ctx, char, maxlen + 1);
while (ret) {
len = readlink(linkname, ret, maxlen);
if (len < 0)
goto fail;
if (len < maxlen)
break;
if (!tal_resize(&ret, maxlen *= 2 + 1))
goto fail;
}
ret[len] = '\0';
out:
if (taken(linkname))
tal_free(linkname);
return ret;
fail:
ret = tal_free(ret);
goto out;
}
char *path_simplify(const tal_t *ctx, const char *path)
{
size_t i, j, start, len;
char *ret;
bool ended = false;
ret = tal_strdup(ctx, path);
if (!ret)
return NULL;
/* Always need first / if there is one. */
if (ret[0] == PATH_SEP)
start = 1;
else
start = 0;
for (i = j = start; !ended; i += len) {
/* Get length of this segment, including terminator. */
for (len = 0; ret[i+len] != PATH_SEP; len++) {
if (!ret[i+len]) {
ended = true;
break;
}
}
len++;
/* Empty segment is //; ignore first one. */
if (len == 1)
continue;
/* Always ignore slashdot. */
if (len == 2 && ret[i] == '.')
continue;
/* .. => remove previous if there is one, unless symlink. */
if (len == 3 && ret[i] == '.' && ret[i+1] == '.') {
struct stat st;
if (j > start) {
/* eg. /foo/, foo/ or foo/bar/ */
assert(ret[j-1] == PATH_SEP);
ret[j-1] = '\0';
/* Avoid stepping back over ..! */
if (streq(ret, "..")
|| strends(ret, PATH_SEP_STR"..")) {
ret[j-1] = PATH_SEP;
goto copy;
}
if (lstat(ret, &st) == 0
&& !S_ISLNK(st.st_mode)) {
char *sep = strrchr(ret, PATH_SEP);
if (sep)
j = sep - ret + 1;
else
j = 0;
}
continue;
} else if (start) {
/* /.. => / */
j = 1;
/* nul term in case we're at end */
ret[1] = '\0';
continue;
}
}
copy:
memmove(ret + j, ret + i, len);
/* Don't count nul terminator. */
j += len - ended;
}
/* Empty string created by ../ elimination. */
if (j == 0) {
ret[0] = '.';
ret[1] = '\0';
} else if (j > 1 && ret[j-1] == PATH_SEP) {
ret[j-1] = '\0';
} else
ret[j] = '\0';
return ret;
}
char *path_basename(const tal_t *ctx, const char *path)
{
const char *sep;
char *ret;
if (unlikely(!path) && taken(path))
return NULL;
sep = strrchr(path, PATH_SEP);
if (!sep)
return tal_strdup(ctx, path);
/* Trailing slashes need to be trimmed. */
if (!sep[1]) {
const char *end;
for (end = sep; end != path; end--)
if (*end != PATH_SEP)
break;
/* Find *previous* / */
for (sep = end; sep >= path && *sep != PATH_SEP; sep--);
/* All /? Just return / */
if (end == sep)
ret = tal_strdup(ctx, PATH_SEP_STR);
else
ret = tal_strndup(ctx, sep+1, end - sep);
} else
ret = tal_strdup(ctx, sep + 1);
if (taken(path))
tal_free(path);
return ret;
}
/* This reuses str if we're to take it. */
static char *fixed_string(const tal_t *ctx,
const char *str, const char *path)
{
char *ret = tal_dup(ctx, char, path, 0, strlen(str)+1);
if (ret)
strcpy(ret, str);
return ret;
}
char *path_dirname(const tal_t *ctx, const char *path)
{
const char *sep;
if (unlikely(!path) && taken(path))
return NULL;
sep = strrchr(path, PATH_SEP);
if (!sep)
return fixed_string(ctx, ".", path);
/* Trailing slashes need to be trimmed. */
if (!sep[1]) {
const char *end;
for (end = sep; end != path; end--)
if (*end != PATH_SEP)
break;
/* Find *previous* / */
for (sep = end; sep > path && *sep != PATH_SEP; sep--);
}
/* In case there are multiple / in a row. */
while (sep > path && sep[-1] == PATH_SEP)
sep--;
if (sep == path) {
if (path_is_abs(path))
return tal_strndup(ctx, path, 1);
else
return fixed_string(ctx, ".", path);
}
return tal_strndup(ctx, path, sep - path);
}
bool path_is_abs(const char *path)
{
return path[0] == PATH_SEP;
}
bool path_is_file(const char *path)
{
struct stat st;
return stat(path, &st) == 0 && S_ISREG(st.st_mode);
}
bool path_is_dir(const char *path)
{
struct stat st;
return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
}
char **path_split(const tal_t *ctx, const char *path)
{
bool empty = path && !path[0];
char **ret = strsplit(ctx, path, PATH_SEP_STR, STR_NO_EMPTY);
/* Handle the "/" case */
if (ret && !empty && !ret[0]) {
if (!tal_resize(&ret, 2))
ret = tal_free(ret);
else {
ret[1] = NULL;
ret[0] = tal_strdup(ret, PATH_SEP_STR);
if (!ret[0])
ret = tal_free(ret);
}
}
return ret;
}
size_t path_ext_off(const char *path)
{
const char *dot, *base;
dot = strrchr(path, '.');
if (dot) {
base = strrchr(path, PATH_SEP);
if (!base)
base = path;
else
base++;
if (dot > base)
return dot - path;
}
return strlen(path);
}
/* Licensed under BSD-MIT - see LICENSE file for details */
#ifndef CCAN_PATH_H
#define CCAN_PATH_H
#include <ccan/tal/tal.h>
#include <stdbool.h>
/**
* path_cwd - get current directory.
* @ctx: the context to tal from
*
* Returns NULL and sets errno on error.
*/
char *path_cwd(const tal_t *ctx);
/**
* path_readlink - get a symbolic link contents
* @ctx: the context to tal the result from
* @link: the link to read (can be take())
*
* Returns NULL and sets errno on error, otherwise returns nul-terminated
* link contents.
*/
char *path_readlink(const tal_t *ctx, const char *link);
/**
* path_canon - return the canonical absolute pathname.
* @ctx: the context to tal the result from.
* @a: path to canonicalize (can be take())
*
* Returns NULL and sets errno on error, otherwise returns an absolute
* path with no symbolic links and no extra separators (ie. as per
* realpath).
*/
char *path_canon(const tal_t *ctx, const char *a);
/**
* path_simplify - remove double-/, ./ and some ../, plus trailing /.
* @ctx: the context to tal the result from
* @a: path to simplify (can be take())
*
* Unlike path_canon(), this routine does not convert a path to absolute
* terms or remove symlinks, but it does neaten it by removing extraneous
* parts.
*/
char *path_simplify(const tal_t *ctx, const char *a);
/**
* path_join - attach one path to another.
* @ctx: the context to tal the result from
* @base: the path to start at (can be take())
* @a: the path to head from there (can be take())
*
* If @a is an absolute path, return a copy of it. Otherwise, attach
* @a to @base.
*/
char *path_join(const tal_t *ctx, const char *base, const char *a);
/**
* path_pushd - save old dir and change to a new one.
* @ctx: the context to tal the result from
* @dir: the directory to return to (can be take())
*/
struct path_pushd *path_pushd(const tal_t *ctx, const char *dir);
/**
* path_popd - return to old, path_pushd dir.
* @olddir: the return from a previous path_pushd.
*
* Returns false and sets errno if it fails.
*/
bool path_popd(struct path_pushd *olddir);
/**
* path_rel - get relative path from a to b.
* @ctx: the context to tal the result from.
* @fromdir: the starting location (can be take())
* @to: the destination location (can be take())
*
* This returns a relative path which leads from @fromdir (assumed to be a
* directory) to @to. If @ctx it TAL_TAKE, frees both @fromdir and @to.
*
* Example:
* char *path = path_rel(NULL, "/tmp", "/");
* assert(strcmp(path, "..") == 0);
*/
char *path_rel(const tal_t *ctx, const char *fromdir, const char *to);
/**
* path_basename - get trailing filename part of path
* @ctx: the context to tal the result from
* @path: the path (can be take())
*
* This follows SUSv2:
* path dirname basename
* "/usr/lib" "/usr" "lib"
* "/usr/" "/" "usr"
* "usr" "." "usr"
* "/" "/" "/"
* "." "." "."
* ".." "." ".."
*
* See Also:
* path_dirname()
*/
char *path_basename(const tal_t *ctx, const char *path);
/**
* path_dirname - get the directory part of path
* @ctx: the context to tal the result from.
* @path: the path (can be take())
*
* This follows SUSv2.
*
* See Also:
* path_basename()
*/
char *path_dirname(const tal_t *ctx, const char *path);
/**
* path_is_abs - is a path absolute?
* @path: the path to examine.
*/
bool path_is_abs(const char *path);
/**
* path_is_file - is a path an existing file (or long to one)?
* @path: the path to examine.
*/
bool path_is_file(const char *path);
/**
* path_is_file - is a path an existing directory (or long to one)?
* @path: the path to examine.
*/
bool path_is_dir(const char *path);
/**
* path_split - split a path into its pathname components
* @ctx: the context to tal the result from
* @path: the path (can be take())
*
* This returns the sections of a path, such that joining them with /
* will restore the original path. This means that the resulting
* strings will never contain / unless the input path was entirely one
* or more "/" characters.
*
* The final char * in the array will be NULL.
*
* See Also:
* strjoin()
*/
char **path_split(const tal_t *ctx, const char *path);
/**
* path_ext_off - get offset of the extension within a pathname.
* @path: the path
*
* This returns the offset of the final . in the pathname (ie.
* path[path_ext_off(path)] == '.') or the length of the string
* if there is no extension.
*
* Note that if the only . in the basename is at the start
* (eg. /home/person/.bashrc), that is not considered an extension!
*/
size_t path_ext_off(const char *path);
#endif /* CCAN_PATH_H */
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
int main(void)
{
char *path, *ctx = tal_strdup(NULL, "ctx");
plan_tests(26);
path = path_basename(ctx, "/usr/lib");
ok1(streq(path, "lib"));
ok1(tal_parent(path) == ctx);
path = path_basename(ctx, "/usr/");
ok1(streq(path, "usr"));
ok1(tal_parent(path) == ctx);
path = path_basename(ctx, "/usr//");
ok1(streq(path, "usr"));
ok1(tal_parent(path) == ctx);
path = path_basename(ctx, "usr");
ok1(streq(path, "usr"));
ok1(tal_parent(path) == ctx);
path = path_basename(ctx, "/");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
path = path_basename(ctx, "//");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
path = path_basename(ctx, ".");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
path = path_basename(ctx, "./");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
path = path_basename(ctx, "..");
ok1(streq(path, ".."));
ok1(tal_parent(path) == ctx);
path = path_basename(ctx, "../");
ok1(streq(path, ".."));
ok1(tal_parent(path) == ctx);
tal_free(ctx);
ctx = tal_strdup(NULL, "ctx");
ok1(!tal_first(ctx));
/* Test take */
path = path_basename(ctx, take(tal_strdup(ctx, "..")));
ok1(streq(path, ".."));
ok1(tal_parent(path) == ctx);
ok1(tal_first(ctx) == path && !tal_next(ctx, path));
tal_free(path);
ok1(path_basename(ctx, take(NULL)) == NULL);
ok1(!tal_first(ctx));
tal_free(ctx);
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
int main(void)
{
char cwd[1024], *path, *path2, *ctx = tal_strdup(NULL, "ctx");
plan_tests(15);
if (!getcwd(cwd, sizeof(cwd)))
abort();
unlink("run-canon-link");
rmdir("run-canon-foo");
if (mkdir("run-canon-foo", 0700) != 0)
abort();
if (symlink("run-canon-foo", "run-canon-link") != 0)
abort();
path = path_canon(ctx, "run-canon-foo");
ok1(tal_parent(path) == ctx);
ok1(strends(path, "run-canon-foo"));
ok1(strstarts(path, cwd));
ok1(path[strlen(cwd)] == PATH_SEP);
ok1(strlen(path) == strlen(cwd) + 1 + strlen("run-canon-foo"));
tal_free(path);
ok1(!path_canon(ctx, take(NULL)));
ok1(tal_first(ctx) == NULL);
/* Test take doesn't leak. */
ok1(tal_first(ctx) == NULL);
path = path_canon(ctx, take(tal_strdup(ctx, "run-canon-foo")));
ok1(strends(path, "run-canon-foo"));
ok1(strstarts(path, cwd));
ok1(path[strlen(cwd)] == PATH_SEP);
ok1(strlen(path) == strlen(cwd) + 1 + strlen("run-canon-foo"));
ok1(tal_first(ctx) == path && tal_next(ctx, path) == NULL);
path2 = path_canon(ctx, "run-canon-link");
ok1(streq(path2, path));
unlink("run-canon-link");
if (symlink(".", "run-canon-link") != 0)
abort();
path = path_canon(ctx, "run-canon-link");
ok1(streq(path, cwd));
tal_free(ctx);
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
int main(void)
{
char path1[1024], *cwd, *ctx = tal_strdup(NULL, "ctx");
/* This is how many tests you plan to run */
plan_tests(5);
if (!getcwd(path1, sizeof(path1)))
abort();
cwd = path_cwd(ctx);
ok1(cwd);
ok1(tal_parent(cwd) == ctx);
tal_free(cwd);
rmdir("run-cwd-long-long-long-name/bar-long-long-long-long-name");
rmdir("run-cwd-long-long-long-name");
if (mkdir("run-cwd-long-long-long-name", 0700) != 0)
abort();
if (mkdir("run-cwd-long-long-long-name/bar-long-long-long-long-name", 0700) != 0)
abort();
if (chdir("run-cwd-long-long-long-name/bar-long-long-long-long-name") != 0)
abort();
cwd = path_cwd(ctx);
ok1(cwd);
ok1(tal_parent(cwd) == ctx);
ok1(strends(cwd,
"run-cwd-long-long-long-name/bar-long-long-long-long-name"));
tal_free(ctx);
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
int main(void)
{
char *path, *ctx = tal_strdup(NULL, "ctx");
plan_tests(26);
path = path_dirname(ctx, "/usr/lib");
ok1(streq(path, "/usr"));
ok1(tal_parent(path) == ctx);
path = path_dirname(ctx, "/usr/");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
path = path_dirname(ctx, "/usr//");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
path = path_dirname(ctx, "usr");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
path = path_dirname(ctx, "/");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
path = path_dirname(ctx, "//");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
path = path_dirname(ctx, ".");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
path = path_dirname(ctx, "./");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
path = path_dirname(ctx, "..");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
path = path_dirname(ctx, "../");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
tal_free(ctx);
ctx = tal_strdup(NULL, "ctx");
ok1(!tal_first(ctx));
/* Test take */
path = path_dirname(ctx, take(tal_strdup(ctx, "..")));
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
ok1(tal_first(ctx) == path && !tal_next(ctx, path));
tal_free(path);
ok1(path_dirname(ctx, take(NULL)) == NULL);
ok1(!tal_first(ctx));
tal_free(ctx);
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
int main(void)
{
plan_tests(9);
ok1(path_ext_off("foo") == 3);
ok1(path_ext_off(".foo") == 4);
ok1(path_ext_off("bar.foo") == 3);
ok1(path_ext_off("bar/foo") == 7);
ok1(path_ext_off("bar/.foo") == 8);
ok1(path_ext_off(".bar/foo") == 8);
ok1(path_ext_off("foo.bar/foo") == 11);
ok1(path_ext_off("foo.bar/foo.") == 11);
ok1(path_ext_off("foo.bar/foo..") == 12);
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
int main(void)
{
plan_tests(5);
ok1(path_is_abs(PATH_SEP_STR "foo"));
ok1(!path_is_abs("foo"));
ok1(!path_is_abs("foo" PATH_SEP_STR));
ok1(path_is_abs(PATH_SEP_STR "foo" PATH_SEP_STR));
ok1(path_is_abs(PATH_SEP_STR "."));
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
char cwd[1024], *path, *ctx = tal_strdup(NULL, "ctx");
plan_tests(6);
if (!getcwd(cwd, sizeof(cwd)))
abort();
unlink("run-is_dir-dir-link");
unlink("run-is_dir-file-link");
unlink("run-is_dir-dir/file");
rmdir("run-is_dir-dir");
if (mkdir("run-is_dir-dir", 0700) != 0)
abort();
if (symlink("run-is_dir-dir", "run-is_dir-dir-link") != 0)
abort();
if (symlink("run-is_dir-dir/file", "run-is_dir-file-link") != 0)
abort();
close(open("run-is_dir-dir/file", O_WRONLY|O_CREAT, 0600));
ok1(path_is_dir("run-is_dir-dir-link"));
ok1(!path_is_dir("run-is_dir-file-link"));
ok1(!path_is_dir("run-is_dir-dir/file"));
ok1(path_is_dir("run-is_dir-dir"));
path = path_join(ctx, cwd, "run-is_dir-dir/file");
ok1(!path_is_dir(path));
ok1(path_is_dir(cwd));
tal_free(ctx);
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void)
{
char cwd[1024], *path, *ctx = tal_strdup(NULL, "ctx");
plan_tests(7);
if (!getcwd(cwd, sizeof(cwd)))
abort();
unlink("run-is_file-dir-link");
unlink("run-is_file-file-link");
unlink("run-is_file-dir/file");
rmdir("run-is_file-dir");
if (mkdir("run-is_file-dir", 0700) != 0)
abort();
if (symlink("run-is_file-dir", "run-is_file-dir-link") != 0)
abort();
if (symlink("run-is_file-dir/file", "run-is_file-file-link") != 0)
abort();
close(open("run-is_file-dir/file", O_WRONLY|O_CREAT, 0600));
ok1(!path_is_file("run-is_file-dir-link"));
ok1(path_is_file("run-is_file-file-link"));
ok1(path_is_file("run-is_file-dir/file"));
ok1(!path_is_file("run-is_file-dir"));
ok1(!path_is_file("run-is_file-nonexist"));
path = path_join(ctx, cwd, "run-is_file-dir/file");
ok1(path_is_file(path));
ok1(!path_is_file(cwd));
tal_free(ctx);
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
int main(void)
{
char *path, *ctx = tal_strdup(NULL, "ctx");
plan_tests(34);
path = path_join(ctx, "foo", "bar");
ok1(streq(path, "foo/bar"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_join(ctx, "foo/", "bar");
ok1(streq(path, "foo/bar"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_join(ctx, "foo/", "/bar");
ok1(streq(path, "/bar"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_join(ctx, "foo", "/bar");
ok1(streq(path, "/bar"));
ok1(tal_parent(path) == ctx);
tal_free(path);
/* Test take */
path = path_join(ctx, "foo", take(tal_strdup(ctx, "bar")));
ok1(streq(path, "foo/bar"));
ok1(tal_parent(path) == ctx);
ok1(tal_first(ctx) == path && tal_next(ctx, path) == NULL);
tal_free(path);
path = path_join(ctx, "foo", take(tal_strdup(ctx, "/bar")));
ok1(streq(path, "/bar"));
ok1(tal_parent(path) == ctx);
ok1(tal_first(ctx) == path && tal_next(ctx, path) == NULL);
tal_free(path);
path = path_join(ctx, take(tal_strdup(ctx, "foo")), "bar");
ok1(streq(path, "foo/bar"));
ok1(tal_parent(path) == ctx);
ok1(tal_first(ctx) == path && tal_next(ctx, path) == NULL);
tal_free(path);
path = path_join(ctx, take(tal_strdup(ctx, "foo")), "/bar");
ok1(streq(path, "/bar"));
ok1(tal_parent(path) == ctx);
ok1(tal_first(ctx) == path && tal_next(ctx, path) == NULL);
tal_free(path);
path = path_join(ctx, take(tal_strdup(ctx, "foo")),
take(tal_strdup(ctx, "bar")));
ok1(streq(path, "foo/bar"));
ok1(tal_parent(path) == ctx);
ok1(tal_first(ctx) == path && tal_next(ctx, path) == NULL);
tal_free(path);
path = path_join(ctx, take(tal_strdup(ctx, "foo")),
take(tal_strdup(ctx, "/bar")));
ok1(streq(path, "/bar"));
ok1(tal_parent(path) == ctx);
ok1(tal_first(ctx) == path && tal_next(ctx, path) == NULL);
tal_free(path);
path = path_join(ctx, take(NULL), "bar");
ok1(!path);
ok1(!tal_first(ctx));
/* This is allowed to succeed, as first arg unneeded. */
path = path_join(ctx, take(NULL), "/bar");
ok1(!path || streq(path, "/bar"));
tal_free(path);
ok1(!tal_first(ctx));
path = path_join(ctx, "foo", take(NULL));
ok1(!path);
ok1(!tal_first(ctx));
path = path_join(ctx, take(NULL), take(NULL));
ok1(!path);
ok1(!tal_first(ctx));
tal_free(ctx);
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
int main(void)
{
struct path_pushd *pd;
char path1[1024], path2[1024], *ctx = tal_strdup(NULL, "ctx");
/* This is how many tests you plan to run */
plan_tests(19);
/* Test pushd/popd */
if (!getcwd(path1, sizeof(path1)))
abort();
pd = path_pushd(NULL, "non-existent-dir");
ok1(errno == ENOENT);
ok1(!pd);
errno = -100;
pd = path_pushd(ctx, take(tal_strdup(ctx, "non-existent-dir")));
ok1(errno == ENOENT);
ok1(!pd);
ok1(!tal_first(ctx));
errno = -100;
pd = path_pushd(ctx, take(NULL));
ok1(!pd);
ok1(!tal_first(ctx));
ok1(errno == -100);
pd = path_pushd(ctx, "/tmp");
ok1(pd);
ok1(tal_parent(pd) == ctx);
if (!getcwd(path2, sizeof(path2)))
abort();
ok1(streq(path2, "/tmp"));
path_popd(pd);
if (!getcwd(path2, sizeof(path2)))
abort();
ok1(streq(path2, path1));
pd = path_pushd(ctx, take(tal_strdup(ctx, "/tmp")));
ok1(pd);
ok1(tal_parent(pd) == ctx);
path_popd(pd);
if (!getcwd(path2, sizeof(path2)))
abort();
ok1(streq(path2, path1));
ok1(!tal_first(ctx));
/* Without fchdir, we can't push a path which no longer exists. */
if (mkdir("run-pushd-dir", 0700) != 0)
abort();
if (chdir("run-pushd-dir") != 0)
abort();
if (rmdir("../run-pushd-dir") != 0)
abort();
pd = path_pushd(ctx, path1);
#if HAVE_FCHDIR
ok1(pd);
ok1(path_popd(pd));
#else
ok1(errno == ENOENT);
ok1(!pd);
#endif
ok1(!tal_first(ctx));
tal_free(ctx);
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
int main(void)
{
char *link, *ctx = tal_strdup(NULL, "ctx");
plan_tests(12);
unlink("run-readlink-link");
link = path_readlink(ctx, "run-readlink-link");
ok1(errno == ENOENT);
ok1(!link);
link = path_readlink(ctx, take(tal_strdup(ctx, "run-readlink-link")));
ok1(errno == ENOENT);
ok1(!link);
ok1(tal_first(ctx) == NULL);
if (symlink("/tmp", "run-readlink-link") != 0)
abort();
link = path_readlink(ctx, "run-readlink-link");
ok1(tal_parent(link) == ctx);
ok1(streq(link, "/tmp"));
tal_free(link);
link = path_readlink(ctx, take(tal_strdup(ctx, "run-readlink-link")));
ok1(tal_parent(link) == ctx);
ok1(streq(link, "/tmp"));
ok1(tal_first(ctx) == link && tal_next(ctx, link) == NULL);
unlink("run-readlink-link");
if (symlink("some-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-long-name", "run-readlink-link") != 0)
abort();
link = path_readlink(ctx, "run-readlink-link");
ok1(tal_parent(link) == ctx);
ok1(streq(link, "some-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-really-long-name"));
tal_free(ctx);
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
int main(void)
{
char cwd[1024], *path, *ctx = tal_strdup(NULL, "ctx");
plan_tests(19);
if (!getcwd(cwd, sizeof(cwd)))
abort();
unlink("run-rel-link");
rmdir("run-rel-foo");
if (mkdir("run-rel-foo", 0700) != 0)
abort();
if (symlink("run-rel-foo", "run-rel-link") != 0)
abort();
path = path_rel(ctx, ".", "run-rel-foo");
ok1(streq(path, "run-rel-foo"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_rel(ctx, "run-rel-foo", ".");
ok1(streq(path, ".."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_rel(ctx, ".", "run-rel-link");
/* This doesn't specify whether it preserves links. */
ok1(streq(path, "run-rel-link") || streq(path, "run-rel-foo"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_rel(ctx, "/", ".");
ok1(streq(path, cwd + 1));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_rel(ctx, "run-rel-foo", "run-rel-foo");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_rel(ctx, take(tal_strdup(ctx, ".")), "run-rel-foo");
ok1(streq(path, "run-rel-foo"));
ok1(tal_parent(path) == ctx);
tal_free(path);
ok1(tal_first(ctx) == NULL);
path = path_rel(ctx, ".", take(tal_strdup(ctx, "run-rel-foo")));
ok1(streq(path, "run-rel-foo"));
ok1(tal_parent(path) == ctx);
tal_free(path);
ok1(tal_first(ctx) == NULL);
path = path_rel(ctx, take(tal_strdup(ctx, ".")),
take(tal_strdup(ctx, "run-rel-foo")));
ok1(streq(path, "run-rel-foo"));
ok1(tal_parent(path) == ctx);
tal_free(path);
ok1(tal_first(ctx) == NULL);
tal_free(ctx);
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
int main(void)
{
char cwd[1024], *path, *ctx = tal_strdup(NULL, "ctx");
plan_tests(87);
if (!getcwd(cwd, sizeof(cwd)))
abort();
rmdir("run-simplify-foo");
unlink("run-simplify-link");
if (mkdir("run-simplify-foo", 0700) != 0)
abort();
if (symlink("run-simplify-foo", "run-simplify-link") != 0)
abort();
/* Handling of . and .. */
path = path_simplify(ctx, ".");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "./");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "..");
ok1(streq(path, ".."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "../");
ok1(streq(path, ".."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "./..");
ok1(streq(path, ".."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "./../");
ok1(streq(path, ".."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "./../.");
ok1(streq(path, ".."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "./.././");
ok1(streq(path, ".."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "./../..");
ok1(streq(path, "../.."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "./../../");
ok1(streq(path, "../.."));
ok1(tal_parent(path) == ctx);
tal_free(path);
/* Handling of /. and /.. */
path = path_simplify(ctx, "/");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "//");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/.");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/./");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/..");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/../");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/./..");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/./../");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/./../.");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/./.././");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/./../..");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/./../../");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
/* Don't trace back over a symlink link */
path = path_simplify(ctx, "run-simplify-foo");
ok1(streq(path, "run-simplify-foo"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "./run-simplify-foo");
ok1(streq(path, "run-simplify-foo"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "./run-simplify-foo/.");
ok1(streq(path, "run-simplify-foo"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "run-simplify-link");
ok1(streq(path, "run-simplify-link"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "./run-simplify-link");
ok1(streq(path, "run-simplify-link"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "./run-simplify-link/.");
ok1(streq(path, "run-simplify-link"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "run-simplify-foo/..");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "run-simplify-foo//..");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "run-simplify-foo//../");
ok1(streq(path, "."));
ok1(tal_parent(path) == ctx);
tal_free(path);
/* This is expected to be a real directory. */
path = path_simplify(ctx, "/tmp");
ok1(streq(path, "/tmp"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/tmp/");
ok1(streq(path, "/tmp"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/tmp/.");
ok1(streq(path, "/tmp"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/./tmp/.");
ok1(streq(path, "/tmp"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/../tmp/.");
ok1(streq(path, "/tmp"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/tmp/..");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/tmp/../");
ok1(streq(path, "/"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/tmp/../tmp");
ok1(streq(path, "/tmp"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/tmp/../tmp/");
ok1(streq(path, "/tmp"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "/tmp/../tmp/.");
ok1(streq(path, "/tmp"));
ok1(tal_parent(path) == ctx);
tal_free(path);
/* take tests */
path = path_simplify(ctx, take(tal_strdup(ctx, "/tmp/../tmp/.")));
ok1(streq(path, "/tmp"));
ok1(tal_parent(path) == ctx);
tal_free(path);
ok1(tal_first(ctx) == NULL);
path = path_simplify(ctx, take(NULL));
ok1(!path);
ok1(tal_first(ctx) == NULL);
tal_free(ctx);
return exit_status();
}
#include <ccan/tal/path/path.h>
#include <ccan/tal/path/path.c>
#include <ccan/tap/tap.h>
int main(void)
{
char *ctx = tal_strdup(NULL, "ctx"), **split;
plan_tests(46);
split = path_split(ctx, "foo" PATH_SEP_STR "bar");
ok1(tal_parent(split) == ctx);
ok1(streq(split[0], "foo"));
ok1(streq(split[1], "bar"));
ok1(split[2] == NULL);
tal_free(split);
split = path_split(ctx, "foo" PATH_SEP_STR "bar" PATH_SEP_STR);
ok1(tal_parent(split) == ctx);
ok1(streq(split[0], "foo"));
ok1(streq(split[1], "bar"));
ok1(split[2] == NULL);
tal_free(split);
split = path_split(ctx, PATH_SEP_STR "foo"
PATH_SEP_STR "bar" PATH_SEP_STR);
ok1(tal_parent(split) == ctx);
ok1(streq(split[0], "foo"));
ok1(streq(split[1], "bar"));
ok1(split[2] == NULL);
tal_free(split);
split = path_split(ctx, PATH_SEP_STR PATH_SEP_STR "foo"
PATH_SEP_STR PATH_SEP_STR "bar"
PATH_SEP_STR PATH_SEP_STR);
ok1(tal_parent(split) == ctx);
ok1(streq(split[0], "foo"));
ok1(streq(split[1], "bar"));
ok1(split[2] == NULL);
tal_free(split);
split = path_split(ctx, "foo");
ok1(tal_parent(split) == ctx);
ok1(streq(split[0], "foo"));
ok1(split[1] == NULL);
tal_free(split);
split = path_split(ctx, PATH_SEP_STR "foo");
ok1(tal_parent(split) == ctx);
ok1(streq(split[0], "foo"));
ok1(split[1] == NULL);
tal_free(split);
split = path_split(ctx, PATH_SEP_STR PATH_SEP_STR "foo");
ok1(tal_parent(split) == ctx);
ok1(streq(split[0], "foo"));
ok1(split[1] == NULL);
tal_free(split);
split = path_split(ctx, "foo" PATH_SEP_STR);
ok1(tal_parent(split) == ctx);
ok1(streq(split[0], "foo"));
ok1(split[1] == NULL);
tal_free(split);
split = path_split(ctx, "foo" PATH_SEP_STR PATH_SEP_STR);
ok1(tal_parent(split) == ctx);
ok1(streq(split[0], "foo"));
ok1(split[1] == NULL);
tal_free(split);
split = path_split(ctx, PATH_SEP_STR "foo" PATH_SEP_STR);
ok1(tal_parent(split) == ctx);
ok1(streq(split[0], "foo"));
ok1(split[1] == NULL);
tal_free(split);
split = path_split(ctx, "");
ok1(tal_parent(split) == ctx);
ok1(split[0] == NULL);
tal_free(split);
split = path_split(ctx, PATH_SEP_STR);
ok1(tal_parent(split) == ctx);
ok1(streq(split[0], PATH_SEP_STR));
ok1(split[1] == NULL);
tal_free(split);
/* Test take */
split = path_split(ctx, take(tal_strdup(ctx, PATH_SEP_STR)));
ok1(tal_parent(split) == ctx);
ok1(streq(split[0], PATH_SEP_STR));
ok1(split[1] == NULL);
tal_free(split);
ok1(tal_first(ctx) == NULL);
split = path_split(ctx, take(NULL));
ok1(!split);
ok1(tal_first(ctx) == NULL);
ok1(tal_first(NULL) == ctx && tal_next(NULL, ctx) == NULL);
tal_free(ctx);
return exit_status();
}
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