[PATCH] config: mmap() the contents of the config file

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This makes it possible to rewrite the config without accessing the config
file twice.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@xxxxxx>

---

	Linus said he'd prefer this.

	One thing I am not quite sure about: should we remember the file's 
	mtime and check it to see if the config file has changed since we 
	mmap()ed it?

	BTW this patch is on top of next, and does not yet contain Sean's
	bug fix.

 config.c |  102 ++++++++++++++++++++++++++++++++------------------------------
 1 files changed, 52 insertions(+), 50 deletions(-)

diff --git a/config.c b/config.c
index 87fb220..05d4d8c 100644
--- a/config.c
+++ b/config.c
@@ -10,31 +10,27 @@ #include <regex.h>
 
 #define MAXNAME (256)
 
-static FILE *config_file;
+static const char *contents = NULL;
+static int config_length = 0, config_offset = 0;
 static const char *config_file_name;
 static int config_linenr;
 static int get_next_char(void)
 {
 	int c;
-	FILE *f;
 
 	c = '\n';
-	if ((f = config_file) != NULL) {
-		c = fgetc(f);
-		if (c == '\r') {
+	if (config_offset < config_length) {
+		c = contents[config_offset++];
+		if (c == '\r' && config_offset < config_length) {
 			/* DOS like systems */
-			c = fgetc(f);
+			c = contents[config_offset++];
 			if (c != '\n') {
-				ungetc(c, f);
+				config_offset--;
 				c = '\r';
 			}
 		}
 		if (c == '\n')
 			config_linenr++;
-		if (c == EOF) {
-			config_file = NULL;
-			c = '\n';
-		}
 	}
 	return c;
 }
@@ -162,7 +158,7 @@ static int git_parse_file(config_fn_t fn
 		int c = get_next_char();
 		if (c == '\n') {
 			/* EOF? */
-			if (!config_file)
+			if (config_offset >= config_length)
 				return 0;
 			comment = 0;
 			continue;
@@ -258,18 +254,41 @@ int git_default_config(const char *var, 
 
 int git_config_from_file(config_fn_t fn, const char *filename)
 {
-	int ret;
-	FILE *f = fopen(filename, "r");
+	int ret, in_fd;
+
+	config_offset = 0;
+
+	if (contents) {
+		if (!strcmp(config_file_name, filename))
+			return git_parse_file(fn);
+		munmap((char*)contents, config_length);
+		free((char*)config_file_name);
+	}
+
+	in_fd = open(filename, O_RDONLY);
 
 	ret = -1;
-	if (f) {
-		config_file = f;
-		config_file_name = filename;
+	if (in_fd > 0) {
+		struct stat st;
+
+		fstat(in_fd, &st);
+		config_length = st.st_size;
+		contents = mmap(NULL, config_length, PROT_READ, MAP_PRIVATE,
+				in_fd, 0);
+		close(in_fd);
+
+		config_file_name = strdup(filename);
 		config_linenr = 1;
+		config_offset = 0;
 		ret = git_parse_file(fn);
-		fclose(f);
-		config_file_name = NULL;
+	} else {
+		contents = NULL;
+		config_length = 0;
+		if (in_fd < 0 && ENOENT != errno )
+			die("opening %s: %s", config_file_name,
+					strerror(errno));
 	}
+
 	return ret;
 }
 
@@ -317,7 +336,7 @@ static int store_aux(const char* key, co
 				return 1;
 			}
 
-			store.offset[store.seen] = ftell(config_file);
+			store.offset[store.seen] = config_offset;
 			store.seen++;
 		}
 		break;
@@ -327,12 +346,12 @@ static int store_aux(const char* key, co
 			break;
 		} else
 			/* do not increment matches: this is no match */
-			store.offset[store.seen] = ftell(config_file);
+			store.offset[store.seen] = config_offset;
 		/* fallthru */
 	case SECTION_END_SEEN:
 	case START:
 		if (matches(key, value)) {
-			store.offset[store.seen] = ftell(config_file);
+			store.offset[store.seen] = config_offset;
 			store.state = KEY_SEEN;
 			store.seen++;
 		} else if (strrchr(key, '.') - key == store.baselen &&
@@ -367,10 +386,9 @@ static void store_write_pair(int fd, con
 	write(fd, "\n", 1);
 }
 
-static int find_beginning_of_line(const char* contents, int size,
-	int offset_, int* found_bracket)
+static int find_beginning_of_line(int offset_, int* found_bracket)
 {
-	int equal_offset = size, bracket_offset = size;
+	int equal_offset = config_length, bracket_offset = config_length;
 	int offset;
 
 	for (offset = offset_-2; offset > 0 
@@ -420,7 +438,7 @@ int git_config_set_multivar(const char* 
 	const char* value_regex, int multi_replace)
 {
 	int i;
-	int fd, in_fd;
+	int fd;
 	int ret;
 	char* config_filename = strdup(git_path("config"));
 	char* lock_file = strdup(git_path("config.lock"));
@@ -471,18 +489,9 @@ int git_config_set_multivar(const char* 
 	/*
 	 * If .git/config does not exist yet, write a minimal version.
 	 */
-	in_fd = open(config_filename, O_RDONLY);
-	if ( in_fd < 0 ) {
+	if (!contents) {
 		free(store.key);
 
-		if ( ENOENT != errno ) {
-			error("opening %s: %s", config_filename,
-			      strerror(errno));
-			close(fd);
-			unlink(lock_file);
-			ret = 3; /* same as "invalid config file" */
-			goto out_free;
-		}
 		/* if nothing to unset, error out */
 		if (value == NULL) {
 			close(fd);
@@ -494,9 +503,7 @@ int git_config_set_multivar(const char* 
 		store.key = (char*)key;
 		store_write_section(fd, key);
 		store_write_pair(fd, key, value);
-	} else{
-		struct stat st;
-		char* contents;
+	} else {
 		int i, copy_begin, copy_end, new_line = 0;
 
 		if (value_regex == NULL)
@@ -555,23 +562,17 @@ int git_config_set_multivar(const char* 
 			goto out_free;
 		}
 
-		fstat(in_fd, &st);
-		contents = mmap(NULL, st.st_size, PROT_READ,
-			MAP_PRIVATE, in_fd, 0);
-		close(in_fd);
-
 		if (store.seen == 0)
 			store.seen = 1;
 
 		for (i = 0, copy_begin = 0; i < store.seen; i++) {
 			if (store.offset[i] == 0) {
-				store.offset[i] = copy_end = st.st_size;
+				store.offset[i] = copy_end = config_length;
 			} else if (store.state != KEY_SEEN) {
 				copy_end = store.offset[i];
 			} else
 				copy_end = find_beginning_of_line(
-					contents, st.st_size,
-					store.offset[i]-2, &new_line);
+					store.offset[i] - 2, &new_line);
 
 			/* write the first part of the config */
 			if (copy_end > copy_begin) {
@@ -591,11 +592,12 @@ int git_config_set_multivar(const char* 
 		}
 
 		/* write the rest of the config */
-		if (copy_begin < st.st_size)
+		if (copy_begin < config_length)
 			write(fd, contents + copy_begin,
-				st.st_size - copy_begin);
+				config_length - copy_begin);
 
-		munmap(contents, st.st_size);
+		munmap((char*)contents, config_length);
+		contents = NULL;
 		unlink(config_filename);
 	}
 
-- 
1.3.1.g5545a

-
: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]