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>
This commit is contained in:
Linus Torvalds 2005-10-10 16:31:08 -07:00 committed by Junio C Hamano
parent b12bbd5986
commit 17712991a5
6 changed files with 234 additions and 1 deletions

View File

@ -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

View File

@ -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 */

222
config.c Normal file
View File

@ -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;
}

View File

@ -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"))

View File

@ -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);

View File

@ -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;