"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
parse remotes from gitconfig
To:
gameoftrees@openbsd.org
Cc:
ori@openbsd.org
Date:
Wed, 27 Nov 2019 18:02:22 -0700

Download raw body.

Thread
  • Stefan Sperling:

    parse remotes from gitconfig

This patch parses remote repository configuration from gitconfig.
For now this only gets the user-specified remote name and the
corresponding repository URL. I hope we won't ever need refspecs.

This could soon become useful ;)

diff refs/heads/master refs/heads/gitconfig-remotes
blob - 4e252ffbac5584645f2d0ce7bd4ba2688825f65d
blob + 270c02605b7ca7f47d34f3b4268483afafcec01f
--- include/got_error.h
+++ include/got_error.h
@@ -126,6 +126,7 @@
 #define GOT_ERR_GIT_REPO_FORMAT	110
 #define GOT_ERR_REBASE_REQUIRED	111
 #define GOT_ERR_REGEX		112
+#define GOT_ERR_GITCONFIG_SYNTAX 113
 
 static const struct got_error {
 	int code;
@@ -258,6 +259,7 @@ static const struct got_error {
 	{ GOT_ERR_GIT_REPO_FORMAT,"unknown git repository format version" },
 	{ GOT_ERR_REBASE_REQUIRED,"specified branch must be rebased first" },
 	{ GOT_ERR_REGEX, "regular expression error" },
+	{ GOT_ERR_GITCONFIG_SYNTAX, "gitconfig syntax error" },
 };
 
 /*
blob - 7ed477b1d49d732af4362f4100cb33659065f814
blob + 0a1c3f25d0677cbebea9562b70a802af37922ef9
--- include/got_repository.h
+++ include/got_repository.h
@@ -44,6 +44,16 @@ const char *got_repo_get_global_gitconfig_author_name(
 /* Obtain global commit author email parsed ~/.gitconfig, else NULL. */
 const char *got_repo_get_global_gitconfig_author_email(struct got_repository *);
 
+/* Information about one remote repository. */
+struct got_remote_repo {
+	char *name;
+	char *url;
+};
+
+/* Obtain the list of remote repositories parsed from gitconfig. */ 
+void got_repo_get_remotes(int *, struct got_remote_repo **,
+    struct got_repository *);
+
 /*
  * Obtain paths to various directories within a repository.
  * The caller must dispose of a path with free(3).
blob - 1ddfb97d543989190d1cf894c45ed4ed1424f1aa
blob + 5188ecbf5c2419c20c0064c42212be9f91f390f0
--- lib/gitconfig.c
+++ lib/gitconfig.c
@@ -494,6 +494,51 @@ got_gitconfig_get_str(struct got_gitconfig *conf, char
 	return 0;
 }
 
+const struct got_error *
+got_gitconfig_get_section_list(struct got_gitconfig_list **sections,
+    struct got_gitconfig *conf)
+{
+	const struct got_error *err = NULL;
+	struct got_gitconfig_list *list = NULL;
+	struct got_gitconfig_list_node *node = 0;
+	struct got_gitconfig_binding *cb;
+	int i;
+
+	*sections = NULL;
+
+	list = malloc(sizeof *list);
+	if (!list)
+		return got_error_from_errno("malloc");
+	TAILQ_INIT(&list->fields);
+	list->cnt = 0;
+	for (i = 0; i < nitems(conf->bindings); i++) {
+		for (cb = LIST_FIRST(&conf->bindings[i]); cb;
+		    cb = LIST_NEXT(cb, link)) {
+			list->cnt++;
+			node = calloc(1, sizeof *node);
+			if (!node) {
+				err = got_error_from_errno("calloc");
+				goto cleanup;
+			}
+			node->field = strdup(cb->section);
+			if (!node->field) {
+				err = got_error_from_errno("strdup");
+				goto cleanup;
+			}
+			TAILQ_INSERT_TAIL(&list->fields, node, link);
+		}
+	}
+
+	*sections = list;
+	return NULL;
+
+cleanup:
+	free(node);
+	if (list)
+		got_gitconfig_free_list(list);
+	return err;
+}
+
 /*
  * Build a list of string values out of the comma separated value denoted by
  * TAG in SECTION.
blob - 4d61ab3964722d849b94e3048f1cb6a15d2edc3f
blob + 615d2690ac39f3fceb3704b2624a85e8de2ba65c
--- lib/got_lib_gitconfig.h
+++ lib/got_lib_gitconfig.h
@@ -39,6 +39,8 @@ struct got_gitconfig_list {
 struct got_gitconfig;
 
 void     got_gitconfig_free_list(struct got_gitconfig_list *);
+const struct got_error *got_gitconfig_get_section_list(
+    struct got_gitconfig_list **, struct got_gitconfig *);
 struct got_gitconfig_list *got_gitconfig_get_list(struct got_gitconfig *,
     char *, char *);
 struct got_gitconfig_list *got_gitconfig_get_tag_list(struct got_gitconfig *, char *);
blob - a37a450e6e18733afec74aa1dc9cb427c9d27acb
blob + b27325da498aa72413f8377303ba76d4012c7813
--- lib/got_lib_privsep.h
+++ lib/got_lib_privsep.h
@@ -111,8 +111,11 @@ enum got_imsg_type {
 	GOT_IMSG_GITCONFIG_REPOSITORY_FORMAT_VERSION_REQUEST,
 	GOT_IMSG_GITCONFIG_AUTHOR_NAME_REQUEST,
 	GOT_IMSG_GITCONFIG_AUTHOR_EMAIL_REQUEST,
+	GOT_IMSG_GITCONFIG_REMOTES_REQUEST,
 	GOT_IMSG_GITCONFIG_INT_VAL,
 	GOT_IMSG_GITCONFIG_STR_VAL,
+	GOT_IMSG_GITCONFIG_REMOTES,
+	GOT_IMSG_GITCONFIG_REMOTE,
 };
 
 /* Structure for GOT_IMSG_ERROR. */
@@ -229,6 +232,24 @@ struct got_imsg_packed_object {
 	int idx;
 } __attribute__((__packed__));
 
+/*
+ * Structure for GOT_IMSG_GITCONFIG_REMOTE data.
+ */
+struct got_imsg_remote {
+	size_t name_len;
+	size_t url_len;
+
+	/* Followed by name_len + url_len data bytes. */
+};
+
+/*
+ * Structure for GOT_IMSG_GITCONFIG_REMOTES data.
+ */
+struct got_imsg_remotes {
+	int nremotes; /* This many GOT_IMSG_GITCONFIG_REMOTE messages follow. */
+};
+
+struct got_remote_repo;
 struct got_pack;
 struct got_packidx;
 struct got_pathlist_head;
@@ -285,11 +306,17 @@ const struct got_error *got_privsep_send_gitconfig_aut
     struct imsgbuf *);
 const struct got_error *got_privsep_send_gitconfig_author_email_req(
     struct imsgbuf *);
+const struct got_error *got_privsep_send_gitconfig_remotes_req(
+    struct imsgbuf *);
 const struct got_error *got_privsep_send_gitconfig_str(struct imsgbuf *,
     const char *);
 const struct got_error *got_privsep_recv_gitconfig_str(char **,
     struct imsgbuf *);
 const struct got_error *got_privsep_send_gitconfig_int(struct imsgbuf *, int);
 const struct got_error *got_privsep_recv_gitconfig_int(int *, struct imsgbuf *);
+const struct got_error *got_privsep_send_gitconfig_remotes(struct imsgbuf *,
+    struct got_remote_repo *, int);
+const struct got_error *got_privsep_recv_gitconfig_remotes(
+    struct got_remote_repo **, int *, struct imsgbuf *);
 
 void got_privsep_exec_child(int[2], const char *, const char *);
blob - c304faf993ada599d3d88ab1f3172d00c4ba1225
blob + caf2d341d728e1ec64eda8a41ce83e7526ac33fb
--- lib/got_lib_repository.h
+++ lib/got_lib_repository.h
@@ -47,6 +47,8 @@ struct got_repository {
 	char *gitconfig_author_email;
 	char *global_gitconfig_author_name;
 	char *global_gitconfig_author_email;
+	int nremotes;
+	struct got_remote_repo *remotes;
 };
 
 const struct got_error*got_repo_cache_object(struct got_repository *,
blob - c2e79e1f376e55f2a9fc904479ab7c82e7cef1b9
blob + 9bf89c79a1b03b9ca2568c5b44f463e6ef4b0ea2
--- lib/privsep.c
+++ lib/privsep.c
@@ -34,6 +34,7 @@
 #include "got_object.h"
 #include "got_error.h"
 #include "got_path.h"
+#include "got_repository.h"
 
 #include "got_lib_sha1.h"
 #include "got_lib_delta.h"
@@ -1281,6 +1282,17 @@ got_privsep_send_gitconfig_author_email_req(struct ims
 }
 
 const struct got_error *
+got_privsep_send_gitconfig_remotes_req(struct imsgbuf *ibuf)
+{
+	if (imsg_compose(ibuf,
+	    GOT_IMSG_GITCONFIG_REMOTES_REQUEST, 0, 0, -1, NULL, 0) == -1)
+		return got_error_from_errno("imsg_compose "
+		    "GITCONFIG_REMOTE_REQUEST");
+
+	return flush_imsg(ibuf);
+}
+
+const struct got_error *
 got_privsep_send_gitconfig_str(struct imsgbuf *ibuf, const char *value)
 {
 	size_t len = value ? strlen(value) + 1 : 0;
@@ -1368,6 +1380,170 @@ got_privsep_recv_gitconfig_int(int *val, struct imsgbu
 	}
 
 	imsg_free(&imsg);
+	return err;
+}
+
+const struct got_error *
+got_privsep_send_gitconfig_remotes(struct imsgbuf *ibuf,
+    struct got_remote_repo *remotes, int nremotes)
+{
+	const struct got_error *err = NULL;
+	struct got_imsg_remotes iremotes;
+	int i;
+
+	iremotes.nremotes = nremotes;
+	if (imsg_compose(ibuf, GOT_IMSG_GITCONFIG_REMOTES, 0, 0, -1,
+	    &iremotes, sizeof(iremotes)) == -1)
+		return got_error_from_errno("imsg_compose GITCONFIG_REMOTES");
+
+	err = flush_imsg(ibuf);
+	imsg_clear(ibuf);
+	if (err)
+		return err;
+
+	for (i = 0; i < nremotes; i++) {
+		struct got_imsg_remote iremote;
+		size_t len = sizeof(iremote);
+		struct ibuf *wbuf;
+
+		iremote.name_len = strlen(remotes[i].name);
+		len += iremote.name_len;
+		iremote.url_len = strlen(remotes[i].url);
+		len += iremote.url_len;
+
+		wbuf = imsg_create(ibuf, GOT_IMSG_GITCONFIG_REMOTE, 0, 0, len);
+		if (wbuf == NULL)
+			return got_error_from_errno(
+			    "imsg_create GITCONFIG_REMOTE");
+
+		if (imsg_add(wbuf, &iremote, sizeof(iremote)) == -1) {
+			err = got_error_from_errno(
+			    "imsg_add GIITCONFIG_REMOTE");
+			ibuf_free(wbuf);
+			return err;
+		}
+
+		if (imsg_add(wbuf, remotes[i].name, iremote.name_len) == -1) {
+			err = got_error_from_errno(
+			    "imsg_add GIITCONFIG_REMOTE");
+			ibuf_free(wbuf);
+			return err;
+		}
+		if (imsg_add(wbuf, remotes[i].url, iremote.url_len) == -1) {
+			err = got_error_from_errno(
+			    "imsg_add GIITCONFIG_REMOTE");
+			ibuf_free(wbuf);
+			return err;
+		}
+
+		wbuf->fd = -1;
+		imsg_close(ibuf, wbuf);
+		err = flush_imsg(ibuf);
+		if (err)
+			return err;
+	}
+
+	return NULL;
+}
+
+const struct got_error *
+got_privsep_recv_gitconfig_remotes(struct got_remote_repo **remotes,
+    int *nremotes, struct imsgbuf *ibuf)
+{
+	const struct got_error *err = NULL;
+	struct imsg imsg;
+	size_t datalen;
+	struct got_imsg_remotes iremotes;
+	struct got_imsg_remote iremote;
+
+	*remotes = NULL;
+	*nremotes = 0;
+
+	err = got_privsep_recv_imsg(&imsg, ibuf, sizeof(iremotes));
+	if (err)
+		return err;
+	datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+
+	switch (imsg.hdr.type) {
+	case GOT_IMSG_GITCONFIG_REMOTES:
+		if (datalen != sizeof(iremotes)) {
+			err = got_error(GOT_ERR_PRIVSEP_LEN);
+			break;
+		}
+		memcpy(&iremotes, imsg.data, sizeof(iremotes));
+		if (iremotes.nremotes == 0) {
+			imsg_free(&imsg);
+			return NULL;
+		}
+		break;
+	default:
+		err = got_error(GOT_ERR_PRIVSEP_MSG);
+		break;
+	}
+
+	imsg_free(&imsg);
+
+	*remotes = recallocarray(NULL, 0, iremotes.nremotes, sizeof(iremote));
+	if (*remotes == NULL)
+		return got_error_from_errno("recallocarray");
+
+	while (*nremotes < iremotes.nremotes) {
+		struct got_remote_repo *remote;
+	
+		err = got_privsep_recv_imsg(&imsg, ibuf, sizeof(iremote));
+		if (err)
+			break;
+		datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
+
+		switch (imsg.hdr.type) {
+		case GOT_IMSG_GITCONFIG_REMOTE:
+			remote = &(*remotes)[*nremotes];
+			if (datalen < sizeof(iremote)) {
+				err = got_error(GOT_ERR_PRIVSEP_LEN);
+				break;
+			}
+			memcpy(&iremote, imsg.data, sizeof(iremote));
+			if (iremote.name_len == 0 || iremote.url_len == 0 ||
+			    (sizeof(iremote) + iremote.name_len +
+			    iremote.url_len) > datalen) {
+				err = got_error(GOT_ERR_PRIVSEP_LEN);
+				break;
+			}
+			remote->name = strndup(imsg.data + sizeof(iremote),
+			    iremote.name_len);
+			if (remote->name == NULL) {
+				err = got_error_from_errno("strndup");
+				break;
+			}
+			remote->url = strndup(imsg.data + sizeof(iremote) +
+			    iremote.name_len, iremote.url_len);
+			if (remote->url == NULL) {
+				err = got_error_from_errno("strndup");
+				free(remote->name);
+				break;
+			}
+			(*nremotes)++;
+			break;
+		default:
+			err = got_error(GOT_ERR_PRIVSEP_MSG);
+			break;
+		}
+
+		imsg_free(&imsg);
+		if (err)
+			break;
+	}
+
+	if (err) {
+		int i;
+		for (i = 0; i < *nremotes; i++) {
+			free((*remotes)[i].name);
+			free((*remotes)[i].url);
+		}
+		free(*remotes);
+		*remotes = NULL;
+		*nremotes = 0;
+	}
 	return err;
 }
 
blob - 9dffdcd83aa6b08ebbb5d3c8526500f507c72877
blob + bbb0fe207f98082b7572f6903331cfd865f6af21
--- lib/repository.c
+++ lib/repository.c
@@ -170,6 +170,14 @@ get_path_gitconfig(char **p, struct got_repository *re
 	return NULL;
 }
 
+void
+got_repo_get_remotes(int *nremotes, struct got_remote_repo **remotes,
+    struct got_repository *repo)
+{
+	*nremotes = repo->nremotes;
+	*remotes = repo->remotes;
+}
+
 static int
 is_git_repo(struct got_repository *repo)
 {
@@ -365,6 +373,7 @@ done:
 static const struct got_error *
 parse_gitconfig_file(int *gitconfig_repository_format_version,
     char **gitconfig_author_name, char **gitconfig_author_email,
+    struct got_remote_repo **remotes, int *nremotes,
     const char *gitconfig_path)
 {
 	const struct got_error *err = NULL, *child_err = NULL;
@@ -442,6 +451,17 @@ parse_gitconfig_file(int *gitconfig_repository_format_
 	if (err)
 		goto done;
 
+	if (remotes && nremotes) {
+		err = got_privsep_send_gitconfig_remotes_req(ibuf);
+		if (err)
+			goto done;
+
+		err = got_privsep_recv_gitconfig_remotes(remotes,
+		    nremotes, ibuf);
+		if (err)
+			goto done;
+	}
+
 	imsg_clear(ibuf);
 	err = got_privsep_send_stop(imsg_fds[0]);
 	child_err = got_privsep_wait_for_child(pid);
@@ -470,7 +490,7 @@ read_gitconfig(struct got_repository *repo, const char
 		err = parse_gitconfig_file(&dummy_repo_version,
 		    &repo->global_gitconfig_author_name,
 		    &repo->global_gitconfig_author_email,
-		    global_gitconfig_path);
+		    NULL, NULL, global_gitconfig_path);
 		if (err)
 			return err;
 	}
@@ -482,7 +502,7 @@ read_gitconfig(struct got_repository *repo, const char
 
 	err = parse_gitconfig_file(&repo->gitconfig_repository_format_version,
 	    &repo->gitconfig_author_name, &repo->gitconfig_author_email,
-	    repo_gitconfig_path);
+	    &repo->remotes, &repo->nremotes, repo_gitconfig_path);
 	if (err)
 		goto done;
 done:
@@ -620,6 +640,11 @@ got_repo_close(struct got_repository *repo)
 
 	free(repo->gitconfig_author_name);
 	free(repo->gitconfig_author_email);
+	for (i = 0; i < repo->nremotes; i++) {
+		free(repo->remotes[i].name);
+		free(repo->remotes[i].url);
+	}
+	free(repo->remotes);
 	free(repo);
 
 	return err;
blob - c14f37dd21a67e7027328b8a776e99a036726c7c
blob + 7c6439deb2b6bd6dd54a308e07d765495636ca39
--- libexec/got-read-gitconfig/got-read-gitconfig.c
+++ libexec/got-read-gitconfig/got-read-gitconfig.c
@@ -32,6 +32,7 @@
 
 #include "got_error.h"
 #include "got_object.h"
+#include "got_repository.h"
 
 #include "got_lib_delta.h"
 #include "got_lib_object.h"
@@ -72,6 +73,75 @@ gitconfig_str_request(struct imsgbuf *ibuf, struct got
 	return got_privsep_send_gitconfig_str(ibuf, value);
 }
 
+static const struct got_error *
+gitconfig_remotes_request(struct imsgbuf *ibuf, struct got_gitconfig *gitconfig)
+{
+	const struct got_error *err = NULL;
+	struct got_gitconfig_list *sections;
+	struct got_gitconfig_list_node *node;
+	struct got_remote_repo *remotes = NULL;
+	int nremotes = 0, i;
+
+	if (gitconfig == NULL)
+		return got_error(GOT_ERR_PRIVSEP_MSG);
+
+	err = got_gitconfig_get_section_list(&sections, gitconfig);
+	if (err)
+		return err;
+
+	TAILQ_FOREACH(node, &sections->fields, link) {
+		if (strncasecmp("remote \"", node->field, 8) != 0)
+			continue;
+		nremotes++;
+	}
+
+	if (nremotes == 0) {
+		err = got_privsep_send_gitconfig_remotes(ibuf, NULL, 0);
+		goto done;
+	}
+
+	remotes = recallocarray(NULL, 0, nremotes, sizeof(*remotes));
+	if (remotes == NULL) {
+		err = got_error_from_errno("recallocarray");
+		goto done;
+	}
+
+	i = 0;
+	TAILQ_FOREACH(node, &sections->fields, link) {
+		char *name, *end;
+
+		if (strncasecmp("remote \"", node->field, 8) != 0)
+			continue;
+
+		name = strdup(node->field + 8);
+		if (name == NULL) {
+			err = got_error_from_errno("strdup");
+			goto done;
+		}
+		end = strrchr(name, '"');
+		if (end)
+			*end = '\0';
+		remotes[i].name = name;
+
+		remotes[i].url = got_gitconfig_get_str(gitconfig,
+		    node->field, "url");
+		if (remotes[i].url == NULL) {
+			err = got_error(GOT_ERR_GITCONFIG_SYNTAX);
+			goto done;
+		}
+
+		i++;
+	}
+
+	err = got_privsep_send_gitconfig_remotes(ibuf, remotes, nremotes);
+done:
+	for (i = 0; i < nremotes; i++)
+		free(remotes[i].name);
+	free(remotes);
+	got_gitconfig_free_list(sections);
+	return err;
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -146,6 +216,9 @@ main(int argc, char *argv[])
 		case GOT_IMSG_GITCONFIG_AUTHOR_EMAIL_REQUEST:
 			err = gitconfig_str_request(&ibuf, gitconfig, "user",
 			    "email");
+			break;
+		case GOT_IMSG_GITCONFIG_REMOTES_REQUEST:
+			err = gitconfig_remotes_request(&ibuf, gitconfig);
 			break;
 		default:
 			err = got_error(GOT_ERR_PRIVSEP_MSG);