summaryrefslogtreecommitdiff
path: root/git.c
blob: 61ab0c0c5d328aac8a54116a1c34e40c8a2cd09f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
#include "config.h"
#include "git.h"
#include "macros.h"
#include "misc.h"


git_repo *add_repo(git_repository *repo, git_repo **first, git_repo **last) {
	git_repo *new_repo = calloc(1, sizeof(git_repo));
	(*last != NULL) ? ((*last)->next = new_repo) : (*first = new_repo);

	new_repo->repo = repo;

	*last = new_repo;

	return new_repo;
}

int find_ignore_file(const char *path) {
	DIR *root = opendir(path);
	struct dirent entry, *result;

	while (readdir_r(root, &entry, &result) == 0 && result != NULL) {
		/* Is this entry a regular file, and is it the ignore file? */
		if (entry.d_type == DT_REG && strcasecmp(entry.d_name, "pullreqd-ignore") == 0) {
			return 1;
		}
	}

	return 0;
}

void cleanup_linked_list(git_repo *root) {
	if (root != NULL) {
		git_repo *repo = root;
		cleanup_linked_list(root->next);
		repo->repo = NULL;
		repo->next = NULL;
		free(repo);
	}
}

void cleanup_git_repos(git_repository **repos) {
	for (int i = 0; repos[i] != NULL; i++) {
		git_repository_free(repos[i]);
		repos[i] = NULL;
	}
	free(repos);
}

void cleanup_git(git_repository **repos) {
	cleanup_git_repos(repos);
	git_libgit2_shutdown();
}

void free_file(file *file) {
	if (file->name != NULL) {
		free(file->name);
	}
	if (file->buf != NULL) {
		free(file->buf);
	}
}

void free_files(file **files) {
	for (int i = 0; files[i] != NULL; i++) {
		free_file(files[i]);
		files[i] = NULL;
	}
	free(files);
}

pull_request *get_pull_request(int id, const char *root) {

}

int get_info_len(pull_request *pr) {
	struct tm tm;
	int len = format_len("title: %s\n", pr->title);

	localtime_r(&pr->date, &tm);

	len += format_len("author: %s\n", pr->author);
	len += format_len("date: ") + strftime(NULL, -1, "%a %b %e %H:%M:%S %Y %z\n", &tm);
	len += format_len("description: %s\n", pr->desc);
	len += format_len("type: %i\n", pr->pr_type);

	/* Is this PR using a remote branch? */
	if (pr->pr_type) {
		len += format_len("branch: %s\n", pr->branch->name);
		len += format_len("repo: %s", pr->branch->repo);
	} else {
		len += format_len("patches:");
		for (int i = 0; pr->patches[i] != NULL; i++) {
			len += format_len(" %s", pr->patches[i]->name);
		}
	}
	return len;

}

int create_info_file(pull_request *pr, const char *pr_root) {
	int len;
	int buf_len;
	int j = 0;
	char *filename;
	char *file_buf;
	FILE *fp;
	struct tm tm;

	/* Is the PR NULL? */
	if (pr == NULL) {
		log(LOG_ERR,"Pull request is NULL.");
		return 0;
	}
	/* Is the PR root NULL? */
	if (pr_root == NULL) {
		log(LOG_ERR,"Pull request root is NULL.");
		return 0;
	}

	localtime_r(&pr->date, &tm);

	len = strlen(pr_root) + strlen("/info");
	buf_len = get_info_len(pr);
	filename = calloc(len+1, sizeof(char));
	file_buf = calloc(buf_len+1, sizeof(char));

	j = sprintf(file_buf, "title: %s\n", pr->title);
	j += sprintf(file_buf+j, "author: %s\n", pr->author);
	j += sprintf(file_buf+j, "date: ");
	j += strftime(file_buf+j, -1, "%a %b %e %H:%M:%S %Y %z\n", &tm);
	j += sprintf(file_buf+j, "description: %s\n", pr->desc);
	j += sprintf(file_buf+j, "type: %i\n", pr->pr_type);

	/* Is this PR using a remote branch? */
	if (pr->pr_type) {
		j += sprintf(file_buf+j, "branch: %s\n", pr->branch->name);
		j += sprintf(file_buf+j, "repo: %s", pr->branch->repo);
	} else {
		j += sprintf(file_buf+j, "patches:");
		for (int i = 0; pr->patches[i] != NULL; i++) {
			j += sprintf(file_buf+j, " %s", pr->patches[i]->name);
		}
	}

	/* Append /info to the PR root. */
	sprintf(filename, "%s/info", pr_root);

	/* Is there already an info file? */
	if (access(filename, F_OK) == 0) {
		long size = 0;
		char *buf = read_file(filename, &size);

		log(LOG_NOTICE, "Info file already exists.");

		/* Did we read the file? */
		if (buf != NULL) {
			/* Are the contents of the info file the same? */
			if (strcmp(file_buf, buf) == 0) {
				log(LOG_NOTICE, "New info file is the same as the existing one.");
				free(filename);
				free(file_buf);
				free(buf);
				return 2;
			}
			free(buf);
		} else {
			log(LOG_ERR, "Couldn't open existing info file.");
			return 0;
		}
	}

	/* Open the info file. */
	fp = fopen(filename, "w");

	/* Did we fail to open the file? */
	if (fp == NULL) {
		log(LOG_ERR, "Failed to open info file.");
		return 0;
	}

	fwrite(file_buf, sizeof(char), buf_len, fp);
	fclose(fp);

	return 1;
}

int get_comment_len(comment *comment) {
	struct tm tm;
	int len = format_len("id: %i\n", comment->id);

	localtime_r(&comment->date, &tm);

	len += format_len("author: %s\n", comment->author);
	len += format_len("date: ") + strftime(NULL, -1, "%a %b %e %H:%M:%S %Y %z\n", &tm);

	/* Is this comment a reply? */
	if (comment->reply != NULL) {
		localtime_r(&comment->reply->date, &tm);
		len += format_len("reply-to: %i\n", comment->reply->id);
		len += format_len("reply-author: %s\n", comment->reply->author);
		len += format_len("reply-date: ") + strftime(NULL, -1, "%a %b %e %H:%M:%S %Y %z\n", &tm);
	}

	len += format_len("description: %s\n\n", comment->desc);

	return len;

}

int add_comment(comment *comment, const char *pr_root) {
	int len;
	int buf_len;
	int j = 0;
	char *filename;
	char *file_buf;
	FILE *fp;
	struct tm tm;

	/* Is the comment NULL? */
	if (comment == NULL) {
		log(LOG_ERR, "Comment is NULL.");
		return 0;
	}
	/* Is the PR directory NULL? */
	if (pr_root == NULL) {
		log(LOG_ERR,"Pull request root is NULL.");
		return 0;
	}

	localtime_r(&comment->date, &tm);

	len = strlen(pr_root) + strlen("/comments");
	buf_len = get_comment_len(comment);
	filename = calloc(len+1, sizeof(char));
	file_buf = calloc(buf_len+1, sizeof(char));

	j = sprintf(file_buf, "id: %i\n", comment->id);
	j += sprintf(file_buf+j, "author: %s\n", comment->author);
	j += sprintf(file_buf+j, "date: ");
	j += strftime(file_buf+j, -1, "%a %b %e %H:%M:%S %Y %z\n", &tm);

	/* Is this comment a reply? */
	if (comment->reply != NULL) {
		localtime_r(&comment->reply->date, &tm);
		j += sprintf(file_buf+j, "reply-to: %i\n", comment->reply->id);
		j += sprintf(file_buf+j, "reply-author: %s\n", comment->reply->author);
		j += sprintf(file_buf+j, "reply-date: ");
		j += strftime(file_buf+j, -1, "%a %b %e %H:%M:%S %Y %z\n", &tm);
	}
	j += sprintf(file_buf+j, "description: %s\n\n", comment->desc);

	/* Append /comments to the PR root. */
	sprintf(filename, "%s/comments", pr_root);

	/* Open the comments file. */
	fp = fopen(filename, "a");

	/* Did we fail to open the file? */
	if (fp == NULL) {
		log(LOG_ERR, "Failed to open comments file.");
		return 0;
	}

	fwrite(file_buf, sizeof(char), buf_len, fp);
	fclose(fp);

	return 1;
}

file **get_branch_commits(git_branch *br) {
	return NULL;
}

int create_pull_request_dir(pull_request *pr, index *idx, const char *root) {
	int ret = 0;
	struct stat st;
	file **commits;
	char *pr_dir;
	char *reason;

	/* Is this a NULL PR? */
	if (pr == NULL) {
		log(LOG_ERR, "Pull Request is NULL.");
		return -1;
	}

	/* Use the index as the directory path. */
	pr_dir = make_index_path(root, idx);

	/* Did we fail to make an indexed path? */
	if (pr_dir == NULL) {
		/* Is the PR title empty? */
		if (is_empty(pr->title)) {
			log(LOG_ERR, "Empty PR title.");
			return -1;
		}

		/* Use the title of the PR as the directory path. */
		pr_dir = sanitized_dir_path_name(root, pr->title);
	}

	/* Is there no existing directory? */
	if (stat(pr_dir, &st) < 0) {
		/* Create a directory with a name of the stringified ID. */
		mkdir(pr_dir, 0755);
	}

	/* Did we fail to create the info file? */
	if (!create_info_file(pr, pr_dir)) {
		log(LOG_ERR, "Failed to create info file.");
		return -1;
	}

	/* Get the patch files. */
	commits = (pr->pr_type) ? get_branch_commits(pr->branch) : pr->patches;

	/* Is the patch file array non-NULL? */
	if (commits != NULL) {
		/* Make patch files for each commit of PR. */
		for (int i = 0; commits[i] != NULL; i++) {
			FILE *fp;
			/*char *filename = dir_path_name(root, sanitize_str(create_num_str(commits[i]->name, i)));*/
			char *filename = create_num_str(commits[i]->name, i);
			char *file = sanitized_dir_path_name(pr_dir, filename);

			/* Does this patch file exists? */
			if (access(file, F_OK) == 0) {
				long size = 0;
				char *buf = read_file(file, &size);
				log(LOG_NOTICE, "Patch file %s already exists.", filename);
				/* Did we read the file? */
				if (buf != NULL) {
					/* Are the contents of the patch file the same? */
					if (strcmp(commits[i]->buf, buf) == 0) {
						/* Skip creating this patch file. */
						log(LOG_NOTICE, "Patch file %s is the same as the new one.", filename);
						free(buf);
						free(filename);
						free(file);
						continue;
					}
					free(buf);
				} else {
					log(LOG_WARNING, "Couldn't open patch file %s.", filename);
					free(filename);
					free(file);
					continue;
				}
			}

			/* Create the format patch file for this commit. */
			fp = fopen(file, "w");
			fwrite(commits[i]->buf, sizeof(char), strlen(commits[i]->buf), fp);
			fclose(fp);
			free(filename);
			free(file);
		}

		if (commits != NULL && pr->pr_type) {
			free_files(commits);
		}
	} else {
		log(LOG_ERR, "Patch file array is NULL.");
		ret = -1;
	}
	free(pr_dir);
	return ret;
}

git_repository **init_git(config *cfg) {
	git_repository **repos = NULL;
	git_repository *repo = NULL;
	git_repo *first = NULL, *last = NULL, *current = NULL;
	DIR *root = opendir(cfg->git_root);
	struct dirent entry, *result;
	int repo_count = 0;

	log(LOG_INFO, "Initializing libgit2.");
	int ret = git_libgit2_init();

	log(LOG_INFO, "Searching \"%s\" for repositories.", cfg->git_root);
	/* Find all git repos in the git root. */
	while (readdir_r(root, &entry, &result) == 0 && result != NULL) {
		/*log(LOG_DEBUG, "entry.d_name: %s, result->d_name: %s", entry.d_name, result->d_name);*/
		/* Is this entry a directory? */
		if (entry.d_type == DT_DIR) {
			/* Is the entry neither ".", nor ".."? */
			if (strcmp(entry.d_name, ".") && strcmp(entry.d_name, "..")) {
				char *repo_dir = calloc(strlen(cfg->git_root) + strlen(entry.d_name) + 2, sizeof(char));

				/* Append the directory name to the git root. */
				/* Could also do this:
				 * memcpy(repo_dir, cfg->git_root, strlen(cfg->git_repo));
				 * strcat(repo_dir, entry.d_name);
				 */
				sprintf(repo_dir, "%s/%s", cfg->git_root, entry.d_name);
				/* Was no ignore file found? */
				if (!find_ignore_file(repo_dir)) {
					/* Did we fail to open the git repo? */
					if (git_repository_open(&repo, repo_dir)) {
						log(LOG_ERR, "Failed to open git repository %s, ignoring.", entry.d_name);
					} else {
						log(LOG_INFO, "Successfully opened git repository %s", entry.d_name);
						current = add_repo(repo, &first, &last);
						repo_count++;
					}
				}
				free(repo_dir);
			}
		}
	}

	/* Did we find any repos? */
	if (repo_count) {
		log(LOG_INFO, "Found, and opened %i repositories.", repo_count);
		/* Allocate repo_count + 1 git repos, since we need a NULL entry to denote the end. */
		repos = calloc(repo_count+1, sizeof(git_repository *));
		/* Add the repos to the array. */
		int i = 0;
		for (git_repo *r = first; r != NULL; /*repos[i] = r->repo,*/ r = r->next, i++) {
			repos[i] = r->repo;
		}
		cleanup_linked_list(first);
		first = NULL;
		last = NULL;
		current = NULL;

	} else {
		log(LOG_ERR, "Couldn't find, and/or open any repositories.");
	}

	return repos;
}