gout

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

fileblob.c (3279B)


      1 #include "writer/gemini/fileblob.h"
      2 
      3 #include <assert.h>
      4 #include <err.h>
      5 #include <libgen.h>
      6 #include <limits.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 
     11 #include "format.h"
     12 #include "utils.h"
     13 #include "writer/gemini/page.h"
     14 
     15 struct GeminiFileBlob {
     16   const GitRepo* repo;
     17   const FileSystem* fs;
     18   FILE* out;
     19   GeminiPage* page;
     20 };
     21 
     22 GeminiFileBlob* gemini_fileblob_create(const GitRepo* repo,
     23                                        const FileSystem* fs,
     24                                        const char* path) {
     25   assert(repo != NULL);
     26   assert(fs != NULL);
     27   assert(path != NULL);
     28   if (!is_safe_repo_path(path)) {
     29     errx(1, "unsafe path: %s", path);
     30   }
     31   GeminiFileBlob* blob = ecalloc(1, sizeof(GeminiFileBlob));
     32   blob->repo = repo;
     33   blob->fs = fs;
     34 
     35   // Create directories.
     36   char filename_buffer[PATH_MAX];
     37   int r = snprintf(filename_buffer, sizeof(filename_buffer), "%s.gmi", path);
     38   if (r < 0 || (size_t)r >= sizeof(filename_buffer)) {
     39     errx(1, "snprintf: filename truncated or error");
     40   }
     41 
     42   char* out_path = path_concat("file", filename_buffer);
     43   char* dir_copy = estrdup(out_path);
     44   const char* d = dirname(dir_copy);
     45   if (!d) {
     46     err(1, "dirname");
     47   }
     48   fs->mkdirp(d);
     49   blob->out = fs->fopen(out_path, "w");
     50   if (!blob->out) {
     51     err(1, "fopen: %s", out_path);
     52   }
     53 
     54   // Compute the relative path.
     55   char* relpath = relpath_from_dir(d);
     56   free(dir_copy);
     57 
     58   char* path_copy = estrdup(path);
     59   const char* title = basename(path_copy);
     60   if (!title) {
     61     err(1, "basename");
     62   }
     63   blob->page = gemini_page_create(blob->out, repo, fs, title, relpath);
     64 
     65   free(out_path);
     66   free(path_copy);
     67   free(relpath);
     68 
     69   return blob;
     70 }
     71 
     72 void gemini_fileblob_free(GeminiFileBlob* blob) {
     73   if (!blob) {
     74     return;
     75   }
     76   blob->fs->fclose(blob->out);
     77   gemini_page_free(blob->page);
     78   free(blob);
     79 }
     80 
     81 void gemini_fileblob_begin(GeminiFileBlob* blob) {
     82   assert(blob != NULL);
     83   gemini_page_begin(blob->page);
     84 }
     85 
     86 void gemini_fileblob_add_file(GeminiFileBlob* blob, const GitFile* file) {
     87   assert(blob != NULL);
     88   assert(file != NULL);
     89   FILE* out = blob->out;
     90 
     91   char path[PATH_MAX];
     92   estrlcpy(path, file->repo_path, sizeof(path));
     93   const char* filename = basename(path);
     94   if (!filename) {
     95     err(1, "basename");
     96   }
     97   fprintf(out, "File: %s (%zdB)\n\n", filename, file->size_bytes);
     98 
     99   ssize_t size_lines = file->size_lines;
    100   if (size_lines == -1) {
    101     fprintf(out, "Binary file.\n");
    102     return;
    103   }
    104   if (size_lines == -2) {
    105     fprintf(out, "File too large to display.\n");
    106     return;
    107   }
    108 
    109   assert(file->content != NULL);
    110   fprintf(out, "```\n");
    111   size_t i = 0;
    112   const char* end = file->content + file->size_bytes;
    113   const char* cur_line = file->content;
    114   while (cur_line < end) {
    115     const char* next_line = memchr(cur_line, '\n', end - cur_line);
    116     size_t len = (next_line ? next_line : end) - cur_line;
    117 
    118     i++;
    119     size_t display_len = len;
    120     if (display_len > 0 && cur_line[display_len - 1] == '\r') {
    121       display_len--;
    122     }
    123     fprintf(out, "%6zu %.*s\n", i, (int)display_len, cur_line);
    124 
    125     if (next_line) {
    126       cur_line = next_line + 1;
    127     } else {
    128       break;
    129     }
    130   }
    131   fprintf(out, "```\n");
    132 }
    133 
    134 void gemini_fileblob_end(GeminiFileBlob* blob) {
    135   assert(blob != NULL);
    136   gemini_page_end(blob->page);
    137 }