summaryrefslogtreecommitdiff
path: root/git.c
diff options
context:
space:
mode:
authormrb0nk500 <b0nk@b0nk.xyz>2022-07-28 13:54:05 -0300
committermrb0nk500 <b0nk@b0nk.xyz>2022-07-28 13:54:05 -0300
commit2ddb5741c9d6c8a0942e0a73f66d21617cf378e8 (patch)
treeee13302392deaaac17649b524e22323427c3e569 /git.c
parentd55488c605c425815c641f22aa46c398da20a950 (diff)
git: Implement all of `get_pull_request()`
`get_pull_request()` takes an index, and the main PR root, and returns the PR of the supplied index if it exists, or NULL if no PR with that index exists.
Diffstat (limited to 'git.c')
-rw-r--r--git.c244
1 files changed, 243 insertions, 1 deletions
diff --git a/git.c b/git.c
index ed4805a..7124a4d 100644
--- a/git.c
+++ b/git.c
@@ -72,8 +72,250 @@ void free_files(file **files) {
free(files);
}
-pull_request *get_pull_request(int id, const char *root) {
+static linked_list *parse_dsv_str(const char *str, const char *delm) {
+ linked_list *tail = NULL;
+ for (; *str != '\0'; str += strspn(str, delm)) {
+ /* Get the length of the token. */
+ size_t tok_len = strcspn(str, delm);
+ /* Create the token. */
+ char *tok = calloc(tok_len+1, sizeof(char));
+ /* Get the token from the string. */
+ memcpy(tok, tmp, tok_len);
+ /* Add the token to the list.. */
+ tail = add_node(&tail, tok);
+ /* Move over the token. */
+ str += tok_len;
+ }
+ return tail;
+}
+
+static void parse_patch_list(void *ctx, void *ret, const keyword *key, keyword_val val) {
+ /* Do we actually have a "patches" keyword? */
+ if (key->type == TYPE_STRING) {
+ const char *root = (const char *)ctx;
+ pull_request *pr = (pull_request *)ret;
+ /* Is the supplied string non-empty? */
+ if (!is_empty(val.str)) {
+ /* Parse the patch list. */
+ linked_list *patch_list = parse_dsv_str(val.str, " \t\v");
+ /* Get the number of patch files in the list. */
+ int num_patches = linked_list_size(patch_list);
+
+ /* Create the patch list. */
+ pr->patches = calloc(num_patches+1, sizeof(file *));
+ /* Set the last entry to NULL, to denote the end of the list. */
+ pr->patches[num_patches--] = NULL;
+ /* For each patch file in the list. */
+ for (linked_list *patch = patch_list; patch != NULL; patch = patch->prev, --num_patches) {
+ const char *patch_file = (const char *)patch->data;
+ /* Create a new entry in the list. */
+ pr->patches[num_patches] = create_file(root, patch_file);
+ }
+
+ cleanup_linked_list(patch_list);
+ }
+ }
+}
+
+static void parse_comment_reply(void *ctx, void *ret, const keyword *key, keyword_val val) {
+ linked_list *comment_list = *(linked_list **)ctx;
+ comment *comment = (comment *)ret;
+
+ /* Do we already have a reply? */
+ if (comment->reply != NULL) {
+ /* Check if this reply matches any other comment in the list. */
+ for (linked_list *node = get_tail(comment_list); node != NULL; node = node->prev) {
+ comment *reply = (comment *)node->data;
+
+ /* Do we have a match? */
+ if (comment->reply == reply) {
+ return;
+ }
+ }
+ }
+
+ /* Do we have a "reply-to" keyword? */
+ if (key->type == TYPE_INT) {
+ int found_reply = 0;
+ /* Check if that comment already exists in the list. */
+ for (linked_list *node = get_tail(comment_list); node != NULL; node = node->prev) {
+ comment *reply = (comment *)node->data;
+
+ /* Do we have a match? */
+ if (reply->id == val.i) {
+ /* Does this comment already have a reply? */
+ if (comment->reply != NULL) {
+ /* Free the reply. */
+ free(comment->reply);
+ }
+
+ /* Set our reply comment to the matched comment. */
+ comment->reply = reply;
+ found_reply = 1;
+ break;
+ }
+ }
+
+ /* Did we find a reply in the list? */
+ if (found_reply) {
+ return;
+ }
+ }
+
+ /* Do we not have a reply? */
+ if (comment->reply == NULL) {
+ /* Create one. */
+ comment->reply = calloc(1, sizeof(comment));
+ }
+ /* Which keyword do we have? */
+ switch (key->type) {
+ /* reply-to */
+ case TYPE_INT : comment->reply->id = val.i; break;
+ /* reply-author */
+ case TYPE_STRING: comment->reply->author = val.str; break;
+ /* reply-date */
+ case TYPE_TIME : comment->reply->date = val.t; break;
+ default : break;
+ }
+}
+
+typedef int (parse_callback)(void **ret, void *ctx, char *buf);
+
+static int parse_colon_key_value_file(void *ret, void *ctx, const keyword **keywords, char *buf, parse_callback *parse_cb) {
+ if (buf != NULL) {
+ int ret = 0;
+ for (;;) {
+ char *lhs, *rhs;
+ /* Find the keyword before the colon. */
+ lhs = strtok_r(skip_whitespace(buf), ":", &buf);
+ /* Remove any whitespace ahead of us. */
+ lhs = strtok_r(lhs, " \t\v\r\n", &rhs);
+
+ /* Did we hit EOF? */
+ if (lhs == NULL) {
+ break;
+ } else {
+ char *tmp = skip_whitespace(buf);
+
+ /* Does the right hand side start with a double, or single quote? */
+ if (*tmp == '\"' || *tmp == '\'') {
+ const char *delm = (delm == '\"') ? "\"" : "\'";
+ /* Get the string in between the start, and end qoute. */
+ rhs = get_str_delm_range(tmp, delm, delm, &buf);
+ } else {
+ /* Get the rest of the line. */
+ rhs = strtok_r(tmp, "\n", &buf);
+ }
+
+ /* Did we fail to parse the keyword? */
+ if (int error = parse_keywords(keywords, lhs, rhs, ret, ctx)) {
+ log(LOG_WARNING, "Failed to parse keyword \"%s\". Error code: %i", lhs, error);
+ ret = error;
+ }
+ }
+
+ /* Do we have a parse callback? */
+ if (parse_cb != NULL) {
+ /* Did the callback return an error. */
+ if (parse_cb(&ret, ctx, buf) < 0) {
+ ret = 5;
+ break;
+ }
+ }
+ }
+ return ret;
+ } else {
+ return 6;
+ }
+}
+
+static int is_end_of_comment(void **ret, void *ctx, char *buf) {
+ /* Do we have at least one blank line? */
+ if (strspn(buf, "\n") >= 1) {
+ linked_list **comment_list = (linked_list **)ctx;
+ if (comment_list != NULL) {
+ /* Is our comment list NULL, or is there an existing entry? */
+ if (*comment_list == NULL || (*comment_list)->data == NULL) {
+ add_node(comment_list, *ret);
+ }
+ *ret = calloc(1, sizeof(comment));
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int parse_comments_file(comment ***comments, char *buf) {
+ if (comments == NULL) {
+ return 7;
+ } else {
+ linked_list *comment_list = NULL;
+ int ret = parse_colon_key_value_file(calloc(1, sizeof(comment)), &comment_list, comment_keywords, buf, is_end_of_comment);
+ *comments = linked_list_to_array(comment_list);
+ cleanup_linked_list(comments_list);
+ return ret;
+ }
+}
+
+
+static int parse_info_file(pull_request *pr, char *buf, const char *root) {
+ return parse_colon_key_value_file(pr, root, info_keywords, buf, NULL);
+}
+
+static int parse_comments_file_path(comment ***comments, const char *root) {
+ file *f = create_file(root, "comments");
+ int ret = parse_comments_file(comments, f->buf);
+ free_file(f);
+ return ret;
+}
+
+static int parse_info_file_path(pull_request *pr, const char *root) {
+ file *f = create_file(root, "info");
+ int ret = parse_info_file(pr, f->buf, root);
+ free_file(f);
+ return ret;
+}
+
+pull_request *get_pull_request(index *idx, const char *root) {
+ /* Do we have a valid index? */
+ if (is_valid_index(idx)) {
+ char *pr_dir;
+ char *idx_str = index_to_str(idx);
+
+ /* Does the converted path of our index exist? */
+ if (index_path_exists(idx, root, &pr_dir) >= 0) {
+ pull_request *pr = calloc(1, sizeof(pull_request));
+
+ log(LOG_NOTICE, "Found PR #%s with path \"%s\".", idx_str, pr_dir);
+
+ /* Did we successfully parse the info file? */
+ if (parse_info_file_path(pr, pr_dir) <= 1) {
+ log(LOG_NOTICE, "Successfully parsed info file of PR #%s.", idx_str);
+
+ /* Did we successfully parse the comments file? */
+ if (parse_comments_file_path(&pr->comments, pr_dir) <= 1) {
+ log(LOG_NOTICE, "Successfully parsed comments file of PR #%s.", idx_str);
+ } else {
+ log(LOG_WARNING, "Failed to parse comments file of PR #%s.", idx_str);
+ }
+
+ return pr;
+ } else {
+ log(LOG_ERR, "Failed to parse info file of PR #%s.", idx_str);
+ free(pr);
+ return NULL;
+ }
+ } else {
+ log(LOG_ERR, "No PR #%s found.", idx_str);
+ free(idx_str);
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
}
int get_info_len(pull_request *pr) {