Commit a69053c7 by Carlos Martín Nieto

Convert config.c to LF

Signed-off-by: Carlos Martín Nieto <cmn@elego.de>
parent 5d4cd003
/* /*
* This file is free software; you can redistribute it and/or modify * This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, * it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation. * as published by the Free Software Foundation.
* *
* In addition to the permissions in the GNU General Public License, * In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled * the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs, * version of this file into combinations with other programs,
* and to distribute those combinations without any restriction * and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License * coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover * restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into * modification of the file, and distribution when not linked into
* a combined executable.) * a combined executable.)
* *
* This file is distributed in the hope that it will be useful, but * This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of * WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to * along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor, * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#include "common.h" #include "common.h"
#include "fileops.h" #include "fileops.h"
#include "hashtable.h" #include "hashtable.h"
#include "config.h" #include "config.h"
#include <ctype.h> #include <ctype.h>
uint32_t config_table_hash(const void *key) uint32_t config_table_hash(const void *key)
{ {
const char *var_name = (char *)key; const char *var_name = (char *)key;
return git__hash(key, strlen(var_name), 0x5273eae3); return git__hash(key, strlen(var_name), 0x5273eae3);
} }
int config_table_haskey(void *object, const void *key) int config_table_haskey(void *object, const void *key)
{ {
git_config_var *var = (git_config_var *)object; git_config_var *var = (git_config_var *)object;
const char *var_name = (const char *)key; const char *var_name = (const char *)key;
return (strcmp(var->name, var_name) == 0); return (strcmp(var->name, var_name) == 0);
} }
int git_config_open(git_config **cfg_out, const char *path) int git_config_open(git_config **cfg_out, const char *path)
{ {
git_config *cfg; git_config *cfg;
assert(cfg_out && path); assert(cfg_out && path);
cfg = git__malloc(sizeof(git_config)); cfg = git__malloc(sizeof(git_config));
if (cfg == NULL) if (cfg == NULL)
return GIT_ENOMEM; return GIT_ENOMEM;
memset(cfg, 0x0, sizeof(git_config)); memset(cfg, 0x0, sizeof(git_config));
cfg->file_path = git__strdup(path); cfg->file_path = git__strdup(path);
if (cfg->file_path == NULL) if (cfg->file_path == NULL)
return GIT_ENOMEM; return GIT_ENOMEM;
cfg->vars = git_hashtable_alloc(16, config_table_hash, config_table_haskey); cfg->vars = git_hashtable_alloc(16, config_table_hash, config_table_haskey);
if (cfg->vars == NULL) if (cfg->vars == NULL)
return GIT_ENOMEM; return GIT_ENOMEM;
*cfg_out = cfg; *cfg_out = cfg;
return GIT_SUCCESS; return GIT_SUCCESS;
} }
void git_config_free(git_config *cfg) void git_config_free(git_config *cfg)
{ {
if (cfg == NULL) if (cfg == NULL)
return; return;
free(cfg->file_path); free(cfg->file_path);
git_hashtable_free(cfg->vars); git_hashtable_free(cfg->vars);
gitfo_free_buf(&cfg->reader.buffer); gitfo_free_buf(&cfg->reader.buffer);
free(cfg); free(cfg);
} }
static int cfg_getchar_raw(git_config *cfg) static int cfg_getchar_raw(git_config *cfg)
{ {
int c; int c;
c = *cfg->reader.read_ptr++; c = *cfg->reader.read_ptr++;
/* /*
Win 32 line breaks: if we find a \r\n sequence, Win 32 line breaks: if we find a \r\n sequence,
return only the \n as a newline return only the \n as a newline
*/ */
if (c == '\r' && *cfg->reader.read_ptr == '\n') { if (c == '\r' && *cfg->reader.read_ptr == '\n') {
cfg->reader.read_ptr++; cfg->reader.read_ptr++;
c = '\n'; c = '\n';
} }
if (c == '\n') if (c == '\n')
cfg->reader.line_number++; cfg->reader.line_number++;
if (c == 0) { if (c == 0) {
cfg->reader.eof = 1; cfg->reader.eof = 1;
c = '\n'; c = '\n';
} }
return c; return c;
} }
#define SKIP_WHITESPACE (1 << 1) #define SKIP_WHITESPACE (1 << 1)
#define SKIP_COMMENTS (1 << 2) #define SKIP_COMMENTS (1 << 2)
static int cfg_getchar(git_config *cfg_file, int flags) static int cfg_getchar(git_config *cfg_file, int flags)
{ {
const int skip_whitespace = (flags & SKIP_WHITESPACE); const int skip_whitespace = (flags & SKIP_WHITESPACE);
const int skip_comments = (flags & SKIP_COMMENTS); const int skip_comments = (flags & SKIP_COMMENTS);
int c; int c;
assert(cfg_file->reader.read_ptr); assert(cfg_file->reader.read_ptr);
do c = cfg_getchar_raw(cfg_file); do c = cfg_getchar_raw(cfg_file);
while (skip_whitespace && isspace(c)); while (skip_whitespace && isspace(c));
if (skip_comments && (c == '#' || c == ';')) { if (skip_comments && (c == '#' || c == ';')) {
do c = cfg_getchar_raw(cfg_file); do c = cfg_getchar_raw(cfg_file);
while (c != '\n'); while (c != '\n');
} }
return c; return c;
} }
static const char *LINEBREAK_UNIX = "\\\n"; static const char *LINEBREAK_UNIX = "\\\n";
static const char *LINEBREAK_WIN32 = "\\\r\n"; static const char *LINEBREAK_WIN32 = "\\\r\n";
static int is_linebreak(const char *pos) static int is_linebreak(const char *pos)
{ {
return memcmp(pos - 1, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0 || return memcmp(pos - 1, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0 ||
memcmp(pos - 2, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0; memcmp(pos - 2, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0;
} }
static char *cfg_readline(git_config *cfg) static char *cfg_readline(git_config *cfg)
{ {
char *line = NULL; char *line = NULL;
char *line_src, *line_end; char *line_src, *line_end;
int line_len; int line_len;
line_src = cfg->reader.read_ptr; line_src = cfg->reader.read_ptr;
line_end = strchr(line_src, '\n'); line_end = strchr(line_src, '\n');
while (is_linebreak(line_end)) while (is_linebreak(line_end))
line_end = strchr(line_end + 1, '\n'); line_end = strchr(line_end + 1, '\n');
/* no newline at EOF */ /* no newline at EOF */
if (line_end == NULL) if (line_end == NULL)
line_end = strchr(line_src, 0); line_end = strchr(line_src, 0);
while (line_src < line_end && isspace(*line_src)) while (line_src < line_end && isspace(*line_src))
line_src++; line_src++;
line = (char *)git__malloc((size_t)(line_end - line_src) + 1); line = (char *)git__malloc((size_t)(line_end - line_src) + 1);
if (line == NULL) if (line == NULL)
return NULL; return NULL;
line_len = 0; line_len = 0;
while (line_src < line_end) { while (line_src < line_end) {
if (memcmp(line_src, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0) { if (memcmp(line_src, LINEBREAK_UNIX, sizeof(LINEBREAK_UNIX)) == 0) {
line_src += sizeof(LINEBREAK_UNIX); line_src += sizeof(LINEBREAK_UNIX);
continue; continue;
} }
if (memcmp(line_src, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0) { if (memcmp(line_src, LINEBREAK_WIN32, sizeof(LINEBREAK_WIN32)) == 0) {
line_src += sizeof(LINEBREAK_WIN32); line_src += sizeof(LINEBREAK_WIN32);
continue; continue;
} }
line[line_len++] = *line_src++; line[line_len++] = *line_src++;
} }
line[line_len] = '\0'; line[line_len] = '\0';
while (--line_len >= 0 && isspace(line[line_len])) while (--line_len >= 0 && isspace(line[line_len]))
line[line_len] = '\0'; line[line_len] = '\0';
if (*line_end == '\n') if (*line_end == '\n')
line_end++; line_end++;
if (*line_end == '\0') if (*line_end == '\0')
cfg->reader.eof = 1; cfg->reader.eof = 1;
cfg->reader.line_number++; cfg->reader.line_number++;
cfg->reader.read_ptr = line_end; cfg->reader.read_ptr = line_end;
return line; return line;
} }
static inline int config_keychar(int c) static inline int config_keychar(int c)
{ {
return isalnum(c) || c == '-'; return isalnum(c) || c == '-';
} }
static char *parse_section_header_ext(char *base_name, git_config *cfg) static char *parse_section_header_ext(char *base_name, git_config *cfg)
{ {
return base_name; return base_name;
} }
static int parse_section_header(char **section_out, const char *line) static int parse_section_header(char **section_out, const char *line)
{ {
char *name, *name_start, *name_end; char *name, *name_start, *name_end;
int name_length, c; int name_length, c;
/* find the end of the variable's name */ /* find the end of the variable's name */
name_end = strchr(name_start, ']'); name_end = strchr(name_start, ']');
if (name_end == NULL) if (name_end == NULL)
return NULL; return NULL;
name = (char *)git__malloc((size_t)(name_end - name_start) + 1); name = (char *)git__malloc((size_t)(name_end - name_start) + 1);
if (name == NULL) if (name == NULL)
return NULL; return NULL;
name_length = 0; name_length = 0;
c = cfg_getchar(cfg, SKIP_WHITESPACE | SKIP_COMMENTS); c = cfg_getchar(cfg, SKIP_WHITESPACE | SKIP_COMMENTS);
do { do {
if (cfg->reader.eof) if (cfg->reader.eof)
goto error; goto error;
if (isspace(c)) if (isspace(c))
return parse_section_name_ext(name, cfg); return parse_section_name_ext(name, cfg);
if (!config_keychar(c) && c != '.') if (!config_keychar(c) && c != '.')
goto error; goto error;
name[name_length++] = tolower(c); name[name_length++] = tolower(c);
} while ((c = cfg_getchar(cfg, SKIP_COMMENTS)) != ']'); } while ((c = cfg_getchar(cfg, SKIP_COMMENTS)) != ']');
name[name_length] = 0; name[name_length] = 0;
return name; return name;
error: error:
free(name); free(name);
return NULL; return NULL;
} }
static int skip_bom(git_config *cfg) static int skip_bom(git_config *cfg)
{ {
static const unsigned char *utf8_bom = "\xef\xbb\xbf"; static const unsigned char *utf8_bom = "\xef\xbb\xbf";
if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0) if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
cfg->reader.read_ptr += sizeof(utf8_bom); cfg->reader.read_ptr += sizeof(utf8_bom);
/* TODO: the reference implementation does pretty stupid /* TODO: the reference implementation does pretty stupid
shit with the BoM shit with the BoM
*/ */
return GIT_SUCCESS; return GIT_SUCCESS;
} }
/* /*
(* basic types *) (* basic types *)
digit = "0".."9" digit = "0".."9"
integer = digit { digit } integer = digit { digit }
alphabet = "a".."z" + "A" .. "Z" alphabet = "a".."z" + "A" .. "Z"
section_char = alphabet | "." | "-" section_char = alphabet | "." | "-"
extension_char = (* any character except newline *) extension_char = (* any character except newline *)
any_char = (* any character *) any_char = (* any character *)
variable_char = "alphabet" | "-" variable_char = "alphabet" | "-"
(* actual grammar *) (* actual grammar *)
config = { section } config = { section }
section = header { definition } section = header { definition }
header = "[" section [subsection | subsection_ext] "]" header = "[" section [subsection | subsection_ext] "]"
subsection = "." section subsection = "." section
subsection_ext = "\"" extension "\"" subsection_ext = "\"" extension "\""
section = section_char { section_char } section = section_char { section_char }
extension = extension_char { extension_char } extension = extension_char { extension_char }
definition = variable_name ["=" variable_value] "\n" definition = variable_name ["=" variable_value] "\n"
variable_name = variable_char { variable_char } variable_name = variable_char { variable_char }
variable_value = string | boolean | integer variable_value = string | boolean | integer
string = quoted_string | plain_string string = quoted_string | plain_string
quoted_string = "\"" plain_string "\"" quoted_string = "\"" plain_string "\""
plain_string = { any_char } plain_string = { any_char }
boolean = boolean_true | boolean_false boolean = boolean_true | boolean_false
boolean_true = "yes" | "1" | "true" | "on" boolean_true = "yes" | "1" | "true" | "on"
boolean_false = "no" | "0" | "false" | "off" boolean_false = "no" | "0" | "false" | "off"
*/ */
static void strip_comments(char *line) static void strip_comments(char *line)
{ {
int quote_count = 0; int quote_count = 0;
char *ptr; char *ptr;
for (ptr = line; *ptr; ++ptr) { for (ptr = line; *ptr; ++ptr) {
if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\') if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
quote_count++; quote_count++;
if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) { if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {
ptr[0] = '\0'; ptr[0] = '\0';
break; break;
} }
} }
if (isspace(ptr[-1])) { if (isspace(ptr[-1])) {
/* TODO skip whitespace */ /* TODO skip whitespace */
} }
} }
static int config_parse(git_config *cfg_file) static int config_parse(git_config *cfg_file)
{ {
int error = GIT_SUCCESS; int error = GIT_SUCCESS;
char *current_section = NULL; char *current_section = NULL;
skip_bom(cfg_file); skip_bom(cfg_file);
while (error == GIT_SUCCESS && !cfg_file->reader.eof) { while (error == GIT_SUCCESS && !cfg_file->reader.eof) {
char *line = cfg_readline(cfg_file); char *line = cfg_readline(cfg_file);
/* not enough memory to allocate line */ /* not enough memory to allocate line */
if (line == NULL) if (line == NULL)
return GIT_ENOMEM; return GIT_ENOMEM;
strip_comments(line); strip_comments(line);
switch (line[0]) { switch (line[0]) {
case '\0': /* empty line (only whitespace) */ case '\0': /* empty line (only whitespace) */
break; break;
case '[': /* section header, new section begins */ case '[': /* section header, new section begins */
error = parse_section_header(&current_section, line); error = parse_section_header(&current_section, line);
break; break;
default: /* assume variable declaration */ default: /* assume variable declaration */
error = parse_variable(cfg_file, current_section, line); error = parse_variable(cfg_file, current_section, line);
break; break;
} }
free(line); free(line);
} }
return error; return error;
} }
static int parse_variable(git_config *cfg, const char *section_name, const char *line) static int parse_variable(git_config *cfg, const char *section_name, const char *line)
{ {
int error; int error;
int has_value = 1; int has_value = 1;
const char *var_end = NULL; const char *var_end = NULL;
const char *value_start = NULL; const char *value_start = NULL;
var_end = strchr(line, '='); var_end = strchr(line, '=');
if (var_end == NULL) if (var_end == NULL)
var_end = strchr(line, '\0'); var_end = strchr(line, '\0');
else else
value_start = var_end + 1; value_start = var_end + 1;
if (isspace(var_end[-1])) { if (isspace(var_end[-1])) {
do var_end--; do var_end--;
while (isspace(var_end[0])); while (isspace(var_end[0]));
} }
if (value_start != NULL) { if (value_start != NULL) {
while (isspace(value_start[0])) while (isspace(value_start[0]))
value_start++; value_start++;
if (value_start[0] == '\0') if (value_start[0] == '\0')
goto error; goto error;
} }
return GIT_SUCCESS; return GIT_SUCCESS;
error: error:
return GIT_EOBJCORRUPTED; return GIT_EOBJCORRUPTED;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment