gout.c (4946B)
1 #include "gout.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 GoutOptions { 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 GoutOptions* gout_options_create(int argc, const char* argv[]) { 29 GoutOptions 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 GoutOptions* options_out = ecalloc(1, sizeof(GoutOptions)); 93 *options_out = options; 94 return options_out; 95 } 96 97 void gout_options_free(GoutOptions* 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 gout_init(const GoutOptions* options) { 109 const char* readonly_paths[] = {options->repodir}; 110 size_t readonly_paths_count = 1; 111 const char* readwrite_paths[2] = {".", NULL}; 112 size_t readwrite_paths_count = 1; 113 if (options->cachefile_path) { 114 readwrite_paths[1] = options->cachefile_path; 115 readwrite_paths_count = 2; 116 } 117 restrict_filesystem_access(readonly_paths, readonly_paths_count, 118 readwrite_paths, readwrite_paths_count); 119 restrict_system_operations( 120 options->cachefile_path != NULL ? kGoutWithCachefile : kGout); 121 122 gout_git_initialize(); 123 } 124 125 void gout_run(const GoutOptions* options) { 126 GitRepo* repo = gitrepo_create(options->repodir); 127 RepoWriter* writer = repowriter_create(options->writer_type, repo); 128 if (options->log_commit_limit >= 0) { 129 repowriter_set_log_commit_limit(writer, options->log_commit_limit); 130 } 131 if (options->cachefile_path) { 132 repowriter_set_log_cachefile(writer, options->cachefile_path); 133 } 134 if (options->baseurl) { 135 repowriter_set_baseurl(writer, options->baseurl); 136 } 137 138 repowriter_begin(writer); 139 gitrepo_for_each_commit(repo, commit_callback, writer); 140 gitrepo_for_each_reference(repo, reference_callback, writer); 141 gitrepo_for_each_file(repo, file_callback, writer); 142 repowriter_end(writer); 143 144 repowriter_free(writer); 145 gitrepo_free(repo); 146 } 147 148 void gout_shutdown(void) { 149 gout_git_shutdown(); 150 } 151 152 void commit_callback(const GitCommit* ci, void* user_data) { 153 RepoWriter* writer = (RepoWriter*)user_data; 154 repowriter_add_commit(writer, ci); 155 } 156 157 void reference_callback(const GitReference* ref, void* user_data) { 158 RepoWriter* writer = (RepoWriter*)user_data; 159 repowriter_add_reference(writer, ref); 160 } 161 162 void file_callback(const GitFile* file, void* user_data) { 163 RepoWriter* writer = (RepoWriter*)user_data; 164 repowriter_add_file(writer, file); 165 }