summaryrefslogtreecommitdiffstats
path: root/src/H5system.c
diff options
context:
space:
mode:
authorDana Robinson <43805+derobins@users.noreply.github.com>2022-08-05 23:11:13 (GMT)
committerGitHub <noreply@github.com>2022-08-05 23:11:13 (GMT)
commitb22984600a04120088f59bb5876c9d5258fd64b7 (patch)
tree9324ab3744739b1b10b5e35a659dbe32edd51baa /src/H5system.c
parent54f116b42db2d2aabd6fdb58cebb99f04f106310 (diff)
downloadhdf5-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/H5system.c')
-rw-r--r--src/H5system.c266
1 files changed, 266 insertions, 0 deletions
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 */