Lindenii Project Forge
Login

server

Lindenii Forge’s main backend daemon
Commit info
ID
7b30bf4bbba59924173a2e250802700e899c1bc7
Author
Runxi Yu <me@runxiyu.org>
Author date
Mon, 23 Jun 2025 09:24:49 +0800
Committer
Runxi Yu <me@runxiyu.org>
Committer date
Mon, 23 Jun 2025 09:24:49 +0800
Actions
git2d: Reformat C code in Linux style
/*-
 * SPDX-License-Identifier: MIT
 * SPDX-FileCopyrightText: Copyright (c) 2022 Frank Smit <https://61924.nl/>
 */

#include <string.h>
#include <stdbool.h>

#include "bare.h"

#define UNUSED(x) (void)(x)

enum {
	U8SZ = 1,
	U16SZ = 2,
	U32SZ = 4,
	U64SZ = 8,
	MAXVARINTSZ = 10,
};

bare_error
bare_put_uint(struct bare_writer *ctx, uint64_t x)
bare_error bare_put_uint(struct bare_writer *ctx, uint64_t x)
{
	uint64_t i = 0;
	uint8_t b[MAXVARINTSZ];

	while (x >= 0x80) {
		b[i] = (uint8_t)x | 0x80;
		b[i] = (uint8_t) x | 0x80;
		x >>= 7;
		i++;
	}

	b[i] = (uint8_t)x;
	b[i] = (uint8_t) x;
	i++;

	return ctx->write(ctx->buffer, b, i);
}

bare_error
bare_get_uint(struct bare_reader *ctx, uint64_t *x)
bare_error bare_get_uint(struct bare_reader *ctx, uint64_t *x)
{
	bare_error err = BARE_ERROR_NONE;

	uint8_t shift = 0;
	uint64_t result = 0;

	for (uint8_t i = 0;i < 10;i++) {
	for (uint8_t i = 0; i < 10; i++) {
		uint8_t b;

		err = ctx->read(ctx->buffer, &b, U8SZ);
		if (err != BARE_ERROR_NONE) {
			break;
		}

		if (b < 0x80) {
			result |= (uint64_t)b << shift;
			result |= (uint64_t) b << shift;
			break;
		} else {
			result |= ((uint64_t)b & 0x7f) << shift;
			result |= ((uint64_t) b & 0x7f) << shift;
			shift += 7;
		}
	}

	*x = result;

	return err;
}

bare_error
bare_put_int(struct bare_writer *ctx, int64_t x)
bare_error bare_put_int(struct bare_writer *ctx, int64_t x)
{
	uint64_t ux = (uint64_t)x << 1;
	uint64_t ux = (uint64_t) x << 1;

	if (x < 0) {
		ux = ~ux;
	}

	return bare_put_uint(ctx, ux);
}

bare_error
bare_get_int(struct bare_reader *ctx, int64_t *x)
bare_error bare_get_int(struct bare_reader *ctx, int64_t *x)
{
	uint64_t ux;

	bare_error err = bare_get_uint(ctx, &ux);

	if (err == BARE_ERROR_NONE) {
		*x = (int64_t)(ux >> 1);
		*x = (int64_t) (ux >> 1);

		if ((ux & 1) != 0) {
			*x = ~(*x);
		}
	}

	return err;
}

bare_error
bare_put_u8(struct bare_writer *ctx, uint8_t x)
bare_error bare_put_u8(struct bare_writer *ctx, uint8_t x)
{
	return ctx->write(ctx->buffer, &x, U8SZ);
}

bare_error
bare_get_u8(struct bare_reader *ctx, uint8_t *x)
bare_error bare_get_u8(struct bare_reader *ctx, uint8_t *x)
{
	return ctx->read(ctx->buffer, x, U8SZ);
}

bare_error
bare_put_u16(struct bare_writer *ctx, uint16_t x)
bare_error bare_put_u16(struct bare_writer *ctx, uint16_t x)
{
	return ctx->write(ctx->buffer, (uint8_t[U16SZ]){x, x >> 8}, U16SZ);
	return ctx->write(ctx->buffer, (uint8_t[U16SZ]) {
			  x, x >> 8}
			  , U16SZ);
}

bare_error
bare_get_u16(struct bare_reader *ctx, uint16_t *x)
bare_error bare_get_u16(struct bare_reader *ctx, uint16_t *x)
{
	bare_error err = ctx->read(ctx->buffer, x, U16SZ);

	if (err == BARE_ERROR_NONE) {
		*x = (uint16_t)((uint8_t *)x)[0]
		   | (uint16_t)((uint8_t *)x)[1] << 8;
		*x = (uint16_t) ((uint8_t *) x)[0]
		    | (uint16_t) ((uint8_t *) x)[1] << 8;
	}

	return err;
}

bare_error
bare_put_u32(struct bare_writer *ctx, uint32_t x)
bare_error bare_put_u32(struct bare_writer *ctx, uint32_t x)
{
	uint8_t buf[U32SZ];

	buf[0] = (uint8_t)(x);
	buf[1] = (uint8_t)(x >> 8);
	buf[2] = (uint8_t)(x >> 16);
	buf[3] = (uint8_t)(x >> 24);
	buf[0] = (uint8_t) (x);
	buf[1] = (uint8_t) (x >> 8);
	buf[2] = (uint8_t) (x >> 16);
	buf[3] = (uint8_t) (x >> 24);

	return ctx->write(ctx->buffer, buf, U32SZ);
}

bare_error
bare_get_u32(struct bare_reader *ctx, uint32_t *x)
bare_error bare_get_u32(struct bare_reader *ctx, uint32_t *x)
{
	bare_error err = ctx->read(ctx->buffer, x, U32SZ);

	if (err == BARE_ERROR_NONE) {
		*x = (uint32_t)(((uint8_t *)x)[0])
		   | (uint32_t)(((uint8_t *)x)[1] << 8)
		   | (uint32_t)(((uint8_t *)x)[2] << 16)
		   | (uint32_t)(((uint8_t *)x)[3] << 24);
		*x = (uint32_t) (((uint8_t *) x)[0])
		    | (uint32_t) (((uint8_t *) x)[1] << 8)
		    | (uint32_t) (((uint8_t *) x)[2] << 16)
		    | (uint32_t) (((uint8_t *) x)[3] << 24);
	}

	return err;
}

bare_error
bare_put_u64(struct bare_writer *ctx, uint64_t x)
bare_error bare_put_u64(struct bare_writer *ctx, uint64_t x)
{
	uint8_t buf[U64SZ];

	buf[0] = x;
	buf[1] = x >> 8;
	buf[2] = x >> 16;
	buf[3] = x >> 24;
	buf[4] = x >> 32;
	buf[5] = x >> 40;
	buf[6] = x >> 48;
	buf[7] = x >> 56;

	return ctx->write(ctx->buffer, buf, U64SZ);
}

bare_error
bare_get_u64(struct bare_reader *ctx, uint64_t *x)
bare_error bare_get_u64(struct bare_reader *ctx, uint64_t *x)
{
	bare_error err = ctx->read(ctx->buffer, x, U64SZ);

	if (err == BARE_ERROR_NONE) {
		*x = (uint64_t)((uint8_t *)x)[0]
		   | (uint64_t)((uint8_t *)x)[1] << 8
		   | (uint64_t)((uint8_t *)x)[2] << 16
		   | (uint64_t)((uint8_t *)x)[3] << 24
		   | (uint64_t)((uint8_t *)x)[4] << 32
		   | (uint64_t)((uint8_t *)x)[5] << 40
		   | (uint64_t)((uint8_t *)x)[6] << 48
		   | (uint64_t)((uint8_t *)x)[7] << 56;
		*x = (uint64_t) ((uint8_t *) x)[0]
		    | (uint64_t) ((uint8_t *) x)[1] << 8
		    | (uint64_t) ((uint8_t *) x)[2] << 16
		    | (uint64_t) ((uint8_t *) x)[3] << 24
		    | (uint64_t) ((uint8_t *) x)[4] << 32
		    | (uint64_t) ((uint8_t *) x)[5] << 40
		    | (uint64_t) ((uint8_t *) x)[6] << 48
		    | (uint64_t) ((uint8_t *) x)[7] << 56;
	}

	return err;
}

bare_error
bare_put_i8(struct bare_writer *ctx, int8_t x)
bare_error bare_put_i8(struct bare_writer *ctx, int8_t x)
{
	return bare_put_u8(ctx, x);
}

bare_error
bare_get_i8(struct bare_reader *ctx, int8_t *x)
bare_error bare_get_i8(struct bare_reader *ctx, int8_t *x)
{
	return bare_get_u8(ctx, (uint8_t *)x);
	return bare_get_u8(ctx, (uint8_t *) x);
}

bare_error
bare_put_i16(struct bare_writer *ctx, int16_t x)
bare_error bare_put_i16(struct bare_writer *ctx, int16_t x)
{
	return bare_put_u16(ctx, x);
}

bare_error
bare_get_i16(struct bare_reader *ctx, int16_t *x)
bare_error bare_get_i16(struct bare_reader *ctx, int16_t *x)
{
	return bare_get_u16(ctx, (uint16_t *)x);
	return bare_get_u16(ctx, (uint16_t *) x);
}

bare_error
bare_put_i32(struct bare_writer *ctx, int32_t x)
bare_error bare_put_i32(struct bare_writer *ctx, int32_t x)
{
	return bare_put_u32(ctx, x);
}

bare_error
bare_get_i32(struct bare_reader *ctx, int32_t *x)
bare_error bare_get_i32(struct bare_reader *ctx, int32_t *x)
{
	return bare_get_u32(ctx, (uint32_t *)x);
	return bare_get_u32(ctx, (uint32_t *) x);
}

bare_error
bare_put_i64(struct bare_writer *ctx, int64_t x)
bare_error bare_put_i64(struct bare_writer *ctx, int64_t x)
{
	return bare_put_u64(ctx, x);
}

bare_error
bare_get_i64(struct bare_reader *ctx, int64_t *x)
bare_error bare_get_i64(struct bare_reader *ctx, int64_t *x)
{
	return bare_get_u64(ctx, (uint64_t *)x);
	return bare_get_u64(ctx, (uint64_t *) x);
}

bare_error
bare_put_f32(struct bare_writer *ctx, float x)
bare_error bare_put_f32(struct bare_writer *ctx, float x)
{
	uint32_t b;
	memcpy(&b, &x, U32SZ);

	return bare_put_u32(ctx, b);
}

bare_error
bare_get_f32(struct bare_reader *ctx, float *x)
bare_error bare_get_f32(struct bare_reader *ctx, float *x)
{
	return ctx->read(ctx->buffer, x, U32SZ);
}

bare_error
bare_put_f64(struct bare_writer *ctx, double x)
bare_error bare_put_f64(struct bare_writer *ctx, double x)
{
	uint64_t b;
	memcpy(&b, &x, U64SZ);

	return bare_put_u64(ctx, b);
}

bare_error
bare_get_f64(struct bare_reader *ctx, double *x)
bare_error bare_get_f64(struct bare_reader *ctx, double *x)
{
	return ctx->read(ctx->buffer, x, U64SZ);
}

bare_error
bare_put_bool(struct bare_writer *ctx, bool x)
bare_error bare_put_bool(struct bare_writer *ctx, bool x)
{
	return bare_put_u8(ctx, (uint8_t)x);
	return bare_put_u8(ctx, (uint8_t) x);
}

bare_error
bare_get_bool(struct bare_reader *ctx, bool *x)
bare_error bare_get_bool(struct bare_reader *ctx, bool *x)
{
	return bare_get_u8(ctx, (uint8_t *)x);
	return bare_get_u8(ctx, (uint8_t *) x);
}

bare_error
bare_put_fixed_data(struct bare_writer *ctx, const uint8_t *src, uint64_t sz)
{
	return ctx->write(ctx->buffer, (void *)src, sz);
}

bare_error
bare_get_fixed_data(struct bare_reader *ctx, uint8_t *dst, uint64_t sz)
{
	return ctx->read(ctx->buffer, dst, sz);
}

bare_error
bare_put_data(struct bare_writer *ctx, const uint8_t *src, uint64_t sz)
{
	bare_error err = BARE_ERROR_NONE;

	err = bare_put_uint(ctx, sz);

	if (err == BARE_ERROR_NONE) {
		err = bare_put_fixed_data(ctx, src, sz);
	}

	return err;
}

bare_error
bare_get_data(struct bare_reader *ctx, uint8_t *dst, uint64_t sz)
bare_error bare_get_data(struct bare_reader *ctx, uint8_t *dst, uint64_t sz)
{
	bare_error err = BARE_ERROR_NONE;
	uint64_t ssz = 0;

	err = bare_get_uint(ctx, &ssz);

	if (err == BARE_ERROR_NONE) {
		err = ssz <= sz \
			? bare_get_fixed_data(ctx, dst, ssz) \
			: BARE_ERROR_BUFFER_TOO_SMALL;
		err = ssz <= sz ? bare_get_fixed_data(ctx, dst, ssz)
		    : BARE_ERROR_BUFFER_TOO_SMALL;
	}

	return err;
}

bare_error
bare_put_str(struct bare_writer *ctx, const char *src, uint64_t sz)
bare_error bare_put_str(struct bare_writer *ctx, const char *src, uint64_t sz)
{
	return bare_put_data(ctx, (uint8_t *)src, sz);
	return bare_put_data(ctx, (uint8_t *) src, sz);
}

bare_error
bare_get_str(struct bare_reader *ctx, char *dst, uint64_t sz)
bare_error bare_get_str(struct bare_reader *ctx, char *dst, uint64_t sz)
{
	return bare_get_data(ctx, (uint8_t *)dst, sz);
	return bare_get_data(ctx, (uint8_t *) dst, sz);
}
/*-
 * SPDX-License-Identifier: AGPL-3.0-only
 * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
 */

#include "x.h"

int
cmd_index(git_repository *repo, struct bare_writer *writer)
int cmd_index(git_repository *repo, struct bare_writer *writer)
{
	/* HEAD tree */

	git_object *obj = NULL;
	int err = git_revparse_single(&obj, repo, "HEAD^{tree}");
	if (err != 0) {
		bare_put_uint(writer, 4);
		return -1;
	}
	git_tree *tree = (git_tree *) obj;

	/* README */

	git_tree_entry *entry = NULL;
	err = git_tree_entry_bypath(&entry, tree, "README.md");
	if (err != 0) {
		bare_put_uint(writer, 5);
		git_tree_free(tree);
		return -1;
	}
	git_otype objtype = git_tree_entry_type(entry);
	if (objtype != GIT_OBJECT_BLOB) {
		bare_put_uint(writer, 6);
		git_tree_entry_free(entry);
		git_tree_free(tree);
		return -1;
	}
	git_object *obj2 = NULL;
	err = git_tree_entry_to_object(&obj2, repo, entry);
	if (err != 0) {
		bare_put_uint(writer, 7);
		git_tree_entry_free(entry);
		git_tree_free(tree);
		return -1;
	}
	git_blob *blob = (git_blob *) obj2;
	const void *content = git_blob_rawcontent(blob);
	if (content == NULL) {
		bare_put_uint(writer, 8);
		git_blob_free(blob);
		git_tree_entry_free(entry);
		git_tree_free(tree);
		return -1;
	}
	bare_put_uint(writer, 0);
	bare_put_data(writer, content, git_blob_rawsize(blob));

	/* Commits */

	/* TODO BUG: This might be a different commit from the displayed README due to races */

	git_revwalk *walker = NULL;
	if (git_revwalk_new(&walker, repo) != 0) {
		bare_put_uint(writer, 9);
		git_blob_free(blob);
		git_tree_entry_free(entry);
		git_tree_free(tree);
		return -1;
	}

	if (git_revwalk_push_head(walker) != 0) {
		bare_put_uint(writer, 9);
		git_revwalk_free(walker);
		git_blob_free(blob);
		git_tree_entry_free(entry);
		git_tree_free(tree);
		return -1;
	}

	int count = 0;
	git_oid oid;
	while (count < 3 && git_revwalk_next(&oid, walker) == 0) {
		git_commit *commit = NULL;
		if (git_commit_lookup(&commit, repo, &oid) != 0)
			break;

		const char *msg = git_commit_summary(commit);
		const git_signature *author = git_commit_author(commit);

		/* ID */
		bare_put_data(writer, oid.id, GIT_OID_RAWSZ);

		/* Title */
		size_t msg_len = msg ? strlen(msg) : 0;
		bare_put_data(writer, (const uint8_t *)(msg ? msg : ""), msg_len);
		bare_put_data(writer, (const uint8_t *)(msg ? msg : ""),
			      msg_len);

		/* Author's name */
		const char *author_name = author ? author->name : "";
		bare_put_data(writer, (const uint8_t *)author_name, strlen(author_name));
		bare_put_data(writer, (const uint8_t *)author_name,
			      strlen(author_name));

		/* Author's email */
		const char *author_email = author ? author->email : "";
		bare_put_data(writer, (const uint8_t *)author_email, strlen(author_email));
		bare_put_data(writer, (const uint8_t *)author_email,
			      strlen(author_email));

		/* Author's date */
		/* TODO: Pass the integer instead of a string */
		time_t time = git_commit_time(commit);
		char timebuf[64];
		struct tm *tm = localtime(&time);
		if (tm)
			strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm);
			strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S",
				 tm);
		else
			strcpy(timebuf, "unknown");
		bare_put_data(writer, (const uint8_t *)timebuf, strlen(timebuf));
		bare_put_data(writer, (const uint8_t *)timebuf,
			      strlen(timebuf));

		git_commit_free(commit);
		count++;
	}

	git_revwalk_free(walker);
	git_blob_free(blob);
	git_tree_entry_free(entry);
	git_tree_free(tree);

	return 0;
}
/*-
 * SPDX-License-Identifier: AGPL-3.0-only
 * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
 */

#include "x.h"

int
cmd_treeraw(git_repository *repo, struct bare_reader *reader, struct bare_writer *writer)
cmd_treeraw(git_repository *repo, struct bare_reader *reader,
	    struct bare_writer *writer)
{
	/* Path */
	char path[4096] = {0};
	int err = bare_get_data(reader, (uint8_t *)path, sizeof(path) - 1);
	char path[4096] = { 0 };
	int err = bare_get_data(reader, (uint8_t *) path, sizeof(path) - 1);
	if (err != BARE_ERROR_NONE) {
		bare_put_uint(writer, 11);
		return -1;
	}
	path[sizeof(path) - 1] = '\0';

	/* HEAD^{tree} */
	git_object *head_obj = NULL;
	err = git_revparse_single(&head_obj, repo, "HEAD^{tree}");
	if (err != 0) {
		bare_put_uint(writer, 4);
		return -1;
	}
	git_tree *tree = (git_tree *)head_obj;
	git_tree *tree = (git_tree *) head_obj;

	/* Path in tree */
	git_tree_entry *entry = NULL;
	git_otype objtype;
	if (strlen(path) == 0) {
		entry = NULL;
		objtype = GIT_OBJECT_TREE;
	} else {
		err = git_tree_entry_bypath(&entry, tree, path);
		if (err != 0) {
			bare_put_uint(writer, 3);
			git_tree_free(tree);
			return 0;
		}
		objtype = git_tree_entry_type(entry);
	}

	if (objtype == GIT_OBJECT_TREE) {
		/* Tree */
		git_object *tree_obj = NULL;
		if (entry == NULL) {
			tree_obj = (git_object *)tree;
			tree_obj = (git_object *) tree;
		} else {
			err = git_tree_entry_to_object(&tree_obj, repo, entry);
			if (err != 0) {
				bare_put_uint(writer, 7);
				goto cleanup;
			}
		}
		git_tree *subtree = (git_tree *)tree_obj;
		git_tree *subtree = (git_tree *) tree_obj;

		size_t count = git_tree_entrycount(subtree);
		bare_put_uint(writer, 0);
		bare_put_uint(writer, 1);
		bare_put_uint(writer, count);
		for (size_t i = 0; i < count; i++) {
			const git_tree_entry *subentry = git_tree_entry_byindex(subtree, i);
			const git_tree_entry *subentry =
			    git_tree_entry_byindex(subtree, i);
			const char *name = git_tree_entry_name(subentry);
			git_otype type = git_tree_entry_type(subentry);
			uint32_t mode = git_tree_entry_filemode(subentry);

			uint8_t entry_type = 0;
			uint64_t size = 0;

			if (type == GIT_OBJECT_TREE) {
				entry_type = 1;
			} else if (type == GIT_OBJECT_BLOB) {
				entry_type = 2;

				git_object *subobj = NULL;
				if (git_tree_entry_to_object(&subobj, repo, subentry) == 0) {
					git_blob *b = (git_blob *)subobj;
				if (git_tree_entry_to_object
				    (&subobj, repo, subentry) == 0) {
					git_blob *b = (git_blob *) subobj;
					size = git_blob_rawsize(b);
					git_blob_free(b);
				}
			}

			bare_put_uint(writer, entry_type);
			bare_put_uint(writer, mode);
			bare_put_uint(writer, size);
			bare_put_data(writer, (const uint8_t *)name, strlen(name));
			bare_put_data(writer, (const uint8_t *)name,
				      strlen(name));
		}
		if (entry != NULL) {
			git_tree_free(subtree);
		}
	} else if (objtype == GIT_OBJECT_BLOB) {
		/* Blob */
		git_object *blob_obj = NULL;
		err = git_tree_entry_to_object(&blob_obj, repo, entry);
		if (err != 0) {
			bare_put_uint(writer, 7);
			goto cleanup;
		}
		git_blob *blob = (git_blob *)blob_obj;
		git_blob *blob = (git_blob *) blob_obj;
		const void *content = git_blob_rawcontent(blob);
		if (content == NULL) {
			bare_put_uint(writer, 8);
			git_blob_free(blob);
			goto cleanup;
		}
		bare_put_uint(writer, 0);
		bare_put_uint(writer, 2);
		bare_put_data(writer, content, git_blob_rawsize(blob));
		git_blob_free(blob);
	} else {
		/* Unknown */
		bare_put_uint(writer, -1);
	}

cleanup:
 cleanup:
	if (entry != NULL)
		git_tree_entry_free(entry);
	git_tree_free(tree);
	return 0;
}
/*-
 * SPDX-License-Identifier: AGPL-3.0-only
 * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
 */

/*
 * TODO: Pool repositories (and take care of thread safety)
 * libgit2 has a nice builtin per-repo cache that we could utilize this way.
 */

#include "x.h"

int
main(int argc, char **argv)
int main(int argc, char **argv)
{
	if (argc != 2) {
		errx(1, "provide one argument: the socket path");
	}

	signal(SIGPIPE, SIG_IGN);

	git_libgit2_init();

	int sock;
	if ((sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0)
		err(1, "socket");

	struct sockaddr_un addr;
	memset(&addr, 0, sizeof(addr));
	addr.sun_family = AF_UNIX;
	strcpy(addr.sun_path, argv[1]);

	umask(0077);

	if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un))) {
		if (errno == EADDRINUSE) {
			unlink(argv[1]);
			if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)))
			if (bind
			    (sock, (struct sockaddr *)&addr,
			     sizeof(struct sockaddr_un)))
				err(1, "bind");
		} else {
			err(1, "bind");
		}
	}

	listen(sock, 128);

	for (;;) {
		int *conn = malloc(sizeof(int));
		if (conn == NULL) {
			warn("malloc");
			continue;
		}

		*conn = accept(sock, 0, 0);
		if (*conn == -1) {
			free(conn);
			warn("accept");
			continue;
		}

		pthread_t thread;

		if (pthread_create(&thread, NULL, session, (void *)conn) != 0) {
			close(*conn);
			free(conn);
			warn("pthread_create");
		}
	}

	close(sock);

	git_libgit2_shutdown();
}
/*-
 * SPDX-License-Identifier: AGPL-3.0-only
 * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
 */

#include "x.h"

bare_error
conn_read(void *buffer, void *dst, uint64_t sz)
bare_error conn_read(void *buffer, void *dst, uint64_t sz)
{
	conn_io_t *io = buffer;
	ssize_t rsz = read(io->fd, dst, sz);
	return (rsz == (ssize_t) sz) ? BARE_ERROR_NONE : BARE_ERROR_READ_FAILED;
}

bare_error
conn_write(void *buffer, const void *src, uint64_t sz)
bare_error conn_write(void *buffer, const void *src, uint64_t sz)
{
	conn_io_t *io = buffer;
	const uint8_t *data = src;
	uint64_t total = 0;

	while (total < sz) {
		ssize_t written = write(io->fd, data + total, sz - total);
		if (written < 0) {
			if (errno == EINTR)
				continue;
			return BARE_ERROR_WRITE_FAILED;
		}
		if (written == 0)
			break;
		total += written;
	}

	return (total == sz) ? BARE_ERROR_NONE : BARE_ERROR_WRITE_FAILED;
}
/*-
 * SPDX-License-Identifier: AGPL-3.0-only
 * SPDX-FileCopyrightText: Copyright (c) 2025 Runxi Yu <https://runxiyu.org>
 */

#include "x.h"

void *
session(void *_conn)
void *session(void *_conn)
{
	int conn = *(int *)_conn;
	free((int *)_conn);

	int err;

	conn_io_t io = {.fd = conn };
	struct bare_reader reader = {
		.buffer = &io,
		.read = conn_read,
	};
	struct bare_writer writer = {
		.buffer = &io,
		.write = conn_write,
	};

	/* Repo path */
	char path[4096] = {0};
	char path[4096] = { 0 };
	err = bare_get_data(&reader, (uint8_t *) path, sizeof(path) - 1);
	if (err != BARE_ERROR_NONE) {
		goto close;
	}
	path[sizeof(path) - 1] = '\0';

	/* Open repo */
	git_repository *repo = NULL;
	err = git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_NO_SEARCH | GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT, NULL);
	err =
	    git_repository_open_ext(&repo, path,
				    GIT_REPOSITORY_OPEN_NO_SEARCH |
				    GIT_REPOSITORY_OPEN_BARE |
				    GIT_REPOSITORY_OPEN_NO_DOTGIT, NULL);
	if (err != 0) {
		bare_put_uint(&writer, 1);
		goto close;
	}

	/* Command */
	uint64_t cmd = 0;
	err = bare_get_uint(&reader, &cmd);
	if (err != BARE_ERROR_NONE) {
		bare_put_uint(&writer, 2);
		goto free_repo;
	}
	switch (cmd) {
	case 1:
		err = cmd_index(repo, &writer);
		if (err != 0)
			goto free_repo;
		break;
	case 2:
		err = cmd_treeraw(repo, &reader, &writer);
		if (err != 0)
			goto free_repo;
		break;
	case 0:
		bare_put_uint(&writer, 3);
		goto free_repo;
	default:
		bare_put_uint(&writer, 3);
		goto free_repo;
	}

free_repo:
 free_repo:
	git_repository_free(repo);

close:
 close:
	close(conn);

	return NULL;
}