summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backends/hiredis.c200
-rw-r--r--src/commit.c6
-rw-r--r--src/errors.c4
-rw-r--r--src/filebuf.c2
-rw-r--r--src/index.c15
-rw-r--r--src/refs.c37
-rw-r--r--src/repository.c26
-rw-r--r--src/revwalk.c5
-rw-r--r--src/signature.c58
-rw-r--r--src/tag.c29
-rw-r--r--src/tree.c5
-rw-r--r--src/util.c79
-rw-r--r--src/util.h2
13 files changed, 429 insertions, 39 deletions
diff --git a/src/backends/hiredis.c b/src/backends/hiredis.c
new file mode 100644
index 000000000..707412bf6
--- /dev/null
+++ b/src/backends/hiredis.c
@@ -0,0 +1,200 @@
+/*
+ * 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/object.h"
+#include "hash.h"
+#include "odb.h"
+
+#include "git2/odb_backend.h"
+
+#ifdef GIT2_HIREDIS_BACKEND
+
+#include <hiredis/hiredis.h>
+
+typedef struct {
+ git_odb_backend parent;
+
+ redisContext *db;
+} hiredis_backend;
+
+int hiredis_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid) {
+ hiredis_backend *backend;
+ int error;
+ redisReply *reply;
+
+ assert(len_p && type_p && _backend && oid);
+
+ backend = (hiredis_backend *) _backend;
+ error = GIT_ERROR;
+
+ reply = redisCommand(backend->db, "HMGET %b %s %s", oid->id, GIT_OID_RAWSZ,
+ "type", "size");
+
+ if (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);
+ *len_p = (size_t) atoi(reply->element[1]->str);
+ error = GIT_SUCCESS;
+ } else {
+ error = GIT_ENOTFOUND;
+ }
+ } else {
+ error = GIT_ERROR;
+ }
+
+ freeReplyObject(reply);
+ return error;
+}
+
+int hiredis_backend__read(void **data_p, size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid) {
+ hiredis_backend *backend;
+ int error;
+ redisReply *reply;
+
+ assert(data_p && len_p && type_p && _backend && oid);
+
+ backend = (hiredis_backend *) _backend;
+ error = GIT_ERROR;
+
+ 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->element[0]->type != REDIS_REPLY_NIL &&
+ reply->element[1]->type != REDIS_REPLY_NIL &&
+ reply->element[2]->type != REDIS_REPLY_NIL) {
+ *type_p = (git_otype) atoi(reply->element[0]->str);
+ *len_p = (size_t) atoi(reply->element[1]->str);
+ *data_p = git__malloc(*len_p);
+ if (*data_p == NULL) {
+ error = GIT_ENOMEM;
+ } else {
+ memcpy(*data_p, reply->element[2]->str, *len_p);
+ error = GIT_SUCCESS;
+ }
+ } else {
+ error = GIT_ENOTFOUND;
+ }
+ } else {
+ error = GIT_ERROR;
+ }
+
+ freeReplyObject(reply);
+ return error;
+}
+
+int hiredis_backend__exists(git_odb_backend *_backend, const git_oid *oid) {
+ hiredis_backend *backend;
+ int found;
+ redisReply *reply;
+
+ assert(_backend && oid);
+
+ backend = (hiredis_backend *) _backend;
+ found = 0;
+
+ reply = redisCommand(backend->db, "exists %b", oid->id, GIT_OID_RAWSZ);
+ if (reply->type != REDIS_REPLY_NIL && reply->type != REDIS_REPLY_ERROR)
+ found = 1;
+
+
+ freeReplyObject(reply);
+ return found;
+}
+
+int hiredis_backend__write(git_oid *id, git_odb_backend *_backend, const void *data, size_t len, git_otype type) {
+ hiredis_backend *backend;
+ int error;
+ redisReply *reply;
+
+ assert(id && _backend && data);
+
+ backend = (hiredis_backend *) _backend;
+ error = GIT_ERROR;
+
+ if ((error = git_odb_hash(id, data, len, type)) < 0)
+ return error;
+
+ reply = redisCommand(backend->db, "HMSET %b "
+ "type %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;
+
+ freeReplyObject(reply);
+ return error;
+}
+
+void hiredis_backend__free(git_odb_backend *_backend) {
+ hiredis_backend *backend;
+ assert(_backend);
+ backend = (hiredis_backend *) _backend;
+
+ redisFree(backend->db);
+
+ free(backend);
+}
+
+int git_odb_backend_hiredis(git_odb_backend **backend_out, const char *host, int port) {
+ hiredis_backend *backend;
+
+ backend = git__calloc(1, sizeof (hiredis_backend));
+ if (backend == NULL)
+ return GIT_ENOMEM;
+
+
+ backend->db = redisConnect(host, port);
+ if (backend->db->err)
+ goto cleanup;
+
+ backend->parent.read = &hiredis_backend__read;
+ backend->parent.read_header = &hiredis_backend__read_header;
+ backend->parent.write = &hiredis_backend__write;
+ backend->parent.exists = &hiredis_backend__exists;
+ backend->parent.free = &hiredis_backend__free;
+
+ *backend_out = (git_odb_backend *) backend;
+
+ return GIT_SUCCESS;
+cleanup:
+ free(backend);
+ return GIT_ERROR;
+}
+
+#else
+
+int git_odb_backend_hiredis(git_odb_backend ** GIT_UNUSED(backend_out),
+ const char *GIT_UNUSED(host), int GIT_UNUSED(port)) {
+ GIT_UNUSED_ARG(backend_out);
+ GIT_UNUSED_ARG(host);
+ GIT_UNUSED_ARG(port);
+ return GIT_ENOTIMPLEMENTED;
+}
+
+
+#endif /* HAVE_HIREDIS */
diff --git a/src/commit.c b/src/commit.c
index 9fc3f0767..9621703c3 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -318,6 +318,7 @@ GIT_COMMIT_GETTER(const char *, message_short, commit->message_short)
GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
GIT_COMMIT_GETTER(unsigned int, parentcount, commit->parent_oids.length)
+GIT_COMMIT_GETTER(const git_oid *, tree_oid, &commit->tree_oid);
int git_commit_tree(git_tree **tree_out, git_commit *commit)
@@ -338,4 +339,9 @@ int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
return git_commit_lookup(parent, commit->object.repo, parent_oid);
}
+const git_oid *git_commit_parent_oid(git_commit *commit, unsigned int n)
+{
+ assert(commit);
+ return git_vector_get(&commit->parent_oids, n);
+}
diff --git a/src/errors.c b/src/errors.c
index 5e59f8205..1ef2baadb 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -29,7 +29,9 @@ static struct {
{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_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)
diff --git a/src/filebuf.c b/src/filebuf.c
index dff9373f6..eb93424ef 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -178,7 +178,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
if (flags & GIT_FILEBUF_DEFLATE_CONTENTS) {
/* Initialize the ZLib stream */
- if (deflateInit(&file->zs, Z_DEFAULT_COMPRESSION) != Z_OK) {
+ if (deflateInit(&file->zs, Z_BEST_SPEED) != Z_OK) {
error = GIT_EZLIB;
goto cleanup;
}
diff --git a/src/index.c b/src/index.c
index 6a31dd5cb..68bb9e2b9 100644
--- a/src/index.c
+++ b/src/index.c
@@ -411,6 +411,7 @@ static git_index_tree *read_tree_internal(
{
git_index_tree *tree;
const char *name_start, *buffer;
+ long count;
if ((tree = git__malloc(sizeof(git_index_tree))) == NULL)
return NULL;
@@ -429,12 +430,22 @@ static git_index_tree *read_tree_internal(
goto error_cleanup;
/* Blank-terminated ASCII decimal number of entries in this tree */
- tree->entries = strtol(buffer, (char **)&buffer, 10);
+ if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS ||
+ count < 0)
+ goto error_cleanup;
+
+ tree->entries = (size_t)count;
+
if (*buffer != ' ' || ++buffer >= buffer_end)
goto error_cleanup;
/* Number of children of the tree, newline-terminated */
- tree->children_count = strtol(buffer, (char **)&buffer, 10);
+ if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS ||
+ count < 0)
+ goto error_cleanup;
+
+ tree->children_count = (size_t)count;
+
if (*buffer != '\n' || ++buffer >= buffer_end)
goto error_cleanup;
diff --git a/src/refs.c b/src/refs.c
index 9661988cc..00b9ff6b2 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -746,7 +746,7 @@ static int packed_write_ref(reference_oid *ref, git_filebuf *file)
*/
static int packed_find_peel(reference_oid *ref)
{
- git_tag *tag;
+ git_object *object;
int error;
if (ref->ref.type & GIT_REF_HAS_PEEL)
@@ -760,25 +760,32 @@ static int packed_find_peel(reference_oid *ref)
return GIT_SUCCESS;
/*
- * Find the tag in the repository. The tag must exist,
- * otherwise this reference is broken and we shouldn't
- * pack it.
+ * Find the tagged object in the repository
*/
- error = git_tag_lookup(&tag, ref->ref.owner, &ref->oid);
+ error = git_object_lookup(&object, ref->ref.owner, &ref->oid, GIT_OBJ_ANY);
if (error < GIT_SUCCESS)
return GIT_EOBJCORRUPTED;
/*
- * Find the object pointed at by this tag
+ * If the tagged object is a Tag object, we need to resolve it;
+ * if the ref is actually a 'weak' ref, we don't need to resolve
+ * anything.
*/
- git_oid_cpy(&ref->peel_target, git_tag_target_oid(tag));
- ref->ref.type |= GIT_REF_HAS_PEEL;
+ if (git_object_type(object) == GIT_OBJ_TAG) {
+ git_tag *tag = (git_tag *)object;
- /*
- * The reference has now cached the resolved OID, and is
- * marked at such. When written to the packfile, it'll be
- * accompanied by this resolved oid
- */
+ /*
+ * Find the object pointed at by this tag
+ */
+ git_oid_cpy(&ref->peel_target, git_tag_target_oid(tag));
+ ref->ref.type |= GIT_REF_HAS_PEEL;
+
+ /*
+ * The reference has now cached the resolved OID, and is
+ * marked at such. When written to the packfile, it'll be
+ * accompanied by this resolved oid
+ */
+ }
return GIT_SUCCESS;
}
@@ -1419,6 +1426,10 @@ int git_reference_delete(git_reference *ref)
assert(ref);
if (ref->type & GIT_REF_PACKED) {
+ /* load the existing packfile */
+ if ((error = packed_load(ref->owner)) < GIT_SUCCESS)
+ return error;
+
git_hashtable_remove(ref->owner->references.packfile, ref->name);
error = packed_write(ref->owner);
} else {
diff --git a/src/repository.c b/src/repository.c
index abbbd126a..c428b00af 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -473,3 +473,29 @@ cleanup:
return error;
}
+int git_repository_is_empty(git_repository *repo)
+{
+ git_reference *head, *branch;
+ int error;
+
+ error = git_reference_lookup(&head, repo, "HEAD");
+ if (error < GIT_SUCCESS)
+ return error;
+
+ if (git_reference_type(head) != GIT_REF_SYMBOLIC)
+ return GIT_EOBJCORRUPTED;
+
+ return git_reference_resolve(&branch, head) == GIT_SUCCESS ? 0 : 1;
+}
+
+const char *git_repository_path(git_repository *repo)
+{
+ assert(repo);
+ return repo->path_repository;
+}
+
+const char *git_repository_workdir(git_repository *repo)
+{
+ assert(repo);
+ return repo->path_workdir;
+}
diff --git a/src/revwalk.c b/src/revwalk.c
index 73bb060f5..b62b09961 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -191,6 +191,7 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
unsigned char *parents_start;
int i, parents = 0;
+ long commit_time;
buffer += STRLEN("tree ") + GIT_OID_HEXSZ + 1;
@@ -227,10 +228,10 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
if (buffer == NULL)
return GIT_EOBJCORRUPTED;
- commit->time = strtol((char *)buffer + 2, NULL, 10);
- if (commit->time == 0)
+ if (git__strtol32(&commit_time, (char *)buffer + 2, NULL, 10) < GIT_SUCCESS)
return GIT_EOBJCORRUPTED;
+ commit->time = (time_t)commit_time;
commit->parsed = 1;
return GIT_SUCCESS;
}
diff --git a/src/signature.c b/src/signature.c
index 0c99755d4..e8014620a 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -65,14 +65,47 @@ git_signature *git_signature_dup(const git_signature *sig)
return git_signature_new(sig->name, sig->email, sig->when.time, sig->when.offset);
}
+git_signature *git_signature_now(const char *name, const char *email)
+{
+ time_t now;
+ time_t offset;
+ struct tm *utc_tm, *local_tm;
+
+#ifndef GIT_WIN32
+ struct tm _utc, _local;
+#endif
+
+ time(&now);
+
+ /**
+ * On Win32, `gmtime_r` doesn't exist but
+ * `gmtime` is threadsafe, so we can use that
+ */
+#ifdef GIT_WIN32
+ utc_tm = gmtime(&now);
+ local_tm = localtime(&now);
+#else
+ utc_tm = gmtime_r(&now, &_utc);
+ local_tm = localtime_r(&now, &_local);
+#endif
+
+ offset = mktime(local_tm) - mktime(utc_tm);
+ offset /= 60;
+
+ /* mktime takes care of setting tm_isdst correctly */
+ if (local_tm->tm_isdst)
+ offset += 60;
+
+ return git_signature_new(name, email, now, (int)offset);
+}
-static int parse_timezone_offset(const char *buffer, int *offset_out)
+static int parse_timezone_offset(const char *buffer, long *offset_out)
{
- int offset, dec_offset;
+ long offset, dec_offset;
int mins, hours;
- const char* offset_start;
- char* offset_end;
+ const char *offset_start;
+ const char *offset_end;
offset_start = buffer + 1;
@@ -84,7 +117,8 @@ static int parse_timezone_offset(const char *buffer, int *offset_out)
if (offset_start[0] != '-' && offset_start[0] != '+')
return GIT_EOBJCORRUPTED;
- dec_offset = strtol(offset_start + 1, &offset_end, 10);
+ if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < GIT_SUCCESS)
+ return GIT_EOBJCORRUPTED;
if (offset_end - offset_start != 5)
return GIT_EOBJCORRUPTED;
@@ -117,7 +151,7 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
int name_length, email_length;
const char *buffer = *buffer_out;
const char *line_end, *name_end, *email_end;
- int offset = 0;
+ long offset = 0, time;
memset(sig, 0x0, sizeof(git_signature));
@@ -139,6 +173,9 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
name_length = name_end - buffer - 1;
sig->name = git__malloc(name_length + 1);
+ if (sig->name == NULL)
+ return GIT_ENOMEM;
+
memcpy(sig->name, buffer, name_length);
sig->name[name_length] = 0;
buffer = name_end + 1;
@@ -152,6 +189,9 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
email_length = email_end - buffer;
sig->email = git__malloc(email_length + 1);
+ if (sig->name == NULL)
+ return GIT_ENOMEM;
+
memcpy(sig->email, buffer, email_length);
sig->email[email_length] = 0;
buffer = email_end + 1;
@@ -159,11 +199,11 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
if (buffer >= line_end)
return GIT_EOBJCORRUPTED;
- sig->when.time = strtol(buffer, (char **)&buffer, 10);
-
- if (sig->when.time == 0)
+ if (git__strtol32(&time, buffer, &buffer, 10) < GIT_SUCCESS)
return GIT_EOBJCORRUPTED;
+ sig->when.time = (time_t)time;
+
if (parse_timezone_offset(buffer, &offset) < GIT_SUCCESS)
return GIT_EOBJCORRUPTED;
diff --git a/src/tag.c b/src/tag.c
index ef58e0982..a3400957f 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -131,12 +131,17 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
text_len = search - buffer;
tag->tag_name = git__malloc(text_len + 1);
+ if (tag->tag_name == NULL)
+ return GIT_ENOMEM;
+
memcpy(tag->tag_name, buffer, text_len);
tag->tag_name[text_len] = '\0';
buffer = search + 1;
tag->tagger = git__malloc(sizeof(git_signature));
+ if (tag->tagger == NULL)
+ return GIT_ENOMEM;
if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ")) != 0) {
free(tag->tag_name);
@@ -147,6 +152,9 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
text_len = buffer_end - ++buffer;
tag->message = git__malloc(text_len + 1);
+ if (tag->message == NULL)
+ return GIT_ENOMEM;
+
memcpy(tag->message, buffer, text_len);
tag->message[text_len] = '\0';
@@ -209,6 +217,17 @@ static int tag_create(
return error;
}
+ if (!git_odb_exists(repo->db, target))
+ return GIT_ENOTFOUND;
+
+ /* Try to find out what the type is */
+ if (target_type == GIT_OBJ_ANY) {
+ size_t _unused;
+ error = git_odb_read_header(&_unused, &target_type, repo->db, target);
+ if (error < GIT_SUCCESS)
+ return error;
+ }
+
type_str = git_object_type2string(target_type);
tagger_str_len = git_signature__write(&tagger_str, "tagger", tagger);
@@ -260,7 +279,6 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
{
git_tag tag;
int error;
- git_object *obj;
assert(oid && buffer);
@@ -269,15 +287,8 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
if ((error = parse_tag_buffer(&tag, buffer, buffer + strlen(buffer))) < GIT_SUCCESS)
return error;
- error = git_object_lookup(&obj, repo, &tag.target, tag.type);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = git_tag_create_o(oid, repo, tag.tag_name, obj, tag.tagger, tag.message);
-
- git_object_close(obj);
+ error = git_tag_create(oid, repo, tag.tag_name, &tag.target, tag.type, tag.tagger, tag.message);
-cleanup:
git_signature_free(tag.tagger);
free(tag.tag_name);
free(tag.message);
diff --git a/src/tree.c b/src/tree.c
index 693ed1caa..64f81d780 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -150,7 +150,8 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf
if (git_vector_insert(&tree->entries, entry) < GIT_SUCCESS)
return GIT_ENOMEM;
- entry->attr = strtol(buffer, (char **)&buffer, 8);
+ if (git__strtol32((long *)&entry->attr, buffer, &buffer, 8) < GIT_SUCCESS)
+ return GIT_EOBJCORRUPTED;
if (*buffer++ != ' ') {
error = GIT_EOBJCORRUPTED;
@@ -364,7 +365,7 @@ int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, con
git_oid_cpy(&entry->oid, id);
entry->attr = attributes;
- if (pos != GIT_ENOTFOUND) {
+ if (pos == GIT_ENOTFOUND) {
if (git_vector_insert(&bld->entries, entry) < 0)
return GIT_ENOMEM;
}
diff --git a/src/util.c b/src/util.c
index 995daf314..55a7ab2a9 100644
--- a/src/util.c
+++ b/src/util.c
@@ -2,6 +2,7 @@
#include "common.h"
#include <stdarg.h>
#include <stdio.h>
+#include <ctype.h>
void git_strarray_free(git_strarray *array)
{
@@ -12,6 +13,84 @@ void git_strarray_free(git_strarray *array)
free(array->strings);
}
+int git__strtol32(long *result, const char *nptr, const char **endptr, int base)
+{
+ const char *p;
+ long n, nn;
+ int c, ovfl, v, neg, ndig;
+
+ p = nptr;
+ neg = 0;
+ n = 0;
+ ndig = 0;
+ ovfl = 0;
+
+ /*
+ * White space
+ */
+ while (isspace(*p))
+ p++;
+
+ /*
+ * Sign
+ */
+ if (*p == '-' || *p == '+')
+ if (*p++ == '-')
+ neg = 1;
+
+ /*
+ * Base
+ */
+ if (base == 0) {
+ if (*p != '0')
+ base = 10;
+ else {
+ base = 8;
+ if (p[1] == 'x' || p[1] == 'X') {
+ p += 2;
+ base = 16;
+ }
+ }
+ } else if (base == 16 && *p == '0') {
+ if (p[1] == 'x' || p[1] == 'X')
+ p += 2;
+ } else if (base < 0 || 36 < base)
+ goto Return;
+
+ /*
+ * Non-empty sequence of digits
+ */
+ for (;; p++,ndig++) {
+ c = *p;
+ v = base;
+ if ('0'<=c && c<='9')
+ v = c - '0';
+ else if ('a'<=c && c<='z')
+ v = c - 'a' + 10;
+ else if ('A'<=c && c<='Z')
+ v = c - 'A' + 10;
+ if (v >= base)
+ break;
+ nn = n*base + v;
+ if (nn < n)
+ ovfl = 1;
+ n = nn;
+ }
+
+Return:
+ if (ndig == 0)
+ return GIT_ENOTNUM;
+
+ if (endptr)
+ *endptr = p;
+
+ if (ovfl)
+ return GIT_EOVERFLOW;
+
+ *result = neg ? -n : n;
+ return GIT_SUCCESS;
+}
+
int git__fmt(char *buf, size_t buf_sz, const char *fmt, ...)
{
va_list va;
diff --git a/src/util.h b/src/util.h
index f477b64fd..3c606493f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -22,6 +22,8 @@ extern int git__fmt(char *, size_t, const char *, ...)
extern int git__prefixcmp(const char *str, const char *prefix);
extern int git__suffixcmp(const char *str, const char *suffix);
+extern int git__strtol32(long *n, const char *buff, const char **end_buf, int base);
+
/*
* The dirname() function shall take a pointer to a character string
* that contains a pathname, and return a pointer to a string that is a