gout

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

fileblob.c (2806B)


      1 #include "writer/gopher/fileblob.h"
      2 
      3 #include <err.h>
      4 #include <libgen.h>
      5 #include <limits.h>
      6 #include <stdbool.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/gopher/page.h"
     14 
     15 struct GopherFileBlob {
     16   const GitRepo* repo;
     17   FILE* out;
     18   GopherPage* page;
     19 };
     20 
     21 GopherFileBlob* gopher_fileblob_create(const GitRepo* repo, const char* path) {
     22   if (!is_safe_repo_path(path)) {
     23     errx(1, "unsafe path: %s", path);
     24   }
     25   GopherFileBlob* blob = ecalloc(1, sizeof(GopherFileBlob));
     26   blob->repo = repo;
     27 
     28   // Create directories.
     29   char filename_buffer[PATH_MAX];
     30   if (snprintf(filename_buffer, sizeof(filename_buffer), "%s.gph", path) < 0) {
     31     err(1, "snprintf");
     32   }
     33 
     34   char* out_path = path_concat("file", filename_buffer);
     35   char* dir_copy = estrdup(out_path);
     36   const char* d = dirname(dir_copy);
     37   if (!d) {
     38     err(1, "dirname");
     39   }
     40   mkdirp(d);
     41   blob->out = efopen(out_path, "w");
     42 
     43   // Compute the relative path.
     44   char* relpath = relpath_from_dir(d);
     45   free(dir_copy);
     46 
     47   char* path_copy = estrdup(path);
     48   const char* title = basename(path_copy);
     49   if (!title) {
     50     err(1, "basename");
     51   }
     52   blob->page = gopher_page_create(blob->out, repo, title, relpath);
     53 
     54   free(out_path);
     55   free(path_copy);
     56   free(relpath);
     57 
     58   return blob;
     59 }
     60 
     61 void gopher_fileblob_free(GopherFileBlob* blob) {
     62   if (!blob) {
     63     return;
     64   }
     65   fclose(blob->out);
     66   blob->out = NULL;
     67   gopher_page_free(blob->page);
     68   blob->page = NULL;
     69   free(blob);
     70 }
     71 
     72 void gopher_fileblob_begin(GopherFileBlob* blob) {
     73   gopher_page_begin(blob->page);
     74 }
     75 
     76 void gopher_fileblob_add_file(GopherFileBlob* blob, const GitFile* file) {
     77   FILE* out = blob->out;
     78 
     79   char path[PATH_MAX];
     80   estrlcpy(path, gitfile_repo_path(file), sizeof(path));
     81   const char* filename = basename(path);
     82   if (!filename) {
     83     err(1, "basename");
     84   }
     85   print_gopher_text(out, filename, false);
     86   fprintf(out, " (%zdB)\n", gitfile_size_bytes(file));
     87   fprintf(out, "---\n");
     88 
     89   ssize_t size_lines = gitfile_size_lines(file);
     90   if (size_lines == -1) {
     91     fprintf(out, "Binary file.\n");
     92     return;
     93   }
     94   if (size_lines == -2) {
     95     fprintf(out, "File too large to display.\n");
     96     return;
     97   }
     98 
     99   size_t i = 0;
    100   const char* content = gitfile_content(file);
    101   const char* end = content + gitfile_size_bytes(file);
    102   const char* cur_line = content;
    103   while (cur_line < end) {
    104     const char* next_line = memchr(cur_line, '\n', end - cur_line);
    105     size_t len = (next_line ? next_line : end) - cur_line;
    106 
    107     i++;
    108     fprintf(out, "%6zu ", i);
    109     print_gopher_text_len(out, cur_line, len, false);
    110     fprintf(out, "\n");
    111 
    112     if (next_line) {
    113       cur_line = next_line + 1;
    114     } else {
    115       break;
    116     }
    117   }
    118 }
    119 
    120 void gopher_fileblob_end(GopherFileBlob* blob) {
    121   gopher_page_end(blob->page);
    122 }