From b22984600a04120088f59bb5876c9d5258fd64b7 Mon Sep 17 00:00:00 2001 From: Dana Robinson <43805+derobins@users.noreply.github.com> Date: Fri, 5 Aug 2022 16:11:13 -0700 Subject: Adds platform-independent basename and dirname (#1951) * Adds platform-independent basename and dirname * Tidy up H5_dirname and H5_basename implementations and add tests * Committing clang-format changes * Fix misspelling * Several fixes for H5_dirname/H5_basename from review * Committing clang-format changes * Add reason to VERIFY_STR macros in th5_system.c * Use H5MM_free instead of HDfree in H5_dirname/H5_basename tests Co-authored-by: Jordan Henderson Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- src/H5MM.c | 47 +++++ src/H5MMprivate.h | 1 + src/H5private.h | 5 + src/H5system.c | 266 +++++++++++++++++++++++++++ src/H5win32defs.h | 2 + test/CMakeLists.txt | 1 + test/Makefile.am | 2 +- test/testhdf5.c | 1 + test/testhdf5.h | 2 + test/th5_system.c | 505 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 831 insertions(+), 1 deletion(-) create mode 100644 test/th5_system.c diff --git a/src/H5MM.c b/src/H5MM.c index 6943887..9c03ceb 100644 --- a/src/H5MM.c +++ b/src/H5MM.c @@ -504,6 +504,53 @@ done: } /* end H5MM_strdup() */ /*------------------------------------------------------------------------- + * Function: H5MM_strndup + * + * Purpose: Duplicates a string, including memory allocation, but only + * copies at most `n` bytes from the string to be duplicated. + * If the string to be duplicated is longer than `n`, only `n` + * bytes are copied and a terminating null byte is added. + * NULL is NOT an acceptable value for the input string. + * + * If the string to be duplicated is the NULL pointer, then + * an error will be raised. + * + * Return: Success: Pointer to a new string + * Failure: NULL + *------------------------------------------------------------------------- + */ +char * +H5MM_strndup(const char *s, size_t n) +{ +#if defined H5_MEMORY_ALLOC_SANITY_CHECK + size_t len; +#endif + char *ret_value = NULL; + + FUNC_ENTER_NOAPI(NULL) + + if (!s) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "NULL string not allowed") + +#if defined H5_MEMORY_ALLOC_SANITY_CHECK + for (len = 0; len < n && s[len] != '\0'; len++) + ; + + if (NULL == (ret_value = H5MM_malloc(len + 1))) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") + + H5MM_memcpy(ret_value, s, len); + ret_value[len] = '\0'; +#else + if (NULL == (ret_value = HDstrndup(s, n))) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "string duplication failed") +#endif + +done: + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5MM_strndup() */ + +/*------------------------------------------------------------------------- * Function: H5MM_xfree * * Purpose: Just like free(3) except null pointers are allowed as diff --git a/src/H5MMprivate.h b/src/H5MMprivate.h index f05a3da..0a5a011 100644 --- a/src/H5MMprivate.h +++ b/src/H5MMprivate.h @@ -44,6 +44,7 @@ H5_DLL void *H5MM_calloc(size_t size) H5_ATTR_MALLOC; H5_DLL void *H5MM_realloc(void *mem, size_t size); H5_DLL char *H5MM_xstrdup(const char *s); H5_DLL char *H5MM_strdup(const char *s); +H5_DLL char *H5MM_strndup(const char *s, size_t n); H5_DLL void *H5MM_xfree(void *mem); H5_DLL void *H5MM_xfree_const(const void *mem); H5_DLL void *H5MM_memcpy(void *dest, const void *src, size_t n); diff --git a/src/H5private.h b/src/H5private.h index f9e7aff..3130bb1 100644 --- a/src/H5private.h +++ b/src/H5private.h @@ -1411,6 +1411,9 @@ H5_DLL H5_ATTR_CONST int Nflock(int fd, int operation); #ifndef HDstrncpy #define HDstrncpy(X, Y, Z) strncpy(X, Y, Z) #endif +#ifndef HDstrndup +#define HDstrndup(S, N) strndup(S, N) +#endif #ifndef HDstrpbrk #define HDstrpbrk(X, Y) strpbrk(X, Y) #endif @@ -2544,6 +2547,8 @@ H5_DLL double H5_get_time(void); /* Functions for building paths, etc. */ H5_DLL herr_t H5_build_extpath(const char *name, char **extpath /*out*/); H5_DLL herr_t H5_combine_path(const char *path1, const char *path2, char **full_name /*out*/); +H5_DLL herr_t H5_dirname(const char *path, char **dirname /*out*/); +H5_DLL herr_t H5_basename(const char *path, char **basename /*out*/); /* getopt(3) equivalent that papers over the lack of long options on BSD * and lack of Windows support. diff --git a/src/H5system.c b/src/H5system.c index 677452d..9a19860 100644 --- a/src/H5system.c +++ b/src/H5system.c @@ -919,8 +919,274 @@ done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5_expand_windows_env_vars() */ + +/*------------------------------------------------------------------------- + * Function: H5_strndup + * + * Purpose: Similar to strndup() for use on Windows. Allocates a new + * string and copies at most `n` bytes from the original + * string into the new string. If the original string is + * longer than `n`, only `n` bytes are copied from the + * original string. In either case, the string being returned + * is guaranteed to be terminated with a null byte. + * + * The returned pointer is allocated by H5MM_malloc in this + * routine and must be freed by the caller with H5MM_free or + * H5MM_xfree. + * + * Return: Pointer to copied string on success + * NULL on failure + * + *------------------------------------------------------------------------- + */ +char * +H5_strndup(const char *s, size_t n) +{ + size_t len; + char *ret_value = NULL; + + FUNC_ENTER_NOAPI_NOINIT + + if (!s) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "string cannot be NULL") + + for (len = 0; len < n && s[len] != '\0'; len++) + ; + + if (NULL == (ret_value = H5MM_malloc(len + 1))) + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "can't allocate buffer for string") + + H5MM_memcpy(ret_value, s, len); + ret_value[len] = '\0'; + +done: + FUNC_LEAVE_NOAPI(ret_value) +} #endif /* H5_HAVE_WIN32_API */ +/* dirname() and basename() are not easily ported to Windows and basename + * behavior varies depending on if you get POSIX vs. GNU. As a more + * platform-indpendent work-around, we've implemented H5_ versions of + * dirname() and basename(). + * + * - The input string is never modified. + * + * - The out parameter is a new string that was allocated with H5MM routines + * and must be freed by the caller via H5MM_free()/H5MM_xfree(). + * + * - NULL pointers are errors. + * + * - On errors, FAIL will be returned and the output parameter will be + * undefined. + * + * - Assumes the file separator is \ on Win32 and / everywhere else, + * including Cygwin. + */ + +/*------------------------------------------------------------------------- + * Function: H5_dirname + * + * Purpose: Similar to dirname(3) but more portable across platforms. + * Returns a pointer to the directory component of a specified + * pathname. The returned pointer is allocated by this routine + * and must be freed by the caller with H5MM_free or + * H5MM_xfree. + * + * Return: Non-negative on success/Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5_dirname(const char *path, char **dirname) +{ + char *sep; + char *out = NULL; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NOINIT + + if (!path) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "path can't be NULL") + if (!dirname) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "dirname can't be NULL") + + if (NULL == (sep = HDstrrchr(path, H5_DIR_SEPC))) { + /* Pathname with no file separator characters */ + out = H5MM_strdup("."); + } + else if (sep == path) { + /* Pathname of form "/" or "/filename" */ + out = H5MM_strdup(H5_DIR_SEPS); + } + else { + if (sep[1] == '\0') { + /* + * Last file separator character is last character in + * pathname. Skip this and any other preceding trailing + * file separator characters + */ + while (sep != path && sep[-1] == H5_DIR_SEPC) + sep--; + + if (sep == path) { + /* Contrived case: "//", "///" and similar */ + out = H5MM_strdup(H5_DIR_SEPS); + sep = NULL; + } + else { + /* + * Must have found the filename component. Search + * backwards to a previous file separator character, + * if any. + */ + while (sep != path && sep[-1] != H5_DIR_SEPC) + sep--; + + if (sep == path) { + /* No directory component found, just return "." */ + out = H5MM_strdup("."); + sep = NULL; + } + } + } + + if (sep) { + ptrdiff_t len; + + /* Skip a possible run of duplicate file separator characters */ + while (sep != path && sep[-1] == H5_DIR_SEPC) + sep--; + + if (sep == path) + /* Pathname of form "/usr/" */ + out = H5MM_strdup(H5_DIR_SEPS); + else { + /* Pathname of form "dir/filename" */ + len = sep - path; + HDassert(len >= 0); + + out = H5MM_strndup(path, (size_t)len); + } + } + } + + if (NULL == out) + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "can't allocate buffer for dirname"); + + *dirname = out; + +done: + if (FAIL == ret_value) { + H5MM_free(out); + if (dirname) + *dirname = NULL; + } + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5_dirname() */ + +/*------------------------------------------------------------------------- + * Function: H5_basename + * + * Purpose: Similar to basename(3) but more portable across platforms. + * Returns a pointer to the filename component of a specified + * pathname. The returned pointer is allocated by this routine + * and must be freed by the caller with H5MM_free or + * H5MM_xfree. + * + * NOTE: This routine follows the POSIX semantics for + * basename(3). That is, passing the path string "/" ("\" on + * Windows) returns the string "/" (again, "\" on Windows) and + * passing a path string with trailing file separator + * characters returns the filename component with the trailing + * file separator characters being ignored. + * + * Return: Non-negative on success/Negative on failure + * + *------------------------------------------------------------------------- + */ +herr_t +H5_basename(const char *path, char **basename) +{ + const char *sep; + char *out = NULL; + herr_t ret_value = SUCCEED; + + FUNC_ENTER_NOAPI_NOINIT + + if (!path) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "path can't be NULL") + if (!basename) + HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "basename can't be NULL") + + if (NULL == (sep = HDstrrchr(path, H5_DIR_SEPC))) { + if (*path == '\0') + /* Empty pathname */ + out = H5MM_strdup("."); + else + /* Pathname with no file separator characters */ + out = H5MM_strdup(path); + } + else if (sep == path) { + if (sep[1] == '\0') + /* Pathname of form "/" */ + out = H5MM_strdup(H5_DIR_SEPS); + else + /* Pathname of form "/filename" */ + out = H5MM_strdup(sep + 1); + } + else { + if (sep[1] != '\0') + /* Pathname of form "dir/filename" */ + out = H5MM_strdup(sep + 1); + else { + /* Pathname of form "filename/", "/dir/filename/", etc. */ + + /* + * Last file separator character is last character in + * pathname. Skip this and any other preceding trailing + * file separator characters + */ + while (sep != path && sep[-1] == H5_DIR_SEPC) + sep--; + + if (sep == path) + /* Contrived case: "//", "///" and similar */ + out = H5MM_strdup(H5_DIR_SEPS); + else { + const char *c_ptr = sep; + ptrdiff_t len; + + /* + * Skip back to a previous file separator character, + * if any, and form final filename component + */ + while (c_ptr != path && c_ptr[-1] != H5_DIR_SEPC) + c_ptr--; + + len = sep - c_ptr; + HDassert(len >= 0); + + out = H5MM_strndup(c_ptr, (size_t)len); + } + } + } + + if (NULL == out) + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "can't allocate buffer for basename"); + + *basename = out; + +done: + if (FAIL == ret_value) { + H5MM_free(out); + if (basename) + *basename = NULL; + } + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5_basename() */ + /* Global variables */ int H5_opterr = 1; /* Get_option prints errors if this is on */ int H5_optind = 1; /* Token pointer */ diff --git a/src/H5win32defs.h b/src/H5win32defs.h index 5f384bb..1039f23 100644 --- a/src/H5win32defs.h +++ b/src/H5win32defs.h @@ -82,6 +82,7 @@ struct timezone { #define HDsleep(S) Sleep(S * 1000) #define HDstat(S, B) _stati64(S, B) #define HDstrcasecmp(A, B) _stricmp(A, B) +#define HDstrndup(S, N) H5_strndup(S, N) #define HDstrtok_r(X, Y, Z) strtok_s(X, Y, Z) #define HDtzset() _tzset() #define HDunlink(S) _unlink(S) @@ -104,6 +105,7 @@ H5_DLL wchar_t *H5_get_utf16_str(const char *s); H5_DLL int Wopen_utf8(const char *path, int oflag, ...); H5_DLL int Wremove_utf8(const char *path); H5_DLL int H5_get_win32_times(H5_timevals_t *tvs); +H5_DLL char *H5_strndup(const char *s, size_t n); #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b9d1208..3149454 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -267,6 +267,7 @@ set (testhdf5_SOURCES ${HDF5_TEST_SOURCE_DIR}/tattr.c ${HDF5_TEST_SOURCE_DIR}/tchecksum.c ${HDF5_TEST_SOURCE_DIR}/tconfig.c + ${HDF5_TEST_SOURCE_DIR}/th5_system.c ${HDF5_TEST_SOURCE_DIR}/tcoords.c ${HDF5_TEST_SOURCE_DIR}/tfile.c ${HDF5_TEST_SOURCE_DIR}/tgenprop.c diff --git a/test/Makefile.am b/test/Makefile.am index 22510dc..d441113 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -231,7 +231,7 @@ CHECK_CLEANFILES+=accum.h5 cmpd_dset.h5 compact_dataset.h5 dataset.h5 dset_offse # Sources for testhdf5 executable testhdf5_SOURCES=testhdf5.c tarray.c tattr.c tchecksum.c tconfig.c tfile.c \ - tgenprop.c th5o.c th5s.c tcoords.c tid.c titerate.c tmeta.c tmisc.c \ + tgenprop.c th5o.c th5s.c th5_system.c tcoords.c tid.c titerate.c tmeta.c tmisc.c \ trefer.c trefer_deprec.c trefstr.c tselect.c tskiplist.c tsohm.c ttime.c tunicode.c \ tvlstr.c tvltypes.c diff --git a/test/testhdf5.c b/test/testhdf5.c index b5db71b..2329354 100644 --- a/test/testhdf5.c +++ b/test/testhdf5.c @@ -43,6 +43,7 @@ main(int argc, char *argv[]) /* Tests are generally arranged from least to most complexity... */ AddTest("config", test_configure, cleanup_configure, "Configure definitions", NULL); + AddTest("h5system", test_h5_system, cleanup_h5_system, "H5system routines", NULL); AddTest("metadata", test_metadata, cleanup_metadata, "Encoding/decoding metadata", NULL); AddTest("checksum", test_checksum, cleanup_checksum, "Checksum algorithm", NULL); AddTest("skiplist", test_skiplist, NULL, "Skip Lists", NULL); diff --git a/test/testhdf5.h b/test/testhdf5.h index d5c2d21..e72e08e 100644 --- a/test/testhdf5.h +++ b/test/testhdf5.h @@ -221,6 +221,7 @@ void test_iterate(void); void test_array(void); void test_genprop(void); void test_configure(void); +void test_h5_system(void); void test_misc(void); void test_ids(void); void test_skiplist(void); @@ -245,6 +246,7 @@ void cleanup_iterate(void); void cleanup_array(void); void cleanup_genprop(void); void cleanup_configure(void); +void cleanup_h5_system(void); void cleanup_sohm(void); void cleanup_misc(void); void cleanup_unicode(void); diff --git a/test/th5_system.c b/test/th5_system.c new file mode 100644 index 0000000..09570cf --- /dev/null +++ b/test/th5_system.c @@ -0,0 +1,505 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Copyright by The HDF Group. * + * All rights reserved. * + * * + * This file is part of HDF5. The full HDF5 copyright notice, including * + * terms governing use, modification, and redistribution, is contained in * + * the COPYING file, which can be found at the root of the source code * + * distribution tree, or in https://www.hdfgroup.org/licenses. * + * If you do not have access to either file, you may request a copy from * + * help@hdfgroup.org. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#define H5_SYSTEM_TEST_PATH_MAX 4096 + +/*********************************************************** + * + * Test program: th5_system + * + * Testing for the routines available in H5system.c + * + *************************************************************/ + +#include "testhdf5.h" + +#include "H5MMprivate.h" + +static void +test_h5_dirname(void) +{ + herr_t ret; + char *path = NULL; + char *dirname = NULL; + + MESSAGE(5, ("Testing H5_dirname\n")); + + path = HDmalloc(H5_SYSTEM_TEST_PATH_MAX); + CHECK_PTR(path, "HDmalloc"); + if (!path) + return; + + /* Check that H5_dirname fails for a NULL path */ + dirname = NULL; + H5E_BEGIN_TRY + { + ret = H5_dirname(NULL, &dirname); + } + H5E_END_TRY; + VERIFY(ret, FAIL, "H5_dirname with NULL path"); + H5Eclear2(H5E_DEFAULT); + + /* Check that H5_dirname fails for a NULL dirname pointer */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "topdir%sunderdir%sfinaldir", H5_DIR_SEPS, H5_DIR_SEPS); + H5E_BEGIN_TRY + { + ret = H5_dirname(path, NULL); + } + H5E_END_TRY; + VERIFY(ret, FAIL, "H5_dirname with NULL dirname pointer"); + H5Eclear2(H5E_DEFAULT); + + /* Check that H5_dirname returns "." for an empty path string */ + *path = '\0'; + dirname = NULL; + ret = H5_dirname(path, &dirname); + CHECK(ret, FAIL, "H5_dirname with empty string"); + VERIFY_STR(dirname, ".", "comparing H5_dirname with empty string to \".\""); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns "." for a path that + * doesn't contain the system file separator character + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "testdirname"); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, ".", "comparing H5_dirname with non-separated path to \".\""); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns the system file separator + * for the simple path containing just the system file separator + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, H5_DIR_SEPS, "comparing H5_dirname with file separator path to file separator"); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns the system file separator + * for a path which contains a leading separator and a path + * component + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%stestdir", H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, H5_DIR_SEPS, "comparing H5_dirname with leading separator path to file separator"); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns the system file separator + * for a path which contains several leading separators and + * a path component + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%s%s%s%stestdir", H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, + H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, H5_DIR_SEPS, "comparing H5_dirname with leading separators path to file separator"); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns the "." for a path which + * contains a path component and a trailing separator + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "testdir%s", H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, ".", "comparing H5_dirname with trailing separator path to \".\""); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns the "." for a path which + * contains a path component and several trailing separators + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "testdir%s%s%s%s", H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, + H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, ".", "comparing H5_dirname with trailing separators path to \".\""); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns the system file separator + * for a path which contains a leading separator, a path + * component and a trailing separator + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%stestdir%s", H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, H5_DIR_SEPS, + "comparing H5_dirname with leading and trailing separator path to file separator"); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns the system file separator + * for a path which contains several leading separators, a + * path component and a trailing separator + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%s%s%s%stestdir%s", H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, + H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR( + dirname, H5_DIR_SEPS, + "comparing H5_dirname with leading separators and a trailing separator path to file separator"); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns the system file separator + * for a path which contains a leading separator, a path + * component and several trailing separators + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%stestdir%s%s%s%s", H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, + H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, H5_DIR_SEPS, + "comparing H5_dirname with leading separator and trailing separators path to file separator"); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns the system file separator + * for a path which contains several leading separators, a + * path component and several trailing separators + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%s%s%s%stestdir%s%s%s%s", H5_DIR_SEPS, H5_DIR_SEPS, + H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, H5_DIR_SEPS, + "comparing H5_dirname with leading and trailing separators path to file separator"); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns a proper dirname with a + * "normal" pathname that has no leading separator + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "topdir%sunderdir", H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, "topdir", "comparing H5_dirname with normal path to proper dirname"); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns a proper dirname with a + * "normal" pathname that has a leading separator + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%stopdir%sunderdir", H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, H5_DIR_SEPS "topdir", "comparing H5_dirname with normal path to proper dirname"); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns a proper dirname with a + * "normal" pathname that has a leading and trailing separator + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%stopdir%sunderdir%s", H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, H5_DIR_SEPS "topdir", "comparing H5_dirname with normal path to proper dirname"); + H5MM_free(dirname); + + /* + * Check that H5_dirname returns a proper dirname with a + * contrived pathname + */ + dirname = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%stopdir%sunderdir%s%s%sfinaldir%s", H5_DIR_SEPS, H5_DIR_SEPS, + H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_dirname(path, &dirname); + VERIFY_STR(dirname, H5_DIR_SEPS "topdir" H5_DIR_SEPS "underdir", + "comparing H5_dirname with contrived path to proper dirname"); + H5MM_free(dirname); + + HDfree(path); +} + +static void +test_h5_basename(void) +{ + herr_t ret; + char *path = NULL; + char *basename = NULL; + + MESSAGE(5, ("Testing H5_basename\n")); + + path = HDmalloc(H5_SYSTEM_TEST_PATH_MAX); + CHECK_PTR(path, "HDmalloc"); + if (!path) + return; + + /* Check that H5_basename fails for a NULL path */ + basename = NULL; + H5E_BEGIN_TRY + { + ret = H5_basename(NULL, &basename); + } + H5E_END_TRY; + VERIFY(ret, FAIL, "H5_basename with NULL path"); + H5Eclear2(H5E_DEFAULT); + + /* Check that H5_basename fails for a NULL basename pointer */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "topdir%sunderdir%sfinaldir", H5_DIR_SEPS, H5_DIR_SEPS); + H5E_BEGIN_TRY + { + ret = H5_basename(path, NULL); + } + H5E_END_TRY; + VERIFY(ret, FAIL, "H5_basename with NULL basename pointer"); + H5Eclear2(H5E_DEFAULT); + + /* Check that H5_basename returns "." for an empty path string */ + *path = '\0'; + basename = NULL; + ret = H5_basename(path, &basename); + CHECK(ret, FAIL, "H5_basename with empty string"); + VERIFY_STR(basename, ".", "comparing H5_basename with empty string to \".\""); + H5MM_free(basename); + + /* + * Check that H5_basename returns the specified path for a + * path that doesn't contain the system file separator character + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "testdirname"); + ret = H5_basename(path, &basename); + VERIFY_STR(basename, "testdirname", "comparing H5_basename with non-separated path to same path"); + H5MM_free(basename); + + /* + * Check that H5_basename returns the system file separator + * for the simple path containing just the system file separator + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR(basename, H5_DIR_SEPS, "comparing H5_basename with file separator path to file separator"); + H5MM_free(basename); + + /* + * Check that H5_basename returns the proper basename for a + * path which contains a leading separator and a path + * component + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%stestdir", H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR(basename, "testdir", + "comparing H5_basename with leading separator path to filename component"); + H5MM_free(basename); + + /* + * Check that H5_basename returns the proper basename for a + * path which contains several leading separators and a path + * component + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%s%s%s%stestdir", H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, + H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR(basename, "testdir", + "comparing H5_basename with leading separators path to filename component"); + H5MM_free(basename); + + /* + * Check that H5_basename returns the proper basename for a + * path which contains a path component and a trailing separator + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "testdir%s", H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR(basename, "testdir", + "comparing H5_basename with trailing separator path to filename component"); + H5MM_free(basename); + + /* + * Check that H5_basename returns the proper basename for a + * path which contains a path component and several trailing + * separators + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "testdir%s%s%s%s", H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, + H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR(basename, "testdir", + "comparing H5_basename with trailing separators path to filename component"); + H5MM_free(basename); + + /* + * Check that H5_basename returns the proper basename for a + * path which contains a leading separator, a path component + * and a trailing separator + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%stestdir%s", H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR(basename, "testdir", + "comparing H5_basename with leading and trailing separator path to filename component"); + H5MM_free(basename); + + /* + * Check that H5_basename returns the proper basename for a + * path which contains several leading separators, a path + * component and a trailing separator + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%s%s%s%stestdir%s", H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, + H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR( + basename, "testdir", + "comparing H5_basename with leading separators and a trailing separator path to filename component"); + H5MM_free(basename); + + /* + * Check that H5_basename returns the proper basename for a + * path which contains a leading separator, a path component + * and several trailing separators + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%stestdir%s%s%s%s", H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, + H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR( + basename, "testdir", + "comparing H5_basename with leading separator and trailing separators path to filename component"); + H5MM_free(basename); + + /* + * Check that H5_basename returns the proper basename for a + * path which contains several leading separators, a path + * component and several trailing separators + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%s%s%s%stestdir%s%s%s%s", H5_DIR_SEPS, H5_DIR_SEPS, + H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR(basename, "testdir", + "comparing H5_basename with leading and trailing separators path to filename component"); + H5MM_free(basename); + + /* + * Check that H5_basename returns a proper basename with + * a "normal" pathname that has no leading separator + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "topdir%sunderdir", H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR(basename, "underdir", "comparing H5_basename with normal path to proper basename"); + H5MM_free(basename); + + /* + * Check that H5_basename returns a proper basename with + * a "normal" pathname that has a leading separator + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%stopdir%sunderdir", H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR(basename, "underdir", "comparing H5_basename with normal path to proper basename"); + H5MM_free(basename); + + /* + * Check that H5_basename returns a proper basename with + * a "normal" pathname that has a leading and trailing separator + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%stopdir%sunderdir%s", H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR(basename, "underdir", "comparing H5_basename with normal path to proper basename"); + H5MM_free(basename); + + /* + * Check that H5_basename returns a proper basename with a + * contrived pathname + */ + basename = NULL; + HDsnprintf(path, H5_SYSTEM_TEST_PATH_MAX, "%stopdir%sunderdir%s%s%sfinaldir%s", H5_DIR_SEPS, H5_DIR_SEPS, + H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS, H5_DIR_SEPS); + ret = H5_basename(path, &basename); + VERIFY_STR(basename, "finaldir", "comparing H5_basename with contrived path to proper basename"); + H5MM_free(basename); + + HDfree(path); +} + +static void +test_h5_strndup(void) +{ +#ifdef H5_HAVE_WIN32_API + const char *const teststr = "myteststring"; + char *str = NULL; + + MESSAGE(5, ("Testing H5_strndup\n")); + + /* Check that H5_strndup fails for a NULL string pointer */ + H5E_BEGIN_TRY + { + str = H5_strndup(NULL, 20); + } + H5E_END_TRY; + CHECK_PTR_NULL(str, "H5_strndup with NULL string pointer"); + H5Eclear2(H5E_DEFAULT); + + /* Check that H5_strndup correctly performs a 0-byte copy */ + str = H5_strndup(teststr, 0); + CHECK_PTR(str, "H5_strndup for 0-byte copy"); + if (str) + VERIFY_STR(str, "", "comparing H5_strndup for 0-byte copy to empty string"); + str = H5MM_xfree(str); + + /* Check that H5_strndup correctly performs partial copies */ + str = H5_strndup(teststr, 6); + CHECK_PTR(str, "H5_strndup for partial copy"); + if (str) + VERIFY_STR(str, "mytest", "comparing H5_strndup for partial copy to partial string"); + str = H5MM_xfree(str); + + /* Check that H5_strndup correctly performs identical copies */ + str = H5_strndup(teststr, HDstrlen(teststr)); + CHECK_PTR(str, "H5_strndup for identical copy"); + if (str) + VERIFY_STR(str, teststr, "comparing H5_strndup for identical copy to original string"); + str = H5MM_xfree(str); + + /* + * Check that H5_strndup correctly performs copies when + * `n` is greater than the original string + */ + str = H5_strndup(teststr, HDstrlen(teststr) + 2); + CHECK_PTR(str, "H5_strndup for larger 'n'"); + if (str) + VERIFY_STR(str, teststr, "comparing H5_strndup with larger 'n' value to original string"); + str = H5MM_xfree(str); +#endif /* H5_HAVE_WIN32_API */ +} + +void +test_h5_system(void) +{ + MESSAGE(5, ("Testing H5system routines\n")); + + test_h5_dirname(); + test_h5_basename(); + test_h5_strndup(); +} + +void +cleanup_h5_system(void) +{ + /* Nothing to cleanup yet */ +} -- cgit v0.12