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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
diff --git a/src/config_file.c b/src/config_file.c
index 3c906e5..c6c9ef5 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, ¤t_section)) == 0 && on_section)
- result = on_section(&reader, current_section, data);
+ if ((result = parse_section_header(reader, ¤t_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;
}