make rrd_mkdir_p work on bsd* unixes too ... their dirname call works differently...
[rrdtool.git] / src / rrd_utils.c
index 39d2aca..add19c3 100644 (file)
@@ -1,4 +1,8 @@
 /**
+ * RRDtool - src/rrd_utils.c
+ * Copyright (C) 2009 Kevin Brintnall
+ * Copyright (C) 2008 Sebastian Harl
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Free Software Foundation; only version 2 of the License is applicable.
  * You should have received a copy of the GNU General Public License along
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   kevin brintnall <kbrint@rufus.net>
+ *   Sebastian Harl <sh@tokkee.org>
  **/
 
 #include "rrd_tool.h"
 
+#include <assert.h>
+#include <errno.h>
 #include <stdlib.h>
-
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef _MSC_VER
+#include <libgen.h>
+#include <unistd.h>
+#endif
 #ifdef WIN32
 #      define random() rand()
 #      define srandom(x) srand(x)
 #      define getpid() 0
 #endif /* WIN32 */
 
+#ifndef S_ISDIR
+#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
+#endif
+
 /* make sure that the random number generator seeded EXACTLY ONCE */
 long rrd_random(void)
 {
@@ -34,3 +54,161 @@ long rrd_random(void)
 
     return random();
 }
+
+/* rrd_add_ptr_chunk: add a pointer to a dynamically sized array of
+ * pointers, realloc as necessary in multiples of "chunk".
+ *
+ * "alloc" is the number of pointers allocated
+ * "dest_size" is the number of valid pointers
+ *
+ * returns 1 on success, 0 on failure.
+ */
+
+int rrd_add_ptr_chunk(void ***dest, size_t *dest_size, void *src,
+                      size_t *alloc, size_t chunk)
+{
+    void **temp;
+
+    assert(dest != NULL);
+    assert(alloc != NULL);
+    assert(*alloc >= *dest_size);
+
+    if (*alloc == *dest_size)
+    {
+        temp = (void **) rrd_realloc(*dest, (*alloc+chunk) * sizeof(*dest));
+        if (!temp)
+            return 0;
+
+        *dest = temp;
+        *alloc += chunk;
+    }
+
+    (*dest)[*dest_size] = src;
+    (*dest_size)++;
+
+    return 1;
+}
+
+/* rrd_add_ptr: add a pointer to a dynamically sized array of pointers,
+ * realloc as necessary.  returns 1 on success, 0 on failure.
+ */
+int rrd_add_ptr(void ***dest, size_t *dest_size, void *src)
+{
+    size_t alloc = *dest_size;
+
+    return rrd_add_ptr_chunk(dest, dest_size, src, &alloc, 1);
+}
+
+/* like rrd_add_ptr_chunk, but calls strdup() on a string first. */
+int rrd_add_strdup_chunk(char ***dest, size_t *dest_size, char *src,
+                         size_t *alloc, size_t chunk)
+{
+    char *dup_src;
+    int add_ok;
+
+    assert(dest != NULL);
+    assert(src  != NULL);
+
+    dup_src = strdup(src);
+    if (!dup_src)
+        return 0;
+
+    add_ok = rrd_add_ptr_chunk((void ***)dest, dest_size, (void *)dup_src, alloc, chunk);
+    if (!add_ok)
+        free(dup_src);
+
+    return add_ok;
+}
+
+int rrd_add_strdup(char ***dest, size_t *dest_size, char *src)
+{
+    size_t alloc = *dest_size;
+
+    return rrd_add_strdup_chunk(dest, dest_size, src, &alloc, 1);
+}
+
+void rrd_free_ptrs(void ***src, size_t *cnt)
+{
+    void **sp;
+
+    assert(src != NULL);
+    sp = *src;
+
+    if (sp == NULL)
+        return;
+
+    while (*cnt > 0) {
+        (*cnt)--;
+        free(sp[*cnt]);
+    }
+
+    free (sp);
+    *src = NULL;
+}
+
+/* recursively create the directory named by 'pathname'
+ * (similar to "mkdir -p" on the command line) */
+int rrd_mkdir_p(const char *pathname, mode_t mode)
+{
+    struct stat sb;
+
+    char *pathname_copy;
+    char *base_dir;
+
+    if ((NULL == pathname) || ('\0' == *pathname)) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    if (0 == stat(pathname, &sb)) {
+        if (! S_ISDIR(sb.st_mode)) {
+            errno = ENOTDIR;
+            return -1;
+        }
+        return 0;
+    }
+
+    /* keep errno as set by stat() */
+    if (ENOENT != errno)
+        return -1;
+
+    /* dirname might modify its first argument */
+    if (NULL == (pathname_copy = strdup(pathname)))
+        return -1;
+
+#ifndef _MSC_VER
+    /* the data pointedd too by dirname might change too (bsd) */
+    if (NULL == (base_dir = strdup(dirname(pathname_copy)))) {
+        free(pathname_copy);
+        return -1;
+    }
+#else
+    _splitpath(pathname_copy, NULL, base_dir, NULL, NULL);
+#endif
+
+    if (0 != rrd_mkdir_p(base_dir, mode)) {
+        int orig_errno = errno;
+        free(pathname_copy);
+#ifndef _MSC_VER
+        free(base_dir);
+#endif
+        errno = orig_errno;
+        return -1;
+    }
+
+    free(pathname_copy);
+#ifndef _MSC_VER
+    free(base_dir);
+#endif
+
+    /* keep errno as set by mkdir() */
+#ifdef _MSC_VER
+    if (0 != mkdir(pathname))
+        return -1;
+#else
+    if (0 != mkdir(pathname, mode))
+        return -1;
+#endif
+    return 0;
+} /* rrd_mkdir_p */
+