diff options
author | Dana Robinson <43805+derobins@users.noreply.github.com> | 2022-08-05 23:11:13 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-05 23:11:13 (GMT) |
commit | b22984600a04120088f59bb5876c9d5258fd64b7 (patch) | |
tree | 9324ab3744739b1b10b5e35a659dbe32edd51baa /src | |
parent | 54f116b42db2d2aabd6fdb58cebb99f04f106310 (diff) | |
download | hdf5-b22984600a04120088f59bb5876c9d5258fd64b7.zip hdf5-b22984600a04120088f59bb5876c9d5258fd64b7.tar.gz hdf5-b22984600a04120088f59bb5876c9d5258fd64b7.tar.bz2 |
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 <jhenderson@hdfgroup.org>
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/H5MM.c | 47 | ||||
-rw-r--r-- | src/H5MMprivate.h | 1 | ||||
-rw-r--r-- | src/H5private.h | 5 | ||||
-rw-r--r-- | src/H5system.c | 266 | ||||
-rw-r--r-- | src/H5win32defs.h | 2 |
5 files changed, 321 insertions, 0 deletions
@@ -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 */ |