lib/PublicInbox/approxidate.h
lib/PublicInbox/khashl.h
lib/PublicInbox/lg2.h
+lib/PublicInbox/lg2_cfg.h
lib/PublicInbox/xap_helper.h
lib/PublicInbox/xh_cidx.h
lib/PublicInbox/xh_date.h
die "E: libgit2 not installed: $err\n" if $?;
$vals->{$k} = $val;
}
+ my $modversion = $vals->{modversion};
+ *modversion = sub { $modversion };
+ my $lg2_ver = eval("v$modversion");
my $f = "$dir/lg2.h";
$c_src = PublicInbox::IO::try_cat $f or die "cat $f: $!";
+ # old versions were broken w/ multi-line, and also lacked the
+ # LIBGIT2_VERSION_CHECK macro (and Inline::C won't let us hide
+ # functions via CPP #if blocks)
+ if ($lg2_ver ge v1.8) {
+ $c_src .= PublicInbox::IO::try_cat "$dir/lg2_cfg.h";
+ }
# append pkg-config results to the source to ensure Inline::C
# can rebuild if there's changes (it doesn't seem to detect
# $CFG{CCFLAGSEX} nor $CFG{CPPFLAGS} changes)
$CFG{CCFLAGSEX} = $vals->{cflags};
$CFG{LIBS} = $vals->{libs};
my $boot = 'git_libgit2_init();';
- eval("v$vals->{modversion}") ge v0.26 and
- $boot .= <<EOM;
+ $boot .= <<EOM if $lg2_ver ge v0.26;
git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0);
EOM
eval <<EOM;
return rc == GIT_OK;
}
-
-#define CFG_OP_EQ(op, cmd0, dlen) \
- (dlen == (sizeof(op) - 1) && !memcmp(op, cmd0, sizeof(op) - 1))
-
-/* leaks on internal bugs: */
-#define FETCH_CSTR(var, ary, i) do { \
- SV **s = av_fetch(ary, i, 0); \
- if (!s) croak("BUG: " #var " = ary[%d] is NULL", i); \
- var = SvPV_nolen(*s); \
-} while (0)
-
-void cfgwr_commit(const char *f, AV *todo)
-{
- I32 i, todo_max = av_len(todo);
- SV **sv;
- git_config *cfg;
- const git_error *e;
- const char *cmd0, *name, *val, *re;
- int rc = git_config_open_ondisk(&cfg, f);
- STRLEN dlen;
- AV *cmd;
-
- for (i = 0; rc == GIT_OK && i <= todo_max; i++) {
- sv = av_fetch(todo, i, 0);
- /* leaks on internal bugs: */
- if (!SvROK(*sv)) croak("BUG: not a ref");
- cmd = (AV *)SvRV(*sv);
- sv = av_fetch(cmd, 0, 0);
- if (!sv) croak("BUG: cmd0 = $todo->[%d]->[0] is NULL", i);
- cmd0 = SvPV(*sv, dlen);
- if (CFG_OP_EQ("--add", cmd0, dlen)) {
- FETCH_CSTR(name, cmd, 1);
- FETCH_CSTR(val, cmd, 2);
- rc = git_config_set_multivar(cfg, name, "$^", val);
- } else if (CFG_OP_EQ("--unset-all", cmd0, dlen)) {
- FETCH_CSTR(name, cmd, 1);
- rc = git_config_delete_multivar(cfg, name, ".*");
- if (rc == GIT_ENOTFOUND)
- rc = GIT_OK;
- } else if (CFG_OP_EQ("--replace-all", cmd0, dlen)) {
- FETCH_CSTR(name, cmd, 1);
- FETCH_CSTR(val, cmd, 2);
- FETCH_CSTR(re, cmd, 3);
- rc = git_config_set_multivar(cfg, name, re, val);
- } else {
- name = cmd0;
- FETCH_CSTR(val, cmd, 1);
- rc = git_config_set_string(cfg, name, val);
- }
- }
- e = rc == GIT_OK ? NULL : giterr_last();
- git_config_free(cfg);
- if (rc != GIT_OK)
- croak("config -f %s (%d %s)",
- f, rc, e ? e->message : "unknown");
-}
--- /dev/null
+/*
+ * Copyright (C) all contributors <meta@public-inbox.org>
+ * License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+ *
+ * multiline configs broken before 1.8, I only noticed the former myself:
+ * dff05bc30 (Multiline config values not preserved on saving, 2021-11-25)
+ * de9a76b92 (config: properly handle multiline quotes, 2023-12-14)
+ */
+#define CFG_OP_EQ(op, cmd0, dlen) \
+ (dlen == (sizeof(op) - 1) && !memcmp(op, cmd0, sizeof(op) - 1))
+
+/* leaks on internal bugs: */
+#define FETCH_CSTR(var, ary, i) do { \
+ SV **s = av_fetch(ary, i, 0); \
+ if (!s) croak("BUG: " #var " = ary[%d] is NULL", i); \
+ var = SvPV_nolen(*s); \
+} while (0)
+
+void cfgwr_commit(const char *f, AV *todo)
+{
+ I32 i, todo_max = av_len(todo);
+ SV **sv;
+ git_config *cfg;
+ const git_error *e;
+ const char *cmd0, *name, *val, *re;
+ int rc = git_config_open_ondisk(&cfg, f);
+ STRLEN dlen;
+ AV *cmd;
+
+ for (i = 0; rc == GIT_OK && i <= todo_max; i++) {
+ sv = av_fetch(todo, i, 0);
+ /* leaks on internal bugs: */
+ if (!SvROK(*sv)) croak("BUG: not a ref");
+ cmd = (AV *)SvRV(*sv);
+ sv = av_fetch(cmd, 0, 0);
+ if (!sv) croak("BUG: cmd0 = $todo->[%d]->[0] is NULL", i);
+ cmd0 = SvPV(*sv, dlen);
+ if (CFG_OP_EQ("--add", cmd0, dlen)) {
+ FETCH_CSTR(name, cmd, 1);
+ FETCH_CSTR(val, cmd, 2);
+ rc = git_config_set_multivar(cfg, name, "$^", val);
+ } else if (CFG_OP_EQ("--unset-all", cmd0, dlen)) {
+ FETCH_CSTR(name, cmd, 1);
+ rc = git_config_delete_multivar(cfg, name, ".*");
+ if (rc == GIT_ENOTFOUND)
+ rc = GIT_OK;
+ } else if (CFG_OP_EQ("--replace-all", cmd0, dlen)) {
+ FETCH_CSTR(name, cmd, 1);
+ FETCH_CSTR(val, cmd, 2);
+ FETCH_CSTR(re, cmd, 3);
+ rc = git_config_set_multivar(cfg, name, re, val);
+ } else {
+ name = cmd0;
+ FETCH_CSTR(val, cmd, 1);
+ rc = git_config_set_string(cfg, name, val);
+ }
+ }
+ e = rc == GIT_OK ? NULL : giterr_last();
+ git_config_free(cfg);
+ if (rc != GIT_OK)
+ croak("config -f %s (%d %s)",
+ f, rc, e ? e->message : "unknown");
+}
}
} : PublicInbox::Lg2->can('cfgwr_commit');
+SKIP: {
+$cfgwr_commit or skip 'libgit2 '.PublicInbox::Lg2->modversion.
+ ' < 1.8 too old for multiline configs', 1;
my $cfg; # for explain() if needed
my $get = sub {
my (@key) = @_;
[ qw(--add x.b f) ] ]);
is_xdeeply $get->('x.b'), [ qw(d e f) ], 'multiple changes chained';
+use PublicInbox::IO qw(write_file try_cat);
+my $src = <<EOM;
+[foo]
+ bar = some \\
+very \\
+long line
+EOM
+write_file '>', $f, $src;
+$cfgwr_commit->($f, [ [ qw(x.b d) ], [ qw(--add x.b e) ] ]);
+like try_cat($f), qr/^\Q$src\E$/sm, 'newlines preserved';
+} # SKIP
+
done_testing;