#define MAXNAME (256)
static FILE *config_file;
+static const char *config_file_name;
static int config_linenr;
static int get_next_char(void)
{
return -1;
if (c == ']')
return baselen;
- if (!isalnum(c))
+ if (!isalnum(c) && c != '.')
return -1;
if (baselen > MAXNAME / 2)
return -1;
if (get_value(fn, var, baselen+1) < 0)
break;
}
- die("bad config file line %d", config_linenr);
+ die("bad config file line %d in %s", config_linenr, config_file_name);
}
int git_config_int(const char *name, const char *value)
if (!*end)
return val;
}
- die("bad config value for '%s'", name);
+ die("bad config value for '%s' in %s", name, config_file_name);
}
int git_config_bool(const char *name, const char *value)
return 0;
}
- if (!strcmp(var, "diff.renamelimit")) {
- diff_rename_limit_default = git_config_int(var, value);
+ if (!strcmp(var, "i18n.commitencoding")) {
+ strncpy(git_commit_encoding, value, sizeof(git_commit_encoding));
return 0;
}
return 0;
}
-int git_config(config_fn_t fn)
+int git_config_from_file(config_fn_t fn, const char *filename)
{
int ret;
- FILE *f = fopen(git_path("config"), "r");
+ FILE *f = fopen(filename, "r");
ret = -1;
if (f) {
config_file = f;
+ config_file_name = filename;
config_linenr = 1;
ret = git_parse_file(fn);
fclose(f);
+ config_file_name = NULL;
}
return ret;
}
+int git_config(config_fn_t fn)
+{
+ return git_config_from_file(fn, git_path("config"));
+}
+
/*
* Find all the stuff for git_config_set() below.
*/
+
+#define MAX_MATCHES 512
+
static struct {
int baselen;
char* key;
+ int do_not_match;
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;
+static int matches(const char* key, const char* value)
+{
+ return !strcmp(key, store.key) &&
+ (store.value_regex == NULL ||
+ (store.do_not_match ^
+ !regexec(store.value_regex, value, 0, NULL, 0)));
+}
+
static int store_aux(const char* key, const char* value)
{
switch (store.state) {
case KEY_SEEN:
- if (!strcmp(key, store.key) &&
- (store.value_regex == NULL ||
- !regexec(store.value_regex, value, 0, NULL, 0))) {
- if (store.seen == 1) {
+ if (matches(key, value)) {
+ 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;
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);
+ if (matches(key, value)) {
+ store.offset[store.seen] = ftell(config_file);
store.state = KEY_SEEN;
store.seen++;
} else if(!strncmp(key, store.key, store.baselen))
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.
*
*
*/
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;
int fd;
- char* config_file = strdup(git_path("config"));
+ char* config_filename = strdup(git_path("config"));
char* lock_file = strdup(git_path("config.lock"));
+ const char* last_dot = strrchr(key, '.');
/*
* Since "key" actually contains the section name and the real
* key name separated by a dot, we have to know where the dot is.
*/
- for (store.baselen = 0;
- key[store.baselen] != '.' && key[store.baselen];
- store.baselen++);
- if (!key[store.baselen] || !key[store.baselen+1]) {
+
+ if (last_dot == NULL) {
fprintf(stderr, "key does not contain a section: %s\n", key);
return 2;
}
+ store.baselen = last_dot - key;
+
+ store.multi_replace = multi_replace;
/*
* Validate the key and while at it, lower case it for matching.
*/
store.key = (char*)malloc(strlen(key)+1);
for (i = 0; key[i]; i++)
- if (i != store.baselen && (!isalnum(key[i]) ||
- (i == store.baselen+1 && !isalpha(key[i])))) {
+ if (i != store.baselen &&
+ ((!isalnum(key[i]) && key[i] != '.') ||
+ (i == store.baselen+1 && !isalpha(key[i])))) {
fprintf(stderr, "invalid key: %s\n", key);
free(store.key);
return 1;
} else
store.key[i] = tolower(key[i]);
+ store.key[i] = 0;
/*
* The lock_file serves a purpose in addition to locking: the new
/*
* If .git/config does not exist yet, write a minimal version.
*/
- if (stat(config_file, &st)) {
+ if (stat(config_filename, &st)) {
static const char contents[] =
"#\n"
"# This is the config file\n"
} 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;
else {
+ if (value_regex[0] == '!') {
+ store.do_not_match = 1;
+ value_regex++;
+ } else
+ store.do_not_match = 0;
+
store.value_regex = (regex_t*)malloc(sizeof(regex_t));
if (regcomp(store.value_regex, value_regex,
REG_EXTENDED)) {
}
}
- store.offset = 0;
+ store.offset[0] = 0;
store.state = START;
store.seen = 0;
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);
+ in_fd = open(config_filename, 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)
}
/* 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);
+ unlink(config_filename);
}
close(fd);
- if (rename(lock_file, config_file) < 0) {
+ if (rename(lock_file, config_filename) < 0) {
fprintf(stderr, "Could not rename the lock file?\n");
return 4;
}
return 0;
}
+