From: Stefan Sperling Subject: parse remotes from gitconfig To: gameoftrees@openbsd.org Cc: ori@openbsd.org Date: Wed, 27 Nov 2019 18:02:22 -0700 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(§ions, gitconfig); + if (err) + return err; + + TAILQ_FOREACH(node, §ions->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, §ions->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);