Merge branch 'tr/mergetool-valgrind'
[gitweb.git] / wrapper.c
index 93562f03eef21b26945d2d9bbdc96818f4de6567..85f09df747637b94e0488ad65984c3f97c732034 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -3,11 +3,26 @@
  */
 #include "cache.h"
 
+static void do_nothing(size_t size)
+{
+}
+
+static void (*try_to_free_routine)(size_t size) = do_nothing;
+
+try_to_free_t set_try_to_free_routine(try_to_free_t routine)
+{
+       try_to_free_t old = try_to_free_routine;
+       if (!routine)
+               routine = do_nothing;
+       try_to_free_routine = routine;
+       return old;
+}
+
 char *xstrdup(const char *str)
 {
        char *ret = strdup(str);
        if (!ret) {
-               release_pack_memory(strlen(str) + 1, -1);
+               try_to_free_routine(strlen(str) + 1);
                ret = strdup(str);
                if (!ret)
                        die("Out of memory, strdup failed");
@@ -21,12 +36,13 @@ void *xmalloc(size_t size)
        if (!ret && !size)
                ret = malloc(1);
        if (!ret) {
-               release_pack_memory(size, -1);
+               try_to_free_routine(size);
                ret = malloc(size);
                if (!ret && !size)
                        ret = malloc(1);
                if (!ret)
-                       die("Out of memory, malloc failed");
+                       die("Out of memory, malloc failed (tried to allocate %lu bytes)",
+                           (unsigned long)size);
        }
 #ifdef XMALLOC_POISON
        memset(ret, 0xA5, size);
@@ -34,6 +50,16 @@ void *xmalloc(size_t size)
        return ret;
 }
 
+void *xmallocz(size_t size)
+{
+       void *ret;
+       if (unsigned_add_overflows(size, 1))
+               die("Data too large to fit into virtual memory space.");
+       ret = xmalloc(size + 1);
+       ((char*)ret)[size] = 0;
+       return ret;
+}
+
 /*
  * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of
  * "data" to the allocated memory, zero terminates the allocated memory,
@@ -42,10 +68,7 @@ void *xmalloc(size_t size)
  */
 void *xmemdupz(const void *data, size_t len)
 {
-       char *p = xmalloc(len + 1);
-       memcpy(p, data, len);
-       p[len] = '\0';
-       return p;
+       return memcpy(xmallocz(len), data, len);
 }
 
 char *xstrndup(const char *str, size_t len)
@@ -60,7 +83,7 @@ void *xrealloc(void *ptr, size_t size)
        if (!ret && !size)
                ret = realloc(ptr, 1);
        if (!ret) {
-               release_pack_memory(size, -1);
+               try_to_free_routine(size);
                ret = realloc(ptr, size);
                if (!ret && !size)
                        ret = realloc(ptr, 1);
@@ -76,7 +99,7 @@ void *xcalloc(size_t nmemb, size_t size)
        if (!ret && (!nmemb || !size))
                ret = calloc(1, 1);
        if (!ret) {
-               release_pack_memory(nmemb * size, -1);
+               try_to_free_routine(nmemb * size);
                ret = calloc(nmemb, size);
                if (!ret && (!nmemb || !size))
                        ret = calloc(1, 1);
@@ -86,21 +109,6 @@ void *xcalloc(size_t nmemb, size_t size)
        return ret;
 }
 
-void *xmmap(void *start, size_t length,
-       int prot, int flags, int fd, off_t offset)
-{
-       void *ret = mmap(start, length, prot, flags, fd, offset);
-       if (ret == MAP_FAILED) {
-               if (!length)
-                       return NULL;
-               release_pack_memory(length, fd);
-               ret = mmap(start, length, prot, flags, fd, offset);
-               if (ret == MAP_FAILED)
-                       die("Out of memory? mmap failed: %s", strerror(errno));
-       }
-       return ret;
-}
-
 /*
  * xread() is the same a read(), but it automatically restarts read()
  * operations with a recoverable error (EAGAIN and EINTR). xread()
@@ -140,8 +148,10 @@ ssize_t read_in_full(int fd, void *buf, size_t count)
 
        while (count > 0) {
                ssize_t loaded = xread(fd, p, count);
-               if (loaded <= 0)
-                       return total ? total : loaded;
+               if (loaded < 0)
+                       return -1;
+               if (loaded == 0)
+                       return total;
                count -= loaded;
                p += loaded;
                total += loaded;
@@ -175,7 +185,7 @@ int xdup(int fd)
 {
        int ret = dup(fd);
        if (ret < 0)
-               die("dup failed: %s", strerror(errno));
+               die_errno("dup failed");
        return ret;
 }
 
@@ -183,16 +193,191 @@ FILE *xfdopen(int fd, const char *mode)
 {
        FILE *stream = fdopen(fd, mode);
        if (stream == NULL)
-               die("Out of memory? fdopen failed: %s", strerror(errno));
+               die_errno("Out of memory? fdopen failed");
        return stream;
 }
 
 int xmkstemp(char *template)
 {
        int fd;
+       char origtemplate[PATH_MAX];
+       strlcpy(origtemplate, template, sizeof(origtemplate));
 
        fd = mkstemp(template);
-       if (fd < 0)
-               die("Unable to create temporary file: %s", strerror(errno));
+       if (fd < 0) {
+               int saved_errno = errno;
+               const char *nonrelative_template;
+
+               if (!template[0])
+                       template = origtemplate;
+
+               nonrelative_template = absolute_path(template);
+               errno = saved_errno;
+               die_errno("Unable to create temporary file '%s'",
+                       nonrelative_template);
+       }
+       return fd;
+}
+
+/* git_mkstemp() - create tmp file honoring TMPDIR variable */
+int git_mkstemp(char *path, size_t len, const char *template)
+{
+       const char *tmp;
+       size_t n;
+
+       tmp = getenv("TMPDIR");
+       if (!tmp)
+               tmp = "/tmp";
+       n = snprintf(path, len, "%s/%s", tmp, template);
+       if (len <= n) {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+       return mkstemp(path);
+}
+
+/* git_mkstemps() - create tmp file with suffix honoring TMPDIR variable. */
+int git_mkstemps(char *path, size_t len, const char *template, int suffix_len)
+{
+       const char *tmp;
+       size_t n;
+
+       tmp = getenv("TMPDIR");
+       if (!tmp)
+               tmp = "/tmp";
+       n = snprintf(path, len, "%s/%s", tmp, template);
+       if (len <= n) {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+       return mkstemps(path, suffix_len);
+}
+
+/* Adapted from libiberty's mkstemp.c. */
+
+#undef TMP_MAX
+#define TMP_MAX 16384
+
+int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
+{
+       static const char letters[] =
+               "abcdefghijklmnopqrstuvwxyz"
+               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+               "0123456789";
+       static const int num_letters = 62;
+       uint64_t value;
+       struct timeval tv;
+       char *template;
+       size_t len;
+       int fd, count;
+
+       len = strlen(pattern);
+
+       if (len < 6 + suffix_len) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (strncmp(&pattern[len - 6 - suffix_len], "XXXXXX", 6)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /*
+        * Replace pattern's XXXXXX characters with randomness.
+        * Try TMP_MAX different filenames.
+        */
+       gettimeofday(&tv, NULL);
+       value = ((size_t)(tv.tv_usec << 16)) ^ tv.tv_sec ^ getpid();
+       template = &pattern[len - 6 - suffix_len];
+       for (count = 0; count < TMP_MAX; ++count) {
+               uint64_t v = value;
+               /* Fill in the random bits. */
+               template[0] = letters[v % num_letters]; v /= num_letters;
+               template[1] = letters[v % num_letters]; v /= num_letters;
+               template[2] = letters[v % num_letters]; v /= num_letters;
+               template[3] = letters[v % num_letters]; v /= num_letters;
+               template[4] = letters[v % num_letters]; v /= num_letters;
+               template[5] = letters[v % num_letters]; v /= num_letters;
+
+               fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode);
+               if (fd > 0)
+                       return fd;
+               /*
+                * Fatal error (EPERM, ENOSPC etc).
+                * It doesn't make sense to loop.
+                */
+               if (errno != EEXIST)
+                       break;
+               /*
+                * This is a random value.  It is only necessary that
+                * the next TMP_MAX values generated by adding 7777 to
+                * VALUE are different with (module 2^32).
+                */
+               value += 7777;
+       }
+       /* We return the null string if we can't find a unique file name.  */
+       pattern[0] = '\0';
+       return -1;
+}
+
+int git_mkstemp_mode(char *pattern, int mode)
+{
+       /* mkstemp is just mkstemps with no suffix */
+       return git_mkstemps_mode(pattern, 0, mode);
+}
+
+int gitmkstemps(char *pattern, int suffix_len)
+{
+       return git_mkstemps_mode(pattern, suffix_len, 0600);
+}
+
+int xmkstemp_mode(char *template, int mode)
+{
+       int fd;
+       char origtemplate[PATH_MAX];
+       strlcpy(origtemplate, template, sizeof(origtemplate));
+
+       fd = git_mkstemp_mode(template, mode);
+       if (fd < 0) {
+               int saved_errno = errno;
+               const char *nonrelative_template;
+
+               if (!template[0])
+                       template = origtemplate;
+
+               nonrelative_template = absolute_path(template);
+               errno = saved_errno;
+               die_errno("Unable to create temporary file '%s'",
+                       nonrelative_template);
+       }
        return fd;
 }
+
+static int warn_if_unremovable(const char *op, const char *file, int rc)
+{
+       if (rc < 0) {
+               int err = errno;
+               if (ENOENT != err) {
+                       warning("unable to %s %s: %s",
+                               op, file, strerror(errno));
+                       errno = err;
+               }
+       }
+       return rc;
+}
+
+int unlink_or_warn(const char *file)
+{
+       return warn_if_unremovable("unlink", file, unlink(file));
+}
+
+int rmdir_or_warn(const char *file)
+{
+       return warn_if_unremovable("rmdir", file, rmdir(file));
+}
+
+int remove_or_warn(unsigned int mode, const char *file)
+{
+       return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file);
+}