#include #include #include #include #include #include #include #include #include #include #include #include #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(); } 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, "%c %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, "%c %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 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, "%c %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, "%c %z\n", &tm); } j += sprintf(file_buf+j, "description: %s", 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; } int create_pull_request_dir(pull_request *pr, int id, const char *root) { int ret = id; struct stat st; file **commits; char *pr_dir; /* Is this a NULL PR? */ if (pr == NULL) { log(LOG_ERR, "Pull Request is NULL."); return -1; } /* Is the ID negative, and the PR title blank, or NULL? */ if (id < 0 && (pr->title == NULL || pr->title[0] == '\0')) { log(LOG_ERR, "Negative ID, and either blank, or NULL Pull Request title."); return -1; } /* Get the path of the PR's directory. */ pr_dir = (pr->title != NULL && pr->title[0] != '\0') ? dir_path_name(root, sanitize_str(pr->title)) : dir_path_num(root, id); /* 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; } /* Is there no existing directory? */ if (stat(pr_dir, &st) < -1) { /* Create a directory with a name of the stringified ID. */ mkdir(pr_dir, 0755); } /* 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; /* Does this patch file exists? */ if (access(commits[i]->name, F_OK) == 0) { long size = 0; char *buf = read_file(commits[i]->name, &size); log(LOG_NOTICE, "Patch file %s already exists.", commits[i]->name); /* 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.", commits[i]->name); free(buf); continue; } free(buf); } else { log(LOG_WARNING, "Couldn't open patch file %s.", commits[i]->name); continue; } } /* Create the format patch file for this commit. */ fp = fopen(commits[i]->name, "w"); fwrite(commits[i]->buf, sizeof(char), strlen(commits[i]->buf), fp); fclose(fp); } 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; }