/*
* Copyright (C) 2020-2021 all contributors
* License: AGPL-3.0+
*
* libgit2 for Inline::C
* Avoiding Git::Raw since it doesn't guarantee a stable API,
* while libgit2 itself seems reasonably stable.
*/
#include
#include
#include
#include
static void croak_if_err(int rc, const char *msg)
{
if (rc != GIT_OK) {
const git_error *e = giterr_last();
croak("%d %s (%s)", rc, msg, e ? e->message : "unknown");
}
}
SV *new()
{
git_odb *odb;
SV *ref, *self;
int rc = git_odb_new(&odb);
croak_if_err(rc, "git_odb_new");
ref = newSViv((IV)odb);
self = newRV_noinc(ref);
sv_bless(self, gv_stashpv("PublicInbox::Gcf2", GV_ADD));
SvREADONLY_on(ref);
return self;
}
static git_odb *odb_ptr(SV *self)
{
return (git_odb *)SvIV(SvRV(self));
}
void DESTROY(SV *self)
{
git_odb_free(odb_ptr(self));
}
/* needs "$GIT_DIR/objects", not $GIT_DIR */
void add_alternate(SV *self, const char *objects_path)
{
int rc = git_odb_add_disk_alternate(odb_ptr(self), objects_path);
croak_if_err(rc, "git_odb_add_disk_alternate");
}
#define CAPA(v) (sizeof(v) / sizeof((v)[0]))
/*
* returns true on success, false on failure
* this requires an unabbreviated git OID
*/
int cat_oid(SV *self, int fd, SV *oidsv)
{
/*
* adjust when libgit2 gets SHA-256 support, we return the
* same header as git-cat-file --batch "$OID $TYPE $SIZE\n"
*/
char hdr[GIT_OID_HEXSZ + sizeof(" commit 18446744073709551615")];
struct iovec vec[3];
size_t nvec = CAPA(vec);
git_oid oid;
git_odb_object *object = NULL;
int rc, err = 0;
STRLEN oidlen;
char *oidptr = SvPV(oidsv, oidlen);
/* same trailer as git-cat-file --batch */
vec[2].iov_len = 1;
vec[2].iov_base = "\n";
rc = git_oid_fromstrn(&oid, oidptr, oidlen);
if (rc == GIT_OK)
rc = git_odb_read(&object, odb_ptr(self), &oid);
if (rc == GIT_OK) {
vec[0].iov_base = hdr;
vec[1].iov_base = (void *)git_odb_object_data(object);
vec[1].iov_len = git_odb_object_size(object);
git_oid_nfmt(hdr, GIT_OID_HEXSZ, git_odb_object_id(object));
vec[0].iov_len = GIT_OID_HEXSZ +
snprintf(hdr + GIT_OID_HEXSZ,
sizeof(hdr) - GIT_OID_HEXSZ,
" %s %zu\n",
git_object_type2string(
git_odb_object_type(object)),
vec[1].iov_len);
} else { /* caller retries */
nvec = 0;
}
while (nvec && !err) {
ssize_t w = writev(fd, vec + CAPA(vec) - nvec, nvec);
if (w > 0) {
size_t done = 0;
size_t i;
for (i = CAPA(vec) - nvec; i < CAPA(vec); i++) {
if (w >= vec[i].iov_len) {
/* fully written vec */
w -= vec[i].iov_len;
done++;
} else { /* partially written vec */
char *p = vec[i].iov_base;
vec[i].iov_base = p + w;
vec[i].iov_len -= w;
break;
}
}
nvec -= done;
} else if (w < 0) {
err = errno;
switch (err) {
case EAGAIN: {
struct pollfd pfd;
pfd.events = POLLOUT;
pfd.fd = fd;
poll(&pfd, 1, -1);
}
/* fall-through */
case EINTR:
err = 0;
}
} else { /* w == 0 */
err = ENOSPC;
}
}
if (object)
git_odb_object_free(object);
if (err)
croak("writev error: %s", strerror(err));
return rc == GIT_OK;
}