1
0
mirror of https://github.com/git/git.git synced 2025-03-27 22:39:34 +00:00
git/config.c
Linus Torvalds 17712991a5 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>
2005-10-10 16:31:08 -07:00

223 lines
3.5 KiB
C

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