summaryrefslogtreecommitdiff
path: root/git.c
diff options
context:
space:
mode:
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) {