diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backends/hiredis.c | 9 | ||||
-rw-r--r-- | src/blob.c | 6 | ||||
-rw-r--r-- | src/cache.c | 86 | ||||
-rw-r--r-- | src/cache.h | 3 | ||||
-rw-r--r-- | src/commit.c | 15 | ||||
-rw-r--r-- | src/common.h | 6 | ||||
-rw-r--r-- | src/config.c | 323 | ||||
-rw-r--r-- | src/config.h | 12 | ||||
-rw-r--r-- | src/config_file.c | 1011 | ||||
-rw-r--r-- | src/errors.c | 66 | ||||
-rw-r--r-- | src/fileops.c | 61 | ||||
-rw-r--r-- | src/index.c | 156 | ||||
-rw-r--r-- | src/odb.c | 14 | ||||
-rw-r--r-- | src/odb_loose.c | 6 | ||||
-rw-r--r-- | src/odb_pack.c | 9 | ||||
-rw-r--r-- | src/refs.c | 51 | ||||
-rw-r--r-- | src/refs.h | 1 | ||||
-rw-r--r-- | src/repository.c | 66 | ||||
-rw-r--r-- | src/revwalk.c | 18 | ||||
-rw-r--r-- | src/signature.c | 26 | ||||
-rw-r--r-- | src/tag.c | 19 | ||||
-rw-r--r-- | src/tree.c | 17 | ||||
-rw-r--r-- | src/util.c | 14 | ||||
-rw-r--r-- | src/util.h | 65 | ||||
-rw-r--r-- | src/vector.c | 11 | ||||
-rw-r--r-- | src/win32/pthread.c | 7 |
26 files changed, 1810 insertions, 268 deletions
diff --git a/src/backends/hiredis.c b/src/backends/hiredis.c index 707412bf6..2533a16da 100644 --- a/src/backends/hiredis.c +++ b/src/backends/hiredis.c @@ -53,7 +53,7 @@ int hiredis_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backe reply = redisCommand(backend->db, "HMGET %b %s %s", oid->id, GIT_OID_RAWSZ, "type", "size"); - if (reply->type == REDIS_REPLY_ARRAY) { + if (reply && reply->type == REDIS_REPLY_ARRAY) { if (reply->element[0]->type != REDIS_REPLY_NIL && reply->element[0]->type != REDIS_REPLY_NIL) { *type_p = (git_otype) atoi(reply->element[0]->str); @@ -83,7 +83,7 @@ int hiredis_backend__read(void **data_p, size_t *len_p, git_otype *type_p, git_o reply = redisCommand(backend->db, "HMGET %b %s %s %s", oid->id, GIT_OID_RAWSZ, "type", "size", "data"); - if (reply->type == REDIS_REPLY_ARRAY) { + if (reply && reply->type == REDIS_REPLY_ARRAY) { if (reply->element[0]->type != REDIS_REPLY_NIL && reply->element[1]->type != REDIS_REPLY_NIL && reply->element[2]->type != REDIS_REPLY_NIL) { @@ -118,7 +118,7 @@ int hiredis_backend__exists(git_odb_backend *_backend, const git_oid *oid) { found = 0; reply = redisCommand(backend->db, "exists %b", oid->id, GIT_OID_RAWSZ); - if (reply->type != REDIS_REPLY_NIL && reply->type != REDIS_REPLY_ERROR) + if (reply && reply->type != REDIS_REPLY_NIL && reply->type != REDIS_REPLY_ERROR) found = 1; @@ -144,7 +144,8 @@ int hiredis_backend__write(git_oid *id, git_odb_backend *_backend, const void *d "size %d " "data %b ", id->id, GIT_OID_RAWSZ, (int) type, len, data, len); - error = reply->type == REDIS_REPLY_ERROR ? GIT_ERROR : GIT_SUCCESS; + + error = (reply == NULL || reply->type == REDIS_REPLY_ERROR) ? GIT_ERROR : GIT_SUCCESS; freeReplyObject(reply); return error; diff --git a/src/blob.c b/src/blob.c index 5e3c22fbf..987169358 100644 --- a/src/blob.c +++ b/src/blob.c @@ -62,7 +62,7 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b git_odb_stream *stream; if ((error = git_odb_open_wstream(&stream, repo->db, len, GIT_OBJ_BLOB)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to create blob. Can't open write stream"); stream->write(stream, buffer, len); @@ -81,7 +81,7 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat git_odb_stream *stream; if (repo->path_workdir == NULL) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Failed to create blob. No workdir given"); git__joinpath(full_path, repo->path_workdir, path); @@ -106,7 +106,7 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat if (read_len < 0) { gitfo_close(fd); stream->free(stream); - return GIT_EOSERR; + return git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file"); } stream->write(stream, buffer, read_len); diff --git a/src/cache.c b/src/cache.c index fd42e2c5b..433fc3d9c 100644 --- a/src/cache.c +++ b/src/cache.c @@ -29,10 +29,7 @@ #include "thread-utils.h" #include "cache.h" -#define GIT_CACHE_OPENADR 3 - - -void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr) +int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr) { size_t i; @@ -52,12 +49,15 @@ void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_p cache->free_obj = free_ptr; cache->nodes = git__malloc((size + 1) * sizeof(cache_node)); + if (cache->nodes == NULL) + return GIT_ENOMEM; for (i = 0; i < (size + 1); ++i) { git_mutex_init(&cache->nodes[i].lock); cache->nodes[i].ptr = NULL; - cache->nodes[i].lru = 0; } + + return GIT_SUCCESS; } void git_cache_free(git_cache *cache) @@ -77,85 +77,53 @@ void git_cache_free(git_cache *cache) void *git_cache_get(git_cache *cache, const git_oid *oid) { const uint32_t *hash; - size_t i, pos, found = 0; cache_node *node = NULL; + void *result = NULL; hash = (const uint32_t *)oid->id; + node = &cache->nodes[hash[0] & cache->size_mask]; - for (i = 0; !found && i < GIT_CACHE_OPENADR; ++i) { - pos = hash[i] & cache->size_mask; - node = &cache->nodes[pos]; - - git_mutex_lock(&node->lock); - { - if (node->ptr && git_cached_obj_compare(node->ptr, oid) == 0) { - git_cached_obj_incref(node->ptr); - node->lru = ++cache->lru_count; - found = 1; - } + git_mutex_lock(&node->lock); + { + if (node->ptr && git_cached_obj_compare(node->ptr, oid) == 0) { + git_cached_obj_incref(node->ptr); + result = node->ptr; } - git_mutex_unlock(&node->lock); } + git_mutex_unlock(&node->lock); - - return found ? node->ptr : NULL; + return result; } void *git_cache_try_store(git_cache *cache, void *entry) { - cache_node *nodes[GIT_CACHE_OPENADR], *lru_node; const uint32_t *hash; const git_oid *oid; - size_t i; + cache_node *node = NULL; oid = &((git_cached_obj*)entry)->oid; hash = (const uint32_t *)oid->id; + node = &cache->nodes[hash[0] & cache->size_mask]; /* increase the refcount on this object, because * the cache now owns it */ git_cached_obj_incref(entry); - - for (i = 0; i < GIT_CACHE_OPENADR; ++i) { - size_t pos = hash[i] & cache->size_mask; - - nodes[i] = &cache->nodes[pos]; - git_mutex_lock(&nodes[i]->lock); - } - - lru_node = nodes[0]; - - for (i = 0; i < GIT_CACHE_OPENADR; ++i) { - - if (nodes[i]->ptr == NULL) { - nodes[i]->ptr = entry; - nodes[i]->lru = ++cache->lru_count; - break; - } else if (git_cached_obj_compare(nodes[i]->ptr, oid) == 0) { - git_cached_obj_decref(entry, cache->free_obj); - entry = nodes[i]->ptr; - nodes[i]->lru = ++cache->lru_count; - break; - } - - if (nodes[i]->lru < lru_node->lru) - lru_node = nodes[i]; - } - - if (i == GIT_CACHE_OPENADR) { - void *old_entry = lru_node->ptr; - assert(old_entry); - - git_cached_obj_decref(old_entry, cache->free_obj); - lru_node->ptr = entry; - lru_node->lru = ++cache->lru_count; + git_mutex_lock(&node->lock); + + if (node->ptr == NULL) { + node->ptr = entry; + } else if (git_cached_obj_compare(node->ptr, oid) == 0) { + git_cached_obj_decref(entry, cache->free_obj); + entry = node->ptr; + } else { + git_cached_obj_decref(node->ptr, cache->free_obj); + node->ptr = entry; } /* increase the refcount again, because we are * returning it to the user */ git_cached_obj_incref(entry); - - for (i = 0; i < GIT_CACHE_OPENADR; ++i) - git_mutex_unlock(&nodes[i]->lock); + git_mutex_unlock(&node->lock); return entry; } diff --git a/src/cache.h b/src/cache.h index 975aaff7e..4794dea3a 100644 --- a/src/cache.h +++ b/src/cache.h @@ -19,7 +19,6 @@ typedef struct { typedef struct { git_cached_obj *ptr; git_mutex lock; - unsigned int lru; } cache_node; typedef struct { @@ -31,7 +30,7 @@ typedef struct { } git_cache; -void git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr); +int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr); void git_cache_free(git_cache *cache); void *git_cache_try_store(git_cache *cache, void *entry); diff --git a/src/commit.c b/src/commit.c index 9621703c3..54d7a47fe 100644 --- a/src/commit.c +++ b/src/commit.c @@ -224,9 +224,18 @@ int git_commit_create( if (error < GIT_SUCCESS) return error; - if (git_reference_type(head) == GIT_REF_SYMBOLIC) { - if ((error = git_reference_resolve(&head, head)) < GIT_SUCCESS) + error = git_reference_resolve(&head, head); + if (error < GIT_SUCCESS) { + if (error != GIT_ENOTFOUND) return error; + /* + * The target of the reference was not found. This can happen + * just after a repository has been initialized (the master + * branch doesn't exist yet, as it doesn't have anything to + * point to) or after an orphan checkout, so if the target + * branch doesn't exist yet, create it and return. + */ + return git_reference_create_oid_f(&head, repo, git_reference_target(head), oid); } error = git_reference_set_oid(head, oid); @@ -277,7 +286,7 @@ int commit_parse_buffer(git_commit *commit, const void *data, size_t len) if (buffer < buffer_end) { const char *line_end; - size_t message_len = buffer_end - buffer; + size_t message_len; /* Long message */ message_len = buffer_end - buffer; diff --git a/src/common.h b/src/common.h index 5ad878e26..f4f11fd2f 100644 --- a/src/common.h +++ b/src/common.h @@ -50,10 +50,14 @@ typedef SSIZE_T ssize_t; #include "git2/common.h" #include "git2/types.h" -#include "util.h" +#include "git2/errors.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/config.c b/src/config.c new file mode 100644 index 000000000..cd0a73ccd --- /dev/null +++ b/src/config.c @@ -0,0 +1,323 @@ +/* + * 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 "fileops.h" +#include "hashtable.h" +#include "config.h" +#include "git2/config_backend.h" +#include "vector.h" + +#include <ctype.h> + +typedef struct { + git_config_backend *backend; + int priority; +} backend_internal; + +int git_config_open_bare(git_config **out, const char *path) +{ + git_config_backend *backend = NULL; + git_config *cfg = NULL; + int error = GIT_SUCCESS; + + error = git_config_new(&cfg); + if (error < GIT_SUCCESS) + goto error; + + error = git_config_backend_file(&backend, path); + if (error < GIT_SUCCESS) + goto error; + + error = git_config_add_backend(cfg, backend, 1); + if (error < GIT_SUCCESS) + goto error; + + error = backend->open(backend); + if (error < GIT_SUCCESS) + goto error; + + *out = cfg; + + return error; + + error: + if(backend) + backend->free(backend); + + return error; +} + +void git_config_free(git_config *cfg) +{ + unsigned int i; + git_config_backend *backend; + backend_internal *internal; + + for(i = 0; i < cfg->backends.length; ++i){ + internal = git_vector_get(&cfg->backends, i); + backend = internal->backend; + backend->free(backend); + free(internal); + } + + git_vector_free(&cfg->backends); + free(cfg); +} + +static int config_backend_cmp(const void *a, const void *b) +{ + const backend_internal *bk_a = *(const backend_internal **)(a); + const backend_internal *bk_b = *(const backend_internal **)(b); + + return bk_b->priority - bk_a->priority; +} + +int git_config_new(git_config **out) +{ + git_config *cfg; + + cfg = git__malloc(sizeof(git_config)); + if (cfg == NULL) + return GIT_ENOMEM; + + memset(cfg, 0x0, sizeof(git_config)); + + if (git_vector_init(&cfg->backends, 3, config_backend_cmp) < 0) { + free(cfg); + return GIT_ENOMEM; + } + + *out = cfg; + + return GIT_SUCCESS; +} + +int git_config_add_backend(git_config *cfg, git_config_backend *backend, int priority) +{ + backend_internal *internal; + + assert(cfg && backend); + + internal = git__malloc(sizeof(backend_internal)); + if (internal == NULL) + return GIT_ENOMEM; + + internal->backend = backend; + internal->priority = priority; + + if (git_vector_insert(&cfg->backends, internal) < 0) { + free(internal); + return GIT_ENOMEM; + } + + git_vector_sort(&cfg->backends); + internal->backend->cfg = cfg; + + return GIT_SUCCESS; +} + +/* + * Loop over all the variables + */ + +int git_config_foreach(git_config *cfg, int (*fn)(const char *, void *), void *data) +{ + int ret = GIT_SUCCESS; + unsigned int i; + backend_internal *internal; + git_config_backend *backend; + + for(i = 0; i < cfg->backends.length && ret == 0; ++i) { + internal = git_vector_get(&cfg->backends, i); + backend = internal->backend; + ret = backend->foreach(backend, fn, data); + } + + return ret; +} + + +/************** + * Setters + **************/ + +/* + * Internal function to actually set the string value of a variable + */ + +int git_config_set_long(git_config *cfg, const char *name, long int value) +{ + char str_value[5]; /* Most numbers should fit in here */ + int buf_len = sizeof(str_value), ret; + char *help_buf = NULL; + + if ((ret = snprintf(str_value, buf_len, "%ld", value)) >= buf_len - 1){ + /* The number is too large, we need to allocate more memory */ + buf_len = ret + 1; + help_buf = git__malloc(buf_len); + snprintf(help_buf, buf_len, "%ld", value); + ret = git_config_set_string(cfg, name, help_buf); + free(help_buf); + } else { + ret = git_config_set_string(cfg, name, str_value); + } + + return ret; +} + +int git_config_set_int(git_config *cfg, const char *name, int value) +{ + return git_config_set_long(cfg, name, value); +} + +int git_config_set_bool(git_config *cfg, const char *name, int value) +{ + const char *str_value; + + if (value == 0) + str_value = "false"; + else + str_value = "true"; + + return git_config_set_string(cfg, name, str_value); +} + +int git_config_set_string(git_config *cfg, const char *name, const char *value) +{ + backend_internal *internal; + git_config_backend *backend; + + assert(cfg->backends.length > 0); + + internal = git_vector_get(&cfg->backends, 0); + backend = internal->backend; + + return backend->set(backend, name, value); +} + +/*********** + * Getters + ***********/ + +int git_config_get_long(git_config *cfg, const char *name, long int *out) +{ + const char *value, *num_end; + int ret; + long int num; + + ret = git_config_get_string(cfg, name, &value); + if (ret < GIT_SUCCESS) + return ret; + + ret = git__strtol32(&num, value, &num_end, 0); + if (ret < GIT_SUCCESS) + return ret; + + switch (*num_end) { + case '\0': + break; + case 'k': + case 'K': + num *= 1024; + break; + case 'm': + case 'M': + num *= 1024 * 1024; + break; + case 'g': + case 'G': + num *= 1024 * 1024 * 1024; + break; + default: + return GIT_EINVALIDTYPE; + } + + *out = num; + + return GIT_SUCCESS; +} + +int git_config_get_int(git_config *cfg, const char *name, int *out) +{ + long int tmp; + int ret; + + ret = git_config_get_long(cfg, name, &tmp); + + *out = (int) tmp; + + return ret; +} + +int git_config_get_bool(git_config *cfg, const char *name, int *out) +{ + const char *value; + int error = GIT_SUCCESS; + + error = git_config_get_string(cfg, name, &value); + if (error < GIT_SUCCESS) + return error; + + /* A missing value means true */ + if (value == NULL) { + *out = 1; + return GIT_SUCCESS; + } + + if (!strcasecmp(value, "true") || + !strcasecmp(value, "yes") || + !strcasecmp(value, "on")) { + *out = 1; + return GIT_SUCCESS; + } + if (!strcasecmp(value, "false") || + !strcasecmp(value, "no") || + !strcasecmp(value, "off")) { + *out = 0; + return GIT_SUCCESS; + } + + /* Try to parse it as an integer */ + error = git_config_get_int(cfg, name, out); + if (error == GIT_SUCCESS) + *out = !!(*out); + + return error; +} + +int git_config_get_string(git_config *cfg, const char *name, const char **out) +{ + backend_internal *internal; + git_config_backend *backend; + + assert(cfg->backends.length > 0); + + internal = git_vector_get(&cfg->backends, 0); + backend = internal->backend; + + return backend->get(backend, name, out); +} + diff --git a/src/config.h b/src/config.h new file mode 100644 index 000000000..a811fd850 --- /dev/null +++ b/src/config.h @@ -0,0 +1,12 @@ +#ifndef INCLUDE_config_h__ +#define INCLUDE_config_h__ + +#include "git2.h" +#include "git2/config.h" +#include "vector.h" + +struct git_config { + git_vector backends; +}; + +#endif diff --git a/src/config_file.c b/src/config_file.c new file mode 100644 index 000000000..37bb2794e --- /dev/null +++ b/src/config_file.c @@ -0,0 +1,1011 @@ +/* + * 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 "config.h" +#include "fileops.h" +#include "git2/config_backend.h" +#include "git2/types.h" + +#include <ctype.h> + +typedef struct cvar_t { + struct cvar_t *next; + char *section; + char *name; + char *value; +} cvar_t; + +typedef struct { + struct cvar_t *head; + struct cvar_t *tail; +} cvar_t_list; + +#define CVAR_LIST_HEAD(list) ((list)->head) + +#define CVAR_LIST_TAIL(list) ((list)->tail) + +#define CVAR_LIST_NEXT(var) ((var)->next) + +#define CVAR_LIST_EMPTY(list) ((list)->head == NULL) + +#define CVAR_LIST_APPEND(list, var) do {\ + if (CVAR_LIST_EMPTY(list)) {\ + CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\ + } else {\ + CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\ + CVAR_LIST_TAIL(list) = var;\ + }\ +} while(0) + +#define CVAR_LIST_REMOVE_HEAD(list) do {\ + CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\ +} while(0) + +#define CVAR_LIST_REMOVE_AFTER(var) do {\ + CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\ +} while(0) + +#define CVAR_LIST_FOREACH(list, iter)\ + for ((iter) = CVAR_LIST_HEAD(list);\ + (iter) != NULL;\ + (iter) = CVAR_LIST_NEXT(iter)) + +/* + * Inspired by the FreeBSD functions + */ +#define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\ + for ((iter) = CVAR_LIST_HEAD(vars);\ + (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\ + (iter) = (tmp)) + + + +typedef struct { + git_config_backend parent; + + cvar_t_list var_list; + + struct { + gitfo_buf buffer; + char *read_ptr; + int line_number; + int eof; + } reader; + + char *file_path; +} file_backend; + +static int config_parse(file_backend *cfg_file); +static int parse_variable(file_backend *cfg, char **var_name, char **var_value); + +static void cvar_free(cvar_t *var) +{ + if (var == NULL) + return; + + free(var->section); + free(var->name); + free(var->value); + free(var); +} + +static void cvar_list_free(cvar_t_list *list) +{ + cvar_t *cur; + + while (!CVAR_LIST_EMPTY(list)) { + cur = CVAR_LIST_HEAD(list); + CVAR_LIST_REMOVE_HEAD(list); + cvar_free(cur); + } +} + +/* + * Compare two strings according to the git section-subsection + * rules. The order of the strings is important because local is + * assumed to have the internal format (only the section name and with + * case information) and input the normalized one (only dots, no case + * information). + */ +static int cvar_match_section(const char *local, const char *input) +{ + char *first_dot, *last_dot; + char *local_sp = strchr(local, ' '); + int comparison_len; + + /* + * If the local section name doesn't contain a space, then we can + * just do a case-insensitive compare. + */ + if (local_sp == NULL) + return !strncasecmp(local, input, strlen(local)); + + /* + * From here onwards, there is a space diving the section and the + * subsection. Anything before the space in local is + * case-insensitive. + */ + if (strncasecmp(local, input, local_sp - local)) + return 0; + + /* + * We compare starting from the first character after the + * quotation marks, which is two characters beyond the space. For + * the input, we start one character beyond the dot. If the names + * have different lengths, then we can fail early, as we know they + * can't be the same. + * The length is given by the length between the quotation marks. + */ + + first_dot = strchr(input, '.'); + last_dot = strrchr(input, '.'); + comparison_len = strlen(local_sp + 2) - 1; + + if (last_dot == first_dot || last_dot - first_dot - 1 != comparison_len) + return 0; + + return !strncmp(local_sp + 2, first_dot + 1, comparison_len); +} + +static int cvar_match_name(const cvar_t *var, const char *str) +{ + const char *name_start; + + if (!cvar_match_section(var->section, str)) { + return 0; + } + /* Early exit if the lengths are different */ + name_start = strrchr(str, '.') + 1; + if (strlen(var->name) != strlen(name_start)) + return 0; + + return !strcasecmp(var->name, name_start); +} + +static cvar_t *cvar_list_find(cvar_t_list *list, const char *name) +{ + cvar_t *iter; + + CVAR_LIST_FOREACH (list, iter) { + if (cvar_match_name(iter, name)) + return iter; + } + + return NULL; +} + +static int cvar_name_normalize(const char *input, char **output) +{ + char *input_sp = strchr(input, ' '); + char *quote, *str; + int i; + + /* We need to make a copy anyway */ + str = git__strdup(input); + if (str == NULL) + return GIT_ENOMEM; + + *output = str; + + /* If there aren't any spaces, we don't need to do anything */ + if (input_sp == NULL) + return GIT_SUCCESS; + + /* + * If there are spaces, we replace the space by a dot, move the + * variable name so that the dot before it replaces the last + * quotation mark and repeat so that the first quotation mark + * disappears. + */ + str[input_sp - input] = '.'; + + for (i = 0; i < 2; ++i) { + quote = strrchr(str, '"'); + memmove(quote, quote + 1, strlen(quote)); + } + + return GIT_SUCCESS; +} + +static int config_open(git_config_backend *cfg) +{ + int error; + file_backend *b = (file_backend *)cfg; + + error = gitfo_read_file(&b->reader.buffer, b->file_path); + if(error < GIT_SUCCESS) + goto cleanup; + + error = config_parse(b); + if (error < GIT_SUCCESS) + goto cleanup; + + gitfo_free_buf(&b->reader.buffer); + + return error; + + cleanup: + cvar_list_free(&b->var_list); + gitfo_free_buf(&b->reader.buffer); + free(cfg); + + return error; +} + +static void backend_free(git_config_backend *_backend) +{ + file_backend *backend = (file_backend *)_backend; + + if (backend == NULL) + return; + + free(backend->file_path); + cvar_list_free(&backend->var_list); + + free(backend); +} + +static int file_foreach(git_config_backend *backend, int (*fn)(const char *, void *), void *data) +{ + int ret = GIT_SUCCESS; + cvar_t *var; + char *normalized; + file_backend *b = (file_backend *)backend; + + CVAR_LIST_FOREACH(&b->var_list, var) { + ret = cvar_name_normalize(var->name, &normalized); + if (ret < GIT_SUCCESS) + return ret; + + ret = fn(normalized, data); + free(normalized); + if (ret) + break; + } + + return ret; +} + +static int config_set(git_config_backend *cfg, const char *name, const char *value) +{ + cvar_t *var = NULL; + cvar_t *existing = NULL; + int error = GIT_SUCCESS; + const char *last_dot; + file_backend *b = (file_backend *)cfg; + + /* + * If it already exists, we just need to update its value. + */ + existing = cvar_list_find(&b->var_list, name); + if (existing != NULL) { + char *tmp = value ? git__strdup(value) : NULL; + if (tmp == NULL && value != NULL) + return GIT_ENOMEM; + + free(existing->value); + existing->value = tmp; + + return GIT_SUCCESS; + } + + /* + * Otherwise, create it and stick it at the end of the queue. + */ + + last_dot = strrchr(name, '.'); + if (last_dot == NULL) { + return git__throw(GIT_EINVALIDTYPE, "Variables without section aren't allowed"); + } + + var = git__malloc(sizeof(cvar_t)); + if (var == NULL) + return GIT_ENOMEM; + + memset(var, 0x0, sizeof(cvar_t)); + + var->section = git__strndup(name, last_dot - name); + if (var->section == NULL) { + error = GIT_ENOMEM; + goto out; + } + + var->name = git__strdup(last_dot + 1); + if (var->name == NULL) { + error = GIT_ENOMEM; + goto out; + } + + var->value = value ? git__strdup(value) : NULL; + if (var->value == NULL && value != NULL) { + error = GIT_ENOMEM; + goto out; + } + + CVAR_LIST_APPEND(&b->var_list, var); + + out: + if (error < GIT_SUCCESS) + cvar_free(var); + + return error; +} + +/* + * Internal function that actually gets the value in string form + */ +static int config_get(git_config_backend *cfg, const char *name, const char **out) +{ + cvar_t *var; + int error = GIT_SUCCESS; + file_backend *b = (file_backend *)cfg; + + var = cvar_list_find(&b->var_list, name); + + if (var == NULL) + return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name); + + *out = var->value; + + return error; +} + +int git_config_backend_file(git_config_backend **out, const char *path) +{ + file_backend *backend; + + backend = git__malloc(sizeof(file_backend)); + if (backend == NULL) + return GIT_ENOMEM; + + memset(backend, 0x0, sizeof(file_backend)); + + backend->file_path = git__strdup(path); + if (backend->file_path == NULL) { + free(backend); + return GIT_ENOMEM; + } + + backend->parent.open = config_open; + backend->parent.get = config_get; + backend->parent.set = config_set; + backend->parent.foreach = file_foreach; + backend->parent.free = backend_free; + + *out = (git_config_backend *)backend; + + return GIT_SUCCESS; +} + +static int cfg_getchar_raw(file_backend *cfg) +{ + int c; + + c = *cfg->reader.read_ptr++; + + /* + Win 32 line breaks: if we find a \r\n sequence, + return only the \n as a newline + */ + if (c == '\r' && *cfg->reader.read_ptr == '\n') { + cfg->reader.read_ptr++; + c = '\n'; + } + + if (c == '\n') + cfg->reader.line_number++; + + if (c == 0) { + cfg->reader.eof = 1; + c = '\n'; + } + + return c; +} + +#define SKIP_WHITESPACE (1 << 1) +#define SKIP_COMMENTS (1 << 2) + +static int cfg_getchar(file_backend *cfg_file, int flags) +{ + const int skip_whitespace = (flags & SKIP_WHITESPACE); + const int skip_comments = (flags & SKIP_COMMENTS); + int c; + + assert(cfg_file->reader.read_ptr); + + do c = cfg_getchar_raw(cfg_file); + while (skip_whitespace && isspace(c)); + + if (skip_comments && (c == '#' || c == ';')) { + do c = cfg_getchar_raw(cfg_file); + while (c != '\n'); + } + + return c; +} + +/* + * Read the next char, but don't move the reading pointer. + */ +static int cfg_peek(file_backend *cfg, int flags) +{ + void *old_read_ptr; + int old_lineno, old_eof; + int ret; + + assert(cfg->reader.read_ptr); + + old_read_ptr = cfg->reader.read_ptr; + old_lineno = cfg->reader.line_number; + old_eof = cfg->reader.eof; + + ret = cfg_getchar(cfg, flags); + + cfg->reader.read_ptr = old_read_ptr; + cfg->reader.line_number = old_lineno; + cfg->reader.eof = old_eof; + + return ret; +} + +static const char *LINEBREAK_UNIX = "\\\n"; +static const char *LINEBREAK_WIN32 = "\\\r\n"; + +static int is_linebreak(const char *pos) +{ + return memcmp(pos - 1, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0 || + memcmp(pos - 2, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0; +} + +/* + * Read and consume a line, returning it in newly-allocated memory. + */ +static char *cfg_readline(file_backend *cfg) +{ + char *line = NULL; + char *line_src, *line_end; + int line_len; + + line_src = cfg->reader.read_ptr; + line_end = strchr(line_src, '\n'); + + /* no newline at EOF */ + if (line_end == NULL) + line_end = strchr(line_src, 0); + else + while (is_linebreak(line_end)) + line_end = strchr(line_end + 1, '\n'); + + + while (line_src < line_end && isspace(*line_src)) + line_src++; + + line = (char *)git__malloc((size_t)(line_end - line_src) + 1); + if (line == NULL) + return NULL; + + line_len = 0; + while (line_src < line_end) { + + if (memcmp(line_src, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0) { + line_src += sizeof(LINEBREAK_UNIX); + continue; + } + + if (memcmp(line_src, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0) { + line_src += sizeof(LINEBREAK_WIN32); + continue; + } + + line[line_len++] = *line_src++; + } + + line[line_len] = '\0'; + + while (--line_len >= 0 && isspace(line[line_len])) + line[line_len] = '\0'; + + if (*line_end == '\n') + line_end++; + + if (*line_end == '\0') + cfg->reader.eof = 1; + + cfg->reader.line_number++; + cfg->reader.read_ptr = line_end; + + return line; +} + +/* + * Consume a line, without storing it anywhere + */ +void cfg_consume_line(file_backend *cfg) +{ + char *line_start, *line_end; + + line_start = cfg->reader.read_ptr; + line_end = strchr(line_start, '\n'); + /* No newline at EOF */ + if(line_end == NULL){ + line_end = strchr(line_start, '\0'); + } + + if (*line_end == '\n') + line_end++; + + if (*line_end == '\0') + cfg->reader.eof = 1; + + cfg->reader.line_number++; + cfg->reader.read_ptr = line_end; +} + +static inline int config_keychar(int c) +{ + return isalnum(c) || c == '-'; +} + +static int parse_section_header_ext(const char *line, const char *base_name, char **section_name) +{ + int buf_len, total_len, pos, rpos; + int c, ret; + char *subsection, *first_quote, *last_quote; + int error = GIT_SUCCESS; + int quote_marks; + /* + * base_name is what came before the space. We should be at the + * first quotation mark, except for now, line isn't being kept in + * sync so we only really use it to calculate the length. + */ + + first_quote = strchr(line, '"'); + last_quote = strrchr(line, '"'); + + if (last_quote - first_quote == 0) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. There is no final quotation mark"); + + buf_len = last_quote - first_quote + 2; + + subsection = git__malloc(buf_len + 2); + if (subsection == NULL) + return GIT_ENOMEM; + + pos = 0; + rpos = 0; + quote_marks = 0; + + line = first_quote; + c = line[rpos++]; + + /* + * At the end of each iteration, whatever is stored in c will be + * added to the string. In case of error, jump to out + */ + do { + switch (c) { + case '"': + if (quote_marks++ >= 2) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. Too many quotes"); + break; + case '\\': + c = line[rpos++]; + switch (c) { + case '"': + case '\\': + break; + default: + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. Unsupported escape char \\%c", c); + goto out; + } + default: + break; + } + + subsection[pos++] = c; + } while ((c = line[rpos++]) != ']'); + + subsection[pos] = '\0'; + + total_len = strlen(base_name) + strlen(subsection) + 2; + *section_name = git__malloc(total_len); + if (*section_name == NULL) { + error = GIT_ENOMEM; + goto out; + } + + ret = snprintf(*section_name, total_len, "%s %s", base_name, subsection); + if (ret >= total_len) { + /* If this fails, we've checked the length wrong */ + error = git__throw(GIT_ERROR, "Failed to parse ext header. Wrong total lenght calculation"); + goto out; + } else if (ret < 0) { + error = git__throw(GIT_EOSERR, "Failed to parse ext header. OS error: %s", strerror(errno)); + goto out; + } + + git__strntolower(*section_name, strchr(*section_name, ' ') - *section_name); + + out: + free(subsection); + + return error; +} + +static int parse_section_header(file_backend *cfg, char **section_out) +{ + char *name, *name_end; + int name_length, c, pos; + int error = GIT_SUCCESS; + char *line; + + line = cfg_readline(cfg); + if (line == NULL) + return GIT_ENOMEM; + + /* find the end of the variable's name */ + name_end = strchr(line, ']'); + if (name_end == NULL) + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Can't find header name end"); + + name = (char *)git__malloc((size_t)(name_end - line) + 1); + if (name == NULL) + return GIT_ENOMEM; + + name_length = 0; + pos = 0; + + /* Make sure we were given a section header */ + c = line[pos++]; + if (c != '[') { + error = git__throw(GIT_ERROR, "Failed to parse header. Didn't get section header. This is a bug"); + goto error; + } + + c = line[pos++]; + + do { + if (cfg->reader.eof){ + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Config file ended unexpectedly"); + goto error; + } + + if (isspace(c)){ + name[name_length] = '\0'; + error = parse_section_header_ext(line, name, section_out); + free(line); + free(name); + return error; + } + + if (!config_keychar(c) && c != '.') { + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Wrong format on header"); + goto error; + } + + name[name_length++] = tolower(c); + + } while ((c = line[pos++]) != ']'); + + name[name_length] = 0; + free(line); + git__strtolower(name); + *section_out = name; + return GIT_SUCCESS; + +error: + free(line); + free(name); + return error; +} + +static int skip_bom(file_backend *cfg) +{ + static const char *utf8_bom = "\xef\xbb\xbf"; + + if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0) + cfg->reader.read_ptr += sizeof(utf8_bom); + + /* TODO: the reference implementation does pretty stupid + shit with the BoM + */ + + return GIT_SUCCESS; +} + +/* + (* basic types *) + digit = "0".."9" + integer = digit { digit } + alphabet = "a".."z" + "A" .. "Z" + + section_char = alphabet | "." | "-" + extension_char = (* any character except newline *) + any_char = (* any character *) + variable_char = "alphabet" | "-" + + + (* actual grammar *) + config = { section } + + section = header { definition } + + header = "[" section [subsection | subsection_ext] "]" + + subsection = "." section + subsection_ext = "\"" extension "\"" + + section = section_char { section_char } + extension = extension_char { extension_char } + + definition = variable_name ["=" variable_value] "\n" + + variable_name = variable_char { variable_char } + variable_value = string | boolean | integer + + string = quoted_string | plain_string + quoted_string = "\"" plain_string "\"" + plain_string = { any_char } + + boolean = boolean_true | boolean_false + boolean_true = "yes" | "1" | "true" | "on" + boolean_false = "no" | "0" | "false" | "off" +*/ + +static void strip_comments(char *line) +{ + int quote_count = 0; + char *ptr; + + for (ptr = line; *ptr; ++ptr) { + if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\') + quote_count++; + + if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) { + ptr[0] = '\0'; + break; + } + } + + if (isspace(ptr[-1])) { + /* TODO skip whitespace */ + } +} + +static int config_parse(file_backend *cfg_file) +{ + int error = GIT_SUCCESS, c; + char *current_section = NULL; + char *var_name; + char *var_value; + cvar_t *var; + + /* Initialise the reading position */ + cfg_file->reader.read_ptr = cfg_file->reader.buffer.data; + cfg_file->reader.eof = 0; + + skip_bom(cfg_file); + + while (error == GIT_SUCCESS && !cfg_file->reader.eof) { + + c = cfg_peek(cfg_file, SKIP_WHITESPACE); + + switch (c) { + case '\0': /* We've arrived at the end of the file */ + break; + + case '[': /* section header, new section begins */ + free(current_section); + error = parse_section_header(cfg_file, ¤t_section); + break; + + case ';': + case '#': + cfg_consume_line(cfg_file); + break; + + default: /* assume variable declaration */ + error = parse_variable(cfg_file, &var_name, &var_value); + + if (error < GIT_SUCCESS) + break; + + var = malloc(sizeof(cvar_t)); + if (var == NULL) { + error = GIT_ENOMEM; + break; + } + + memset(var, 0x0, sizeof(cvar_t)); + + var->section = git__strdup(current_section); + if (var->section == NULL) { + error = GIT_ENOMEM; + free(var); + break; + } + + var->name = var_name; + var->value = var_value; + git__strtolower(var->name); + + CVAR_LIST_APPEND(&cfg_file->var_list, var); + + break; + } + } + + if (current_section) + free(current_section); + + return error; +} + +static int is_multiline_var(const char *str) +{ + char *end = strrchr(str, '\0') - 1; + + while (isspace(*end)) + --end; + + return *end == '\\'; +} + +static int parse_multiline_variable(file_backend *cfg, const char *first, char **out) +{ + char *line = NULL, *end; + int error = GIT_SUCCESS, len, ret; + char *buf; + + /* Check that the next line exists */ + line = cfg_readline(cfg); + if (line == NULL) + return GIT_ENOMEM; + + /* We've reached the end of the file, there is input missing */ + if (line[0] == '\0') { + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse multiline var. File ended unexpectedly"); + goto out; + } + + strip_comments(line); + + /* If it was just a comment, pretend it didn't exist */ + if (line[0] == '\0') { + error = parse_multiline_variable(cfg, first, out); + goto out; + } + + /* Find the continuation character '\' and strip the whitespace */ + end = strrchr(first, '\\'); + while (isspace(end[-1])) + --end; + + *end = '\0'; /* Terminate the string here */ + + len = strlen(first) + strlen(line) + 2; + buf = git__malloc(len); + if (buf == NULL) { + error = GIT_ENOMEM; + goto out; + } + + ret = snprintf(buf, len, "%s %s", first, line); + if (ret < 0) { + error = git__throw(GIT_EOSERR, "Failed to parse multiline var. Failed to put together two lines. OS err: %s", strerror(errno)); + free(buf); + goto out; + } + + /* + * If we need to continue reading the next line, pretend + * everything we've read up to now was in one line and call + * ourselves. + */ + if (is_multiline_var(buf)) { + char *final_val; + error = parse_multiline_variable(cfg, buf, &final_val); + free(buf); + buf = final_val; + } + + *out = buf; + + out: + free(line); + return error; +} + +static int parse_variable(file_backend *cfg, char **var_name, char **var_value) +{ + char *tmp; + int error = GIT_SUCCESS; + const char *var_end = NULL; + const char *value_start = NULL; + char *line; + + line = cfg_readline(cfg); + if (line == NULL) + return GIT_ENOMEM; + + strip_comments(line); + + var_end = strchr(line, '='); + + if (var_end == NULL) + var_end = strchr(line, '\0'); + else + value_start = var_end + 1; + + if (isspace(var_end[-1])) { + do var_end--; + while (isspace(var_end[0])); + } + + tmp = git__strndup(line, var_end - line + 1); + if (tmp == NULL) { + error = GIT_ENOMEM; + goto out; + } + + *var_name = tmp; + + /* + * Now, let's try to parse the value + */ + if (value_start != NULL) { + + while (isspace(value_start[0])) + value_start++; + + if (value_start[0] == '\0') + goto out; + + if (is_multiline_var(value_start)) { + error = parse_multiline_variable(cfg, value_start, var_value); + if (error < GIT_SUCCESS) + free(*var_name); + goto out; + } + + tmp = strdup(value_start); + if (tmp == NULL) { + free(*var_name); + error = GIT_ENOMEM; + goto out; + } + + *var_value = tmp; + } else { + /* If thre is no value, boolean true is assumed */ + *var_value = NULL; + } + + out: + free(line); + return error; +} diff --git a/src/errors.c b/src/errors.c index c3a495cc9..1fb68ee65 100644 --- a/src/errors.c +++ b/src/errors.c @@ -1,6 +1,35 @@ +/* + * 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 */ +#include <stdarg.h> + +static GIT_TLS char g_last_error[1024]; + static struct { int num; const char *str; @@ -13,7 +42,7 @@ static struct { {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_EINVALIDTYPE, "The object or config variable 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"}, @@ -46,3 +75,38 @@ const char *git_strerror(int num) return "Unknown error"; } + +int git__rethrow(int error, const char *msg, ...) +{ + 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); + + 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 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/fileops.c b/src/fileops.c index 5dd4a3806..45dd8f444 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -10,7 +10,7 @@ int gitfo_mkdir_2file(const char *file_path) error = git__dirname_r(target_folder_path, sizeof(target_folder_path), file_path); if (error < GIT_SUCCESS) - return error; + return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path); /* Does the containing folder exist? */ if (gitfo_isdir(target_folder_path)) { @@ -19,7 +19,7 @@ int gitfo_mkdir_2file(const char *file_path) /* Let's create the tree structure */ error = gitfo_mkdir_recurs(target_folder_path, mode); if (error < GIT_SUCCESS) - return error; + return error; /* The callee already takes care of setting the correct error message. */ } return GIT_SUCCESS; @@ -164,19 +164,19 @@ int gitfo_read_file(gitfo_buf *obj, const char *path) if (((size = gitfo_size(fd)) < 0) || !git__is_sizet(size+1)) { gitfo_close(fd); - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to read file `%s`. Either an error occured while calculating its size or the file is too large", path); } len = (size_t) size; if ((buff = git__malloc(len + 1)) == NULL) { gitfo_close(fd); - return GIT_ERROR; + return GIT_ENOMEM; } if (gitfo_read(fd, buff, len) < 0) { gitfo_close(fd); free(buff); - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to read file `%s`", path); } buff[len] = '\0'; @@ -197,13 +197,15 @@ void gitfo_free_buf(gitfo_buf *obj) int gitfo_mv(const char *from, const char *to) { + int error; + #ifdef GIT_WIN32 /* * Win32 POSIX compilance my ass. If the destination * file exists, the `rename` call fails. This is as * close as it gets with the Win32 API. */ - return MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR; + error = MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR; #else /* Don't even try this on Win32 */ if (!link(from, to)) { @@ -214,16 +216,21 @@ int gitfo_mv(const char *from, const char *to) if (!rename(from, to)) return GIT_SUCCESS; - return GIT_EOSERR; + error = GIT_EOSERR; #endif + + if (error < GIT_SUCCESS) + return git__throw(error, "Failed to move file from `%s`to `%s`", from, to); + + return GIT_SUCCESS; } int gitfo_mv_force(const char *from, const char *to) { if (gitfo_mkdir_2file(to) < GIT_SUCCESS) - return GIT_EOSERR; + return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */ - return gitfo_mv(from, to); + return gitfo_mv(from, to); /* The callee already takes care of setting the correct error message. */ } int gitfo_map_ro(git_map *out, git_file fd, git_off_t begin, size_t len) @@ -338,7 +345,7 @@ int gitfo_dirent( struct dirent *de; if (!wd_len || path_sz < wd_len + 2) - return GIT_ERROR; + return git__throw(GIT_EINVALIDARGS, "Failed to process `%s` tree structure. Path is either empty or buffer size is too short", path); while (path[wd_len - 1] == '/') wd_len--; @@ -347,7 +354,7 @@ int gitfo_dirent( dir = opendir(path); if (!dir) - return GIT_EOSERR; + return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path); while ((de = readdir(dir)) != NULL) { size_t de_len; @@ -364,14 +371,14 @@ int gitfo_dirent( de_len = strlen(de->d_name); if (path_sz < wd_len + de_len + 1) { closedir(dir); - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to process `%s` tree structure. Buffer size is too short", path); } strcpy(path + wd_len, de->d_name); result = fn(arg, path); if (result < GIT_SUCCESS) { closedir(dir); - return result; + return result; /* The callee is reponsible for setting the correct error message */ } if (result > 0) { closedir(dir); @@ -399,7 +406,7 @@ int retrieve_path_root_offset(const char *path) if (*(path + offset) == '/') return offset; - return GIT_ERROR; + return -1; /* Not a real error. Rather a signal than the path is not rooted */ } @@ -438,7 +445,11 @@ int gitfo_mkdir_recurs(const char *path, int mode) error = gitfo_mkdir(path, mode); free(path_copy); - return error; + + if (error < GIT_SUCCESS) + return git__throw(error, "Failed to recursively create `%s` tree structure", path); + + return GIT_SUCCESS; } static int retrieve_previous_path_component_start(const char *path) @@ -484,7 +495,7 @@ int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path) if (root_path_offset < 0) { error = gitfo_getcwd(buffer_out, size); if (error < GIT_SUCCESS) - return error; + return error; /* The callee already takes care of setting the correct error message. */ len = strlen(buffer_out); buffer_out += len; @@ -529,7 +540,7 @@ int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path) /* Are we escaping out of the root dir? */ if (len < 0) - return GIT_EINVALIDPATH; + return git__throw(GIT_EINVALIDPATH, "Failed to normalize path `%s`. The path escapes out of the root directory", path); buffer_out = (char *)buffer_out_start + len; continue; @@ -537,7 +548,7 @@ int gitfo_prettify_dir_path(char *buffer_out, size_t size, const char *path) /* Guard against potential multiple dot path traversal (cf http://cwe.mitre.org/data/definitions/33.html) */ if (only_dots && segment_len > 0) - return GIT_EINVALIDPATH; + return git__throw(GIT_EINVALIDPATH, "Failed to normalize path `%s`. The path contains a segment with three `.` or more", path); *buffer_out++ = '/'; len++; @@ -557,21 +568,21 @@ int gitfo_prettify_file_path(char *buffer_out, size_t size, const char *path) /* Let's make sure the filename isn't empty nor a dot */ if (path_len == 0 || (path_len == 1 && *path == '.')) - return GIT_EINVALIDPATH; + return git__throw(GIT_EINVALIDPATH, "Failed to normalize file path `%s`. The path is either empty or equals `.`", path); /* Let's make sure the filename doesn't end with "/", "/." or "/.." */ for (i = 1; path_len > i && i < 4; i++) { if (!strncmp(path + path_len - i, pattern, i)) - return GIT_EINVALIDPATH; + return git__throw(GIT_EINVALIDPATH, "Failed to normalize file path `%s`. The path points to a folder", path); } error = gitfo_prettify_dir_path(buffer_out, size, path); if (error < GIT_SUCCESS) - return error; + return error; /* The callee already takes care of setting the correct error message. */ path_len = strlen(buffer_out); - if (path_len < 2) - return GIT_EINVALIDPATH; + if (path_len < 2) /* TODO: Fixme. We should also take of detecting Windows rooted path (probably through usage of retrieve_path_root_offset) */ + return git__throw(GIT_EINVALIDPATH, "Failed to normalize file path `%s`. The path points to a folder", path); /* Remove the trailing slash */ buffer_out[path_len - 1] = '\0'; @@ -616,11 +627,11 @@ int gitfo_getcwd(char *buffer_out, size_t size) #ifdef GIT_WIN32 cwd_buffer = _getcwd(buffer_out, size); #else - cwd_buffer = getcwd(buffer_out, size); //TODO: Fixme. Ensure the required headers are correctly included + cwd_buffer = getcwd(buffer_out, size); #endif if (cwd_buffer == NULL) - return GIT_EOSERR; + return git__throw(GIT_EOSERR, "Failed to retrieve current working directory"); posixify_path(buffer_out); diff --git a/src/index.c b/src/index.c index 94d1c9e2a..d912d90f7 100644 --- a/src/index.c +++ b/src/index.c @@ -103,6 +103,7 @@ static git_index_tree *read_tree_internal(const char **, const char *, git_index static int read_unmerged_internal(git_index *, const char **, size_t buffer_size); static int parse_index(git_index *index, const char *buffer, size_t buffer_size); +static int is_index_extended(git_index *index); static void sort_index(git_index *index); static int write_index(git_index *index, git_filebuf *file); @@ -317,56 +318,12 @@ git_index_entry *git_index_get(git_index *index, int n) return git_vector_get(&index->entries, (unsigned int)n); } -int git_index_add(git_index *index, const char *rel_path, int stage) -{ - git_index_entry entry; - char full_path[GIT_PATH_MAX]; - struct stat st; - int error; - - if (index->repository == NULL) - return GIT_EBAREINDEX; - - git__joinpath(full_path, index->repository->path_workdir, rel_path); - - if (gitfo_exists(full_path) < 0) - return GIT_ENOTFOUND; - - if (gitfo_stat(full_path, &st) < 0) - return GIT_EOSERR; - - if (stage < 0 || stage > 3) - return GIT_ERROR; - - memset(&entry, 0x0, sizeof(git_index_entry)); - - entry.ctime.seconds = (git_time_t)st.st_ctime; - entry.mtime.seconds = (git_time_t)st.st_mtime; - /* entry.mtime.nanoseconds = st.st_mtimensec; */ - /* entry.ctime.nanoseconds = st.st_ctimensec; */ - entry.dev= st.st_rdev; - entry.ino = st.st_ino; - entry.mode = st.st_mode; - entry.uid = st.st_uid; - entry.gid = st.st_gid; - entry.file_size = st.st_size; - - /* write the blob to disk and get the oid */ - if ((error = git_blob_create_fromfile(&entry.oid, index->repository, rel_path)) < GIT_SUCCESS) - return error; - - entry.flags |= (stage << GIT_IDXENTRY_STAGESHIFT); - entry.path = (char *)rel_path; /* do not duplicate; index_insert already does this */ - - return git_index_insert(index, &entry); -} - -void sort_index(git_index *index) +static void sort_index(git_index *index) { git_vector_sort(&index->entries); } -int git_index_insert(git_index *index, const git_index_entry *source_entry) +static int index_insert(git_index *index, const git_index_entry *source_entry, int replace) { git_index_entry *entry; size_t path_length; @@ -402,13 +359,15 @@ int git_index_insert(git_index *index, const git_index_entry *source_entry) /* look if an entry with this path already exists */ position = git_index_find(index, source_entry->path); - /* if no entry exists, add the entry at the end; + /* if no entry exists and replace is not set, + * add the entry at the end; * the index is no longer sorted */ - if (position == GIT_ENOTFOUND) { + if (!replace || position == GIT_ENOTFOUND) { if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS) return GIT_ENOMEM; - /* if a previous entry exists, replace it */ + /* if a previous entry exists and replace is set, + * replace it */ } else { git_index_entry **entry_array = (git_index_entry **)index->entries.contents; @@ -421,6 +380,81 @@ int git_index_insert(git_index *index, const git_index_entry *source_entry) return GIT_SUCCESS; } +static int index_init_entry(git_index_entry *entry, git_index *index, const char *rel_path, int stage) +{ + char full_path[GIT_PATH_MAX]; + struct stat st; + int error; + + if (index->repository == NULL) + return GIT_EBAREINDEX; + + git__joinpath(full_path, index->repository->path_workdir, rel_path); + + if (gitfo_exists(full_path) < 0) + return GIT_ENOTFOUND; + + if (gitfo_stat(full_path, &st) < 0) + return GIT_EOSERR; + + if (stage < 0 || stage > 3) + return GIT_ERROR; + + memset(entry, 0x0, sizeof(git_index_entry)); + + entry->ctime.seconds = (git_time_t)st.st_ctime; + entry->mtime.seconds = (git_time_t)st.st_mtime; + /* entry.mtime.nanoseconds = st.st_mtimensec; */ + /* entry.ctime.nanoseconds = st.st_ctimensec; */ + entry->dev= st.st_rdev; + entry->ino = st.st_ino; + entry->mode = st.st_mode; + entry->uid = st.st_uid; + entry->gid = st.st_gid; + entry->file_size = st.st_size; + + /* write the blob to disk and get the oid */ + if ((error = git_blob_create_fromfile(&entry->oid, index->repository, rel_path)) < GIT_SUCCESS) + return error; + + entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT); + entry->path = (char *)rel_path; /* do not duplicate; index_insert already does this */ + return GIT_SUCCESS; +} + +int git_index_add(git_index *index, const char *path, int stage) +{ + int error; + git_index_entry entry; + + if ((error = index_init_entry(&entry, index, path, stage)) < GIT_SUCCESS) + return error; + + return index_insert(index, &entry, 1); +} + +int git_index_append(git_index *index, const char *path, int stage) +{ + int error; + git_index_entry entry; + + if ((error = index_init_entry(&entry, index, path, stage)) < GIT_SUCCESS) + return error; + + return index_insert(index, &entry, 0); +} + +int git_index_add2(git_index *index, const git_index_entry *source_entry) +{ + return index_insert(index, source_entry, 1); +} + +int git_index_append2(git_index *index, const git_index_entry *source_entry) +{ + return index_insert(index, source_entry, 0); +} + + int git_index_remove(git_index *index, int position) { assert(index); @@ -798,6 +832,24 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) return GIT_SUCCESS; } +static int is_index_extended(git_index *index) +{ + unsigned int i, extended; + + extended = 0; + + for (i = 0; i < index->entries.length; ++i) { + git_index_entry *entry; + entry = git_vector_get(&index->entries, i); + entry->flags &= ~GIT_IDXENTRY_EXTENDED; + if (entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS) { + extended++; + entry->flags |= GIT_IDXENTRY_EXTENDED; + } + } + return extended; +} + static int write_disk_entry(git_filebuf *file, git_index_entry *entry) { struct entry_short *ondisk; @@ -866,12 +918,14 @@ static int write_index(git_index *index, git_filebuf *file) struct index_header header; - int is_extended = 1; + int is_extended; assert(index && file); + is_extended = is_index_extended(index); + header.signature = htonl(INDEX_HEADER_SIG); - header.version = htonl(is_extended ? INDEX_VERSION_NUMBER : INDEX_VERSION_NUMBER_EXT); + header.version = htonl(is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER); header.entry_count = htonl(index->entries.length); git_filebuf_write(file, &header, sizeof(struct index_header)); @@ -221,7 +221,7 @@ static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend * ***********************************************************/ -int backend_sort_cmp(const void *a, const void *b) +static int backend_sort_cmp(const void *a, const void *b) { const backend_internal *backend_a = *(const backend_internal **)(a); const backend_internal *backend_b = *(const backend_internal **)(b); @@ -234,15 +234,19 @@ int backend_sort_cmp(const void *a, const void *b) int git_odb_new(git_odb **out) { + int error; + git_odb *db = git__calloc(1, sizeof(*db)); if (!db) return GIT_ENOMEM; - git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object); + error = git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object); + if (error < GIT_SUCCESS) + return error; - if (git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) { + if ((error = git_vector_init(&db->backends, 4, backend_sort_cmp)) < GIT_SUCCESS) { free(db); - return GIT_ENOMEM; + return error; } *out = db; @@ -444,7 +448,7 @@ int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git return error; *len_p = object->raw.len; - *type_p = object->raw.len; + *type_p = object->raw.type; git_odb_object_close(object); } diff --git a/src/odb_loose.c b/src/odb_loose.c index 8ee01cd2c..873dbfa0a 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -336,7 +336,6 @@ static int inflate_disk_obj(git_rawobj *out, gitfo_buf *obj) { unsigned char head[64], *buf; z_stream zs; - int z_status; obj_hdr hdr; size_t used; @@ -350,7 +349,7 @@ static int inflate_disk_obj(git_rawobj *out, gitfo_buf *obj) * inflate the initial part of the io buffer in order * to parse the object header (type and size). */ - if ((z_status = start_inflate(&zs, obj, head, sizeof(head))) < Z_OK) + if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK) return GIT_ERROR; if ((used = get_object_header(&hdr, head)) == 0) @@ -434,6 +433,9 @@ static int read_header_loose(git_rawobj *out, const char *loc) if ((read_bytes = read(fd, raw_buffer, sizeof(raw_buffer))) > 0) { set_stream_input(&zs, raw_buffer, read_bytes); z_return = inflate(&zs, 0); + } else { + z_return = Z_STREAM_END; + break; } } while (z_return == Z_OK); diff --git a/src/odb_pack.c b/src/odb_pack.c index 561b32935..57ad5e34b 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -113,7 +113,7 @@ struct pack_backend { git_vector packs; struct pack_file *last_found; char *pack_folder; - off_t pack_folder_size; + time_t pack_folder_mtime; size_t window_size; /* needs default value */ @@ -874,7 +874,7 @@ static int packfile_refresh_all(struct pack_backend *backend) if (gitfo_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode)) return GIT_ENOTFOUND; - if (st.st_size != backend->pack_folder_size) { + if (st.st_mtime != backend->pack_folder_mtime) { char path[GIT_PATH_MAX]; strcpy(path, backend->pack_folder); @@ -884,7 +884,7 @@ static int packfile_refresh_all(struct pack_backend *backend) return error; git_vector_sort(&backend->packs); - backend->pack_folder_size = st.st_size; + backend->pack_folder_mtime = st.st_mtime; } return GIT_SUCCESS; @@ -1385,6 +1385,7 @@ void pack_backend__free(git_odb_backend *_backend) } git_vector_free(&backend->packs); + free(backend->pack_folder); free(backend); } @@ -1408,7 +1409,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir) git__joinpath(path, objects_dir, "pack"); if (gitfo_isdir(path) == GIT_SUCCESS) { backend->pack_folder = git__strdup(path); - backend->pack_folder_size = -1; + backend->pack_folder_mtime = 0; if (backend->pack_folder == NULL) { free(backend); diff --git a/src/refs.c b/src/refs.c index 00b9ff6b2..c4d3d6ae6 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; } @@ -684,7 +689,7 @@ static int packed_loadloose(git_repository *repository) /* Remove any loose references from the cache */ { - const void *_unused; + const void *GIT_UNUSED(_unused); git_reference *reference; GIT_HASHTABLE_FOREACH(repository->references.loose_cache, _unused, reference, @@ -787,6 +792,8 @@ static int packed_find_peel(reference_oid *ref) */ } + git_object_close(object); + return GIT_SUCCESS; } @@ -868,7 +875,7 @@ static int packed_write(git_repository *repo) /* Load all the packfile into a vector */ { git_reference *reference; - const void *_unused; + const void *GIT_UNUSED(_unused); GIT_HASHTABLE_FOREACH(repo->references.packfile, _unused, reference, git_vector_insert(&packing_list, reference); /* cannot fail: vector already has the right size */ @@ -1480,8 +1487,9 @@ int git_reference_resolve(git_reference **resolved_ref, git_reference *ref) for (i = 0; i < MAX_NESTING_LEVEL; ++i) { reference_symbolic *ref_sym; + *resolved_ref = ref; + if (ref->type & GIT_REF_OID) { - *resolved_ref = ref; return GIT_SUCCESS; } @@ -1518,7 +1526,7 @@ int git_reference_listcb(git_repository *repo, unsigned int list_flags, int (*ca /* list all the packed references first */ if (list_flags & GIT_REF_PACKED) { const char *ref_name; - void *_unused; + void *GIT_UNUSED(_unused); if ((error = packed_load(repo)) < GIT_SUCCESS) return error; @@ -1597,7 +1605,7 @@ int git_repository__refcache_init(git_refcache *refs) void git_repository__refcache_free(git_refcache *refs) { git_reference *reference; - const void *_unused; + const void *GIT_UNUSED(_unused); assert(refs); @@ -1692,8 +1700,9 @@ static int normalize_name(char *buffer_out, const char *name, int is_oid_ref) } /* Object id refname have to contain at least one slash, except - * for HEAD in a detached state */ - if (is_oid_ref && !contains_a_slash && strcmp(name, GIT_HEAD_FILE)) + * for HEAD in a detached state or MERGE_HEAD if we're in the + * middle of a merge */ + if (is_oid_ref && !contains_a_slash && (strcmp(name, GIT_HEAD_FILE) && strcmp(name, GIT_MERGE_HEAD_FILE))) return GIT_EINVALIDREFNAME; /* A refname can not end with ".lock" */ diff --git a/src/refs.h b/src/refs.h index bebb1b97d..b8f3e2f6d 100644 --- a/src/refs.h +++ b/src/refs.h @@ -17,6 +17,7 @@ #define MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH 100 #define GIT_HEAD_FILE "HEAD" +#define GIT_MERGE_HEAD_FILE "MERGE_HEAD" #define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master" struct git_reference { diff --git a/src/repository.c b/src/repository.c index c428b00af..eab56fc9a 100644 --- a/src/repository.c +++ b/src/repository.c @@ -58,7 +58,6 @@ static int assign_repository_dirs( const char *git_work_tree) { char path_aux[GIT_PATH_MAX]; - size_t git_dir_path_len; int error = GIT_SUCCESS; assert(repo); @@ -70,8 +69,6 @@ static int assign_repository_dirs( if (error < GIT_SUCCESS) return error; - git_dir_path_len = strlen(path_aux); - /* store GIT_DIR */ repo->path_repository = git__strdup(path_aux); if (repo->path_repository == NULL) @@ -127,16 +124,16 @@ static int check_repository_dirs(git_repository *repo) char path_aux[GIT_PATH_MAX]; if (gitfo_isdir(repo->path_repository) < GIT_SUCCESS) - return GIT_ENOTAREPO; + return git__throw(GIT_ENOTAREPO, "`%s` is not a folder", repo->path_repository); /* Ensure GIT_OBJECT_DIRECTORY exists */ if (gitfo_isdir(repo->path_odb) < GIT_SUCCESS) - return GIT_ENOTAREPO; + return git__throw(GIT_ENOTAREPO, "`%s` does not exist", repo->path_odb); /* Ensure HEAD file exists */ git__joinpath(path_aux, repo->path_repository, GIT_HEAD_FILE); if (gitfo_exists(path_aux) < 0) - return GIT_ENOTAREPO; + return git__throw(GIT_ENOTAREPO, "HEAD file is missing"); return GIT_SUCCESS; } @@ -148,12 +145,12 @@ static int guess_repository_dirs(git_repository *repo, const char *repository_pa /* Git directory name */ if (git__basename_r(buffer, sizeof(buffer), repository_path) < 0) - return GIT_EINVALIDPATH; + return git__throw(GIT_EINVALIDPATH, "Unable to parse folder name from `%s`", repository_path); if (strcmp(buffer, DOT_GIT) == 0) { /* Path to working dir */ if (git__dirname_r(buffer, sizeof(buffer), repository_path) < 0) - return GIT_EINVALIDPATH; + return git__throw(GIT_EINVALIDPATH, "Unable to parse parent folder name from `%s`", repository_path); path_work_tree = buffer; } @@ -162,13 +159,19 @@ static int guess_repository_dirs(git_repository *repo, const char *repository_pa static git_repository *repository_alloc() { + int error; + git_repository *repo = git__malloc(sizeof(git_repository)); if (!repo) return NULL; memset(repo, 0x0, sizeof(git_repository)); - git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free); + error = git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free); + if (error < GIT_SUCCESS) { + free(repo); + return NULL; + } if (git_repository__refcache_init(&repo->references) < GIT_SUCCESS) { free(repo); @@ -180,7 +183,7 @@ static git_repository *repository_alloc() static int init_odb(git_repository *repo) { - return git_odb_open(&repo->db, repo->path_odb); + return git_odb_open(&repo->db, repo->path_odb); /* TODO: Move odb.c to new error handling */ } int git_repository_open3(git_repository **repo_out, @@ -195,7 +198,7 @@ int git_repository_open3(git_repository **repo_out, assert(repo_out); if (object_database == NULL) - return GIT_ERROR; + return git__throw(GIT_EINVALIDARGS, "Failed to open repository. `object_database` can't be null"); repo = repository_alloc(); if (repo == NULL) @@ -221,7 +224,7 @@ int git_repository_open3(git_repository **repo_out, cleanup: git_repository_free(repo); - return error; + return git__rethrow(error, "Failed to open repository"); } @@ -262,7 +265,7 @@ int git_repository_open2(git_repository **repo_out, cleanup: git_repository_free(repo); - return error; + return git__rethrow(error, "Failed to open repository"); } int git_repository_open(git_repository **repo_out, const char *path) @@ -293,7 +296,7 @@ int git_repository_open(git_repository **repo_out, const char *path) cleanup: git_repository_free(repo); - return error; + return git__rethrow(error, "Failed to open repository"); } void git_repository_free(git_repository *repo) @@ -312,8 +315,10 @@ void git_repository_free(git_repository *repo) if (repo->db != NULL) git_odb_close(repo->db); - if (repo->index != NULL) + if (repo->index != NULL) { + repo->index->repository = NULL; git_index_free(repo->index); + } free(repo); } @@ -325,7 +330,7 @@ int git_repository_index(git_index **index_out, git_repository *repo) assert(index_out && repo); if (repo->index == NULL) { - error = git_index_open_inrepo(&repo->index, repo); + error = git_index_open_inrepo(&repo->index, repo); /* TODO: move index.c to new error handling */ if (error < GIT_SUCCESS) return error; @@ -352,7 +357,7 @@ static int repo_init_reinit(repo_init *results) static int repo_init_createhead(git_repository *repo) { git_reference *head_reference; - return git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE); + return git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE); /* TODO: finalize moving refs.c to new error handling */ } static int repo_init_check_head_existence(char * repository_path) @@ -366,6 +371,7 @@ static int repo_init_check_head_existence(char * repository_path) static int repo_init_structure(repo_init *results) { const int mode = 0755; /* or 0777 ? */ + int error; char temp_path[GIT_PATH_MAX]; char *git_dir = results->path_repository; @@ -375,23 +381,27 @@ static int repo_init_structure(repo_init *results) /* Creates the '/objects/info/' directory */ git__joinpath(temp_path, git_dir, GIT_OBJECTS_INFO_DIR); - if (gitfo_mkdir_recurs(temp_path, mode) < GIT_SUCCESS) - return GIT_ERROR; + error = gitfo_mkdir_recurs(temp_path, mode); + if (error < GIT_SUCCESS) + return error; /* Creates the '/objects/pack/' directory */ git__joinpath(temp_path, git_dir, GIT_OBJECTS_PACK_DIR); - if (gitfo_mkdir(temp_path, mode)) - return GIT_ERROR; + error = gitfo_mkdir(temp_path, mode); + if (error < GIT_SUCCESS) + return git__throw(error, "Unable to create `%s` folder", temp_path); /* Creates the '/refs/heads/' directory */ git__joinpath(temp_path, git_dir, GIT_REFS_HEADS_DIR); - if (gitfo_mkdir_recurs(temp_path, mode)) - return GIT_ERROR; + error = gitfo_mkdir_recurs(temp_path, mode); + if (error < GIT_SUCCESS) + return error; /* Creates the '/refs/tags/' directory */ git__joinpath(temp_path, git_dir, GIT_REFS_TAGS_DIR); - if (gitfo_mkdir(temp_path, mode)) - return GIT_ERROR; + error = gitfo_mkdir(temp_path, mode); + if (error < GIT_SUCCESS) + return git__throw(error, "Unable to create `%s` folder", temp_path); /* TODO: what's left? templates? */ @@ -470,7 +480,7 @@ int git_repository_init(git_repository **repo_out, const char *path, unsigned is cleanup: free(results.path_repository); git_repository_free(repo); - return error; + return git__rethrow(error, "Failed to (re)init the repository `%s`", path); } int git_repository_is_empty(git_repository *repo) @@ -480,10 +490,10 @@ int git_repository_is_empty(git_repository *repo) error = git_reference_lookup(&head, repo, "HEAD"); if (error < GIT_SUCCESS) - return error; + return git__throw(error, "Failed to determine the emptiness of the repository. An error occured while retrieving the HEAD reference"); if (git_reference_type(head) != GIT_REF_SYMBOLIC) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to determine the emptiness of the repository. HEAD is probably in detached state"); return git_reference_resolve(&branch, head) == GIT_SUCCESS ? 0 : 1; } diff --git a/src/revwalk.c b/src/revwalk.c index b62b09961..8163851a3 100644 --- a/src/revwalk.c +++ b/src/revwalk.c @@ -210,7 +210,7 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo git_oid oid; if (git_oid_mkstr(&oid, (char *)buffer + STRLEN("parent ")) < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Parent object is corrupted"); commit->parents[i] = commit_lookup(walk, &oid); if (commit->parents[i] == NULL) @@ -222,14 +222,14 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo commit->out_degree = (unsigned short)parents; if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Object is corrupted"); buffer = memchr(buffer, '>', buffer_end - buffer); if (buffer == NULL) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Can't find author"); if (git__strtol32(&commit_time, (char *)buffer + 2, NULL, 10) < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Can't parse commit time"); commit->time = (time_t)commit_time; commit->parsed = 1; @@ -245,11 +245,11 @@ static int commit_parse(git_revwalk *walk, commit_object *commit) return GIT_SUCCESS; if ((error = git_odb_read(&obj, walk->repo->db, &commit->oid)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to parse commit. Can't read object"); if (obj->raw.type != GIT_OBJ_COMMIT) { git_odb_object_close(obj); - return GIT_EOBJTYPE; + return git__throw(GIT_EOBJTYPE, "Failed to parse commit. Object is no commit object"); } error = commit_quick_parse(walk, commit, &obj->raw); @@ -305,7 +305,7 @@ static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting) commit = commit_lookup(walk, oid); if (commit == NULL) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Failed to push commit. Object not found"); commit->uninteresting = uninteresting; @@ -483,7 +483,7 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo) void git_revwalk_free(git_revwalk *walk) { unsigned int i; - const void *_unused; + const void *GIT_UNUSED(_unused); commit_object *commit; if (walk == NULL) @@ -558,7 +558,7 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk) void git_revwalk_reset(git_revwalk *walk) { - const void *_unused; + const void *GIT_UNUSED(_unused); commit_object *commit; assert(walk); diff --git a/src/signature.c b/src/signature.c index e8014620a..62bd28b9a 100644 --- a/src/signature.c +++ b/src/signature.c @@ -115,22 +115,22 @@ static int parse_timezone_offset(const char *buffer, long *offset_out) } if (offset_start[0] != '-' && offset_start[0] != '+') - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It doesn't start with '+' or '-'"); if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It isn't a number"); if (offset_end - offset_start != 5) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Invalid length"); hours = dec_offset / 100; mins = dec_offset % 100; if (hours > 14) // see http://www.worldtimezone.com/faq.html - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Hour value too large");; if (mins > 59) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Minute value too large"); offset = (hours * 60) + mins; @@ -157,19 +157,19 @@ int git_signature__parse(git_signature *sig, const char **buffer_out, line_end = memchr(buffer, '\n', buffer_end - buffer); if (!line_end) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. No newline found");; if (buffer + (header_len + 1) > line_end) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Signature too short"); if (memcmp(buffer, header, header_len) != 0) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Expected prefix '%s' doesn't match actual", header); buffer += header_len; /* Parse name */ if ((name_end = memchr(buffer, '<', buffer_end - buffer)) == NULL) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Can't find e-mail start"); name_length = name_end - buffer - 1; sig->name = git__malloc(name_length + 1); @@ -181,11 +181,11 @@ int git_signature__parse(git_signature *sig, const char **buffer_out, buffer = name_end + 1; if (buffer >= line_end) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Ended unexpectedly"); /* Parse email */ if ((email_end = memchr(buffer, '>', buffer_end - buffer)) == NULL) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Can't find e-mail end"); email_length = email_end - buffer; sig->email = git__malloc(email_length + 1); @@ -197,10 +197,10 @@ int git_signature__parse(git_signature *sig, const char **buffer_out, buffer = email_end + 1; if (buffer >= line_end) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Ended unexpectedly"); if (git__strtol32(&time, buffer, &buffer, 10) < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Timestamp isn't a number"); sig->when.time = (time_t)time; @@ -90,13 +90,13 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer int error; if ((error = git__parse_oid(&tag->target, &buffer, buffer_end, "object ")) < 0) - return error; + return git__rethrow(error, "Failed to parse tag. Object field invalid"); if (buffer + 5 >= buffer_end) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short"); if (memcmp(buffer, "type ", 5) != 0) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Type field not found"); buffer += 5; tag->type = GIT_OBJ_BAD; @@ -105,7 +105,7 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer size_t type_length = strlen(tag_types[i]); if (buffer + type_length >= buffer_end) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short"); if (memcmp(buffer, tag_types[i], type_length) == 0) { tag->type = i; @@ -115,18 +115,19 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer } if (tag->type == GIT_OBJ_BAD) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Invalid object type"); if (buffer + 4 >= buffer_end) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short"); if (memcmp(buffer, "tag ", 4) != 0) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Tag field not found"); + buffer += 4; search = memchr(buffer, '\n', buffer_end - buffer); if (search == NULL) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short"); text_len = search - buffer; @@ -218,7 +219,7 @@ static int tag_create( } if (!git_odb_exists(repo->db, target)) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Failed to create tag. Object to tag doesn't exist"); /* Try to find out what the type is */ if (target_type == GIT_OBJ_ANY) { diff --git a/src/tree.c b/src/tree.c index 64f81d780..6f9fc8607 100644 --- a/src/tree.c +++ b/src/tree.c @@ -151,15 +151,15 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf return GIT_ENOMEM; if (git__strtol32((long *)&entry->attr, buffer, &buffer, 8) < GIT_SUCCESS) - return GIT_EOBJCORRUPTED; + return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Can't parse attributes"); if (*buffer++ != ' ') { - error = GIT_EOBJCORRUPTED; + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted"); break; } if (memchr(buffer, 0, buffer_end - buffer) == NULL) { - error = GIT_EOBJCORRUPTED; + error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted"); break; } @@ -274,7 +274,7 @@ int git_tree_create_fromindex(git_oid *oid, git_index *index) int error; if (index->repository == NULL) - return GIT_EBAREINDEX; + return git__throw(GIT_EBAREINDEX, "Failed to create tree. The index file is not backed up by an existing repository"); error = write_index(oid, index, "", 0, 0, git_index_entrycount(index)); return (error < GIT_SUCCESS) ? error : GIT_SUCCESS; @@ -343,7 +343,7 @@ int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, con assert(bld && id && filename); if (!valid_attributes(attributes)) - return GIT_ERROR; + return git__throw(GIT_ERROR, "Failed to insert entry. Invalid atrributes"); if ((pos = git_vector_bsearch2(&bld->entries, entry_search_cmp, filename)) != GIT_ENOTFOUND) { entry = git_vector_get(&bld->entries, pos); @@ -400,7 +400,7 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename) git_tree_entry *remove_ptr = (git_tree_entry *)git_treebuilder_get(bld, filename); if (remove_ptr == NULL || remove_ptr->removed) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Failed to remove entry. File isn't in the tree"); remove_ptr->removed = 1; bld->entry_count--; @@ -424,13 +424,14 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b if (entry->removed) continue; - size += (entry->attr > 0x7FF) ? 7 : 6; + snprintf(filemode, sizeof(filemode), "%o ", entry->attr); + size += strlen(filemode); size += entry->filename_len + 1; size += GIT_OID_RAWSZ; } if ((error = git_odb_open_wstream(&stream, git_repository_database(repo), size, GIT_OBJ_TREE)) < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to write tree. Can't open write stream"); for (i = 0; i < bld->entries.length; ++i) { git_tree_entry *entry = bld->entries.contents[i]; diff --git a/src/util.c b/src/util.c index 55a7ab2a9..9499ceadf 100644 --- a/src/util.c +++ b/src/util.c @@ -104,6 +104,20 @@ int git__fmt(char *buf, size_t buf_sz, const char *fmt, ...) return r; } +void git__strntolower(char *str, int len) +{ + int i; + + for (i = 0; i < len; ++i) { + str[i] = tolower(str[i]); + } +} + +void git__strtolower(char *str) +{ + git__strntolower(str, strlen(str)); +} + int git__prefixcmp(const char *str, const char *prefix) { for (;;) { diff --git a/src/util.h b/src/util.h index 3c606493f..e5a2ebe5f 100644 --- a/src/util.h +++ b/src/util.h @@ -6,16 +6,60 @@ #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(char *) git__strndup(const char *str, size_t n) +{ + size_t length; + char *ptr; + + length = strlen(str); + if (n < length) + length = n; + + ptr = malloc(length + 1); + if (!ptr) + git__throw(GIT_ENOMEM, "Out of memory. Failed to duplicate string"); + + memcpy(ptr, str, length); + ptr[length] = 0; + + 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); @@ -98,6 +142,9 @@ GIT_INLINE(int) git__is_sizet(git_off_t p) extern char *git__strtok(char *output, char *src, char *delimit); extern char *git__strtok_keep(char *output, char *src, char *delimit); +extern void git__strntolower(char *str, int len); +extern void git__strtolower(char *str); + #define STRLEN(str) (sizeof(str) - 1) #define GIT_OID_LINE_LENGTH(header) (STRLEN(header) + 1 + GIT_OID_HEXSZ + 1) diff --git a/src/vector.c b/src/vector.c index d0b0c5c56..1ddc26e3e 100644 --- a/src/vector.c +++ b/src/vector.c @@ -106,7 +106,7 @@ int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *ke /* need comparison function to sort the vector */ if (v->_cmp == NULL) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Can't sort vector. No comparison function set"); git_vector_sort(v); @@ -114,7 +114,7 @@ int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *ke if (find != NULL) return (int)(find - v->contents); - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Can't find element"); } int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key) @@ -128,7 +128,7 @@ int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key return i; } - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Can't find element"); } static int strict_comparison(const void *a, const void *b) @@ -143,9 +143,6 @@ int git_vector_search(git_vector *v, const void *entry) int git_vector_bsearch(git_vector *v, const void *key) { - if (v->_cmp == NULL) - return GIT_ENOTFOUND; - return git_vector_bsearch2(v, v->_cmp, key); } @@ -156,7 +153,7 @@ int git_vector_remove(git_vector *v, unsigned int idx) assert(v); if (idx >= v->length || v->length == 0) - return GIT_ENOTFOUND; + return git__throw(GIT_ENOTFOUND, "Can't remove element. Index out of bounds"); for (i = idx; i < v->length - 1; ++i) v->contents[i] = v->contents[i + 1]; diff --git a/src/win32/pthread.c b/src/win32/pthread.c index f47364a76..7e17b6bdf 100644 --- a/src/win32/pthread.c +++ b/src/win32/pthread.c @@ -48,16 +48,15 @@ int pthread_join(pthread_t thread, void **value_ptr) int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT mutex, const pthread_mutexattr_t *GIT_RESTRICT GIT_UNUSED(mutexattr)) { - GIT_UNUSED_ARG(mutexattr); + GIT_UNUSED_ARG(mutexattr); InitializeCriticalSection(mutex); return 0; } int pthread_mutex_destroy(pthread_mutex_t *mutex) { - int ret; - ret = CloseHandle(mutex); - return -(!ret); + DeleteCriticalSection(mutex); + return 0; } int pthread_mutex_lock(pthread_mutex_t *mutex) |