svn rev #25074: trunk/src/ include/ util/support/

ghudson@MIT.EDU ghudson at MIT.EDU
Sat Aug 6 21:12:28 EDT 2011


http://src.mit.edu/fisheye/changelog/krb5/?cs=25074
Commit By: ghudson
Log Message:
Add internal APIs for portable path manipulation.

k5_path_split separates a path into dirname and basename.
k5_path_join joins two paths.
k5_path_isabs determines if a path is absolute.

All three functions follow the Python path function semantics.
Currently the test module doesn't run in the Windows build, but the
Windows path semantics are tested in the Unix build using specially
built objects.


Changed Files:
U   trunk/src/include/k5-platform.h
U   trunk/src/util/support/Makefile.in
U   trunk/src/util/support/libkrb5support-fixed.exports
A   trunk/src/util/support/path.c
A   trunk/src/util/support/t_path.c
Modified: trunk/src/include/k5-platform.h
===================================================================
--- trunk/src/include/k5-platform.h	2011-08-05 16:59:52 UTC (rev 25073)
+++ trunk/src/include/k5-platform.h	2011-08-07 01:12:28 UTC (rev 25074)
@@ -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 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

Modified: trunk/src/util/support/Makefile.in
===================================================================
--- trunk/src/util/support/Makefile.in	2011-08-05 16:59:52 UTC (rev 25073)
+++ trunk/src/util/support/Makefile.in	2011-08-07 01:12:28 UTC (rev 25074)
@@ -63,6 +63,7 @@
 	utf8.o \
 	utf8_conv.o \
 	zap.o \
+	path.o \
 	$(IPC_ST_OBJ) \
 	$(STRLCPY_ST_OBJ) \
 	$(PRINTF_ST_OBJ) \
@@ -79,6 +80,7 @@
 	$(OUTPRE)utf8.$(OBJEXT) \
 	$(OUTPRE)utf8_conv.$(OBJEXT) \
 	$(OUTPRE)zap.$(OBJEXT) \
+	$(OUTPRE)path.$(OBJEXT) \
 	$(IPC_OBJ) \
 	$(STRLCPY_OBJ) \
 	$(PRINTF_OBJ) \
@@ -104,7 +106,8 @@
 	$(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: $(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::

Modified: trunk/src/util/support/libkrb5support-fixed.exports
===================================================================
--- trunk/src/util/support/libkrb5support-fixed.exports	2011-08-05 16:59:52 UTC (rev 25073)
+++ trunk/src/util/support/libkrb5support-fixed.exports	2011-08-07 01:12:28 UTC (rev 25074)
@@ -1,3 +1,6 @@
+k5_path_isabs
+k5_path_join
+k5_path_split
 krb5int_key_register
 krb5int_key_delete
 krb5int_getspecific

Added: trunk/src/util/support/path.c
===================================================================
--- trunk/src/util/support/path.c	                        (rev 0)
+++ trunk/src/util/support/path.c	2011-08-07 01:12:28 UTC (rev 25074)
@@ -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
+}

Added: trunk/src/util/support/t_path.c
===================================================================
--- trunk/src/util/support/t_path.c	                        (rev 0)
+++ trunk/src/util/support/t_path.c	2011-08-07 01:12:28 UTC (rev 25074)
@@ -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;
+}




More information about the cvs-krb5 mailing list