diff options
-rw-r--r-- | src/include/k5-platform.h | 22 | ||||
-rw-r--r-- | src/util/support/Makefile.in | 21 | ||||
-rw-r--r-- | src/util/support/libkrb5support-fixed.exports | 3 | ||||
-rw-r--r-- | src/util/support/path.c | 161 | ||||
-rw-r--r-- | src/util/support/t_path.c | 190 |
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; +} |