aboutsummaryrefslogtreecommitdiff
path: root/config.c
diff options
context:
space:
mode:
Diffstat (limited to 'config.c')
-rw-r--r--config.c116
1 files changed, 71 insertions, 45 deletions
diff --git a/config.c b/config.c
index bbcafff29..697d79f53 100644
--- a/config.c
+++ b/config.c
@@ -263,11 +263,15 @@ int git_config(config_fn_t fn)
/*
* Find all the stuff for git_config_set() below.
*/
+
+#define MAX_MATCHES 512
+
static struct {
int baselen;
char* key;
regex_t* value_regex;
- off_t offset;
+ int multi_replace;
+ off_t offset[MAX_MATCHES];
enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;
int seen;
} store;
@@ -279,12 +283,16 @@ static int store_aux(const char* key, const char* value)
if (!strcmp(key, store.key) &&
(store.value_regex == NULL ||
!regexec(store.value_regex, value, 0, NULL, 0))) {
- if (store.seen == 1) {
+ if (store.seen == 1 && store.multi_replace == 0) {
fprintf(stderr,
"Warning: %s has multiple values\n",
key);
+ } else if (store.seen >= MAX_MATCHES) {
+ fprintf(stderr, "Too many matches\n");
+ return 1;
}
- store.offset = ftell(config_file);
+
+ store.offset[store.seen] = ftell(config_file);
store.seen++;
}
break;
@@ -293,14 +301,15 @@ static int store_aux(const char* key, const char* value)
store.state = SECTION_END_SEEN;
break;
} else
- store.offset = ftell(config_file);
+ /* do not increment matches: this is no match */
+ store.offset[store.seen] = ftell(config_file);
/* fallthru */
case SECTION_END_SEEN:
case START:
if (!strcmp(key, store.key) &&
(store.value_regex == NULL ||
!regexec(store.value_regex, value, 0, NULL, 0))) {
- store.offset = ftell(config_file);
+ store.offset[store.seen] = ftell(config_file);
store.state = KEY_SEEN;
store.seen++;
} else if(!strncmp(key, store.key, store.baselen))
@@ -334,14 +343,38 @@ static void store_write_pair(int fd, const char* key, const char* value)
write(fd, "\n", 1);
}
+static int find_beginning_of_line(const char* contents, int size,
+ int offset_, int* found_bracket)
+{
+ int equal_offset = size, bracket_offset = size;
+ int offset;
+
+ for (offset = offset_-2; offset > 0
+ && contents[offset] != '\n'; offset--)
+ switch (contents[offset]) {
+ case '=': equal_offset = offset; break;
+ case ']': bracket_offset = offset; break;
+ }
+ if (bracket_offset < equal_offset) {
+ *found_bracket = 1;
+ offset = bracket_offset+1;
+ } else
+ offset++;
+
+ return offset;
+}
+
int git_config_set(const char* key, const char* value)
{
- return git_config_set_multivar(key, value, NULL);
+ return git_config_set_multivar(key, value, NULL, 0);
}
/*
* If value==NULL, unset in (remove from) config,
* if value_regex!=NULL, disregard key/value pairs where value does not match.
+ * if multi_replace==0, nothing, or only one matching key/value is replaced,
+ * else all matching key/values (regardless how many) are removed,
+ * before the new pair is written.
*
* Returns 0 on success.
*
@@ -360,7 +393,7 @@ int git_config_set(const char* key, const char* value)
*
*/
int git_config_set_multivar(const char* key, const char* value,
- const char* value_regex)
+ const char* value_regex, int multi_replace)
{
int i;
struct stat st;
@@ -368,6 +401,8 @@ int git_config_set_multivar(const char* key, const char* value,
char* config_file = strdup(git_path("config"));
char* lock_file = strdup(git_path("config.lock"));
+ store.multi_replace = multi_replace;
+
/*
* Since "key" actually contains the section name and the real
* key name separated by a dot, we have to know where the dot is.
@@ -431,7 +466,7 @@ int git_config_set_multivar(const char* key, const char* value,
} else{
int in_fd;
char* contents;
- int offset, new_line = 0;
+ int i, copy_begin, copy_end, new_line = 0;
if (value_regex == NULL)
store.value_regex = NULL;
@@ -446,7 +481,7 @@ int git_config_set_multivar(const char* key, const char* value,
}
}
- store.offset = 0;
+ store.offset[0] = 0;
store.state = START;
store.seen = 0;
@@ -472,52 +507,42 @@ int git_config_set_multivar(const char* key, const char* value,
free(store.value_regex);
}
- /* if nothing to unset, error out */
- if (store.seen == 0 && value == NULL) {
+ /* if nothing to unset, or too many matches, error out */
+ if ((store.seen == 0 && value == NULL) ||
+ (store.seen > 1 && multi_replace == 0)) {
close(fd);
unlink(lock_file);
return 5;
}
- store.key = (char*)key;
-
in_fd = open(config_file, O_RDONLY, 0666);
contents = mmap(NULL, st.st_size, PROT_READ,
MAP_PRIVATE, in_fd, 0);
close(in_fd);
- if (store.offset == 0) {
- store.offset = offset = st.st_size;
- } else if (store.state != KEY_SEEN) {
- offset = store.offset;
- } else {
- int equal_offset = st.st_size,
- bracket_offset = st.st_size;
-
- if (value == NULL && store.seen > 1) {
- fprintf(stderr, "Cannot remove multivar (%s has %d values\n", key, store.seen);
- close(fd);
- unlink(lock_file);
- return 7;
- }
- for (offset = store.offset-2; offset > 0
- && contents[offset] != '\n'; offset--)
- switch (contents[offset]) {
- case '=': equal_offset = offset; break;
- case ']': bracket_offset = offset; break;
- }
- if (bracket_offset < equal_offset) {
- new_line = 1;
- offset = bracket_offset+1;
+ 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;
+ } else if (store.state != KEY_SEEN) {
+ copy_end = store.offset[i];
} else
- offset++;
+ copy_end = find_beginning_of_line(
+ contents, st.st_size,
+ store.offset[i]-2, &new_line);
+
+ /* write the first part of the config */
+ if (copy_end > copy_begin) {
+ write(fd, contents + copy_begin,
+ copy_end - copy_begin);
+ if (new_line)
+ write(fd, "\n", 1);
+ }
+ copy_begin = store.offset[i];
}
- /* write the first part of the config */
- write(fd, contents, offset);
- if (new_line)
- write(fd, "\n", 1);
-
/* write the pair (value == NULL means unset) */
if (value != NULL) {
if (store.state == START)
@@ -526,9 +551,9 @@ int git_config_set_multivar(const char* key, const char* value,
}
/* write the rest of the config */
- if (store.offset < st.st_size)
- write(fd, contents + store.offset,
- st.st_size - store.offset);
+ if (copy_begin < st.st_size)
+ write(fd, contents + copy_begin,
+ st.st_size - copy_begin);
munmap(contents, st.st_size);
unlink(config_file);
@@ -544,3 +569,4 @@ int git_config_set_multivar(const char* key, const char* value,
return 0;
}
+