gitout

A static git page generator
git clone https://git.bracken.jp/gitout.git
Log | Files | Refs | Submodules | README | LICENSE

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 }