Commit 82cb8e236a07d8684c53aa8ee5b1c6195f788371

Russell Belfer 2013-07-05T10:52:24

Merge pull request #1692 from arrbee/fix-1692 Segmentation fault on git_clone

diff --git a/src/buffer.c b/src/buffer.c
index 6e3ffe5..b5b2fd6 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -259,6 +259,15 @@ void git_buf_truncate(git_buf *buf, size_t len)
 	}
 }
 
+void git_buf_shorten(git_buf *buf, size_t amount)
+{
+	if (amount > buf->size)
+		amount = buf->size;
+
+	buf->size = buf->size - amount;
+	buf->ptr[buf->size] = '\0';
+}
+
 void git_buf_rtruncate_at_char(git_buf *buf, char separator)
 {
 	ssize_t idx = git_buf_rfind_next(buf, separator);
diff --git a/src/buffer.h b/src/buffer.h
index 5402f38..f3e1d50 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -91,6 +91,7 @@ int git_buf_vprintf(git_buf *buf, const char *format, va_list ap);
 void git_buf_clear(git_buf *buf);
 void git_buf_consume(git_buf *buf, const char *end);
 void git_buf_truncate(git_buf *buf, size_t len);
+void git_buf_shorten(git_buf *buf, size_t amount);
 void git_buf_rtruncate_at_char(git_buf *path, char separator);
 
 int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
diff --git a/src/clone.c b/src/clone.c
index 5b6c6f7..5c11872 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -204,7 +204,7 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
 
 	/* Get the remote's HEAD. This is always the first ref in remote->refs. */
 	remote_head = NULL;
-	
+
 	if (!remote->transport->ls(remote->transport, get_head_callback, &remote_head))
 		return -1;
 
@@ -220,7 +220,7 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
 		memset(&dummy_spec, 0, sizeof(git_refspec));
 		head_info.refspec = &dummy_spec;
 	}
-	
+
 	/* Determine the remote tracking reference name from the local master */
 	if (git_refspec_transform_r(
 		&remote_master_name,
diff --git a/src/config_file.c b/src/config_file.c
index dec9521..2b0732a 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -1395,7 +1395,7 @@ static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int i
 	 * standard, this character **has** to be last one in the buf, with
 	 * no whitespace after it */
 	assert(is_multiline_var(value->ptr));
-	git_buf_truncate(value, git_buf_len(value) - 1);
+	git_buf_shorten(value, 1);
 
 	proc_line = fixup_line(line, in_quotes);
 	if (proc_line == NULL) {
diff --git a/src/indexer.c b/src/indexer.c
index 1b5339f..1b638cd 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -602,7 +602,7 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *
 	git_vector_sort(&idx->objects);
 
 	git_buf_sets(&filename, idx->pack->pack_name);
-	git_buf_truncate(&filename, filename.size - strlen("pack"));
+	git_buf_shorten(&filename, strlen("pack"));
 	git_buf_puts(&filename, "idx");
 	if (git_buf_oom(&filename))
 		return -1;
diff --git a/src/refspec.c b/src/refspec.c
index a907df8..492c6ed 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -225,25 +225,31 @@ int git_refspec_rtransform(char *out, size_t outlen, const git_refspec *spec, co
 	return refspec_transform_internal(out, outlen, spec->dst, spec->src, name);
 }
 
-static int refspec_transform(git_buf *out, const char *from, const char *to, const char *name)
+static int refspec_transform(
+	git_buf *out, const char *from, const char *to, const char *name)
 {
-	if (git_buf_sets(out, to) < 0)
-		return -1;
+	size_t to_len   = to   ? strlen(to)   : 0;
+	size_t from_len = from ? strlen(from) : 0;
+	size_t name_len = name ? strlen(name) : 0;
 
-	/*
-	 * No '*' at the end means that it's mapped to one specific
-	 * branch, so no actual transformation is needed.
-	 */
-	if (git_buf_len(out) > 0 && out->ptr[git_buf_len(out) - 1] != '*')
-		return 0;
+	if (git_buf_set(out, to, to_len) < 0)
+		return -1;
 
-	git_buf_truncate(out, git_buf_len(out) - 1); /* remove trailing '*' */
-	git_buf_puts(out, name + strlen(from) - 1);
+	if (to_len > 0) {
+		/* No '*' at the end of 'to' means that refspec is mapped to one
+		 * specific branch, so no actual transformation is needed.
+		 */
+		if (out->ptr[to_len - 1] != '*')
+			return 0;
+		git_buf_shorten(out, 1); /* remove trailing '*' copied from 'to' */
+	}
 
-	if (git_buf_oom(out))
-		return -1;
+	if (from_len > 0) /* ignore trailing '*' from 'from' */
+		from_len--;
+	if (from_len > name_len)
+		from_len = name_len;
 
-	return 0;
+	return git_buf_put(out, name + from_len, name_len - from_len);
 }
 
 int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name)