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