From: Linus Torvalds Date: Mon, 10 Oct 2005 23:31:08 +0000 (-0700) Subject: Add ".git/config" file parser X-Git-Tag: v0.99.9~161 X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=17712991a59824a8d22d5115c0c154d3122fc17b;p=git.git Add ".git/config" file parser This is a first cut at a very simple parser for a git config file. The format of the file is a simple ini-file like thing, with simple variable/value pairs. You can (and should) make the variables have a simple single-level scope, ie a valid file looks something like this: # # This is the config file, and # a '#' or ';' character indicates # a comment # ; core variables [core] ; Don't trust file modes filemode = false ; Our diff algorithm [diff] external = "/usr/local/bin/gnu-diff -u" renames = true which parses into three variables: "core.filemode" is associated with the string "false", and "diff.external" gets the appropriate quoted value. Right now we only react to one variable: "core.filemode" is a boolean that decides if we should care about the 0100 (user-execute) bit of the stat information. Even that is just a parsing demonstration - this doesn't actually implement that st_mode compare logic itself. Different programs can react to different config options, although they should always fall back to calling "git_default_config()" on any config option name that they don't recognize. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff --git a/Makefile b/Makefile index 0ca8e8d2..c31af7b3 100644 --- a/Makefile +++ b/Makefile @@ -158,7 +158,7 @@ LIB_OBJS = \ object.o pack-check.o patch-delta.o path.o pkt-line.o \ quote.o read-cache.o refs.o run-command.o \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ - tag.o tree.o usage.o $(DIFF_OBJS) + tag.o tree.o usage.o config.o $(DIFF_OBJS) LIBS = $(LIB_FILE) LIBS += -lz diff --git a/cache.h b/cache.h index 5987d4c1..0571282e 100644 --- a/cache.h +++ b/cache.h @@ -178,6 +178,8 @@ extern int hold_index_file_for_update(struct cache_file *, const char *path); extern int commit_index_file(struct cache_file *); extern void rollback_index_file(struct cache_file *); +extern int trust_executable_bit; + #define MTIME_CHANGED 0x0001 #define CTIME_CHANGED 0x0002 #define OWNER_CHANGED 0x0004 @@ -372,4 +374,10 @@ extern int gitfakemunmap(void *start, size_t length); #endif +typedef int (*config_fn_t)(const char *, const char *); +extern int git_default_config(const char *, const char *); +extern int git_config(config_fn_t fn); +extern int git_config_int(const char *, const char *); +extern int git_config_bool(const char *, const char *); + #endif /* CACHE_H */ diff --git a/config.c b/config.c new file mode 100644 index 00000000..f3c4fa42 --- /dev/null +++ b/config.c @@ -0,0 +1,222 @@ +#include + +#include "cache.h" + +#define MAXNAME (256) + +static FILE *config_file; +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 == '\n') + config_linenr++; + if (c == EOF) { + config_file = NULL; + 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 (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; + return NULL; + } + value[len++] = c; + continue; + } + if (c == '"') { + quote = 1-quote; + continue; + } + if (!quote) { + if (c == ';' || c == '#') { + comment = 1; + continue; + } + } + value[len++] = 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 (c == EOF) + break; + if (!isalnum(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_base_var(char *name) +{ + int baselen = 0; + + for (;;) { + int c = get_next_char(); + if (c == EOF) + return -1; + if (c == ']') + return baselen; + if (!isalnum(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') { + /* EOF? */ + if (!config_file) + 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] = c; + if (get_value(fn, var, baselen+1) < 0) + break; + } + die("bad config file line %d", config_linenr); +} + +int git_config_int(const char *name, const char *value) +{ + if (value && *value) { + char *end; + int val = strtol(value, &end, 0); + if (!*end) + return val; + } + die("bad config value for '%s'", name); +} + +int git_config_bool(const char *name, const char *value) +{ + if (!value) + return 1; + if (!*value) + return 0; + if (!strcasecmp(value, "true")) + return 1; + if (!strcasecmp(value, "false")) + 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; + } + + /* Add other config variables here.. */ + return 0; +} + +int git_config(config_fn_t fn) +{ + int ret; + FILE *f = fopen(git_path("config"), "r"); + + ret = -1; + if (f) { + config_file = f; + config_linenr = 1; + ret = git_parse_file(fn); + fclose(f); + } + return ret; +} diff --git a/diff-files.c b/diff-files.c index 5e598322..96d2c7f1 100644 --- a/diff-files.c +++ b/diff-files.c @@ -38,6 +38,7 @@ int main(int argc, const char **argv) const char *prefix = setup_git_directory(); int entries, i; + git_config(git_default_config); diff_setup(&diff_options); while (1 < argc && argv[1][0] == '-') { if (!strcmp(argv[1], "-q")) diff --git a/diff-tree.c b/diff-tree.c index b2d74eb1..2203fa56 100644 --- a/diff-tree.c +++ b/diff-tree.c @@ -408,6 +408,7 @@ int main(int argc, const char **argv) unsigned char sha1[2][20]; const char *prefix = setup_git_directory(); + git_config(git_default_config); nr_sha1 = 0; diff_setup(&diff_options); diff --git a/read-cache.c b/read-cache.c index d2aebdd6..c7f3b268 100644 --- a/read-cache.c +++ b/read-cache.c @@ -5,6 +5,7 @@ */ #include "cache.h" +int trust_executable_bit = 1; struct cache_entry **active_cache = NULL; unsigned int active_nr = 0, active_alloc = 0, active_cache_changed = 0;