Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
git-mirror
/
git
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
0
Pull requests
0
Actions
Projects
0
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Security
Insights
Files
ef5b9d6
Documentation
arm
compat
contrib
git-gui
gitk-git
gitweb
mozilla-sha1
perl
ppc
t
templates
xdiff
.gitignore
.mailmap
COPYING
GIT-VERSION-GEN
INSTALL
Makefile
README
RelNotes
alloc.c
archive-tar.c
archive-zip.c
archive.c
archive.h
attr.c
attr.h
base85.c
blob.c
blob.h
builtin-add.c
builtin-annotate.c
builtin-apply.c
builtin-archive.c
builtin-blame.c
builtin-branch.c
builtin-bundle.c
builtin-cat-file.c
builtin-check-attr.c
builtin-check-ref-format.c
builtin-checkout-index.c
builtin-clean.c
builtin-commit-tree.c
builtin-commit.c
builtin-config.c
builtin-count-objects.c
builtin-describe.c
builtin-diff-files.c
builtin-diff-index.c
builtin-diff-tree.c
builtin-diff.c
builtin-fast-export.c
builtin-fetch--tool.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-fmt-merge-msg.c
builtin-for-each-ref.c
builtin-fsck.c
builtin-gc.c
builtin-grep.c
builtin-http-fetch.c
builtin-init-db.c
builtin-log.c
builtin-ls-files.c
builtin-ls-remote.c
builtin-ls-tree.c
builtin-mailinfo.c
builtin-mailsplit.c
builtin-merge-base.c
builtin-merge-file.c
builtin-merge-ours.c
builtin-mv.c
builtin-name-rev.c
builtin-pack-objects.c
builtin-pack-refs.c
builtin-prune-packed.c
builtin-prune.c
builtin-push.c
builtin-read-tree.c
builtin-reflog.c
builtin-rerere.c
builtin-reset.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-revert.c
builtin-rm.c
builtin-send-pack.c
builtin-shortlog.c
builtin-show-branch.c
builtin-show-ref.c
builtin-stripspace.c
builtin-symbolic-ref.c
builtin-tag.c
builtin-tar-tree.c
builtin-unpack-objects.c
builtin-update-index.c
builtin-update-ref.c
builtin-upload-archive.c
builtin-verify-pack.c
builtin-verify-tag.c
builtin-write-tree.c
builtin.h
bundle.c
bundle.h
cache-tree.c
cache-tree.h
cache.h
check-builtins.sh
check-racy.c
color.c
color.h
combine-diff.c
command-list.txt
commit.c
commit.h
config.c
config.mak.in
configure.ac
connect.c
convert.c
copy.c
csum-file.c
csum-file.h
ctype.c
daemon.c
date.c
decorate.c
decorate.h
delta.h
diff-delta.c
diff-lib.c
diff.c
diff.h
diffcore-break.c
diffcore-delta.c
diffcore-order.c
diffcore-pickaxe.c
diffcore-rename.c
diffcore.h
dir.c
dir.h
dump-cache-tree.c
entry.c
environment.c
exec_cmd.c
exec_cmd.h
fast-import.c
fetch-pack.h
fixup-builtins
generate-cmdlist.sh
git-add--interactive.perl
git-am.sh
git-archimport.perl
git-bisect.sh
git-checkout.sh
git-clone.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-filter-branch.sh
git-help--browse.sh
git-instaweb.sh
git-lost-found.sh
git-merge-octopus.sh
git-merge-one-file.sh
git-merge-resolve.sh
git-merge-stupid.sh
git-merge.sh
git-mergetool.sh
git-parse-remote.sh
git-pull.sh
git-quiltimport.sh
git-rebase--interactive.sh
git-rebase.sh
git-relink.perl
git-remote.perl
git-repack.sh
git-request-pull.sh
git-send-email.perl
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git.c
git.spec.in
grep.c
grep.h
hash-object.c
hash.c
hash.h
help.c
http-push.c
http-walker.c
http.c
http.h
ident.c
imap-send.c
index-pack.c
interpolate.c
interpolate.h
list-objects.c
list-objects.h
lockfile.c
log-tree.c
log-tree.h
mailmap.c
mailmap.h
match-trees.c
merge-file.c
merge-index.c
merge-recursive.c
merge-tree.c
mktag.c
mktree.c
object-refs.c
object.c
object.h
pack-check.c
pack-redundant.c
pack-write.c
pack.h
pager.c
parse-options.c
parse-options.h
patch-delta.c
patch-id.c
patch-ids.c
patch-ids.h
path-list.c
path-list.h
path.c
pkt-line.c
pkt-line.h
pretty.c
progress.c
progress.h
quote.c
quote.h
reachable.c
reachable.h
read-cache.c
receive-pack.c
reflog-walk.c
reflog-walk.h
refs.c
refs.h
remote.c
remote.h
revision.c
revision.h
run-command.c
run-command.h
send-pack.h
server-info.c
setup.c
sha1_file.c
sha1_name.c
shallow.c
shell.c
show-index.c
sideband.c
sideband.h
strbuf.c
strbuf.h
symlinks.c
tag.c
tag.h
tar.h
test-absolute-path.c
test-chmtime.c
test-date.c
test-delta.c
test-genrandom.c
test-match-trees.c
test-parse-options.c
test-sha1.c
test-sha1.sh
trace.c
transport.c
transport.h
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
tree.h
unpack-file.c
unpack-trees.c
unpack-trees.h
update-server-info.c
upload-pack.c
usage.c
utf8.c
utf8.h
var.c
walker.c
walker.h
write_or_die.c
ws.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.h
Breadcrumbs
git
/
config.c
Blame
Blame
Latest commit
Johannes Sixt
and
Junio C Hamano
Fix misuse of prefix_path()
Feb 5, 2008
ef5b9d6
·
Feb 5, 2008
History
History
1076 lines (960 loc) · 23.3 KB
Breadcrumbs
git
/
config.c
Top
File metadata and controls
Code
Blame
1076 lines (960 loc) · 23.3 KB
Raw
/* * GIT - The information manager from hell * * Copyright (C) Linus Torvalds, 2005 * Copyright (C) Johannes Schindelin, 2005 * */ #include "cache.h" #include "exec_cmd.h" #define MAXNAME (256) static FILE *config_file; static const char *config_file_name; static int config_linenr; static int config_file_eof; static int zlib_compression_seen; static int get_next_char(void) { int c; FILE *f; c = '\n'; if ((f = config_file) != NULL) { c = fgetc(f); if (c == '\r') { /* DOS like systems */ c = fgetc(f); if (c != '\n') { ungetc(c, f); c = '\r'; } } if (c == '\n') config_linenr++; if (c == EOF) { config_file_eof = 1; c = '\n'; } } return c; } static char *parse_value(void) { static char value[1024]; int quote = 0, comment = 0, len = 0, space = 0; for (;;) { int c = get_next_char(); if (len >= sizeof(value)) return NULL; if (c == '\n') { if (quote) return NULL; value[len] = 0; return value; } if (comment) continue; if (isspace(c) && !quote) { space = 1; continue; } if (!quote) { if (c == ';' || c == '#') { comment = 1; continue; } } if (space) { if (len) value[len++] = ' '; space = 0; } if (c == '\\') { c = get_next_char(); switch (c) { case '\n': continue; case 't': c = '\t'; break; case 'b': c = '\b'; break; case 'n': c = '\n'; break; /* Some characters escape as themselves */ case '\\': case '"': break; /* Reject unknown escape sequences */ default: return NULL; } value[len++] = c; continue; } if (c == '"') { quote = 1-quote; continue; } value[len++] = c; } } static inline int iskeychar(int c) { return isalnum(c) || c == '-'; } static int get_value(config_fn_t fn, char *name, unsigned int len) { int c; char *value; /* Get the full name */ for (;;) { c = get_next_char(); if (config_file_eof) break; if (!iskeychar(c)) break; name[len++] = tolower(c); if (len >= MAXNAME) return -1; } name[len] = 0; while (c == ' ' || c == '\t') c = get_next_char(); value = NULL; if (c != '\n') { if (c != '=') return -1; value = parse_value(); if (!value) return -1; } return fn(name, value); } static int get_extended_base_var(char *name, int baselen, int c) { do { if (c == '\n') return -1; c = get_next_char(); } while (isspace(c)); /* We require the format to be '[base "extension"]' */ if (c != '"') return -1; name[baselen++] = '.'; for (;;) { int c = get_next_char(); if (c == '\n') return -1; if (c == '"') break; if (c == '\\') { c = get_next_char(); if (c == '\n') return -1; } name[baselen++] = c; if (baselen > MAXNAME / 2) return -1; } /* Final ']' */ if (get_next_char() != ']') return -1; return baselen; } static int get_base_var(char *name) { int baselen = 0; for (;;) { int c = get_next_char(); if (config_file_eof) return -1; if (c == ']') return baselen; if (isspace(c)) return get_extended_base_var(name, baselen, c); if (!iskeychar(c) && c != '.') return -1; if (baselen > MAXNAME / 2) return -1; name[baselen++] = tolower(c); } } static int git_parse_file(config_fn_t fn) { int comment = 0; int baselen = 0; static char var[MAXNAME]; for (;;) { int c = get_next_char(); if (c == '\n') { if (config_file_eof) return 0; comment = 0; continue; } if (comment || isspace(c)) continue; if (c == '#' || c == ';') { comment = 1; continue; } if (c == '[') { baselen = get_base_var(var); if (baselen <= 0) break; var[baselen++] = '.'; var[baselen] = 0; continue; } if (!isalpha(c)) break; var[baselen] = tolower(c); if (get_value(fn, var, baselen+1) < 0) break; } die("bad config file line %d in %s", config_linenr, config_file_name); } static int parse_unit_factor(const char *end, unsigned long *val) { if (!*end) return 1; else if (!strcasecmp(end, "k")) { *val *= 1024; return 1; } else if (!strcasecmp(end, "m")) { *val *= 1024 * 1024; return 1; } else if (!strcasecmp(end, "g")) { *val *= 1024 * 1024 * 1024; return 1; } return 0; } int git_parse_long(const char *value, long *ret) { if (value && *value) { char *end; long val = strtol(value, &end, 0); unsigned long factor = 1; if (!parse_unit_factor(end, &factor)) return 0; *ret = val * factor; return 1; } return 0; } int git_parse_ulong(const char *value, unsigned long *ret) { if (value && *value) { char *end; unsigned long val = strtoul(value, &end, 0); if (!parse_unit_factor(end, &val)) return 0; *ret = val; return 1; } return 0; } int git_config_int(const char *name, const char *value) { long ret; if (!git_parse_long(value, &ret)) die("bad config value for '%s' in %s", name, config_file_name); return ret; } unsigned long git_config_ulong(const char *name, const char *value) { unsigned long ret; if (!git_parse_ulong(value, &ret)) die("bad config value for '%s' in %s", name, config_file_name); return ret; } int git_config_bool(const char *name, const char *value) { if (!value) return 1; if (!*value) return 0; if (!strcasecmp(value, "true") || !strcasecmp(value, "yes")) return 1; if (!strcasecmp(value, "false") || !strcasecmp(value, "no")) return 0; return git_config_int(name, value) != 0; } int git_default_config(const char *var, const char *value) { /* This needs a better name */ if (!strcmp(var, "core.filemode")) { trust_executable_bit = git_config_bool(var, value); return 0; } if (!strcmp(var, "core.quotepath")) { quote_path_fully = git_config_bool(var, value); return 0; } if (!strcmp(var, "core.symlinks")) { has_symlinks = git_config_bool(var, value); return 0; } if (!strcmp(var, "core.bare")) { is_bare_repository_cfg = git_config_bool(var, value); return 0; } if (!strcmp(var, "core.ignorestat")) { assume_unchanged = git_config_bool(var, value); return 0; } if (!strcmp(var, "core.prefersymlinkrefs")) { prefer_symlink_refs = git_config_bool(var, value); return 0; } if (!strcmp(var, "core.logallrefupdates")) { log_all_ref_updates = git_config_bool(var, value); return 0; } if (!strcmp(var, "core.warnambiguousrefs")) { warn_ambiguous_refs = git_config_bool(var, value); return 0; } if (!strcmp(var, "core.loosecompression")) { int level = git_config_int(var, value); if (level == -1) level = Z_DEFAULT_COMPRESSION; else if (level < 0 || level > Z_BEST_COMPRESSION) die("bad zlib compression level %d", level); zlib_compression_level = level; zlib_compression_seen = 1; return 0; } if (!strcmp(var, "core.compression")) { int level = git_config_int(var, value); if (level == -1) level = Z_DEFAULT_COMPRESSION; else if (level < 0 || level > Z_BEST_COMPRESSION) die("bad zlib compression level %d", level); core_compression_level = level; core_compression_seen = 1; if (!zlib_compression_seen) zlib_compression_level = level; return 0; } if (!strcmp(var, "core.packedgitwindowsize")) { int pgsz_x2 = getpagesize() * 2; packed_git_window_size = git_config_int(var, value); /* This value must be multiple of (pagesize * 2) */ packed_git_window_size /= pgsz_x2; if (packed_git_window_size < 1) packed_git_window_size = 1; packed_git_window_size *= pgsz_x2; return 0; } if (!strcmp(var, "core.packedgitlimit")) { packed_git_limit = git_config_int(var, value); return 0; } if (!strcmp(var, "core.deltabasecachelimit")) { delta_base_cache_limit = git_config_int(var, value); return 0; } if (!strcmp(var, "core.autocrlf")) { if (value && !strcasecmp(value, "input")) { auto_crlf = -1; return 0; } auto_crlf = git_config_bool(var, value); return 0; } if (!strcmp(var, "user.name")) { strlcpy(git_default_name, value, sizeof(git_default_name)); return 0; } if (!strcmp(var, "user.email")) { strlcpy(git_default_email, value, sizeof(git_default_email)); return 0; } if (!strcmp(var, "i18n.commitencoding")) { git_commit_encoding = xstrdup(value); return 0; } if (!strcmp(var, "i18n.logoutputencoding")) { git_log_output_encoding = xstrdup(value); return 0; } if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) { pager_use_color = git_config_bool(var,value); return 0; } if (!strcmp(var, "core.pager")) { pager_program = xstrdup(value); return 0; } if (!strcmp(var, "core.editor")) { editor_program = xstrdup(value); return 0; } if (!strcmp(var, "core.excludesfile")) { if (!value) die("core.excludesfile without value"); excludes_file = xstrdup(value); return 0; } if (!strcmp(var, "core.whitespace")) { whitespace_rule_cfg = parse_whitespace_rule(value); return 0; } /* Add other config variables here and to Documentation/config.txt. */ return 0; } int git_config_from_file(config_fn_t fn, const char *filename) { int ret; FILE *f = fopen(filename, "r"); ret = -1; if (f) { config_file = f; config_file_name = filename; config_linenr = 1; config_file_eof = 0; ret = git_parse_file(fn); fclose(f); config_file_name = NULL; } return ret; } const char *git_etc_gitconfig(void) { static const char *system_wide; if (!system_wide) { system_wide = ETC_GITCONFIG; if (!is_absolute_path(system_wide)) { /* interpret path relative to exec-dir */ struct strbuf d = STRBUF_INIT; strbuf_addf(&d, "%s/%s", git_exec_path(), system_wide); system_wide = strbuf_detach(&d, NULL); } } return system_wide; } int git_config(config_fn_t fn) { int ret = 0; char *repo_config = NULL; const char *home = NULL, *filename; /* $GIT_CONFIG makes git read _only_ the given config file, * $GIT_CONFIG_LOCAL will make it process it in addition to the * global config file, the same way it would the per-repository * config file otherwise. */ filename = getenv(CONFIG_ENVIRONMENT); if (!filename) { if (!access(git_etc_gitconfig(), R_OK)) ret += git_config_from_file(fn, git_etc_gitconfig()); home = getenv("HOME"); filename = getenv(CONFIG_LOCAL_ENVIRONMENT); if (!filename) filename = repo_config = xstrdup(git_path("config")); } if (home) { char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); if (!access(user_config, R_OK)) ret = git_config_from_file(fn, user_config); free(user_config); } ret += git_config_from_file(fn, filename); free(repo_config); return ret; } /* * 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; int multi_replace; size_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) { const char *ep; size_t section_len; switch (store.state) { case KEY_SEEN: 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[store.seen] = ftell(config_file); store.seen++; } break; case SECTION_SEEN: /* * What we are looking for is in store.key (both * section and var), and its section part is baselen * long. We found key (again, both section and var). * We would want to know if this key is in the same * section as what we are looking for. We already * know we are in the same section as what should * hold store.key. */ ep = strrchr(key, '.'); section_len = ep - key; if ((section_len != store.baselen) || memcmp(key, store.key, section_len+1)) { store.state = SECTION_END_SEEN; break; } /* * Do not increment matches: this is no match, but we * just made sure we are in the desired section. */ store.offset[store.seen] = ftell(config_file); /* fallthru */ case SECTION_END_SEEN: case START: if (matches(key, value)) { store.offset[store.seen] = ftell(config_file); store.state = KEY_SEEN; store.seen++; } else { if (strrchr(key, '.') - key == store.baselen && !strncmp(key, store.key, store.baselen)) { store.state = SECTION_SEEN; store.offset[store.seen] = ftell(config_file); } } } return 0; } static int write_error(void) { fprintf(stderr, "Failed to write new configuration file\n"); /* Same error code as "failed to rename". */ return 4; } static int store_write_section(int fd, const char* key) { const char *dot; int i, success; struct strbuf sb; strbuf_init(&sb, 0); dot = memchr(key, '.', store.baselen); if (dot) { strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key); for (i = dot - key + 1; i < store.baselen; i++) { if (key[i] == '"') strbuf_addch(&sb, '\\'); strbuf_addch(&sb, key[i]); } strbuf_addstr(&sb, "\"]\n"); } else { strbuf_addf(&sb, "[%.*s]\n", store.baselen, key); } success = write_in_full(fd, sb.buf, sb.len) == sb.len; strbuf_release(&sb); return success; } static int store_write_pair(int fd, const char* key, const char* value) { int i, success; int length = strlen(key + store.baselen + 1); const char *quote = ""; struct strbuf sb; /* * Check to see if the value needs to be surrounded with a dq pair. * Note that problematic characters are always backslash-quoted; this * check is about not losing leading or trailing SP and strings that * follow beginning-of-comment characters (i.e. ';' and '#') by the * configuration parser. */ if (value[0] == ' ') quote = "\""; for (i = 0; value[i]; i++) if (value[i] == ';' || value[i] == '#') quote = "\""; if (i && value[i - 1] == ' ') quote = "\""; strbuf_init(&sb, 0); strbuf_addf(&sb, "\t%.*s = %s", length, key + store.baselen + 1, quote); for (i = 0; value[i]; i++) switch (value[i]) { case '\n': strbuf_addstr(&sb, "\\n"); break; case '\t': strbuf_addstr(&sb, "\\t"); break; case '"': case '\\': strbuf_addch(&sb, '\\'); default: strbuf_addch(&sb, value[i]); break; } strbuf_addf(&sb, "%s\n", quote); success = write_in_full(fd, sb.buf, sb.len) == sb.len; strbuf_release(&sb); return success; } static ssize_t find_beginning_of_line(const char* contents, size_t size, size_t offset_, int* found_bracket) { size_t equal_offset = size, bracket_offset = size; ssize_t 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, 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 i, dot; int fd = -1, in_fd; int ret; char* config_filename; struct lock_file *lock = NULL; const char* last_dot = strrchr(key, '.'); config_filename = getenv(CONFIG_ENVIRONMENT); if (!config_filename) { config_filename = getenv(CONFIG_LOCAL_ENVIRONMENT); if (!config_filename) config_filename = git_path("config"); } config_filename = xstrdup(config_filename); /* * 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); 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))) { fprintf(stderr, "invalid key: %s\n", key); free(store.key); ret = 1; goto out_free; } c = tolower(c); } else if (c == '\n') { fprintf(stderr, "invalid key (newline): %s\n", 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) { fprintf(stderr, "could not lock config file\n"); 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)) { fprintf(stderr, "Invalid pattern: %s\n", 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)) { fprintf(stderr, "invalid config file\n"); 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) { fprintf(stderr, "Cannot commit config file!\n"); 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(); goto out_free; } static int section_name_match (const char *buf, const char *name) { int i = 0, j = 0, dot = 0; for (; buf[i] && buf[i] != ']'; i++) { if (!dot && isspace(buf[i])) { dot = 1; if (name[j++] != '.') break; for (i++; isspace(buf[i]); i++) ; /* do nothing */ if (buf[i] != '"') break; continue; } if (buf[i] == '\\' && dot) i++; else if (buf[i] == '"' && dot) { for (i++; isspace(buf[i]); i++) ; /* do_nothing */ break; } if (buf[i] != name[j++]) break; } return (buf[i] == ']' && name[j] == 0); } /* if new_name == NULL, the section is removed instead */ int git_config_rename_section(const char *old_name, const char *new_name) { int ret = 0, remove = 0; char *config_filename; struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1); int out_fd; char buf[1024]; config_filename = getenv(CONFIG_ENVIRONMENT); if (!config_filename) { config_filename = getenv(CONFIG_LOCAL_ENVIRONMENT); if (!config_filename) config_filename = git_path("config"); } config_filename = xstrdup(config_filename); out_fd = hold_lock_file_for_update(lock, config_filename, 0); if (out_fd < 0) { ret = error("Could not lock config file!"); goto out; } if (!(config_file = fopen(config_filename, "rb"))) { /* no config file means nothing to rename, no error */ goto unlock_and_out; } while (fgets(buf, sizeof(buf), config_file)) { int i; int length; for (i = 0; buf[i] && isspace(buf[i]); i++) ; /* do nothing */ if (buf[i] == '[') { /* it's a section */ if (section_name_match (&buf[i+1], old_name)) { ret++; if (new_name == NULL) { remove = 1; continue; } store.baselen = strlen(new_name); if (!store_write_section(out_fd, new_name)) { ret = write_error(); goto out; } continue; } remove = 0; } if (remove) continue; length = strlen(buf); if (write_in_full(out_fd, buf, length) != length) { ret = write_error(); goto out; } } fclose(config_file); unlock_and_out: if (commit_lock_file(lock) < 0) ret = error("Cannot commit config file!"); out: free(config_filename); return ret; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
You can’t perform that action at this time.