git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 6/9] Rewrite launch_editor, create_tag and stripspace to use strbufs.

From
Kristian Høgsberg <krh@redhat.com>
Date
Sep 6, 2007, 00:23 UTC
Message-ID
<1189038225525-git-send-email-krh@redhat.com>
In-Reply-To
<11890382252522-git-send-email-krh@redhat.com>
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
---
 builtin-stripspace.c |   45 +++++++++++++-------------
 builtin-tag.c        |   84 +++++++++++++++++++++-----------------------------
 builtin.h            |    1 -
 strbuf.c             |   43 +++++---------------------
 strbuf.h             |    2 +
 5 files changed, 68 insertions(+), 107 deletions(-)
diff --git a/builtin-stripspace.c b/builtin-stripspace.c
index 916355c..f0264a8 100644
--- a/builtin-stripspace.c
+++ b/builtin-stripspace.c
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "strbuf.h"
 
 /*
  * Returns the length of a line, without trailing spaces.
@@ -34,34 +35,34 @@ static size_t cleanup(char *line, size_t len)
  * If the input has only empty lines and spaces,
  * no output will be produced.
  *
- * If last line has a newline at the end, it will be removed.
+ * If the last line does not have a newline, one will be added.
  *
  * Enable skip_comments to skip every line starting with "#".
  */
-size_t stripspace(char *buffer, size_t length, int skip_comments)
+size_t stripspace(struct strbuf *sb, int skip_comments)
 {
 	int empties = -1;
 	size_t i, j, len, newlen;
 	char *eol;
 
-	for (i = j = 0; i < length; i += len, j += newlen) {
-		eol = memchr(buffer + i, '\n', length - i);
-		len = eol ? eol - (buffer + i) + 1 : length - i;
+	for (i = j = 0; i < sb->len; i += len, j += newlen) {
+		eol = memchr(sb->buf + i, '\n', sb->len - i);
+		len = eol ? eol - (sb->buf + i) + 1 : sb->len - i;
 
-		if (skip_comments && len && buffer[i] == '#') {
+		if (skip_comments && len && sb->buf[i] == '#') {
 			newlen = 0;
 			continue;
 		}
-		newlen = cleanup(buffer + i, len);
+		newlen = cleanup(sb->buf + i, len);
 
 		/* Not just an empty line? */
 		if (newlen) {
 			if (empties != -1)
-				buffer[j++] = '\n';
+				sb->buf[j++] = '\n';
 			if (empties > 0)
-				buffer[j++] = '\n';
+				sb->buf[j++] = '\n';
 			empties = 0;
-			memmove(buffer + j, buffer + i, newlen);
+			memmove(sb->buf + j, sb->buf + i, newlen);
 			continue;
 		}
 		if (empties < 0)
@@ -69,31 +70,31 @@ size_t stripspace(char *buffer, size_t length, int skip_comments)
 		empties++;
 	}
 
-	return j;
+	sb->len = j;
+	if (j > 0)
+		strbuf_add_char(sb, '\n');
+
+	return sb->len;
 }
 
 int cmd_stripspace(int argc, const char **argv, const char *prefix)
 {
-	char *buffer;
-	unsigned long size;
+	struct strbuf sb;
 	int strip_comments = 0;
 
 	if (argc > 1 && (!strcmp(argv[1], "-s") ||
 				!strcmp(argv[1], "--strip-comments")))
 		strip_comments = 1;
 
-	size = 1024;
-	buffer = xmalloc(size);
-	if (read_fd(0, &buffer, &size)) {
-		free(buffer);
+	strbuf_init(&sb);
+	if (strbuf_read_fd(&sb, 0) < 0) {
+		strbuf_release(&sb);
 		die("could not read the input");
 	}
 
-	size = stripspace(buffer, size, strip_comments);
-	write_or_die(1, buffer, size);
-	if (size)
-		putc('\n', stdout);
+	stripspace(&sb, strip_comments);
+	write_or_die(1, sb.buf, sb.len);
+	strbuf_release(&sb);
 
-	free(buffer);
 	return 0;
 }
diff --git a/builtin-tag.c b/builtin-tag.c
index d6d38ad..1aff952 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -11,13 +11,14 @@
 #include "refs.h"
 #include "tag.h"
 #include "run-command.h"
+#include "strbuf.h"
 
 static const char builtin_tag_usage[] =
   "git-tag [-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg> | -F <file>] <tagname> [<head>]";
 
 static char signingkey[1000];
 
-static void launch_editor(const char *path, char **buffer, unsigned long *len)
+static void launch_editor(const char *path, struct strbuf *sb)
 {
 	const char *editor, *terminal;
 	struct child_process child;
@@ -55,10 +56,9 @@ static void launch_editor(const char *path, char **buffer, unsigned long *len)
 	fd = open(path, O_RDONLY);
 	if (fd < 0)
 		die("could not open '%s': %s", path, strerror(errno));
-	if (read_fd(fd, buffer, len)) {
-		free(*buffer);
+	if (strbuf_read_fd(sb, fd) < 0) {
 		die("could not read message file '%s': %s",
-						path, strerror(errno));
+		    path, strerror(errno));
 	}
 	close(fd);
 }
@@ -191,7 +191,7 @@ static int verify_tag(const char *name, const char *ref,
 	return 0;
 }
 
-static ssize_t do_sign(char *buffer, size_t size, size_t max)
+static int do_sign(struct strbuf *sb)
 {
 	struct child_process gpg;
 	const char *args[4];
@@ -219,17 +219,13 @@ static ssize_t do_sign(char *buffer, size_t size, size_t max)
 	if (start_command(&gpg))
 		return error("could not run gpg.");
 
-	write_or_die(gpg.in, buffer, size);
+	write_or_die(gpg.in, sb->buf, sb->len);
 	close(gpg.in);
 	gpg.close_in = 0;
-	len = read_in_full(gpg.out, buffer + size, max - size);
-
+	len = strbuf_read_fd(sb, gpg.out);
 	finish_command(&gpg);
 
-	if (len == max - size)
-		return error("could not read the entire signature from gpg.");
-
-	return size + len;
+	return len;
 }
 
 static const char tag_template[] =
@@ -258,26 +254,24 @@ static void create_tag(const unsigned char *object, const char *tag,
 		       char *message, int sign, unsigned char *result)
 {
 	enum object_type type;
-	char header_buf[1024], *buffer = NULL;
-	int header_len, max_size;
-	unsigned long size = 0;
+	struct strbuf sb;
+	int header_len;
 
 	type = sha1_object_info(object, NULL);
 	if (type <= OBJ_NONE)
 	    die("bad object type.");
 
-	header_len = snprintf(header_buf, sizeof(header_buf),
-			  "object %s\n"
-			  "type %s\n"
-			  "tag %s\n"
-			  "tagger %s\n\n",
-			  sha1_to_hex(object),
-			  typename(type),
-			  tag,
-			  git_committer_info(1));
-
-	if (header_len > sizeof(header_buf) - 1)
-		die("tag header too big.");
+	strbuf_init(&sb);
+	strbuf_printf(&sb,
+		      "object %s\n"
+		      "type %s\n"
+		      "tag %s\n"
+		      "tagger %s\n\n",
+		      sha1_to_hex(object),
+		      typename(type),
+		      tag,
+		      git_committer_info(1));
+	header_len = sb.len;
 
 	if (!message) {
 		char *path;
@@ -292,39 +286,31 @@ static void create_tag(const unsigned char *object, const char *tag,
 		write_or_die(fd, tag_template, strlen(tag_template));
 		close(fd);
 
-		launch_editor(path, &buffer, &size);
+		launch_editor(path, &sb);
 
 		unlink(path);
 		free(path);
 	}
 	else {
-		buffer = message;
-		size = strlen(message);
+		strbuf_add(&sb, message, strlen(message));
 	}
 
-	size = stripspace(buffer, size, 1);
-
-	if (!message && !size)
+	if (stripspace(&sb, 1) <= header_len && !message)
 		die("no tag message?");
 
-	/* insert the header and add the '\n' if needed: */
-	max_size = header_len + size + (sign ? MAX_SIGNATURE_LENGTH : 0) + 1;
-	buffer = xrealloc(buffer, max_size);
-	if (size)
-		buffer[size++] = '\n';
-	memmove(buffer + header_len, buffer, size);
-	memcpy(buffer, header_buf, header_len);
-	size += header_len;
-
-	if (sign) {
-		size = do_sign(buffer, size, max_size);
-		if (size < 0)
-			die("unable to sign the tag");
-	}
+	/* Debatable, but makes t7401-tag.sh pass.  Creates an extra
+	 * blank line after the header block for tags with empty
+	 * messages. */
+	if (sb.len < header_len)
+		strbuf_add_char(&sb, '\n');
 
-	if (write_sha1_file(buffer, size, tag_type, result) < 0)
+	if (sign && do_sign(&sb) < 0)
+		die("unable to sign the tag");
+
+	if (write_sha1_file(sb.buf, sb.len, tag_type, result) < 0)
 		die("unable to write tag file");
-	free(buffer);
+
+	strbuf_release(&sb);
 }
 
 int cmd_tag(int argc, const char **argv, const char *prefix)
diff --git a/builtin.h b/builtin.h
index bb72000..91bc595 100644
--- a/builtin.h
+++ b/builtin.h
@@ -7,7 +7,6 @@ extern const char git_version_string[];
 extern const char git_usage_string[];
 
 extern void help_unknown_cmd(const char *cmd);
-extern size_t stripspace(char *buffer, size_t length, int skip_comments);
 extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
 extern void prune_packed_objects(int);
 
diff --git a/strbuf.c b/strbuf.c
index fcfc05e..ed2afea 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -6,7 +6,8 @@ void strbuf_init(struct strbuf *sb) {
 	sb->eof = sb->alloc = sb->len = 0;
 }
 
-static void strbuf_begin(struct strbuf *sb) {
+void strbuf_release(struct strbuf *sb)
+{
 	free(sb->buf);
 	strbuf_init(sb);
 }
@@ -35,7 +36,7 @@ static void strbuf_end(struct strbuf *sb) {
 
 void read_line(struct strbuf *sb, FILE *fp, int term) {
 	int ch;
-	strbuf_begin(sb);
+	strbuf_release(sb);
 	if (feof(fp)) {
 		sb->eof = 1;
 		return;
@@ -73,43 +74,15 @@ void strbuf_printf(struct strbuf *sb, const char *fmt, ...)
 {
 	char buffer[2048];
 	va_list args;
-	int len, size = 2 * sizeof buffer;
+	int len;
 
 	va_start(args, fmt);
 	len = vsnprintf(buffer, sizeof(buffer), fmt, args);
 	va_end(args);
 
-	if (len > sizeof(buffer)) {
-		/*
-		 * Didn't fit in the buffer, but this vsnprintf at
-		 * least gives us the required length back.  Grow the
-		 * buffer acccordingly and try again.
-		 */
-		strbuf_grow(sb, len);
-		va_start(args, fmt);
-		len = vsnprintf(sb->buf + sb->len,
-				sb->alloc - sb->len, fmt, args);
-		va_end(args);
-	} else if (len >= 0) {
-		/*
-		 * The initial vsnprintf fit in the temp buffer, just
-		 * copy it to the strbuf.
-		 */
-		strbuf_add(sb, buffer, len);
-	} else {
-		/*
-		 * This vnsprintf sucks and just returns -1 when the
-		 * buffer is too small.  Keep doubling the size until
-		 * it fits.
-		 */
-		while (len < 0) {
-			strbuf_grow(sb, size);
-			va_start(args, fmt);
-			len = vsnprintf(sb->buf + sb->len,
-					sb->alloc - sb->len, fmt, args);
-			va_end(args);
-			size *= 2;
-		}
-	}
+	if (len > sizeof(buffer) || len < 0)
+		die("out of buffer space\n");
+
+	strbuf_add(sb, buffer, len);
 }
 
diff --git a/strbuf.h b/strbuf.h
index 6e630ea..a93b9e1 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -8,10 +8,12 @@ struct strbuf {
 };
 
 extern void strbuf_init(struct strbuf *);
+extern void strbuf_release(struct strbuf *);
 extern void read_line(struct strbuf *, FILE *, int);
 extern void strbuf_add(struct strbuf *sb, const char *data, size_t len);
 extern void strbuf_add_char(struct strbuf *sb, int ch);
 extern void strbuf_printf(struct strbuf *sb, const char *fmt, ...);
 extern int strbuf_read_fd(struct strbuf *sb, int fd);
+extern size_t stripspace(struct strbuf *sb, int skip_comments);
 
 #endif /* STRBUF_H */
-- 
1.5.2.GIT
Previous: Kristian HøgsbergNext: Kristian Høgsberg
Message 6 of 25 in “Enable wt-status output to a given FILE pointer.”
  1. 1/9 Enable wt-status output to a given FILE pointer.Kristian Høgsberg, Sep 6, 2007
  2. 2/9 Enable wt-status to run against non-standard index file.Kristian Høgsberg, Sep 6, 2007
  3. 3/9 Add strbuf_printf() to do formatted printing to a strbuf.Kristian Høgsberg, Sep 6, 2007
  4. 4/9 Introduce entry point for launching add--interactive.Kristian Høgsberg, Sep 6, 2007
  5. 5/9 Introduce strbuf_read_fd().Kristian Høgsberg, Sep 6, 2007
  6. 6/9 Rewrite launch_editor, create_tag and stripspace to use strbufs.Kristian Høgsberg, Sep 6, 2007
  7. 7/9 Add strbuf_read_path().Kristian Høgsberg, Sep 6, 2007
  8. 8/9 Export rerere() and launch_editor().Kristian Høgsberg, Sep 6, 2007
  9. 9/9 Implement git commit as a builtin command.Kristian Høgsberg, Sep 6, 2007
  10. Johannes SchindelinSep 6, 2007
  11. Kristian HøgsbergSep 17, 2007
  12. Johannes SchindelinSep 17, 2007
  13. Jeff KingSep 17, 2007
  14. Kristian HøgsbergSep 18, 2007
  15. Johannes SchindelinSep 6, 2007
  16. Johannes SchindelinSep 6, 2007
  17. Kristian HøgsbergSep 17, 2007
  18. Johannes SchindelinSep 6, 2007
  19. Kristian HøgsbergSep 17, 2007
  20. Johannes SchindelinSep 17, 2007
  21. Junio C HamanoSep 6, 2007
  22. Pierre HabouzitSep 6, 2007
  23. Pierre HabouzitSep 6, 2007
  24. Johannes SchindelinSep 6, 2007
  25. Kristian HøgsbergSep 17, 2007

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.