gitout.c (5038B)
1 #include "gitout.h" 2 3 #include <errno.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 7 #include "git/commit.h" 8 #include "git/file.h" 9 #include "git/git.h" 10 #include "git/reference.h" 11 #include "git/repo.h" 12 #include "security.h" 13 #include "utils.h" 14 #include "writer/repo_writer.h" 15 16 struct GitoutOptions { 17 const char* repodir; 18 long long log_commit_limit; /* -1 indicates not used */ 19 const char* cachefile_path; 20 const char* baseurl; /* base URL to make absolute RSS/Atom URI */ 21 RepoWriterType writer_type; 22 }; 23 24 static void commit_callback(const GitCommit* commit, void* user_data); 25 static void reference_callback(const GitReference* ref, void* user_data); 26 static void file_callback(const GitFile* file, void* user_data); 27 28 GitoutOptions* gitout_options_create(int argc, const char* argv[]) { 29 GitoutOptions options = { 30 .repodir = NULL, 31 .log_commit_limit = -1, 32 .cachefile_path = NULL, 33 .baseurl = "", 34 .writer_type = kRepoWriterTypeHtml, 35 }; 36 for (int i = 1; i < argc; i++) { 37 if (argv[i][0] != '-') { 38 // Cannot specify more than one repo directory. 39 if (options.repodir) { 40 return NULL; 41 } 42 options.repodir = argv[i]; 43 } else if (argv[i][1] == 'c') { 44 // Cache the entries of the log page up to the point of the last commit. 45 // The cachefile will store the last commit id and the entries in the 46 // HTML table. It is up to the user to make sure the state of the 47 // cachefile is in sync with the history of the repository. 48 49 // Mutually exclusive with -l. 50 if (options.log_commit_limit > 0) { 51 return NULL; 52 } 53 // Requires cachefile path argument. 54 if (i + 1 >= argc) { 55 return NULL; 56 } 57 options.cachefile_path = argv[++i]; 58 } else if (argv[i][1] == 'l') { 59 // Write a maximum number of commits to the log.html file only. However 60 // the commit files are written as usual. 61 62 // Mutually exclusive with -c option. 63 if (options.cachefile_path) { 64 return NULL; 65 } 66 // Requires log commits argument. 67 if (i + 1 >= argc) { 68 return NULL; 69 } 70 errno = 0; 71 char* p; 72 options.log_commit_limit = strtoll(argv[++i], &p, 10); 73 if (!argv[i][0] || *p || options.log_commit_limit <= 0 || errno) { 74 return NULL; 75 } 76 } else if (argv[i][1] == 'u') { 77 // Requires log commits argument. 78 if (i + 1 >= argc) { 79 return NULL; 80 } 81 options.baseurl = argv[++i]; 82 } else if (argv[i][1] == 'H') { 83 options.writer_type = kRepoWriterTypeHtml; 84 } else if (argv[i][1] == 'G') { 85 options.writer_type = kRepoWriterTypeGopher; 86 } 87 } 88 // Must specify at least one repo directory. 89 if (!options.repodir) { 90 return NULL; 91 } 92 GitoutOptions* options_out = ecalloc(1, sizeof(GitoutOptions)); 93 *options_out = options; 94 return options_out; 95 } 96 97 void gitout_options_free(GitoutOptions* options) { 98 if (!options) { 99 return; 100 } 101 options->repodir = NULL; 102 options->log_commit_limit = -1; 103 options->cachefile_path = NULL; 104 options->baseurl = NULL; 105 free(options); 106 } 107 108 void gitout_init(const GitoutOptions* options) { 109 // Restrict our permissions to the minimum necessary. 110 const char* readonly_paths[] = {options->repodir}; 111 size_t readonly_paths_count = 1; 112 const char* readwrite_paths[2] = {".", NULL}; 113 size_t readwrite_paths_count = 1; 114 if (options->cachefile_path) { 115 readwrite_paths[1] = options->cachefile_path; 116 readwrite_paths_count = 2; 117 } 118 restrict_filesystem_access(readonly_paths, readonly_paths_count, 119 readwrite_paths, readwrite_paths_count); 120 restrict_system_operations( 121 options->cachefile_path != NULL ? kGitoutWithCachefile : kGitout); 122 123 gitout_git_initialize(); 124 } 125 126 void gitout_run(const GitoutOptions* options) { 127 GitRepo* repo = gitrepo_create(options->repodir); 128 RepoWriter* writer = repowriter_create(options->writer_type, repo); 129 if (options->log_commit_limit >= 0) { 130 repowriter_set_log_commit_limit(writer, options->log_commit_limit); 131 } 132 if (options->cachefile_path) { 133 repowriter_set_log_cachefile(writer, options->cachefile_path); 134 } 135 if (options->baseurl) { 136 repowriter_set_baseurl(writer, options->baseurl); 137 } 138 139 repowriter_begin(writer); 140 gitrepo_for_each_commit(repo, commit_callback, writer); 141 gitrepo_for_each_reference(repo, reference_callback, writer); 142 gitrepo_for_each_file(repo, file_callback, writer); 143 repowriter_end(writer); 144 145 repowriter_free(writer); 146 gitrepo_free(repo); 147 } 148 149 void gitout_shutdown(void) { 150 gitout_git_shutdown(); 151 } 152 153 void commit_callback(const GitCommit* ci, void* user_data) { 154 RepoWriter* writer = (RepoWriter*)user_data; 155 repowriter_add_commit(writer, ci); 156 } 157 158 void reference_callback(const GitReference* ref, void* user_data) { 159 RepoWriter* writer = (RepoWriter*)user_data; 160 repowriter_add_reference(writer, ref); 161 } 162 163 void file_callback(const GitFile* file, void* user_data) { 164 RepoWriter* writer = (RepoWriter*)user_data; 165 repowriter_add_file(writer, file); 166 }