Commit 5711ca93d13b74767788c0d7d68fb948e0f9e170

Vicent Marti 2011-05-09T21:58:26

Merge branch 'error-handling' into development

diff --git a/include/git2/common.h b/include/git2/common.h
index 22c7cc4..2aae648 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -170,6 +170,12 @@
 /** The given literal is not a valid number */
 #define GIT_ENOTNUM (GIT_ERROR - 25)
 
+/** Streaming error */
+#define GIT_ESTREAM (GIT_ERROR - 26)
+
+/** invalid arguments to function */
+#define GIT_EINVALIDARGS (GIT_ERROR - 27)
+
 GIT_BEGIN_DECL
 
 typedef struct {
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 627e67c..fde0dc7 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -34,11 +34,11 @@
 GIT_BEGIN_DECL
 
 /**
- * strerror() for the Git library
- * @param num The error code to explain
- * @return a string explaining the error code
+ * Return a detailed error string with the latest error
+ * that occurred in the library.
+ * @return a string explaining the error
  */
-GIT_EXTERN(const char *) git_strerror(int num);
+GIT_EXTERN(const char *) git_lasterror(void);
 
 /** @} */
 GIT_END_DECL
diff --git a/include/git2/thread-utils.h b/include/git2/thread-utils.h
index fb8644b..e26876b 100644
--- a/include/git2/thread-utils.h
+++ b/include/git2/thread-utils.h
@@ -35,6 +35,7 @@
 
 #if defined(__APPLE__) && defined(__MACH__)
 # undef GIT_TLS
+# define GIT_TLS
 
 #elif defined(__GNUC__) || \
       defined(__SUNPRO_C) || \
diff --git a/src/common.h b/src/common.h
index 5ad878e..e5b9f15 100644
--- a/src/common.h
+++ b/src/common.h
@@ -50,10 +50,13 @@ typedef SSIZE_T ssize_t;
 
 #include "git2/common.h"
 #include "git2/types.h"
-#include "util.h"
 #include "thread-utils.h"
 #include "bswap.h"
 
 #define GIT_PATH_MAX 4096
+extern int git__throw(int error, const char *, ...) GIT_FORMAT_PRINTF(2, 3);
+extern int git__rethrow(int error, const char *, ...) GIT_FORMAT_PRINTF(2, 3);
+
+#include "util.h"
 
 #endif /* INCLUDE_common_h__ */
diff --git a/src/errors.c b/src/errors.c
index c3a495c..40b0feb 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -1,48 +1,66 @@
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file.  (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
 #include "common.h"
+#include "git2/thread-utils.h" /* for GIT_TLS */
 #include "thread-utils.h" /* for GIT_TLS */
 
-static struct {
-	int num;
-	const char *str;
-} error_codes[] = {
-	{GIT_ERROR, "Unspecified error"},
-	{GIT_ENOTOID, "Input was not a properly formatted Git object id."},
-	{GIT_ENOTFOUND, "Object does not exist in the scope searched."},
-	{GIT_ENOMEM, "Not enough space available."},
-	{GIT_EOSERR, "Consult the OS error information."},
-	{GIT_EOBJTYPE, "The specified object is of invalid type"},
-	{GIT_EOBJCORRUPTED, "The specified object has its data corrupted"},
-	{GIT_ENOTAREPO, "The specified repository is invalid"},
-	{GIT_EINVALIDTYPE, "The object type is invalid or doesn't match"},
-	{GIT_EMISSINGOBJDATA, "The object cannot be written that because it's missing internal data"},
-	{GIT_EPACKCORRUPTED, "The packfile for the ODB is corrupted"},
-	{GIT_EFLOCKFAIL, "Failed to adquire or release a file lock"},
-	{GIT_EZLIB, "The Z library failed to inflate/deflate an object's data"},
-	{GIT_EBUSY, "The queried object is currently busy"},
-	{GIT_EINVALIDPATH, "The path is invalid"},
-	{GIT_EBAREINDEX, "The index file is not backed up by an existing repository"},
-	{GIT_EINVALIDREFNAME, "The name of the reference is not valid"},
-	{GIT_EREFCORRUPTED, "The specified reference has its data corrupted"},
-	{GIT_ETOONESTEDSYMREF, "The specified symbolic reference is too deeply nested"},
-	{GIT_EPACKEDREFSCORRUPTED, "The pack-refs file is either corrupted of its format is not currently supported"},
-	{GIT_EINVALIDPATH, "The path is invalid" },
-	{GIT_EREVWALKOVER, "The revision walker is empty; there are no more commits left to iterate"},
-	{GIT_EINVALIDREFSTATE, "The state of the reference is not valid"},
-	{GIT_ENOTIMPLEMENTED, "This feature has not been implemented yet"},
-	{GIT_EEXISTS, "A reference with this name already exists"},
-	{GIT_EOVERFLOW, "The given integer literal is too large to be parsed"},
-	{GIT_ENOTNUM, "The given literal is not a valid number"},
-};
-
-const char *git_strerror(int num)
+#include <stdarg.h>
+
+static GIT_TLS char g_last_error[1024];
+
+int git__rethrow(int error, const char *msg, ...)
 {
-	size_t i;
+	char new_error[1024];
+	char *old_error = NULL;
+
+	va_list va;
+
+	va_start(va, msg);
+	vsnprintf(new_error, sizeof(new_error), msg, va);
+	va_end(va);
 
-	if (num == GIT_EOSERR)
-		return strerror(errno);
-	for (i = 0; i < ARRAY_SIZE(error_codes); i++)
-		if (num == error_codes[i].num)
-			return error_codes[i].str;
+	old_error = strdup(g_last_error);
+	snprintf(g_last_error, sizeof(g_last_error), "%s \n    - %s", new_error, old_error);
+	free(old_error);
 
-	return "Unknown error";
+	return error;
 }
+
+int git__throw(int error, const char *msg, ...)
+{
+	va_list va;
+
+	va_start(va, msg);
+	vsnprintf(g_last_error, sizeof(g_last_error), msg, va);
+	va_end(va);
+
+	return error;
+}
+
+const char *git_lasterror(void)
+{
+	return g_last_error;
+}
+
diff --git a/src/refs.c b/src/refs.c
index ea96819..c4d3d6a 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -122,7 +122,8 @@ static int reference_create(
 	else if (type == GIT_REF_OID)
 		size = sizeof(reference_oid);
 	else
-		return GIT_EINVALIDREFSTATE;
+		return git__throw(GIT_EINVALIDARGS,
+			"Invalid reference type. Use either GIT_REF_OID or GIT_REF_SYMBOLIC as type specifier");
 
 	reference = git__malloc(size);
 	if (reference == NULL)
@@ -159,11 +160,9 @@ static int reference_read(gitfo_buf *file_content, time_t *mtime, const char *re
 	/* Determine the full path of the file */
 	git__joinpath(path, repo_path, ref_name);
 
-	if (gitfo_stat(path, &st) < 0)
-		return GIT_ENOTFOUND;
-
-	if (S_ISDIR(st.st_mode))
-		return GIT_EOBJCORRUPTED;
+	if (gitfo_stat(path, &st) < 0 || S_ISDIR(st.st_mode))
+		return git__throw(GIT_ENOTFOUND,
+			"Cannot read reference file '%s'", ref_name);
 
 	if (mtime)
 		*mtime = st.st_mtime;
@@ -205,7 +204,8 @@ static int loose_update(git_reference *ref)
 	else if (ref->type == GIT_REF_OID)
 		error = loose_parse_oid(ref, &ref_file);
 	else
-		error = GIT_EINVALIDREFSTATE;
+		error = git__throw(GIT_EOBJCORRUPTED,
+			"Invalid reference type (%d) for loose reference", ref->type);
 
 	gitfo_free_buf(&ref_file);
 
@@ -229,7 +229,8 @@ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content)
 	ref_sym = (reference_symbolic *)ref;
 
 	if (file_content->len < (header_len + 1))
-		return GIT_EREFCORRUPTED;
+		return git__throw(GIT_EOBJCORRUPTED,
+			"Failed to parse loose reference. Object too short");
 
 	/* 
 	 * Assume we have already checked for the header
@@ -246,7 +247,8 @@ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content)
 	/* remove newline at the end of file */
 	eol = strchr(ref_sym->target, '\n');
 	if (eol == NULL)
-		return GIT_EREFCORRUPTED;
+		return git__throw(GIT_EOBJCORRUPTED,
+			"Failed to parse loose reference. Missing EOL");
 
 	*eol = '\0';
 	if (eol[-1] == '\r')
@@ -257,6 +259,7 @@ static int loose_parse_symbolic(git_reference *ref, gitfo_buf *file_content)
 
 static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content)
 {
+	int error;
 	reference_oid *ref_oid;
 	char *buffer;
 
@@ -265,17 +268,19 @@ static int loose_parse_oid(git_reference *ref, gitfo_buf *file_content)
 
 	/* File format: 40 chars (OID) + newline */
 	if (file_content->len < GIT_OID_HEXSZ + 1)
-		return GIT_EREFCORRUPTED;
+		return git__throw(GIT_EOBJCORRUPTED,
+			"Failed to parse loose reference. Reference too short");
 
-	if (git_oid_mkstr(&ref_oid->oid, buffer) < GIT_SUCCESS)
-		return GIT_EREFCORRUPTED;
+	if ((error = git_oid_mkstr(&ref_oid->oid, buffer)) < GIT_SUCCESS)
+		return git__rethrow(GIT_EOBJCORRUPTED, "Failed to parse loose reference.");
 
 	buffer = buffer + GIT_OID_HEXSZ;
 	if (*buffer == '\r')
 		buffer++;
 
 	if (*buffer != '\n')
-		return GIT_EREFCORRUPTED;
+		return git__throw(GIT_EOBJCORRUPTED,
+			"Failed to parse loose reference. Missing EOL");
 
 	return GIT_SUCCESS;
 }
@@ -387,7 +392,7 @@ static int loose_write(git_reference *ref)
 		strcpy(ref_contents, GIT_SYMREF);
 		strcat(ref_contents, ref_sym->target);
 	} else {
-		error = GIT_EINVALIDREFSTATE;
+		error = git__throw(GIT_EOBJCORRUPTED, "Failed to write reference. Invalid reference type");
 		goto unlock;
 	}
 
diff --git a/src/util.h b/src/util.h
index 3c60649..6724e8d 100644
--- a/src/util.h
+++ b/src/util.h
@@ -6,16 +6,41 @@
 #define MSB(x, bits) ((x) & (~0ULL << (bitsizeof(x) - (bits))))
 
 /* 
- * Don't wrap malloc/calloc.
- * Use the default versions in glibc, and make
- * sure that any methods that allocate memory
- * return a GIT_ENOMEM error when allocation
- * fails.
+ * Custom memory allocation wrappers
+ * that set error code and error message
+ * on allocation failure
  */
-#define git__malloc malloc
-#define git__calloc calloc
-#define git__realloc realloc
-#define git__strdup strdup
+GIT_INLINE(void *) git__malloc(size_t len)
+{
+	void *ptr = malloc(len);
+	if (!ptr)
+		git__throw(GIT_ENOMEM, "Out of memory. Failed to allocate %d bytes.", (int)len);
+	return ptr;
+}
+
+GIT_INLINE(void *) git__calloc(size_t nelem, size_t elsize)
+{
+	void *ptr = calloc(nelem, elsize);
+	if (!ptr)
+		git__throw(GIT_ENOMEM, "Out of memory. Failed to allocate %d bytes.", (int)elsize);
+	return ptr;
+}
+
+GIT_INLINE(char *) git__strdup(const char *str)
+{
+	char *ptr = strdup(str);
+	if (!ptr)
+		git__throw(GIT_ENOMEM, "Out of memory. Failed to duplicate string");
+	return ptr;
+}
+
+GIT_INLINE(void *) git__realloc(void *ptr, size_t size)
+{
+	void *new_ptr = realloc(ptr, size);
+	if (!new_ptr)
+		git__throw(GIT_ENOMEM, "Out of memory. Failed to allocate %d bytes.", (int)size);
+	return new_ptr;
+}
 
 extern int git__fmt(char *, size_t, const char *, ...)
 	GIT_FORMAT_PRINTF(3, 4);