diff options
| -rw-r--r-- | git.c | 244 | ||||
| -rw-r--r-- | git.h | 29 | 
2 files changed, 272 insertions, 1 deletions
| @@ -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) { @@ -5,6 +5,7 @@  #include <stdint.h>  #include <time.h>  #include "index.h" +#include "keyword.h"  typedef struct pull_request pull_request;  typedef struct file file; @@ -48,4 +49,32 @@ extern git_repository **init_git(config *cfg);  extern int add_comment(comment *comment, const char *pr_root);  extern int create_pull_request_dir(pull_request *pr, index *idx, const char *root); +void parse_patch_list(void *ctx, void *ret, const keyword *key, keyword_val val); +void parse_comment_reply(void *ctx, void *ret, const keyword *key, keyword_val val); + +#define offset_list(...) (size_t []){__VA_ARGS__, -1} +static const keyword *info_keywords[] = { +	&(const keyword *){"title", "Title of Pull Request.", NULL, TYPE_STRING, offset_list(offsetof(pull_request, title)), NULL}, +	&(const keyword *){"author", "Author of Pull Request.", NULL, TYPE_STRING, offset_list(offsetof(pull_request, author)), NULL}, +	&(const keyword *){"date", "Date of Pull Request's creation.", "%a %b %e %H:%M:%S %Y %z", TYPE_TIME, offset_list(offsetof(pull_request, date)), NULL}, +	&(const keyword *){"description", "Description of Pull Request.", NULL, TYPE_STRING, offset_list(offsetof(pull_request, desc)), NULL}, +	&(const keyword *){"type", "Type of Pull Request (0 = patch files, 1 = remote repo).", NULL, TYPE_INT, offset_list(offsetof(pull_request, pr_type)), NULL}, +	&(const keyword *){"branch", "Branch of remote repo.", NULL, TYPE_STRING, offset_list(offsetof(pull_request, branch), offsetof(git_branch, name)), NULL}, +	&(const keyword *){"repo", "URL of remote repo.", NULL, TYPE_STRING, offset_list(offsetof(pull_request, branch), offsetof(git_branch, repo)), NULL}, +	&(const keyword *){"patches", "Patch file(s) of Pull Request.", NULL, TYPE_STRING, offset_list(offsetof(pull_request, patches), offsetof(file, name)), parse_patch_list}, +	NULL, +}; + +static const keyword *comment_keywords[] = { +	&(const keyword *){"id", "ID of Comment.", NULL, TYPE_INT, offset_list(offsetof(comment, id)), NULL}, +	&(const keyword *){"author", "Author of Comment.", NULL, TYPE_STRING, offset_list(offsetof(comment, author)), NULL}, +	&(const keyword *){"date", "Date of Comment's creation.", "%a %b %e %H:%M:%S %Y %z", TYPE_TIME, offset_list(offsetof(comment, date)), NULL}, +	&(const keyword *){"reply-to", "ID of replied Comment.", NULL, TYPE_INT, offset_list(offsetof(comment, reply), offsetof(comment, id)), parse_comment_reply}, +	&(const keyword *){"reply-author", "Author of replied Comment.", NULL, TYPE_STRING, offset_list(offsetof(comment, reply), offsetof(comment, author)), parse_comment_reply}, +	&(const keyword *){"reply-date", "Date of replied Comment's creation", "%a %b %e %H:%M:%S %Y %z", TYPE_TIME, offset_list(offsetof(comment, reply), offsetof(comment, date)), parse_comment_reply}, +	&(const keyword *){"description", "Message of Comment.", NULL, TYPE_STRING, offset_list(offsetof(comment, desc)), NULL}, +	NULL, +}; +#undef offset_list +  #endif | 
