aboutsummaryrefslogtreecommitdiff
path: root/config.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@osdl.org>2005-10-10 16:31:08 -0700
committerJunio C Hamano <junkio@cox.net>2005-10-10 16:31:08 -0700
commit17712991a59824a8d22d5115c0c154d3122fc17b (patch)
tree0c8b9e1023f9c5fbaaa7151bcc105783b98c9f10 /config.c
parentb12bbd5986e1fae4120d7a415c1271c0273cad7e (diff)
downloadgit-17712991a59824a8d22d5115c0c154d3122fc17b.tar.gz
git-17712991a59824a8d22d5115c0c154d3122fc17b.tar.xz
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 <torvalds@osdl.org> Signed-off-by: Junio C Hamano <junkio@cox.net>
Diffstat (limited to 'config.c')
-rw-r--r--config.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/config.c b/config.c
new file mode 100644
index 000000000..f3c4fa42a
--- /dev/null
+++ b/config.c
@@ -0,0 +1,222 @@
+#include <ctype.h>
+
+#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;
+}