Subject: [PATCH] Add editor.c grouping functions for editing text files.

From: Carlos Rica <[email-masked]>
Date: Sun, 29 Jul 2007 20:21:06 +0200

The new file is grouping the previous function stripspace
from "builtin-stripspace.c", the previous launch_editor
from "builtin-tag.c", and a new function read_file, designed
to be also shared with the upcoming "builtin-commit.c".

Signed-off-by: Carlos Rica <[email-masked]>
---

   This change is committed on top of the current "next".

   It is the first proposal for the file editor.c with
   some functions intended to be shared at least between the
   recent "builtin-tag.c" and the upcoming "builtin-commit.c"
   which Kristian is working on.

   read_file is a reimplementation of the function read_path
   which was helping to Kristian in his builtin-commit.c:
   http://article.gmane.org/gmane.comp.version-control.git/52892

   launch_editor now is just the same that "builtin-tag.c" was using,
   but since it is currently different than the launch_editor from
   "builtin-commit.c", it will need more changes which should be
   discussed here. Every suggestion will be appreciated.

 Makefile             |    5 +-
 builtin-stripspace.c |   71 ---------------------
 builtin-tag.c        |   47 +--------------
 builtin.h            |    1 -
 editor.c             |  165 ++++++++++++++++++++++++++++++++++++++++++++++++++
 editor.h             |   10 +++
 6 files changed, 179 insertions(+), 120 deletions(-)
 create mode 100644 editor.c
 create mode 100644 editor.h

diff --git a/Makefile b/Makefile
index 98670bb..7417d95 100644
--- a/Makefile
+++ b/Makefile
@@ -284,7 +284,7 @@ LIB_H = \
 	run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
 	tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
 	utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
-	mailmap.h remote.h
+	mailmap.h remote.h editor.h

 DIFF_OBJS = \
 	diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -306,7 +306,8 @@ LIB_OBJS = \
 	write_or_die.o trace.o list-objects.o grep.o match-trees.o \
 	alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
 	color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
-	convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o
+	convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
+	editor.o

 BUILTIN_OBJS = \
 	builtin-add.o \
diff --git a/builtin-stripspace.c b/builtin-stripspace.c
index 916355c..7cb538a 100644
--- a/builtin-stripspace.c
+++ b/builtin-stripspace.c
@@ -1,77 +1,6 @@
 #include "builtin.h"
 #include "cache.h"

-/*
- * Returns the length of a line, without trailing spaces.
- *
- * If the line ends with newline, it will be removed too.
- */
-static size_t cleanup(char *line, size_t len)
-{
-	if (len) {
-		if (line[len - 1] == '\n')
-			len--;
-
-		while (len) {
-			unsigned char c = line[len - 1];
-			if (!isspace(c))
-				break;
-			len--;
-		}
-	}
-	return len;
-}
-
-/*
- * Remove empty lines from the beginning and end
- * and also trailing spaces from every line.
- *
- * Note that the buffer will not be NUL-terminated.
- *
- * Turn multiple consecutive empty lines between paragraphs
- * into just one empty line.
- *
- * 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.
- *
- * Enable skip_comments to skip every line starting with "#".
- */
-size_t stripspace(char *buffer, size_t length, 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;
-
-		if (skip_comments && len && buffer[i] == '#') {
-			newlen = 0;
-			continue;
-		}
-		newlen = cleanup(buffer + i, len);
-
-		/* Not just an empty line? */
-		if (newlen) {
-			if (empties != -1)
-				buffer[j++] = '\n';
-			if (empties > 0)
-				buffer[j++] = '\n';
-			empties = 0;
-			memmove(buffer + j, buffer + i, newlen);
-			continue;
-		}
-		if (empties < 0)
-			continue;
-		empties++;
-	}
-
-	return j;
-}
-
 int cmd_stripspace(int argc, const char **argv, const char *prefix)
 {
 	char *buffer;
diff --git a/builtin-tag.c b/builtin-tag.c
index d6d38ad..4957d56 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -11,58 +11,13 @@
 #include "refs.h"
 #include "tag.h"
 #include "run-command.h"
+#include "editor.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)
-{
-	const char *editor, *terminal;
-	struct child_process child;
-	const char *args[3];
-	int fd;
-
-	editor = getenv("GIT_EDITOR");
-	if (!editor && editor_program)
-		editor = editor_program;
-	if (!editor)
-		editor = getenv("VISUAL");
-	if (!editor)
-		editor = getenv("EDITOR");
-
-	terminal = getenv("TERM");
-	if (!editor && (!terminal || !strcmp(terminal, "dumb"))) {
-		fprintf(stderr,
-		"Terminal is dumb but no VISUAL nor EDITOR defined.\n"
-		"Please supply the message using either -m or -F option.\n");
-		exit(1);
-	}
-
-	if (!editor)
-		editor = "vi";
-
-	memset(&child, 0, sizeof(child));
-	child.argv = args;
-	args[0] = editor;
-	args[1] = path;
-	args[2] = NULL;
-
-	if (run_command(&child))
-		die("There was a problem with the editor %s.", editor);
-
-	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);
-		die("could not read message file '%s': %s",
-						path, strerror(errno));
-	}
-	close(fd);
-}
-
 struct tag_filter {
 	const char *pattern;
 	int lines;
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/editor.c b/editor.c
new file mode 100644
index 0000000..f0c3dc6
--- /dev/null
+++ b/editor.c
@@ -0,0 +1,165 @@
+/*
+ * Utility functions for reading and editing text files.
+ *
+ * Copyright (c) 2007 Carlos Rica <[email-masked]>
+ */
+
+#include "cache.h"
+#include "run-command.h"
+#include "editor.h"
+
+void launch_editor(const char *path, char **buffer, unsigned long *len)
+{
+	const char *editor, *terminal;
+	struct child_process child;
+	const char *args[3];
+	int fd;
+
+	editor = getenv("GIT_EDITOR");
+	if (!editor && editor_program)
+		editor = editor_program;
+	if (!editor)
+		editor = getenv("VISUAL");
+	if (!editor)
+		editor = getenv("EDITOR");
+
+	terminal = getenv("TERM");
+	if (!editor && (!terminal || !strcmp(terminal, "dumb"))) {
+		fprintf(stderr,
+		"Terminal is dumb but no VISUAL nor EDITOR defined.\n"
+		"Please supply the message using either -m or -F option.\n");
+		exit(1);
+	}
+
+	if (!editor)
+		editor = "vi";
+
+	memset(&child, 0, sizeof(child));
+	child.argv = args;
+	args[0] = editor;
+	args[1] = path;
+	args[2] = NULL;
+
+	if (run_command(&child))
+		die("There was a problem with the editor %s.", editor);
+
+	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);
+		die("could not read message file '%s': %s",
+						path, strerror(errno));
+	}
+	close(fd);
+}
+
+/*
+ * reads a file into memory, allocating a buffer to hold it and then
+ * returning it in *return_buf and setting also *return_size to save
+ * the size of its contents. Note that both variables are overwritten.
+ *
+ * If the path is "-" and the flag ALLOW_STDIN is enabled,
+ * then standard input is used to read the data.
+ * Currently, no other flags are defined.
+ *
+ * In case of failure, when -1 is returned, the allocated buffer is freed,
+ * otherwise the buffer must be freed by the caller.
+ */
+int read_file(const char *path, int flags,
+		char** return_buf, unsigned long* return_size)
+{
+	int fd;
+
+	if (flags & ALLOW_STDIN && path[0] == '-' && path[1] == '\0')
+		fd = 0;
+	else {
+		fd = open(path, O_RDONLY);
+		if (fd < 0)
+			return -1;
+	}
+
+	*return_size = 4096;
+	*return_buf = xmalloc(*return_size);
+	if (read_fd(fd, return_buf, return_size)) {
+		free(*return_buf);
+		if (fd)
+			close(fd);
+		return -1;
+	}
+	if (fd)
+		close(fd);
+
+	return 0;
+}
+
+/*
+ * Returns the length of a line, without trailing spaces.
+ *
+ * If the line ends with newline, it will be removed too.
+ */
+static size_t cleanup(char *line, size_t len)
+{
+	if (len) {
+		if (line[len - 1] == '\n')
+			len--;
+
+		while (len) {
+			unsigned char c = line[len - 1];
+			if (!isspace(c))
+				break;
+			len--;
+		}
+	}
+	return len;
+}
+
+/*
+ * Remove empty lines from the beginning and end
+ * and also trailing spaces from every line.
+ *
+ * Note that the buffer will not be NUL-terminated.
+ *
+ * Turn multiple consecutive empty lines between paragraphs
+ * into just one empty line.
+ *
+ * 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.
+ *
+ * Enable skip_comments to skip every line starting with "#".
+ */
+size_t stripspace(char *buffer, size_t length, 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;
+
+		if (skip_comments && len && buffer[i] == '#') {
+			newlen = 0;
+			continue;
+		}
+		newlen = cleanup(buffer + i, len);
+
+		/* Not just an empty line? */
+		if (newlen) {
+			if (empties != -1)
+				buffer[j++] = '\n';
+			if (empties > 0)
+				buffer[j++] = '\n';
+			empties = 0;
+			memmove(buffer + j, buffer + i, newlen);
+			continue;
+		}
+		if (empties < 0)
+			continue;
+		empties++;
+	}
+
+	return j;
+}
diff --git a/editor.h b/editor.h
new file mode 100644
index 0000000..1d3e771
--- /dev/null
+++ b/editor.h
@@ -0,0 +1,10 @@
+#ifndef EDITOR_H
+#define EDITOR_H
+
+void launch_editor(const char *path, char **buffer, unsigned long *len);
+#define ALLOW_STDIN 0x01
+int read_file(const char *path, int flags,
+		char** return_buf, unsigned long* return_size);
+size_t stripspace(char *buffer, size_t length, int skip_comments);
+
+#endif
-- 
1.5.0