gout

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

gout.c (4607B)


      1 #include "gout.h"
      2 
      3 #include <assert.h>
      4 #include <errno.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <unistd.h>
      8 
      9 #include "git/commit.h"
     10 #include "git/file.h"
     11 #include "git/git.h"
     12 #include "git/reference.h"
     13 #include "security.h"
     14 #include "utils.h"
     15 #include "writer/repo_writer.h"
     16 
     17 static void repo_callback(Git* git, void* user_data);
     18 static void commit_callback(const GitCommit* commit, void* user_data);
     19 static void reference_callback(const GitReference* ref, void* user_data);
     20 static void file_callback(const GitFile* file, void* user_data);
     21 
     22 GoutOptions* gout_options_create(int argc, const char* argv[]) {
     23   assert(argv != NULL);
     24   GoutOptions options = {
     25       .repodir = NULL,
     26       .log_commit_limit = -1,
     27       .cachefile_path = NULL,
     28       .baseurl = "",
     29       .writer_type = kRepoWriterTypeHtml,
     30   };
     31 
     32   int ch;
     33   optind = 1;
     34   opterr = 0;
     35   while ((ch = getopt(argc, (char* const*)argv, "c:l:u:HGM")) != -1) {
     36     switch (ch) {
     37       case 'c':
     38         // Mutually exclusive with -l.
     39         if (options.log_commit_limit > 0) {
     40           return NULL;
     41         }
     42         options.cachefile_path = optarg;
     43         break;
     44       case 'l':
     45         // Mutually exclusive with -c.
     46         if (options.cachefile_path) {
     47           return NULL;
     48         }
     49         errno = 0;
     50         char* p;
     51         options.log_commit_limit = strtoll(optarg, &p, 10);
     52         if (!optarg[0] || *p || options.log_commit_limit <= 0 || errno) {
     53           return NULL;
     54         }
     55         break;
     56       case 'u':
     57         options.baseurl = optarg;
     58         break;
     59       case 'H':
     60         options.writer_type = kRepoWriterTypeHtml;
     61         break;
     62       case 'G':
     63         options.writer_type = kRepoWriterTypeGopher;
     64         break;
     65       case 'M':
     66         options.writer_type = kRepoWriterTypeGemini;
     67         break;
     68       default:
     69         return NULL;
     70     }
     71   }
     72   argc -= optind;
     73   argv += optind;
     74 
     75   // Must specify exactly one repo directory.
     76   if (argc != 1) {
     77     return NULL;
     78   }
     79   options.repodir = argv[0];
     80 
     81   GoutOptions* options_out = ecalloc(1, sizeof(GoutOptions));
     82   *options_out = options;
     83   return options_out;
     84 }
     85 
     86 void gout_options_free(GoutOptions* options) {
     87   if (!options) {
     88     return;
     89   }
     90   options->repodir = NULL;
     91   options->log_commit_limit = -1;
     92   options->cachefile_path = NULL;
     93   options->baseurl = NULL;
     94   free(options);
     95 }
     96 
     97 void gout_init(const GoutOptions* options, Git* git) {
     98   assert(options != NULL);
     99   assert(git != NULL);
    100 
    101   const char* readonly_paths[] = {options->repodir};
    102   size_t readonly_paths_count = 1;
    103   const char* readwrite_paths[2] = {".", NULL};
    104   size_t readwrite_paths_count = 1;
    105   if (options->cachefile_path) {
    106     readwrite_paths[1] = options->cachefile_path;
    107     readwrite_paths_count = 2;
    108   }
    109   restrict_filesystem_access(readonly_paths, readonly_paths_count,
    110                              readwrite_paths, readwrite_paths_count);
    111   restrict_system_operations(
    112       options->cachefile_path != NULL ? kGoutWithCachefile : kGout);
    113 
    114   git->initialize(git);
    115 }
    116 
    117 void gout_run(const GoutOptions* options, Git* git) {
    118   assert(options != NULL);
    119   assert(git != NULL);
    120   git->for_repo(git, options->repodir, repo_callback, (void*)options);
    121 }
    122 
    123 static void repo_callback(Git* git, void* user_data) {
    124   assert(git != NULL);
    125   assert(user_data != NULL);
    126   const GoutOptions* options = (const GoutOptions*)user_data;
    127   RepoWriter* writer =
    128       repowriter_create(options->writer_type, git->repo, git->fs);
    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   git->for_each_commit(git, commit_callback, writer);
    141   git->for_each_reference(git, reference_callback, writer);
    142   git->for_each_file(git, file_callback, writer);
    143   repowriter_end(writer);
    144 
    145   repowriter_free(writer);
    146 }
    147 
    148 static void commit_callback(const GitCommit* ci, void* user_data) {
    149   assert(ci != NULL);
    150   assert(user_data != NULL);
    151   RepoWriter* writer = (RepoWriter*)user_data;
    152   repowriter_add_commit(writer, ci);
    153 }
    154 
    155 static void reference_callback(const GitReference* ref, void* user_data) {
    156   assert(ref != NULL);
    157   assert(user_data != NULL);
    158   RepoWriter* writer = (RepoWriter*)user_data;
    159   repowriter_add_reference(writer, ref);
    160 }
    161 
    162 static void file_callback(const GitFile* file, void* user_data) {
    163   assert(file != NULL);
    164   assert(user_data != NULL);
    165   RepoWriter* writer = (RepoWriter*)user_data;
    166   repowriter_add_file(writer, file);
    167 }