rugged 0.23.0b1 → 0.23.0b2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/ext/rugged/rugged_note.c +7 -2
  3. data/ext/rugged/rugged_revwalk.c +321 -145
  4. data/lib/rugged/version.rb +1 -1
  5. data/vendor/libgit2/CMakeLists.txt +83 -8
  6. data/vendor/libgit2/cmake/Modules/FindCoreFoundation.cmake +9 -0
  7. data/vendor/libgit2/cmake/Modules/FindIconv.cmake +0 -3
  8. data/vendor/libgit2/cmake/Modules/FindSecurity.cmake +9 -0
  9. data/vendor/libgit2/deps/winhttp/urlmon.h +45 -0
  10. data/vendor/libgit2/deps/winhttp/winhttp.def +29 -0
  11. data/vendor/libgit2/deps/winhttp/winhttp.h +592 -0
  12. data/vendor/libgit2/deps/winhttp/winhttp64.def +29 -0
  13. data/vendor/libgit2/include/git2/diff.h +6 -1
  14. data/vendor/libgit2/include/git2/index.h +1 -1
  15. data/vendor/libgit2/include/git2/merge.h +2 -1
  16. data/vendor/libgit2/include/git2/notes.h +2 -2
  17. data/vendor/libgit2/include/git2/pack.h +12 -0
  18. data/vendor/libgit2/include/git2/push.h +30 -0
  19. data/vendor/libgit2/include/git2/rebase.h +36 -16
  20. data/vendor/libgit2/include/git2/remote.h +6 -0
  21. data/vendor/libgit2/include/git2/reset.h +2 -2
  22. data/vendor/libgit2/include/git2/revwalk.h +10 -8
  23. data/vendor/libgit2/include/git2/submodule.h +16 -0
  24. data/vendor/libgit2/include/git2/sys/transport.h +12 -4
  25. data/vendor/libgit2/include/git2/types.h +1 -0
  26. data/vendor/libgit2/src/attr.c +3 -3
  27. data/vendor/libgit2/src/attr_file.c +24 -3
  28. data/vendor/libgit2/src/attr_file.h +3 -1
  29. data/vendor/libgit2/src/checkout.c +31 -7
  30. data/vendor/libgit2/src/config.c +5 -6
  31. data/vendor/libgit2/src/config_file.c +533 -469
  32. data/vendor/libgit2/src/describe.c +1 -1
  33. data/vendor/libgit2/src/diff.c +20 -10
  34. data/vendor/libgit2/src/diff_driver.c +1 -1
  35. data/vendor/libgit2/src/diff_tform.c +8 -2
  36. data/vendor/libgit2/src/filter.c +6 -3
  37. data/vendor/libgit2/src/global.c +17 -15
  38. data/vendor/libgit2/src/global.h +3 -1
  39. data/vendor/libgit2/src/ignore.c +48 -8
  40. data/vendor/libgit2/src/ignore.h +1 -1
  41. data/vendor/libgit2/src/index.c +12 -8
  42. data/vendor/libgit2/src/iterator.c +133 -12
  43. data/vendor/libgit2/src/netops.h +2 -2
  44. data/vendor/libgit2/src/notes.c +40 -21
  45. data/vendor/libgit2/src/openssl_stream.c +5 -1
  46. data/vendor/libgit2/src/pack-objects.c +36 -0
  47. data/vendor/libgit2/src/path.c +277 -140
  48. data/vendor/libgit2/src/path.h +132 -60
  49. data/vendor/libgit2/src/posix.h +0 -1
  50. data/vendor/libgit2/src/push.c +43 -4
  51. data/vendor/libgit2/src/push.h +8 -1
  52. data/vendor/libgit2/src/rebase.c +139 -119
  53. data/vendor/libgit2/src/reflog.c +1 -1
  54. data/vendor/libgit2/src/refs.c +3 -5
  55. data/vendor/libgit2/src/remote.c +6 -5
  56. data/vendor/libgit2/src/repository.c +7 -3
  57. data/vendor/libgit2/src/reset.c +3 -3
  58. data/vendor/libgit2/src/revwalk.c +26 -2
  59. data/vendor/libgit2/src/settings.c +3 -3
  60. data/vendor/libgit2/src/stransport_stream.c +249 -0
  61. data/vendor/libgit2/src/stransport_stream.h +14 -0
  62. data/vendor/libgit2/src/submodule.c +26 -2
  63. data/vendor/libgit2/src/tls_stream.c +28 -0
  64. data/vendor/libgit2/src/tls_stream.h +21 -0
  65. data/vendor/libgit2/src/transport.c +4 -4
  66. data/vendor/libgit2/src/transports/git.c +4 -1
  67. data/vendor/libgit2/src/transports/http.c +6 -4
  68. data/vendor/libgit2/src/transports/local.c +2 -1
  69. data/vendor/libgit2/src/transports/smart.c +1 -1
  70. data/vendor/libgit2/src/transports/ssh.c +5 -1
  71. data/vendor/libgit2/src/transports/winhttp.c +30 -23
  72. data/vendor/libgit2/src/tree.c +2 -2
  73. data/vendor/libgit2/src/unix/posix.h +1 -0
  74. data/vendor/libgit2/src/util.h +117 -0
  75. data/vendor/libgit2/src/win32/buffer.c +55 -0
  76. data/vendor/libgit2/src/win32/buffer.h +18 -0
  77. data/vendor/libgit2/src/win32/path_w32.c +75 -0
  78. data/vendor/libgit2/src/win32/path_w32.h +3 -0
  79. data/vendor/libgit2/src/win32/posix.h +2 -2
  80. data/vendor/libgit2/src/win32/posix_w32.c +2 -118
  81. data/vendor/libgit2/src/win32/pthread.c +2 -0
  82. data/vendor/libgit2/src/win32/utf-conv.c +0 -4
  83. data/vendor/libgit2/src/win32/utf-conv.h +4 -0
  84. data/vendor/libgit2/src/win32/w32_util.h +72 -0
  85. metadata +14 -2
@@ -114,8 +114,7 @@ typedef struct {
114
114
  diskfile_backend *snapshot_from;
115
115
  } diskfile_readonly_backend;
116
116
 
117
- static int config_parse(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
118
- static int parse_variable(struct reader *reader, char **var_name, char **var_value);
117
+ static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
119
118
  static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
120
119
  static char *escape_value(const char *ptr);
121
120
 
@@ -288,7 +287,7 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
288
287
  if (res == GIT_ENOTFOUND)
289
288
  return 0;
290
289
 
291
- if (res < 0 || (res = config_parse(b->header.values->values, b, reader, level, 0)) < 0) {
290
+ if (res < 0 || (res = config_read(b->header.values->values, b, reader, level, 0)) < 0) {
292
291
  refcounted_strmap_free(b->header.values);
293
292
  b->header.values = NULL;
294
293
  }
@@ -313,7 +312,7 @@ static int config__refresh(git_config_backend *cfg)
313
312
  reader = git_array_get(b->readers, git_array_size(b->readers) - 1);
314
313
  GITERR_CHECK_ALLOC(reader);
315
314
 
316
- if ((error = config_parse(values->values, b, reader, b->level, 0)) < 0)
315
+ if ((error = config_read(values->values, b, reader, b->level, 0)) < 0)
317
316
  goto out;
318
317
 
319
318
  git_mutex_lock(&b->header.values_mutex);
@@ -576,7 +575,7 @@ static int config_set_multivar(
576
575
  }
577
576
 
578
577
  result = regcomp(&preg, regexp, REG_EXTENDED);
579
- if (result < 0) {
578
+ if (result != 0) {
580
579
  giterr_set_regex(&preg, result);
581
580
  result = -1;
582
581
  goto out;
@@ -662,7 +661,7 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
662
661
  refcounted_strmap_free(map);
663
662
 
664
663
  result = regcomp(&preg, regexp, REG_EXTENDED);
665
- if (result < 0) {
664
+ if (result != 0) {
666
665
  giterr_set_regex(&preg, result);
667
666
  result = -1;
668
667
  goto out;
@@ -831,7 +830,7 @@ static int reader_getchar_raw(struct reader *reader)
831
830
 
832
831
  if (c == 0) {
833
832
  reader->eof = 1;
834
- c = '\n';
833
+ c = '\0';
835
834
  }
836
835
 
837
836
  return c;
@@ -850,13 +849,12 @@ static int reader_getchar(struct reader *reader, int flags)
850
849
 
851
850
  do {
852
851
  c = reader_getchar_raw(reader);
853
- } while (skip_whitespace && git__isspace(c) &&
854
- !reader->eof);
852
+ } while (c != '\n' && c != '\0' && skip_whitespace && git__isspace(c));
855
853
 
856
854
  if (skip_comments && (c == '#' || c == ';')) {
857
855
  do {
858
856
  c = reader_getchar_raw(reader);
859
- } while (c != '\n');
857
+ } while (c != '\n' && c != '\0');
860
858
  }
861
859
 
862
860
  return c;
@@ -1162,17 +1160,24 @@ static int skip_bom(struct reader *reader)
1162
1160
 
1163
1161
  static int strip_comments(char *line, int in_quotes)
1164
1162
  {
1165
- int quote_count = in_quotes;
1163
+ int quote_count = in_quotes, backslash_count = 0;
1166
1164
  char *ptr;
1167
1165
 
1168
1166
  for (ptr = line; *ptr; ++ptr) {
1169
1167
  if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
1170
1168
  quote_count++;
1171
1169
 
1172
- if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {
1170
+ if ((ptr[0] == ';' || ptr[0] == '#') &&
1171
+ (quote_count % 2) == 0 &&
1172
+ (backslash_count % 2) == 0) {
1173
1173
  ptr[0] = '\0';
1174
1174
  break;
1175
1175
  }
1176
+
1177
+ if (ptr[0] == '\\')
1178
+ backslash_count++;
1179
+ else
1180
+ backslash_count = 0;
1176
1181
  }
1177
1182
 
1178
1183
  /* skip any space at the end */
@@ -1193,395 +1198,6 @@ static int included_path(git_buf *out, const char *dir, const char *path)
1193
1198
  return git_path_join_unrooted(out, path, dir, NULL);
1194
1199
  }
1195
1200
 
1196
- static int config_parse(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
1197
- {
1198
- int c;
1199
- char *current_section = NULL;
1200
- char *var_name;
1201
- char *var_value;
1202
- cvar_t *var;
1203
- git_buf buf = GIT_BUF_INIT;
1204
- int result = 0;
1205
- uint32_t reader_idx;
1206
-
1207
- if (depth >= MAX_INCLUDE_DEPTH) {
1208
- giterr_set(GITERR_CONFIG, "Maximum config include depth reached");
1209
- return -1;
1210
- }
1211
-
1212
- reader_idx = git_array_size(cfg_file->readers) - 1;
1213
- /* Initialize the reading position */
1214
- reader->read_ptr = reader->buffer.ptr;
1215
- reader->eof = 0;
1216
-
1217
- /* If the file is empty, there's nothing for us to do */
1218
- if (*reader->read_ptr == '\0')
1219
- return 0;
1220
-
1221
- skip_bom(reader);
1222
-
1223
- while (result == 0 && !reader->eof) {
1224
-
1225
- c = reader_peek(reader, SKIP_WHITESPACE);
1226
-
1227
- switch (c) {
1228
- case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */
1229
- reader->eof = 1;
1230
- break;
1231
-
1232
- case '[': /* section header, new section begins */
1233
- git__free(current_section);
1234
- current_section = NULL;
1235
- result = parse_section_header(reader, &current_section);
1236
- break;
1237
-
1238
- case ';':
1239
- case '#':
1240
- reader_consume_line(reader);
1241
- break;
1242
-
1243
- default: /* assume variable declaration */
1244
- result = parse_variable(reader, &var_name, &var_value);
1245
- if (result < 0)
1246
- break;
1247
-
1248
- git__strtolower(var_name);
1249
- git_buf_printf(&buf, "%s.%s", current_section, var_name);
1250
- git__free(var_name);
1251
-
1252
- if (git_buf_oom(&buf)) {
1253
- git__free(var_value);
1254
- return -1;
1255
- }
1256
-
1257
- var = git__calloc(1, sizeof(cvar_t));
1258
- GITERR_CHECK_ALLOC(var);
1259
- var->entry = git__calloc(1, sizeof(git_config_entry));
1260
- GITERR_CHECK_ALLOC(var->entry);
1261
-
1262
- var->entry->name = git_buf_detach(&buf);
1263
- var->entry->value = var_value;
1264
- var->entry->level = level;
1265
- var->included = !!depth;
1266
-
1267
-
1268
- if ((result = append_entry(values, var)) < 0)
1269
- break;
1270
- else
1271
- result = 0;
1272
-
1273
- /* Add or append the new config option */
1274
- if (!git__strcmp(var->entry->name, "include.path")) {
1275
- struct reader *r;
1276
- git_buf path = GIT_BUF_INIT;
1277
- char *dir;
1278
- uint32_t index;
1279
-
1280
- r = git_array_alloc(cfg_file->readers);
1281
- /* The reader may have been reallocated */
1282
- reader = git_array_get(cfg_file->readers, reader_idx);
1283
- memset(r, 0, sizeof(struct reader));
1284
- if ((result = git_path_dirname_r(&path, reader->file_path)) < 0)
1285
- break;
1286
-
1287
- /* We need to know our index in the array, as the next config_parse call may realloc */
1288
- index = git_array_size(cfg_file->readers) - 1;
1289
- dir = git_buf_detach(&path);
1290
- result = included_path(&path, dir, var->entry->value);
1291
- git__free(dir);
1292
-
1293
- if (result < 0)
1294
- break;
1295
-
1296
- r->file_path = git_buf_detach(&path);
1297
- git_buf_init(&r->buffer, 0);
1298
- result = git_futils_readbuffer_updated(&r->buffer, r->file_path, &r->file_mtime,
1299
- &r->file_size, NULL);
1300
-
1301
- if (result == 0) {
1302
- result = config_parse(values, cfg_file, r, level, depth+1);
1303
- r = git_array_get(cfg_file->readers, index);
1304
- reader = git_array_get(cfg_file->readers, reader_idx);
1305
- }
1306
- else if (result == GIT_ENOTFOUND) {
1307
- giterr_clear();
1308
- result = 0;
1309
- }
1310
-
1311
- git_buf_free(&r->buffer);
1312
-
1313
- if (result < 0)
1314
- break;
1315
- }
1316
-
1317
- break;
1318
- }
1319
- }
1320
-
1321
- git__free(current_section);
1322
- return result;
1323
- }
1324
-
1325
- static int write_section(git_filebuf *file, const char *key)
1326
- {
1327
- int result;
1328
- const char *dot;
1329
- git_buf buf = GIT_BUF_INIT;
1330
-
1331
- /* All of this just for [section "subsection"] */
1332
- dot = strchr(key, '.');
1333
- git_buf_putc(&buf, '[');
1334
- if (dot == NULL) {
1335
- git_buf_puts(&buf, key);
1336
- } else {
1337
- char *escaped;
1338
- git_buf_put(&buf, key, dot - key);
1339
- escaped = escape_value(dot + 1);
1340
- GITERR_CHECK_ALLOC(escaped);
1341
- git_buf_printf(&buf, " \"%s\"", escaped);
1342
- git__free(escaped);
1343
- }
1344
- git_buf_puts(&buf, "]\n");
1345
-
1346
- if (git_buf_oom(&buf))
1347
- return -1;
1348
-
1349
- result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
1350
- git_buf_free(&buf);
1351
-
1352
- return result;
1353
- }
1354
-
1355
- static const char *quotes_for_value(const char *value)
1356
- {
1357
- const char *ptr;
1358
-
1359
- if (value[0] == ' ' || value[0] == '\0')
1360
- return "\"";
1361
-
1362
- for (ptr = value; *ptr; ++ptr) {
1363
- if (*ptr == ';' || *ptr == '#')
1364
- return "\"";
1365
- }
1366
-
1367
- if (ptr[-1] == ' ')
1368
- return "\"";
1369
-
1370
- return "";
1371
- }
1372
-
1373
- /*
1374
- * This is pretty much the parsing, except we write out anything we don't have
1375
- */
1376
- static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
1377
- {
1378
- int result, c;
1379
- int section_matches = 0, last_section_matched = 0, preg_replaced = 0, write_trailer = 0;
1380
- const char *pre_end = NULL, *post_start = NULL, *data_start, *write_start;
1381
- char *current_section = NULL, *section, *name, *ldot;
1382
- git_filebuf file = GIT_FILEBUF_INIT;
1383
- struct reader *reader = git_array_get(cfg->readers, 0);
1384
-
1385
- /* We need to read in our own config file */
1386
- result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
1387
-
1388
- /* Initialise the reading position */
1389
- if (result == GIT_ENOTFOUND) {
1390
- reader->read_ptr = NULL;
1391
- reader->eof = 1;
1392
- data_start = NULL;
1393
- git_buf_clear(&reader->buffer);
1394
- } else if (result == 0) {
1395
- reader->read_ptr = reader->buffer.ptr;
1396
- reader->eof = 0;
1397
- data_start = reader->read_ptr;
1398
- } else {
1399
- return -1; /* OS error when reading the file */
1400
- }
1401
-
1402
- write_start = data_start;
1403
-
1404
- /* Lock the file */
1405
- if ((result = git_filebuf_open(
1406
- &file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) {
1407
- git_buf_free(&reader->buffer);
1408
- return result;
1409
- }
1410
-
1411
- skip_bom(reader);
1412
- ldot = strrchr(key, '.');
1413
- name = ldot + 1;
1414
- section = git__strndup(key, ldot - key);
1415
-
1416
- while (!reader->eof) {
1417
- c = reader_peek(reader, SKIP_WHITESPACE);
1418
-
1419
- if (c == '\0') { /* We've arrived at the end of the file */
1420
- break;
1421
-
1422
- } else if (c == '[') { /* section header, new section begins */
1423
- /*
1424
- * We set both positions to the current one in case we
1425
- * need to add a variable to the end of a section. In that
1426
- * case, we want both variables to point just before the
1427
- * new section. If we actually want to replace it, the
1428
- * default case will take care of updating them.
1429
- */
1430
- pre_end = post_start = reader->read_ptr;
1431
-
1432
- git__free(current_section);
1433
- current_section = NULL;
1434
- if (parse_section_header(reader, &current_section) < 0)
1435
- goto rewrite_fail;
1436
-
1437
- /* Keep track of when it stops matching */
1438
- last_section_matched = section_matches;
1439
- section_matches = !strcmp(current_section, section);
1440
- }
1441
-
1442
- else if (c == ';' || c == '#') {
1443
- reader_consume_line(reader);
1444
- }
1445
-
1446
- else {
1447
- /*
1448
- * If the section doesn't match, but the last section did,
1449
- * it means we need to add a variable (so skip the line
1450
- * otherwise). If both the section and name match, we need
1451
- * to overwrite the variable (so skip the line
1452
- * otherwise). pre_end needs to be updated each time so we
1453
- * don't loose that information, but we only need to
1454
- * update post_start if we're going to use it in this
1455
- * iteration.
1456
- */
1457
- if (!section_matches) {
1458
- if (!last_section_matched) {
1459
- reader_consume_line(reader);
1460
- continue;
1461
- }
1462
- } else {
1463
- int has_matched = 0;
1464
- char *var_name, *var_value;
1465
-
1466
- pre_end = reader->read_ptr;
1467
- if (parse_variable(reader, &var_name, &var_value) < 0)
1468
- goto rewrite_fail;
1469
-
1470
- /* First try to match the name of the variable */
1471
- if (strcasecmp(name, var_name) == 0)
1472
- has_matched = 1;
1473
-
1474
- /* If the name matches, and we have a regex to match the
1475
- * value, try to match it */
1476
- if (has_matched && preg != NULL)
1477
- has_matched = (regexec(preg, var_value, 0, NULL, 0) == 0);
1478
-
1479
- git__free(var_name);
1480
- git__free(var_value);
1481
-
1482
- /* if there is no match, keep going */
1483
- if (!has_matched)
1484
- continue;
1485
-
1486
- post_start = reader->read_ptr;
1487
- }
1488
-
1489
- /* We've found the variable we wanted to change, so
1490
- * write anything up to it */
1491
- git_filebuf_write(&file, write_start, pre_end - write_start);
1492
- preg_replaced = 1;
1493
-
1494
- /* Then replace the variable. If the value is NULL, it
1495
- * means we want to delete it, so don't write anything. */
1496
- if (value != NULL) {
1497
- const char *q = quotes_for_value(value);
1498
- git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q);
1499
- }
1500
-
1501
- /*
1502
- * If we have a multivar, we should keep looking for entries,
1503
- * but only if we're in the right section. Otherwise we'll end up
1504
- * looping on the edge of a matching and a non-matching section.
1505
- */
1506
- if (section_matches && preg != NULL) {
1507
- write_start = post_start;
1508
- continue;
1509
- }
1510
-
1511
- write_trailer = 1;
1512
- break; /* break from the loop */
1513
- }
1514
- }
1515
-
1516
- /*
1517
- * Being here can mean that
1518
- *
1519
- * 1) our section is the last one in the file and we're
1520
- * adding a variable
1521
- *
1522
- * 2) we didn't find a section for us so we need to create it
1523
- * ourselves.
1524
- *
1525
- * 3) we're setting a multivar with a regex, which means we
1526
- * continue to search for matching values
1527
- *
1528
- * In the last case, if we've already replaced a value, we
1529
- * want to write the rest of the file. Otherwise we need to write
1530
- * out the whole file and then the new variable.
1531
- */
1532
- if (write_trailer) {
1533
- /* Write out rest of the file */
1534
- git_filebuf_write(&file, post_start, reader->buffer.size - (post_start - data_start));
1535
- } else {
1536
- if (preg_replaced) {
1537
- git_filebuf_printf(&file, "\n%s", write_start);
1538
- } else {
1539
- const char *q;
1540
-
1541
- git_filebuf_write(&file, reader->buffer.ptr, reader->buffer.size);
1542
-
1543
- if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n')
1544
- git_filebuf_write(&file, "\n", 1);
1545
-
1546
- /* And now if we just need to add a variable */
1547
- if (!section_matches && write_section(&file, section) < 0)
1548
- goto rewrite_fail;
1549
-
1550
- /* Sanity check: if we are here, and value is NULL, that means that somebody
1551
- * touched the config file after our initial read. We should probably assert()
1552
- * this, but instead we'll handle it gracefully with an error. */
1553
- if (value == NULL) {
1554
- giterr_set(GITERR_CONFIG,
1555
- "race condition when writing a config file (a cvar has been removed)");
1556
- goto rewrite_fail;
1557
- }
1558
-
1559
- /* If we are here, there is at least a section line */
1560
- q = quotes_for_value(value);
1561
- git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q);
1562
- }
1563
- }
1564
-
1565
- git__free(section);
1566
- git__free(current_section);
1567
-
1568
- /* refresh stats - if this errors, then commit will error too */
1569
- (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file);
1570
-
1571
- result = git_filebuf_commit(&file);
1572
- git_buf_free(&reader->buffer);
1573
-
1574
- return result;
1575
-
1576
- rewrite_fail:
1577
- git__free(section);
1578
- git__free(current_section);
1579
-
1580
- git_filebuf_cleanup(&file);
1581
- git_buf_free(&reader->buffer);
1582
- return -1;
1583
- }
1584
-
1585
1201
  static const char *escapes = "ntb\"\\";
1586
1202
  static const char *escaped = "\n\t\b\"\\";
1587
1203
 
@@ -1619,76 +1235,69 @@ static char *escape_value(const char *ptr)
1619
1235
  }
1620
1236
 
1621
1237
  /* '\"' -> '"' etc */
1622
- static char *fixup_line(const char *ptr, int quote_count)
1238
+ static int unescape_line(
1239
+ char **out, bool *is_multi, const char *ptr, int quote_count)
1623
1240
  {
1624
- char *str, *out, *esc;
1241
+ char *str, *fixed, *esc;
1625
1242
  size_t ptr_len = strlen(ptr), alloc_len;
1626
1243
 
1244
+ *is_multi = false;
1245
+
1627
1246
  if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, ptr_len, 1) ||
1628
1247
  (str = git__malloc(alloc_len)) == NULL) {
1629
- return NULL;
1248
+ return -1;
1630
1249
  }
1631
1250
 
1632
- out = str;
1251
+ fixed = str;
1633
1252
 
1634
1253
  while (*ptr != '\0') {
1635
1254
  if (*ptr == '"') {
1636
1255
  quote_count++;
1637
1256
  } else if (*ptr != '\\') {
1638
- *out++ = *ptr;
1257
+ *fixed++ = *ptr;
1639
1258
  } else {
1640
1259
  /* backslash, check the next char */
1641
1260
  ptr++;
1642
1261
  /* if we're at the end, it's a multiline, so keep the backslash */
1643
1262
  if (*ptr == '\0') {
1644
- *out++ = '\\';
1645
- goto out;
1263
+ *is_multi = true;
1264
+ goto done;
1646
1265
  }
1647
1266
  if ((esc = strchr(escapes, *ptr)) != NULL) {
1648
- *out++ = escaped[esc - escapes];
1267
+ *fixed++ = escaped[esc - escapes];
1649
1268
  } else {
1650
1269
  git__free(str);
1651
1270
  giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
1652
- return NULL;
1271
+ return -1;
1653
1272
  }
1654
1273
  }
1655
1274
  ptr++;
1656
1275
  }
1657
1276
 
1658
- out:
1659
- *out = '\0';
1660
-
1661
- return str;
1662
- }
1663
-
1664
- static int is_multiline_var(const char *str)
1665
- {
1666
- int count = 0;
1667
- const char *end = str + strlen(str);
1668
- while (end > str && end[-1] == '\\') {
1669
- count++;
1670
- end--;
1671
- }
1277
+ done:
1278
+ *fixed = '\0';
1279
+ *out = str;
1672
1280
 
1673
- /* An odd number means last backslash wasn't escaped, so it's multiline */
1674
- return count & 1;
1281
+ return 0;
1675
1282
  }
1676
1283
 
1677
1284
  static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
1678
1285
  {
1679
1286
  char *line = NULL, *proc_line = NULL;
1680
1287
  int quote_count;
1288
+ bool multiline;
1681
1289
 
1682
1290
  /* Check that the next line exists */
1683
1291
  line = reader_readline(reader, false);
1684
1292
  if (line == NULL)
1685
1293
  return -1;
1686
1294
 
1687
- /* We've reached the end of the file, there is input missing */
1295
+ /* We've reached the end of the file, there is no continuation.
1296
+ * (this is not an error).
1297
+ */
1688
1298
  if (line[0] == '\0') {
1689
- set_parse_error(reader, 0, "Unexpected end of file while parsing multine var");
1690
1299
  git__free(line);
1691
- return -1;
1300
+ return 0;
1692
1301
  }
1693
1302
 
1694
1303
  quote_count = strip_comments(line, !!in_quotes);
@@ -1700,18 +1309,12 @@ static int parse_multiline_variable(struct reader *reader, git_buf *value, int i
1700
1309
  /* TODO: unbounded recursion. This **could** be exploitable */
1701
1310
  }
1702
1311
 
1703
- /* Drop the continuation character '\': to closely follow the UNIX
1704
- * standard, this character **has** to be last one in the buf, with
1705
- * no whitespace after it */
1706
- assert(is_multiline_var(value->ptr));
1707
- git_buf_shorten(value, 1);
1708
-
1709
- proc_line = fixup_line(line, in_quotes);
1710
- if (proc_line == NULL) {
1312
+ if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) {
1711
1313
  git__free(line);
1712
1314
  return -1;
1713
1315
  }
1714
1316
  /* add this line to the multiline var */
1317
+
1715
1318
  git_buf_puts(value, proc_line);
1716
1319
  git__free(line);
1717
1320
  git__free(proc_line);
@@ -1720,18 +1323,57 @@ static int parse_multiline_variable(struct reader *reader, git_buf *value, int i
1720
1323
  * If we need to continue reading the next line, let's just
1721
1324
  * keep putting stuff in the buffer
1722
1325
  */
1723
- if (is_multiline_var(value->ptr))
1326
+ if (multiline)
1724
1327
  return parse_multiline_variable(reader, value, quote_count);
1725
1328
 
1726
1329
  return 0;
1727
1330
  }
1728
1331
 
1332
+ GIT_INLINE(bool) is_namechar(char c)
1333
+ {
1334
+ return isalnum(c) || c == '-';
1335
+ }
1336
+
1337
+ static int parse_name(
1338
+ char **name, const char **value, struct reader *reader, const char *line)
1339
+ {
1340
+ const char *name_end = line, *value_start;
1341
+
1342
+ *name = NULL;
1343
+ *value = NULL;
1344
+
1345
+ while (*name_end && is_namechar(*name_end))
1346
+ name_end++;
1347
+
1348
+ if (line == name_end) {
1349
+ set_parse_error(reader, 0, "Invalid configuration key");
1350
+ return -1;
1351
+ }
1352
+
1353
+ value_start = name_end;
1354
+
1355
+ while (*value_start && git__isspace(*value_start))
1356
+ value_start++;
1357
+
1358
+ if (*value_start == '=') {
1359
+ *value = value_start + 1;
1360
+ } else if (*value_start) {
1361
+ set_parse_error(reader, 0, "Invalid configuration key");
1362
+ return -1;
1363
+ }
1364
+
1365
+ if ((*name = git__strndup(line, name_end - line)) == NULL)
1366
+ return -1;
1367
+
1368
+ return 0;
1369
+ }
1370
+
1729
1371
  static int parse_variable(struct reader *reader, char **var_name, char **var_value)
1730
1372
  {
1731
- const char *var_end = NULL;
1732
1373
  const char *value_start = NULL;
1733
1374
  char *line;
1734
1375
  int quote_count;
1376
+ bool multiline;
1735
1377
 
1736
1378
  line = reader_readline(reader, true);
1737
1379
  if (line == NULL)
@@ -1739,22 +1381,12 @@ static int parse_variable(struct reader *reader, char **var_name, char **var_val
1739
1381
 
1740
1382
  quote_count = strip_comments(line, 0);
1741
1383
 
1742
- var_end = strchr(line, '=');
1743
-
1744
- if (var_end == NULL)
1745
- var_end = strchr(line, '\0');
1746
- else
1747
- value_start = var_end + 1;
1748
-
1749
- do var_end--;
1750
- while (var_end>line && git__isspace(*var_end));
1751
-
1752
- *var_name = git__strndup(line, var_end - line + 1);
1753
- GITERR_CHECK_ALLOC(*var_name);
1754
-
1755
1384
  /* If there is no value, boolean true is assumed */
1756
1385
  *var_value = NULL;
1757
1386
 
1387
+ if (parse_name(var_name, &value_start, reader, line) < 0)
1388
+ goto on_error;
1389
+
1758
1390
  /*
1759
1391
  * Now, let's try to parse the value
1760
1392
  */
@@ -1762,31 +1394,463 @@ static int parse_variable(struct reader *reader, char **var_name, char **var_val
1762
1394
  while (git__isspace(value_start[0]))
1763
1395
  value_start++;
1764
1396
 
1765
- if (is_multiline_var(value_start)) {
1397
+ if (unescape_line(var_value, &multiline, value_start, 0) < 0)
1398
+ goto on_error;
1399
+
1400
+ if (multiline) {
1766
1401
  git_buf multi_value = GIT_BUF_INIT;
1767
- char *proc_line = fixup_line(value_start, 0);
1768
- GITERR_CHECK_ALLOC(proc_line);
1769
- git_buf_puts(&multi_value, proc_line);
1770
- git__free(proc_line);
1771
- if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
1772
- git__free(*var_name);
1773
- git__free(line);
1402
+ git_buf_attach(&multi_value, *var_value, 0);
1403
+
1404
+ if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 ||
1405
+ git_buf_oom(&multi_value)) {
1774
1406
  git_buf_free(&multi_value);
1775
- return -1;
1407
+ goto on_error;
1776
1408
  }
1777
1409
 
1778
1410
  *var_value = git_buf_detach(&multi_value);
1779
-
1780
- }
1781
- else if (value_start[0] != '\0') {
1782
- *var_value = fixup_line(value_start, 0);
1783
- GITERR_CHECK_ALLOC(*var_value);
1784
- } else { /* equals sign but missing rhs */
1785
- *var_value = git__strdup("");
1786
- GITERR_CHECK_ALLOC(*var_value);
1787
1411
  }
1788
1412
  }
1789
1413
 
1790
1414
  git__free(line);
1791
1415
  return 0;
1416
+
1417
+ on_error:
1418
+ git__free(*var_name);
1419
+ git__free(line);
1420
+ return -1;
1421
+ }
1422
+
1423
+ static int config_parse(
1424
+ struct reader *reader,
1425
+ int (*on_section)(struct reader **reader, const char *current_section, const char *line, size_t line_len, void *data),
1426
+ 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),
1427
+ int (*on_comment)(struct reader **reader, const char *line, size_t line_len, void *data),
1428
+ int (*on_eof)(struct reader **reader, void *data),
1429
+ void *data)
1430
+ {
1431
+ char *current_section = NULL, *var_name, *var_value, *line_start;
1432
+ char c;
1433
+ size_t line_len;
1434
+ int result = 0;
1435
+
1436
+ skip_bom(reader);
1437
+
1438
+ while (result == 0 && !reader->eof) {
1439
+ line_start = reader->read_ptr;
1440
+
1441
+ c = reader_peek(reader, SKIP_WHITESPACE);
1442
+
1443
+ switch (c) {
1444
+ case '\0': /* EOF when peeking, set EOF in the reader to exit the loop */
1445
+ reader->eof = 1;
1446
+ break;
1447
+
1448
+ case '[': /* section header, new section begins */
1449
+ git__free(current_section);
1450
+ current_section = NULL;
1451
+
1452
+ if ((result = parse_section_header(reader, &current_section)) == 0 && on_section) {
1453
+ line_len = reader->read_ptr - line_start;
1454
+ result = on_section(&reader, current_section, line_start, line_len, data);
1455
+ }
1456
+ break;
1457
+
1458
+ case '\n': /* comment or whitespace-only */
1459
+ case ';':
1460
+ case '#':
1461
+ reader_consume_line(reader);
1462
+
1463
+ if (on_comment) {
1464
+ line_len = reader->read_ptr - line_start;
1465
+ result = on_comment(&reader, line_start, line_len, data);
1466
+ }
1467
+ break;
1468
+
1469
+ default: /* assume variable declaration */
1470
+ if ((result = parse_variable(reader, &var_name, &var_value)) == 0 && on_variable) {
1471
+ line_len = reader->read_ptr - line_start;
1472
+ result = on_variable(&reader, current_section, var_name, var_value, line_start, line_len, data);
1473
+ }
1474
+ break;
1475
+ }
1476
+ }
1477
+
1478
+ if (on_eof)
1479
+ result = on_eof(&reader, data);
1480
+
1481
+ git__free(current_section);
1482
+ return result;
1483
+ }
1484
+
1485
+ struct parse_data {
1486
+ git_strmap *values;
1487
+ diskfile_backend *cfg_file;
1488
+ uint32_t reader_idx;
1489
+ git_config_level_t level;
1490
+ int depth;
1491
+ };
1492
+
1493
+ static int read_on_variable(
1494
+ struct reader **reader,
1495
+ const char *current_section,
1496
+ char *var_name,
1497
+ char *var_value,
1498
+ const char *line,
1499
+ size_t line_len,
1500
+ void *data)
1501
+ {
1502
+ struct parse_data *parse_data = (struct parse_data *)data;
1503
+ git_buf buf = GIT_BUF_INIT;
1504
+ cvar_t *var;
1505
+ int result = 0;
1506
+
1507
+ GIT_UNUSED(line);
1508
+ GIT_UNUSED(line_len);
1509
+
1510
+ git__strtolower(var_name);
1511
+ git_buf_printf(&buf, "%s.%s", current_section, var_name);
1512
+ git__free(var_name);
1513
+
1514
+ if (git_buf_oom(&buf)) {
1515
+ git__free(var_value);
1516
+ return -1;
1517
+ }
1518
+
1519
+ var = git__calloc(1, sizeof(cvar_t));
1520
+ GITERR_CHECK_ALLOC(var);
1521
+ var->entry = git__calloc(1, sizeof(git_config_entry));
1522
+ GITERR_CHECK_ALLOC(var->entry);
1523
+
1524
+ var->entry->name = git_buf_detach(&buf);
1525
+ var->entry->value = var_value;
1526
+ var->entry->level = parse_data->level;
1527
+ var->included = !!parse_data->depth;
1528
+
1529
+ if ((result = append_entry(parse_data->values, var)) < 0)
1530
+ return result;
1531
+
1532
+ result = 0;
1533
+
1534
+ /* Add or append the new config option */
1535
+ if (!git__strcmp(var->entry->name, "include.path")) {
1536
+ struct reader *r;
1537
+ git_buf path = GIT_BUF_INIT;
1538
+ char *dir;
1539
+ uint32_t index;
1540
+
1541
+ r = git_array_alloc(parse_data->cfg_file->readers);
1542
+ /* The reader may have been reallocated */
1543
+ *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx);
1544
+ memset(r, 0, sizeof(struct reader));
1545
+
1546
+ if ((result = git_path_dirname_r(&path, (*reader)->file_path)) < 0)
1547
+ return result;
1548
+
1549
+ /* We need to know our index in the array, as the next config_parse call may realloc */
1550
+ index = git_array_size(parse_data->cfg_file->readers) - 1;
1551
+ dir = git_buf_detach(&path);
1552
+ result = included_path(&path, dir, var->entry->value);
1553
+ git__free(dir);
1554
+
1555
+ if (result < 0)
1556
+ return result;
1557
+
1558
+ r->file_path = git_buf_detach(&path);
1559
+ git_buf_init(&r->buffer, 0);
1560
+
1561
+ result = git_futils_readbuffer_updated(
1562
+ &r->buffer, r->file_path, &r->file_mtime, &r->file_size, NULL);
1563
+
1564
+ if (result == 0) {
1565
+ result = config_read(parse_data->values, parse_data->cfg_file, r, parse_data->level, parse_data->depth+1);
1566
+ r = git_array_get(parse_data->cfg_file->readers, index);
1567
+ *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx);
1568
+ } else if (result == GIT_ENOTFOUND) {
1569
+ giterr_clear();
1570
+ result = 0;
1571
+ }
1572
+
1573
+ git_buf_free(&r->buffer);
1574
+ }
1575
+
1576
+ return result;
1577
+ }
1578
+
1579
+ static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
1580
+ {
1581
+ struct parse_data parse_data;
1582
+
1583
+ if (depth >= MAX_INCLUDE_DEPTH) {
1584
+ giterr_set(GITERR_CONFIG, "Maximum config include depth reached");
1585
+ return -1;
1586
+ }
1587
+
1588
+ /* Initialize the reading position */
1589
+ reader->read_ptr = reader->buffer.ptr;
1590
+ reader->eof = 0;
1591
+
1592
+ /* If the file is empty, there's nothing for us to do */
1593
+ if (*reader->read_ptr == '\0')
1594
+ return 0;
1595
+
1596
+ parse_data.values = values;
1597
+ parse_data.cfg_file = cfg_file;
1598
+ parse_data.reader_idx = git_array_size(cfg_file->readers) - 1;
1599
+ parse_data.level = level;
1600
+ parse_data.depth = depth;
1601
+
1602
+ return config_parse(reader, NULL, read_on_variable, NULL, NULL, &parse_data);
1603
+ }
1604
+
1605
+ static int write_section(git_filebuf *file, const char *key)
1606
+ {
1607
+ int result;
1608
+ const char *dot;
1609
+ git_buf buf = GIT_BUF_INIT;
1610
+
1611
+ /* All of this just for [section "subsection"] */
1612
+ dot = strchr(key, '.');
1613
+ git_buf_putc(&buf, '[');
1614
+ if (dot == NULL) {
1615
+ git_buf_puts(&buf, key);
1616
+ } else {
1617
+ char *escaped;
1618
+ git_buf_put(&buf, key, dot - key);
1619
+ escaped = escape_value(dot + 1);
1620
+ GITERR_CHECK_ALLOC(escaped);
1621
+ git_buf_printf(&buf, " \"%s\"", escaped);
1622
+ git__free(escaped);
1623
+ }
1624
+ git_buf_puts(&buf, "]\n");
1625
+
1626
+ if (git_buf_oom(&buf))
1627
+ return -1;
1628
+
1629
+ result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
1630
+ git_buf_free(&buf);
1631
+
1632
+ return result;
1792
1633
  }
1634
+
1635
+ static const char *quotes_for_value(const char *value)
1636
+ {
1637
+ const char *ptr;
1638
+
1639
+ if (value[0] == ' ' || value[0] == '\0')
1640
+ return "\"";
1641
+
1642
+ for (ptr = value; *ptr; ++ptr) {
1643
+ if (*ptr == ';' || *ptr == '#')
1644
+ return "\"";
1645
+ }
1646
+
1647
+ if (ptr[-1] == ' ')
1648
+ return "\"";
1649
+
1650
+ return "";
1651
+ }
1652
+
1653
+ struct write_data {
1654
+ git_filebuf *file;
1655
+ unsigned int in_section : 1,
1656
+ preg_replaced : 1;
1657
+ const char *section;
1658
+ const char *name;
1659
+ const regex_t *preg;
1660
+ const char *value;
1661
+ };
1662
+
1663
+ static int write_line(struct write_data *write_data, const char *line, size_t line_len)
1664
+ {
1665
+ int result = git_filebuf_write(write_data->file, line, line_len);
1666
+
1667
+ if (!result && line_len && line[line_len-1] != '\n')
1668
+ result = git_filebuf_printf(write_data->file, "\n");
1669
+
1670
+ return result;
1671
+ }
1672
+
1673
+ static int write_value(struct write_data *write_data)
1674
+ {
1675
+ const char *q;
1676
+ int result;
1677
+
1678
+ q = quotes_for_value(write_data->value);
1679
+ result = git_filebuf_printf(write_data->file,
1680
+ "\t%s = %s%s%s\n", write_data->name, q, write_data->value, q);
1681
+
1682
+ /* If we are updating a single name/value, we're done. Setting `value`
1683
+ * to `NULL` will prevent us from trying to write it again later (in
1684
+ * `write_on_section`) if we see the same section repeated.
1685
+ */
1686
+ if (!write_data->preg)
1687
+ write_data->value = NULL;
1688
+
1689
+ return result;
1690
+ }
1691
+
1692
+ static int write_on_section(
1693
+ struct reader **reader,
1694
+ const char *current_section,
1695
+ const char *line,
1696
+ size_t line_len,
1697
+ void *data)
1698
+ {
1699
+ struct write_data *write_data = (struct write_data *)data;
1700
+ int result = 0;
1701
+
1702
+ GIT_UNUSED(reader);
1703
+
1704
+ /* If we were previously in the correct section (but aren't anymore)
1705
+ * and haven't written our value (for a simple name/value set, not
1706
+ * a multivar), then append it to the end of the section before writing
1707
+ * the new one.
1708
+ */
1709
+ if (write_data->in_section && !write_data->preg && write_data->value)
1710
+ result = write_value(write_data);
1711
+
1712
+ write_data->in_section = strcmp(current_section, write_data->section) == 0;
1713
+
1714
+ if (!result)
1715
+ result = write_line(write_data, line, line_len);
1716
+
1717
+ return result;
1718
+ }
1719
+
1720
+ static int write_on_variable(
1721
+ struct reader **reader,
1722
+ const char *current_section,
1723
+ char *var_name,
1724
+ char *var_value,
1725
+ const char *line,
1726
+ size_t line_len,
1727
+ void *data)
1728
+ {
1729
+ struct write_data *write_data = (struct write_data *)data;
1730
+ bool has_matched = false;
1731
+
1732
+ GIT_UNUSED(reader);
1733
+ GIT_UNUSED(current_section);
1734
+
1735
+ /* See if we are to update this name/value pair; first examine name */
1736
+ if (write_data->in_section &&
1737
+ strcasecmp(write_data->name, var_name) == 0)
1738
+ has_matched = true;
1739
+
1740
+ /* If we have a regex to match the value, see if it matches */
1741
+ if (has_matched && write_data->preg != NULL)
1742
+ has_matched = (regexec(write_data->preg, var_value, 0, NULL, 0) == 0);
1743
+
1744
+ git__free(var_name);
1745
+ git__free(var_value);
1746
+
1747
+ /* If this isn't the name/value we're looking for, simply dump the
1748
+ * existing data back out and continue on.
1749
+ */
1750
+ if (!has_matched)
1751
+ return write_line(write_data, line, line_len);
1752
+
1753
+ write_data->preg_replaced = 1;
1754
+
1755
+ /* If value is NULL, we are deleting this value; write nothing. */
1756
+ if (!write_data->value)
1757
+ return 0;
1758
+
1759
+ return write_value(write_data);
1760
+ }
1761
+
1762
+ static int write_on_comment(struct reader **reader, const char *line, size_t line_len, void *data)
1763
+ {
1764
+ struct write_data *write_data;
1765
+
1766
+ GIT_UNUSED(reader);
1767
+
1768
+ write_data = (struct write_data *)data;
1769
+ return write_line(write_data, line, line_len);
1770
+ }
1771
+
1772
+ static int write_on_eof(struct reader **reader, void *data)
1773
+ {
1774
+ struct write_data *write_data = (struct write_data *)data;
1775
+ int result = 0;
1776
+
1777
+ GIT_UNUSED(reader);
1778
+
1779
+ /* If we are at the EOF and have not written our value (again, for a
1780
+ * simple name/value set, not a multivar) then we have never seen the
1781
+ * section in question and should create a new section and write the
1782
+ * value.
1783
+ */
1784
+ if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
1785
+ if ((result = write_section(write_data->file, write_data->section)) == 0)
1786
+ result = write_value(write_data);
1787
+ }
1788
+
1789
+ return result;
1790
+ }
1791
+
1792
+ /*
1793
+ * This is pretty much the parsing, except we write out anything we don't have
1794
+ */
1795
+ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
1796
+ {
1797
+ int result;
1798
+ char *section, *name, *ldot;
1799
+ git_filebuf file = GIT_FILEBUF_INIT;
1800
+ struct reader *reader = git_array_get(cfg->readers, 0);
1801
+ struct write_data write_data;
1802
+
1803
+ /* Lock the file */
1804
+ if ((result = git_filebuf_open(
1805
+ &file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) {
1806
+ git_buf_free(&reader->buffer);
1807
+ return result;
1808
+ }
1809
+
1810
+ /* We need to read in our own config file */
1811
+ result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
1812
+
1813
+ /* Initialise the reading position */
1814
+ if (result == GIT_ENOTFOUND) {
1815
+ reader->read_ptr = NULL;
1816
+ reader->eof = 1;
1817
+ git_buf_clear(&reader->buffer);
1818
+ } else if (result == 0) {
1819
+ reader->read_ptr = reader->buffer.ptr;
1820
+ reader->eof = 0;
1821
+ } else {
1822
+ git_filebuf_cleanup(&file);
1823
+ return -1; /* OS error when reading the file */
1824
+ }
1825
+
1826
+ ldot = strrchr(key, '.');
1827
+ name = ldot + 1;
1828
+ section = git__strndup(key, ldot - key);
1829
+
1830
+ write_data.file = &file;
1831
+ write_data.section = section;
1832
+ write_data.in_section = 0;
1833
+ write_data.preg_replaced = 0;
1834
+ write_data.name = name;
1835
+ write_data.preg = preg;
1836
+ write_data.value = value;
1837
+
1838
+ result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data);
1839
+ git__free(section);
1840
+
1841
+ if (result < 0) {
1842
+ git_filebuf_cleanup(&file);
1843
+ goto done;
1844
+ }
1845
+
1846
+ /* refresh stats - if this errors, then commit will error too */
1847
+ (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file);
1848
+
1849
+ result = git_filebuf_commit(&file);
1850
+ git_buf_free(&reader->buffer);
1851
+
1852
+ done:
1853
+ git_buf_free(&reader->buffer);
1854
+ return result;
1855
+ }
1856
+