summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/include/k5-platform.h22
-rw-r--r--src/util/support/Makefile.in21
-rw-r--r--src/util/support/libkrb5support-fixed.exports3
-rw-r--r--src/util/support/path.c161
-rw-r--r--src/util/support/t_path.c190
5 files changed, 395 insertions, 2 deletions
diff --git a/src/include/k5-platform.h b/src/include/k5-platform.h
index d8324839b..147f87fb7 100644
--- a/src/include/k5-platform.h
+++ b/src/include/k5-platform.h
@@ -36,6 +36,9 @@
* + consistent getpwnam/getpwuid interfaces
* + va_copy fudged if not provided
* + [v]asprintf
+ * + mkstemp
+ * + zap (support function; macro is in k5-int.h)
+ * + path manipulation
* + _, N_, dgettext, bindtextdomain, setlocale (for localization)
*/
@@ -1012,6 +1015,25 @@ extern int krb5int_mkstemp(char *);
extern void krb5int_zap(void *ptr, size_t len);
/*
+ * Split a path into parent directory and basename. Either output parameter
+ * may be NULL if the caller doesn't need it. parent_out will be empty if path
+ * has no basename. basename_out will be empty if path ends with a path
+ * separator. Returns 0 on success or ENOMEM on allocation failure.
+ */
+long k5_path_split(const char *path, char **parent_out, char **basename_out);
+
+/*
+ * Compose two path components, inserting the platform-appropriate path
+ * separator if needed. If path2 is an absolute path, path1 will be discarded
+ * and path_out will be a copy of path2. Returns 0 on success or ENOMEM on
+ * allocation failure.
+ */
+long k5_path_join(const char *path1, const char *path2, char **path_out);
+
+/* Return 1 if path is absolute, 0 if it is relative. */
+int k5_path_isabs(const char *path);
+
+/*
* Localization macros. If we have gettext, define _ appropriately for
* translating a string. If we do not have gettext, define _, bindtextdomain,
* and setlocale as no-ops. N_ is always a no-op; it marks a string for
diff --git a/src/util/support/Makefile.in b/src/util/support/Makefile.in
index 88520438f..8e4f70f3c 100644
--- a/src/util/support/Makefile.in
+++ b/src/util/support/Makefile.in
@@ -63,6 +63,7 @@ STLIBOBJS= \
utf8.o \
utf8_conv.o \
zap.o \
+ path.o \
$(IPC_ST_OBJ) \
$(STRLCPY_ST_OBJ) \
$(PRINTF_ST_OBJ) \
@@ -79,6 +80,7 @@ LIBOBJS= \
$(OUTPRE)utf8.$(OBJEXT) \
$(OUTPRE)utf8_conv.$(OBJEXT) \
$(OUTPRE)zap.$(OBJEXT) \
+ $(OUTPRE)path.$(OBJEXT) \
$(IPC_OBJ) \
$(STRLCPY_OBJ) \
$(PRINTF_OBJ) \
@@ -104,7 +106,8 @@ SRCS=\
$(srcdir)/mkstemp.c \
$(srcdir)/t_k5buf.c \
$(srcdir)/t_unal.c \
- $(srcdir)/zap.c
+ $(srcdir)/zap.c \
+ $(srcdir)/path.c
SHLIB_EXPDEPS =
# Add -lm if dumping thread stats, for sqrt.
@@ -155,13 +158,27 @@ T_K5BUF_OBJS= t_k5buf.o k5buf.o $(PRINTF_ST_OBJ)
t_k5buf: $(T_K5BUF_OBJS)
$(CC_LINK) -o t_k5buf $(T_K5BUF_OBJS)
+t_path: t_path.o path.o $(PRINTF_ST_OBJ)
+ $(CC_LINK) -o $@ t_path.o path.o $(PRINTF_ST_OBJ)
+
+t_path_win: t_path_win.o path_win.o $(PRINTF_ST_OBJ)
+ $(CC_LINK) -o $@ t_path_win.o path_win.o $(PRINTF_ST_OBJ)
+
+t_path_win.o: $(srcdir)/t_path.c
+ $(CC) $(ALL_CFLAGS) -DWINDOWS_PATHS -c $(srcdir)/t_path.c -o $@
+
+path_win.o: $(srcdir)/path.c
+ $(CC) $(ALL_CFLAGS) -DWINDOWS_PATHS -c $(srcdir)/path.c -o $@
+
t_unal: t_unal.o
$(CC_LINK) -o t_unal t_unal.o
-TEST_PROGS= t_k5buf t_unal
+TEST_PROGS= t_k5buf t_path t_path_win t_unal
check-unix:: $(TEST_PROGS)
./t_k5buf
+ ./t_path
+ ./t_path_win
./t_unal
clean::
diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports
index 40023e709..496259c99 100644
--- a/src/util/support/libkrb5support-fixed.exports
+++ b/src/util/support/libkrb5support-fixed.exports
@@ -1,3 +1,6 @@
+k5_path_isabs
+k5_path_join
+k5_path_split
krb5int_key_register
krb5int_key_delete
krb5int_getspecific
diff --git a/src/util/support/path.c b/src/util/support/path.c
new file mode 100644
index 000000000..221fb4a6a
--- /dev/null
+++ b/src/util/support/path.c
@@ -0,0 +1,161 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/path.c - Portable path manipulation functions */
+/*
+ * Copyright (C) 2011 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <k5-platform.h>
+
+/* For testing purposes, use a different symbol for Windows path semantics. */
+#ifdef _WIN32
+#define WINDOWS_PATHS
+#endif
+
+/*
+ * This file implements a limited set of portable path manipulation functions.
+ * When in doubt about edge cases, we follow the Python os.path semantics.
+ */
+
+#ifdef WINDOWS_PATHS
+#define SEP '\\'
+#define IS_SEPARATOR(c) ((c) == '\\' || (c) == '/')
+#else
+#define SEP '/'
+#define IS_SEPARATOR(c) ((c) == '/')
+#endif
+
+/* Find the rightmost path separator in path, or NULL if there is none. */
+static inline const char *
+find_sep(const char *path)
+{
+#ifdef WINDOWS_PATHS
+ const char *slash, *backslash;
+
+ slash = strrchr(path, '/');
+ backslash = strrchr(path, '\\');
+ if (slash != NULL && backslash != NULL)
+ return (slash > backslash) ? slash : backslash;
+ else
+ return (slash != NULL) ? slash : backslash;
+#else
+ return strrchr(path, '/');
+#endif
+}
+
+/* XXX drive letter prefixes */
+long
+k5_path_split(const char *path, char **parent_out, char **basename_out)
+{
+ const char *pathstart, *sep, *pend, *bstart;
+ char *parent = NULL, *basename = NULL;
+
+ if (parent_out != NULL)
+ *parent_out = NULL;
+ if (basename_out != NULL)
+ *basename_out = NULL;
+
+ pathstart = path;
+#ifdef WINDOWS_PATHS
+ if (*path != '\0' && path[1] == ':')
+ pathstart = path + 2;
+#endif
+
+ sep = find_sep(pathstart);
+ if (sep != NULL) {
+ bstart = sep + 1;
+ /* Strip off excess separators before the one we found. */
+ pend = sep;
+ while (pend > pathstart && IS_SEPARATOR(pend[-1]))
+ pend--;
+ /* But if we hit the start, keep the whole separator sequence. */
+ if (pend == pathstart)
+ pend = sep + 1;
+ } else {
+ bstart = pathstart;
+ pend = pathstart;
+ }
+
+ if (parent_out) {
+ parent = malloc(pend - path + 1);
+ if (parent == NULL)
+ return ENOMEM;
+ memcpy(parent, path, pend - path);
+ parent[pend - path] = '\0';
+ }
+ if (basename_out) {
+ basename = strdup(bstart);
+ if (basename == NULL) {
+ free(parent);
+ return ENOMEM;
+ }
+ }
+
+ if (parent_out)
+ *parent_out = parent;
+ if (basename_out)
+ *basename_out = basename;
+ return 0;
+}
+
+long
+k5_path_join(const char *path1, const char *path2, char **path_out)
+{
+ char *path, c;
+ int ret;
+
+ *path_out = NULL;
+ if (k5_path_isabs(path2) || *path1 == '\0') {
+ /* Discard path1 and return a copy of path2. */
+ path = strdup(path2);
+ if (path == NULL)
+ return ENOMEM;
+ } else {
+ /*
+ * Compose path1 and path2, adding a separator if path1 is non-empty
+ * there's no separator between them already. (*path2 can be a
+ * separator in the weird case where it starts with /: or \: on
+ * Windows, and Python doesn't insert a separator in this case.)
+ */
+ c = path1[strlen(path1) - 1];
+ if (IS_SEPARATOR(c) || IS_SEPARATOR(*path2))
+ ret = asprintf(&path, "%s%s", path1, path2);
+ else
+ ret = asprintf(&path, "%s%c%s", path1, SEP, path2);
+ if (ret < 0)
+ return ENOMEM;
+ }
+ *path_out = path;
+ return 0;
+}
+
+int
+k5_path_isabs(const char *path)
+{
+#ifdef WINDOWS_PATHS
+ if (*path != '\0' && path[1] == ':')
+ path += 2;
+ return (*path == '/' || *path == '\\');
+#else
+ return (*path == '/');
+#endif
+}
diff --git a/src/util/support/t_path.c b/src/util/support/t_path.c
new file mode 100644
index 000000000..550d748b7
--- /dev/null
+++ b/src/util/support/t_path.c
@@ -0,0 +1,190 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/t_path.c - Path manipulation tests */
+/*
+ * Copyright (C) 2011 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <k5-platform.h>
+
+/* For testing purposes, use a different symbol for Windows path semantics. */
+#ifdef _WIN32
+#define WINDOWS_PATHS
+#endif
+
+/*
+ * The ultimate arbiter of these tests is the dirname, basename, and isabs
+ * methods of the Python posixpath and ntpath modules.
+ */
+
+struct {
+ const char *path;
+ const char *posix_dirname;
+ const char *posix_basename;
+ const char *win_dirname;
+ const char *win_basename;
+} split_tests[] = {
+ { "", "", "", "", "" },
+ { "a/b/c", "a/b", "c", "a/b", "c" },
+ { "a/b/", "a/b", "", "a/b", "" },
+ { "a\\b\\c", "", "a\\b\\c", "a\\b", "c" },
+ { "a\\b\\", "", "a\\b\\", "a\\b", "" },
+ { "a/b\\c", "a", "b\\c", "a/b", "c" },
+ { "a//b", "a", "b", "a", "b" },
+ { "a/\\/b", "a/\\", "b", "a", "b" },
+ { "a//b/c", "a//b", "c", "a//b", "c" },
+
+ { "/", "/", "", "/", "" },
+ { "\\", "", "\\", "\\", "" },
+ { "/a/b/c", "/a/b", "c", "/a/b", "c" },
+ { "\\a/b/c", "\\a/b", "c", "\\a/b", "c" },
+ { "/a", "/", "a", "/", "a" },
+ { "//a", "//", "a", "//", "a" },
+ { "\\//\\a", "\\", "\\a", "\\//\\", "a" },
+
+ { "/:", "/", ":", "/:", "" },
+ { "c:\\", "", "c:\\", "c:\\", "" },
+ { "c:/", "c:", "", "c:/", "" },
+ { "c:/\\a", "c:", "\\a", "c:/\\", "a" },
+ { "c:a", "", "c:a", "c:", "a" },
+};
+
+struct {
+ const char *path1;
+ const char *path2;
+ const char *posix_result;
+ const char *win_result;
+} join_tests[] = {
+ { "", "", "", "" },
+ { "", "a", "a", "a" },
+ { "", "/a", "/a", "/a" },
+ { "", "c:", "c:", "c:" },
+
+ { "a", "", "a/", "a\\" },
+ { "a/", "", "a/", "a/" },
+ { "a\\", "", "a\\/", "a\\" },
+ { "a/\\", "", "a/\\/", "a/\\" },
+
+ { "a", "b", "a/b", "a\\b" },
+ { "a", "/b", "/b", "/b" },
+ { "a", "c:", "a/c:", "a\\c:" },
+ { "a", "c:/", "a/c:/", "c:/" },
+ { "a", "c:/a", "a/c:/a", "c:/a" },
+ { "a", "/:", "/:", "a/:" },
+ { "a/", "b", "a/b", "a/b" },
+ { "a/", "", "a/", "a/" },
+ { "a\\", "b", "a\\/b", "a\\b" },
+
+ { "a//", "b", "a//b", "a//b" },
+ { "a/\\", "b", "a/\\/b", "a/\\b" },
+};
+
+struct {
+ const char *path;
+ int posix_result;
+ int win_result;
+} isabs_tests[] = {
+ { "", 0, 0 },
+ { "/", 1, 1 },
+ { "/a", 1, 1 },
+ { "a/b", 0, 0 },
+ { "\\", 0, 1 },
+ { "\\a", 0, 1 },
+ { "c:", 0, 0 },
+ { "/:", 1, 0 },
+ { "\\:", 0, 0 },
+ { "c:/a", 0, 1 },
+ { "c:\\a", 0, 1 },
+ { "c:a", 0, 0 },
+ { "c:a/b", 0, 0 },
+ { "/:a/b", 1, 0 },
+};
+
+int
+main(void)
+{
+ char *dirname, *basename, *joined;
+ const char *edirname, *ebasename, *ejoined, *ipath, *path1, *path2;
+ int result, eresult, status = 0;
+ size_t i;
+
+ for (i = 0; i < sizeof(split_tests) / sizeof(*split_tests); i++) {
+ ipath = split_tests[i].path;
+#ifdef WINDOWS_PATHS
+ edirname = split_tests[i].win_dirname;
+ ebasename = split_tests[i].win_basename;
+#else
+ edirname = split_tests[i].posix_dirname;
+ ebasename = split_tests[i].posix_basename;
+#endif
+ assert(k5_path_split(ipath, NULL, NULL) == 0);
+ assert(k5_path_split(ipath, &dirname, NULL) == 0);
+ free(dirname);
+ assert(k5_path_split(ipath, NULL, &basename) == 0);
+ free(basename);
+ assert(k5_path_split(ipath, &dirname, &basename) == 0);
+ if (strcmp(dirname, edirname) != 0) {
+ fprintf(stderr, "Split test %d: dirname %s != expected %s\n",
+ (int)i, dirname, edirname);
+ status = 1;
+ }
+ if (strcmp(basename, ebasename) != 0) {
+ fprintf(stderr, "Split test %d: basename %s != expected %s\n",
+ (int)i, basename, ebasename);
+ status = 1;
+ }
+ free(dirname);
+ free(basename);
+ }
+
+ for (i = 0; i < sizeof(join_tests) / sizeof(*join_tests); i++) {
+ path1 = join_tests[i].path1;
+ path2 = join_tests[i].path2;
+#ifdef WINDOWS_PATHS
+ ejoined = join_tests[i].win_result;
+#else
+ ejoined = join_tests[i].posix_result;
+#endif
+ assert(k5_path_join(path1, path2, &joined) == 0);
+ if (strcmp(joined, ejoined) != 0) {
+ fprintf(stderr, "Join test %d: %s != expected %s\n",
+ (int)i, joined, ejoined);
+ status = 1;
+ }
+ }
+
+ for (i = 0; i < sizeof(isabs_tests) / sizeof(*isabs_tests); i++) {
+#ifdef WINDOWS_PATHS
+ eresult = isabs_tests[i].win_result;
+#else
+ eresult = isabs_tests[i].posix_result;
+#endif
+ result = k5_path_isabs(isabs_tests[i].path);
+ if (result != eresult) {
+ fprintf(stderr, "isabs test %d: %d != expected %d\n",
+ (int)i, result, eresult);
+ status = 1;
+ }
+ }
+
+ return status;
+}