str_talloc \
take \
tal \
+ tal/path \
tal/str \
talloc \
talloc_link \
--- /dev/null
+../../../licenses/BSD-MIT
\ No newline at end of file
--- /dev/null
+#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;
+}
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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 */
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}
--- /dev/null
+#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();
+}