/* * 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. * * This function does this: * * - it locks the config file by creating ".git/config.lock" * * - it then parses the config using store_aux() as validator to find * the position on the key/value pair to replace. If it is to be unset, * it must be found exactly once. * * - the config file is mmap()ed and the part before the match (if any) is * written to the lock file, then the changed part and the rest. * * - the config file is removed and the lock file rename()d to it. * */ int git_config_set_multivar(const char* key, const char* value, const char* value_regex, int multi_replace) { int i, dot; int fd = -1, in_fd; int ret; char* config_filename; struct lock_file *lock = NULL; const char* last_dot = strrchr(key, '.'); if (config_exclusive_filename) config_filename = xstrdup(config_exclusive_filename); else config_filename = git_pathdup("config"); /* * Since "key" actually contains the section name and the real * key name separated by a dot, we have to know where the dot is. */ if (last_dot == NULL) { error("key does not contain a section: %s", key); ret = 2; goto out_free; } store.baselen = last_dot - key; store.multi_replace = multi_replace; /* * Validate the key and while at it, lower case it for matching. */ store.key = xmalloc(strlen(key) + 1); dot = 0; for (i = 0; key[i]; i++) { unsigned char c = key[i]; if (c == '.') dot = 1; /* Leave the extended basename untouched.. */ if (!dot || i > store.baselen) { if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) { error("invalid key: %s", key); free(store.key); ret = 1; goto out_free; } c = tolower(c); } else if (c == '\n') { error("invalid key (newline): %s", key); free(store.key); ret = 1; goto out_free; } store.key[i] = c; } store.key[i] = 0; /* * The lock serves a purpose in addition to locking: the new * contents of .git/config will be written into it. */ lock = xcalloc(sizeof(struct lock_file), 1); fd = hold_lock_file_for_update(lock, config_filename, 0); if (fd < 0) { error("could not lock config file %s", config_filename); free(store.key); ret = -1; goto out_free; } /* * If .git/config does not exist yet, write a minimal version. */ in_fd = open(config_filename, O_RDONLY); if ( in_fd < 0 ) { free(store.key); if ( ENOENT != errno ) { error("opening %s: %s", config_filename, strerror(errno)); ret = 3; /* same as "invalid config file" */ goto out_free; } /* if nothing to unset, error out */ if (value == NULL) { ret = 5; goto out_free; } store.key = (char*)key; if (!store_write_section(fd, key) || !store_write_pair(fd, key, value)) goto write_err_out; } else { struct stat st; char* contents; size_t contents_sz, copy_begin, copy_end; int i, 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*)xmalloc(sizeof(regex_t)); if (regcomp(store.value_regex, value_regex, REG_EXTENDED)) { error("invalid pattern: %s", value_regex); free(store.value_regex); ret = 6; goto out_free; } } store.offset[0] = 0; store.state = START; store.seen = 0; /* * After this, store.offset will contain the *end* offset * of the last match, or remain at 0 if no match was found. * As a side effect, we make sure to transform only a valid * existing config file. */ if (git_config_from_file(store_aux, config_filename, NULL)) { error("invalid config file %s", config_filename); free(store.key); if (store.value_regex != NULL) { regfree(store.value_regex); free(store.value_regex); } ret = 3; goto out_free; } free(store.key); if (store.value_regex != NULL) { regfree(store.value_regex); free(store.value_regex); } /* if nothing to unset, or too many matches, error out */ if ((store.seen == 0 && value == NULL) || (store.seen > 1 && multi_replace == 0)) { ret = 5; goto out_free; } fstat(in_fd, &st); contents_sz = xsize_t(st.st_size); contents = xmmap(NULL, contents_sz, 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 = contents_sz; } else if (store.state != KEY_SEEN) { copy_end = store.offset[i]; } else copy_end = find_beginning_of_line( contents, contents_sz, store.offset[i]-2, &new_line); if (copy_end > 0 && contents[copy_end-1] != '\n') new_line = 1; /* write the first part of the config */ if (copy_end > copy_begin) { if (write_in_full(fd, contents + copy_begin, copy_end - copy_begin) < copy_end - copy_begin) goto write_err_out; if (new_line && write_in_full(fd, "\n", 1) != 1) goto write_err_out; } copy_begin = store.offset[i]; } /* write the pair (value == NULL means unset) */ if (value != NULL) { if (store.state == START) { if (!store_write_section(fd, key)) goto write_err_out; } if (!store_write_pair(fd, key, value)) goto write_err_out; } /* write the rest of the config */ if (copy_begin < contents_sz) if (write_in_full(fd, contents + copy_begin, contents_sz - copy_begin) < contents_sz - copy_begin) goto write_err_out; munmap(contents, contents_sz); } if (commit_lock_file(lock) < 0) { error("could not commit config file %s", config_filename); ret = 4; goto out_free; } /* * lock is committed, so don't try to roll it back below. * NOTE: Since lockfile.c keeps a linked list of all created * lock_file structures, it isn't safe to free(lock). It's * better to just leave it hanging around. */ lock = NULL; ret = 0; out_free: if (lock) rollback_lock_file(lock); free(config_filename); return ret; write_err_out: ret = write_error(lock->filename); goto out_free; }
/* * 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. * * This function does this: * * - it locks the config file by creating ".git/config.lock" * * - it then parses the config using store_aux() as validator to find * the position on the key/value pair to replace. If it is to be unset, * it must be found exactly once. * * - the config file is mmap()ed and the part before the match (if any) is * written to the lock file, then the changed part and the rest. * * - the config file is removed and the lock file rename()d to it. * */ int fg_config_set_multivar(const char *key, const char *value, const char *value_regex, int multi_replace) { int fd = -1, in_fd; int ret; char *config_filename; struct lockfile *lock = NULL; if (config_exclusive_filename) config_filename = xstrdup(config_exclusive_filename); else return -1; /* parse-key returns negative; flip the sign to feed exit(3) */ ret = 0 - fg_config_parse_key(key, &store.key, &store.baselen); if (ret) goto out_free; store.multi_replace = multi_replace; /* * The lock serves a purpose in addition to locking: the new * contents of .git/config will be written into it. */ lock = xcalloc(sizeof(struct lockfile), 1); //fd = hold_lock_file_for_update(lock, config_filename, 0); if (strlen(config_filename) >= sizeof(lock->filename)) return -1; //strcpy(lock->filename, config_filename); snprintf(lock->filename, PATH_MAX, "%s.lock", config_filename); lock->fd = open(lock->filename, O_RDWR | O_CREAT | O_EXCL, 0666); fd = lock->fd; if (fd < 0) { error("could not lock config file %s: %s", config_filename, strerror(errno)); free(store.key); ret = -1; goto out_free; } in_fd = open(config_filename, O_RDONLY); if ( in_fd < 0 ) { /* * If .git/config does not exist yet, write a minimal version. */ } else { struct stat st; char *contents; size_t contents_sz, copy_begin, copy_end; int i, 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*)xmalloc(sizeof(regex_t)); if (regcomp(store.value_regex, value_regex, REG_EXTENDED)) { error("invalid pattern: %s", value_regex); free(store.value_regex); ret = 6; goto out_free; } } */ store.offset[0] = 0; store.state = START; store.seen = 0; /* * After this, store.offset will contain the *end* offset * of the last match, or remain at 0 if no match was found. * As a side effect, we make sure to transform only a valid * existing config file. */ if (fg_config_from_file(store_aux, config_filename, NULL)) { error("invalid config file %s", config_filename); free(store.key); if (store.value_regex != NULL) { regfree(store.value_regex); free(store.value_regex); } ret = 3; goto out_free; } free(store.key); /* if (store.value_regex != NULL) { regfree(store.value_regex); free(store.value_regex); } */ /* if nothing to unset, or too many matches, error out */ if ((store.seen == 0 && value == NULL) || (store.seen > 1 && multi_replace == 0)) { ret = 5; goto out_free; } fstat(in_fd, &st); contents_sz = xsize_t(st.st_size); contents = mmap(NULL, contents_sz, PROT_READ, MAP_PRIVATE, in_fd, 0); if (!contents) die_errno("Out of memory? mmap failed"); 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 = contents_sz; } else if (store.state != KEY_SEEN) { copy_end = store.offset[i]; } else copy_end = find_beginning_of_line( contents, contents_sz, store.offset[i]-2, &new_line); if (copy_end > 0 && contents[copy_end-1] != '\n') new_line = 1; /* write the first part of the config */ if (copy_end > copy_begin) { if (write_in_full(fd, contents + copy_begin, copy_end - copy_begin) < copy_end - copy_begin) goto write_err_out; if (new_line && write_str_in_full(fd, "\n") != 1) goto write_err_out; } copy_begin = store.offset[i]; } /* write the pair (value == NULL means unset) */ if (value != NULL) { if (store.state == START) { if (!store_write_section(fd, key)) goto write_err_out; } if (!store_write_pair(fd, key, value)) goto write_err_out; } /* write the rest of the config */ if (copy_begin < contents_sz) if (write_in_full(fd, contents + copy_begin, contents_sz - copy_begin) < contents_sz - copy_begin) goto write_err_out; munmap(contents, contents_sz); } if (lock->fd >= 0) { char result_file[PATH_MAX]; int i; close(lock->fd); strcpy(result_file, lock->filename); i = strlen(result_file) - 5; /* .lock */ result_file[i] = 0; if (rename(lock->filename, result_file)) return -1; lock->filename[0] = 0; } else { ret = -1; goto out_free; } /* if (commit_lock_file(lock) < 0) { error("could not commit config file %s", config_filename); ret = 4; goto out_free; } */ /* * lock is committed, so don't try to roll it back below. * NOTE: Since lockfile.c keeps a linked list of all created * lock_file structures, it isn't safe to free(lock). It's * better to just leave it hanging around. */ lock = NULL; ret = 0; out_free: if (lock) { if (lock->filename[0]) { if (lock->fd >= 0) close(lock->fd); unlink(lock->filename); } lock->filename[0] = 0; } free(config_filename); return ret; write_err_out: // ret = write_error(lock->filename); goto out_free; }
/* * 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. * * This function does this: * * - it locks the config file by creating ".git/config.lock" * * - it then parses the config using store_aux() as validator to find * the position on the key/value pair to replace. If it is to be unset, * it must be found exactly once. * * - the config file is mmap()ed and the part before the match (if any) is * written to the lock file, then the changed part and the rest. * * - the config file is removed and the lock file rename()d to it. * */ int git_config_set_multivar(const char* key, const char* value, const char* value_regex, int multi_replace) { int i; struct stat st; int fd; 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. */ 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]) && 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 * contents of .git/config will be written into it. */ fd = open(lock_file, O_WRONLY | O_CREAT | O_EXCL, 0666); if (fd < 0) { fprintf(stderr, "could not lock config file\n"); free(store.key); return -1; } /* * If .git/config does not exist yet, write a minimal version. */ if (stat(config_filename, &st)) { free(store.key); /* if nothing to unset, error out */ if (value == NULL) { close(fd); unlink(lock_file); return 5; } store.key = (char*)key; store_write_section(fd, key); store_write_pair(fd, key, value); } else{ int in_fd; char* contents; 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)) { fprintf(stderr, "Invalid pattern: %s", value_regex); free(store.value_regex); return 6; } } store.offset[0] = 0; store.state = START; store.seen = 0; /* * After this, store.offset will contain the *end* offset * of the last match, or remain at 0 if no match was found. * As a side effect, we make sure to transform only a valid * existing config file. */ if (git_config(store_aux)) { fprintf(stderr, "invalid config file\n"); free(store.key); if (store.value_regex != NULL) { regfree(store.value_regex); free(store.value_regex); } return 3; } free(store.key); if (store.value_regex != NULL) { regfree(store.value_regex); free(store.value_regex); } /* 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; } 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.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 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 pair (value == NULL means unset) */ if (value != NULL) { if (store.state == START) store_write_section(fd, key); store_write_pair(fd, key, value); } /* write the rest of the config */ if (copy_begin < st.st_size) write(fd, contents + copy_begin, st.st_size - copy_begin); munmap(contents, st.st_size); unlink(config_filename); } close(fd); if (rename(lock_file, config_filename) < 0) { fprintf(stderr, "Could not rename the lock file?\n"); return 4; } return 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. * * This function does this: * * - it locks the config file by creating ".git/config.lock" * * - it then parses the config using store_aux() as validator to find * the position on the key/value pair to replace. If it is to be unset, * it must be found exactly once. * * - the config file is mmap()ed and the part before the match (if any) is * written to the lock file, then the changed part and the rest. * * - the config file is removed and the lock file rename()d to it. * */ int git_config_set_multivar(const char *key, const char *value, const char *value_regex, int multi_replace) { int fd = -1, in_fd; int ret; char *config_filename; struct lock_file *lock = NULL; if (config_exclusive_filename) config_filename = xstrdup(config_exclusive_filename); else config_filename = git_pathdup("config"); /* parse-key returns negative; flip the sign to feed exit(3) */ ret = 0 - git_config_parse_key(key, &store.key, &store.baselen); if (ret) goto out_free; store.multi_replace = multi_replace; /* * The lock serves a purpose in addition to locking: the new * contents of .git/config will be written into it. */ lock = xcalloc(sizeof(struct lock_file), 1); fd = hold_lock_file_for_update(lock, config_filename, 0); if (fd < 0) { error("could not lock config file %s: %s", config_filename, strerror(errno)); free(store.key); ret = CONFIG_NO_LOCK; goto out_free; } /* * If .git/config does not exist yet, write a minimal version. */ in_fd = open(config_filename, O_RDONLY); if ( in_fd < 0 ) { free(store.key); if ( ENOENT != errno ) { error("opening %s: %s", config_filename, strerror(errno)); ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */ goto out_free; } /* if nothing to unset, error out */ if (value == NULL) { ret = CONFIG_NOTHING_SET; goto out_free; } store.key = (char *)key; if (!store_write_section(fd, key) || !store_write_pair(fd, key, value)) goto write_err_out; } else { struct stat st; char *contents; size_t contents_sz, copy_begin, copy_end; int i, 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*)xmalloc(sizeof(regex_t)); if (regcomp(store.value_regex, value_regex, REG_EXTENDED)) { error("invalid pattern: %s", value_regex); free(store.value_regex); ret = CONFIG_INVALID_PATTERN; goto out_free; } } store.offset[0] = 0; store.state = START; store.seen = 0; /* * After this, store.offset will contain the *end* offset * of the last match, or remain at 0 if no match was found. * As a side effect, we make sure to transform only a valid * existing config file. */ if (git_config_from_file(store_aux, config_filename, NULL)) { error("invalid config file %s", config_filename); free(store.key); if (store.value_regex != NULL) { regfree(store.value_regex); free(store.value_regex); } ret = CONFIG_INVALID_FILE; goto out_free; } free(store.key); if (store.value_regex != NULL) { regfree(store.value_regex); free(store.value_regex); } /* if nothing to unset, or too many matches, error out */ if ((store.seen == 0 && value == NULL) || (store.seen > 1 && multi_replace == 0)) { ret = CONFIG_NOTHING_SET; goto out_free; } fstat(in_fd, &st); contents_sz = xsize_t(st.st_size); contents = xmmap(NULL, contents_sz, 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 = contents_sz; } else if (store.state != KEY_SEEN) { copy_end = store.offset[i]; } else copy_end = find_beginning_of_line( contents, contents_sz, store.offset[i]-2, &new_line); if (copy_end > 0 && contents[copy_end-1] != '\n') new_line = 1; /* write the first part of the config */ if (copy_end > copy_begin) { if (write_in_full(fd, contents + copy_begin, copy_end - copy_begin) < copy_end - copy_begin) goto write_err_out; if (new_line && write_str_in_full(fd, "\n") != 1) goto write_err_out; } copy_begin = store.offset[i]; } /* write the pair (value == NULL means unset) */ if (value != NULL) { if (store.state == START) { if (!store_write_section(fd, key)) goto write_err_out; } if (!store_write_pair(fd, key, value)) goto write_err_out; } /* write the rest of the config */ if (copy_begin < contents_sz) if (write_in_full(fd, contents + copy_begin, contents_sz - copy_begin) < contents_sz - copy_begin) goto write_err_out; munmap(contents, contents_sz); } if (commit_lock_file(lock) < 0) { error("could not commit config file %s", config_filename); ret = CONFIG_NO_WRITE; goto out_free; } /* * lock is committed, so don't try to roll it back below. * NOTE: Since lockfile.c keeps a linked list of all created * lock_file structures, it isn't safe to free(lock). It's * better to just leave it hanging around. */ lock = NULL; ret = 0; out_free: if (lock) rollback_lock_file(lock); free(config_filename); return ret; write_err_out: ret = write_error(lock->filename); goto out_free; }