/* * 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; }