summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@microsoft.com>2015-04-27 10:43:50 -0400
committerEdward Thomson <ethomson@edwardthomson.com>2015-05-04 07:41:35 -0500
commit2a950c945a24fd279f89469c1a2e78772378fb14 (patch)
tree9c9af4063225935ab1071c5ebdcef7ee69c9e069
parentbf99390eefb66c03ad56cd5131078817092d3322 (diff)
downloadlibgit2-2a950c945a24fd279f89469c1a2e78772378fb14.tar.gz
config: write existing lines as-is when rewriting
When updating a configuration file, we want to copy the old data from the file to preserve comments and funny whitespace, instead of writing it in some "canonical" format. Thus, we keep a pointer to the start of the line and the line length to preserve these things we don't care to rewrite.
-rw-r--r--src/config_file.c98
1 files changed, 70 insertions, 28 deletions
diff --git a/src/config_file.c b/src/config_file.c
index 3c906e522..c6c9ef5e2 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -830,7 +830,7 @@ static int reader_getchar_raw(struct reader *reader)
if (c == 0) {
reader->eof = 1;
- c = '\n';
+ c = '\0';
}
return c;
@@ -849,13 +849,12 @@ static int reader_getchar(struct reader *reader, int flags)
do {
c = reader_getchar_raw(reader);
- } while (skip_whitespace && git__isspace(c) &&
- !reader->eof);
+ } while (c != '\n' && c != '\0' && skip_whitespace && git__isspace(c));
if (skip_comments && (c == '#' || c == ';')) {
do {
c = reader_getchar_raw(reader);
- } while (c != '\n');
+ } while (c != '\n' && c != '\0');
}
return c;
@@ -1423,22 +1422,26 @@ on_error:
static int config_parse(
struct reader *reader,
- int (*on_section)(struct reader **reader, const char *current_section, void *data),
- int (*on_variable)(struct reader **reader, const char *current_section, char *var_name, char *var_value, void *data),
+ int (*on_section)(struct reader **reader, const char *current_section, const char *line, size_t line_len, void *data),
+ int (*on_variable)(struct reader **reader, const char *current_section, char *var_name, char *var_value, const char *line, size_t line_len, void *data),
+ int (*on_comment)(struct reader **reader, const char *line, size_t line_len, void *data),
int (*on_eof)(struct reader **reader, void *data),
void *data)
{
- char *current_section = NULL, *var_name, *var_value;
+ char *current_section = NULL, *var_name, *var_value, *line_start;
char c;
+ size_t line_len;
int result = 0;
skip_bom(reader);
while (result == 0 && !reader->eof) {
+ line_start = reader->read_ptr;
+
c = reader_peek(reader, SKIP_WHITESPACE);
switch (c) {
- case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */
+ case '\0': /* EOF when peeking, set EOF in the reader to exit the loop */
reader->eof = 1;
break;
@@ -1446,19 +1449,28 @@ static int config_parse(
git__free(current_section);
current_section = NULL;
- if ((result = parse_section_header(reader, &current_section)) == 0 && on_section)
- result = on_section(&reader, current_section, data);
+ if ((result = parse_section_header(reader, &current_section)) == 0 && on_section) {
+ line_len = reader->read_ptr - line_start;
+ result = on_section(&reader, current_section, line_start, line_len, data);
+ }
break;
+ case '\n': /* comment or whitespace-only */
case ';':
case '#':
- /* TODO: handle comments */
reader_consume_line(reader);
+
+ if (on_comment) {
+ line_len = reader->read_ptr - line_start;
+ result = on_comment(&reader, line_start, line_len, data);
+ }
break;
default: /* assume variable declaration */
- if ((result = parse_variable(reader, &var_name, &var_value)) == 0 && on_variable)
- result = on_variable(&reader, current_section, var_name, var_value, data);
+ if ((result = parse_variable(reader, &var_name, &var_value)) == 0 && on_variable) {
+ line_len = reader->read_ptr - line_start;
+ result = on_variable(&reader, current_section, var_name, var_value, line_start, line_len, data);
+ }
break;
}
}
@@ -1478,7 +1490,14 @@ struct parse_data {
int depth;
};
-static int read_on_variable(struct reader **reader, const char *current_section, char *var_name, char *var_value, void *data)
+static int read_on_variable(
+ struct reader **reader,
+ const char *current_section,
+ char *var_name,
+ char *var_value,
+ const char *line,
+ size_t line_len,
+ void *data)
{
struct parse_data *parse_data = (struct parse_data *)data;
git_buf buf = GIT_BUF_INIT;
@@ -1577,7 +1596,7 @@ static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct re
parse_data.level = level;
parse_data.depth = depth;
- return config_parse(reader, NULL, read_on_variable, NULL, &parse_data);
+ return config_parse(reader, NULL, read_on_variable, NULL, NULL, &parse_data);
}
static int write_section(git_filebuf *file, const char *key)
@@ -1638,6 +1657,16 @@ struct write_data {
const char *value;
};
+static int write_line(struct write_data *write_data, const char *line, size_t line_len)
+{
+ int result = git_filebuf_write(write_data->file, line, line_len);
+
+ if (!result && line_len && line[line_len-1] != '\n')
+ result = git_filebuf_printf(write_data->file, "\n");
+
+ return result;
+}
+
static int write_value(struct write_data *write_data)
{
const char *q;
@@ -1657,7 +1686,12 @@ static int write_value(struct write_data *write_data)
return result;
}
-static int write_on_section(struct reader **reader, const char *current_section, void *data)
+static int write_on_section(
+ struct reader **reader,
+ const char *current_section,
+ const char *line,
+ size_t line_len,
+ void *data)
{
struct write_data *write_data = (struct write_data *)data;
int result = 0;
@@ -1672,14 +1706,20 @@ static int write_on_section(struct reader **reader, const char *current_section,
write_data->in_section = strcmp(current_section, write_data->section) == 0;
- /* todo: no, write what's there */
if (!result)
- result = write_section(write_data->file, current_section);
+ result = write_line(write_data, line, line_len);
return result;
}
-static int write_on_variable(struct reader **reader, const char *current_section, char *var_name, char *var_value, void *data)
+static int write_on_variable(
+ struct reader **reader,
+ const char *current_section,
+ char *var_name,
+ char *var_value,
+ const char *line,
+ size_t line_len,
+ void *data)
{
struct write_data *write_data = (struct write_data *)data;
bool has_matched = false;
@@ -1694,18 +1734,14 @@ static int write_on_variable(struct reader **reader, const char *current_section
if (has_matched && write_data->preg != NULL)
has_matched = (regexec(write_data->preg, var_value, 0, NULL, 0) == 0);
- // TODO: do this
-// git__free(var_name);
-// git__free(var_value);
+ git__free(var_name);
+ git__free(var_value);
/* If this isn't the name/value we're looking for, simply dump the
* existing data back out and continue on.
*/
- if (!has_matched) {
- // TODO: write write write
- const char *q = quotes_for_value(var_value);
- return git_filebuf_printf(write_data->file, "\t%s = %s%s%s\n", var_name, q, var_value, q);
- }
+ if (!has_matched)
+ return write_line(write_data, line, line_len);
write_data->preg_replaced = 1;
@@ -1716,6 +1752,12 @@ static int write_on_variable(struct reader **reader, const char *current_section
return write_value(write_data);
}
+static int write_on_comment(struct reader **reader, const char *line, size_t line_len, void *data)
+{
+ struct write_data *write_data = (struct write_data *)data;
+ return write_line(write_data, line, line_len);
+}
+
static int write_on_eof(struct reader **reader, void *data)
{
struct write_data *write_data = (struct write_data *)data;
@@ -1785,7 +1827,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
write_data.preg = preg;
write_data.value = value;
- if ((result = config_parse(reader, write_on_section, write_on_variable, write_on_eof, &write_data)) < 0) {
+ if ((result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data)) < 0) {
git_filebuf_cleanup(&file);
goto done;
}