gitout

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

cache.c (4171B)


      1 #include "cache.h"
      2 
      3 #include <err.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 #include <sys/stat.h>
      7 #include <sys/types.h>
      8 #include <unistd.h>
      9 
     10 #include "git/git.h"
     11 #include "utils.h"
     12 
     13 struct Cache {
     14   bool can_add_commits;
     15   char* cache_path;
     16   char* temp_cache_path;
     17   WriteCommitRow write_commit_row;
     18   FILE* cache_in;
     19   FILE* cache_out;
     20   char* lastoid_in;
     21   bool wrote_lastoid_out;
     22 };
     23 
     24 static const char* kTempCachePath = "cache.XXXXXXXXXXXX";
     25 static const mode_t kReadWriteAll =
     26     S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
     27 
     28 Cache* cache_create(const char* cache_path, WriteCommitRow write_func) {
     29   Cache* cache = ecalloc(1, sizeof(Cache));
     30   cache->can_add_commits = true;
     31   cache->cache_path = estrdup(cache_path);
     32   cache->temp_cache_path = estrdup(kTempCachePath);
     33   cache->write_commit_row = write_func;
     34 
     35   // Open previous cache for reading, if it exists, and read lastoid.
     36   cache->lastoid_in = ecalloc(kOidLen, sizeof(char));
     37   cache->cache_in = fopen(cache_path, "r");
     38   if (cache->cache_in) {
     39     // OID + '\n' + '\0'.
     40     char buf[kOidLen + 1];
     41     char* oid_str = fgets(buf, sizeof(buf), cache->cache_in);
     42     if (!oid_str) {
     43       err(1, "fgets");
     44     }
     45     estrlcpy(cache->lastoid_in, oid_str, sizeof(buf));
     46   }
     47   cache->wrote_lastoid_out = false;
     48 
     49   // Create temporary cache and open for writing.
     50   int out_fd = mkstemp(cache->temp_cache_path);
     51   if (out_fd == -1) {
     52     err(1, "mkstemp");
     53   }
     54   cache->cache_out = fdopen(out_fd, "w");
     55   if (!cache->cache_out) {
     56     err(1, "fdopen");
     57   }
     58   return cache;
     59 }
     60 
     61 void cache_free(Cache* cache) {
     62   if (!cache) {
     63     return;
     64   }
     65   free(cache->cache_path);
     66   cache->cache_path = NULL;
     67   free(cache->temp_cache_path);
     68   cache->temp_cache_path = NULL;
     69   // Clean up in case the cache wasn't written.
     70   if (cache->cache_in) {
     71     fclose(cache->cache_in);
     72     cache->cache_in = NULL;
     73   }
     74   if (cache->cache_out) {
     75     fclose(cache->cache_out);
     76     cache->cache_out = NULL;
     77   }
     78   free(cache->lastoid_in);
     79   cache->lastoid_in = NULL;
     80   free(cache);
     81 }
     82 
     83 bool cache_can_add_commits(const Cache* cache) {
     84   return cache->can_add_commits;
     85 }
     86 
     87 void cache_add_commit_row(Cache* cache, const GitCommit* commit) {
     88   // The first row of the file is the last OID.
     89   if (!cache->wrote_lastoid_out) {
     90     fprintf(cache->cache_out, "%s\n", gitcommit_oid(commit));
     91     cache->wrote_lastoid_out = true;
     92   }
     93   // If all commits are already written; do nothing.
     94   if (!cache->can_add_commits ||
     95       strncmp(gitcommit_oid(commit), cache->lastoid_in, kOidLen - 1) == 0) {
     96     cache->can_add_commits = false;
     97     return;
     98   }
     99   // The rest of the file is the log contents up to and including that commit.
    100   cache->write_commit_row(cache->cache_out, commit);
    101 }
    102 
    103 void cache_write(Cache* cache) {
    104   if (cache->cache_in) {
    105     // If we didn't write any records, copy the previous cache lastoid.
    106     if (!cache->wrote_lastoid_out) {
    107       fprintf(cache->cache_out, "%s\n", cache->lastoid_in);
    108       cache->wrote_lastoid_out = true;
    109     }
    110     // append previous cached log to new cached log.
    111     char buf[BUFSIZ];
    112     while (!feof(cache->cache_in)) {
    113       size_t n = fread(buf, 1, sizeof(buf), cache->cache_in);
    114       if (ferror(cache->cache_in)) {
    115         break;
    116       }
    117       if (fwrite(buf, 1, n, cache->cache_out) != n) {
    118         break;
    119       }
    120     }
    121     fclose(cache->cache_in);
    122     cache->cache_in = NULL;
    123   }
    124   fclose(cache->cache_out);
    125   cache->cache_out = NULL;
    126 
    127   // Replace previous cache with new cache.
    128   if (rename(cache->temp_cache_path, cache->cache_path)) {
    129     err(1, "rename");
    130   }
    131 
    132   // Set the cache to read-write for user, group, other, modulo umask.
    133   mode_t mask;
    134   umask(mask = umask(0));
    135   if (chmod(cache->cache_path, kReadWriteAll & ~mask)) {
    136     err(1, "chmod");
    137   }
    138 }
    139 
    140 void cache_copy_log(Cache* cache, FILE* out) {
    141   FILE* fcache = efopen(cache->cache_path, "r");
    142 
    143   // Copy the log lines to out.
    144   char* line = NULL;
    145   size_t len = 0;
    146   bool is_sha = true;
    147   while (getline(&line, &len, fcache) != -1) {
    148     // Skip the first line containing the commit SHA.
    149     if (is_sha) {
    150       is_sha = false;
    151       continue;
    152     }
    153     fprintf(out, "%s", line);
    154   }
    155   free(line);
    156 
    157   fclose(fcache);
    158 }