4546738b58
Do our own ctype.h, just to get the sane semantics: we want locale-independence, _and_ we want the right signed behaviour. Plus we only use a very small subset of ctype.h anyway (isspace, isalpha, isdigit and isalnum). Signed-off-by: Junio C Hamano <junkio@cox.net>
237 lines
3.8 KiB
C
237 lines
3.8 KiB
C
|
|
#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;
|
|
/* 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;
|
|
}
|
|
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] = tolower(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;
|
|
}
|
|
|
|
if (!strcmp(var, "user.name")) {
|
|
strncpy(git_default_name, value, sizeof(git_default_name));
|
|
return 0;
|
|
}
|
|
|
|
if (!strcmp(var, "user.email")) {
|
|
strncpy(git_default_email, value, sizeof(git_default_email));
|
|
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;
|
|
}
|