gout

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

fileblob.c (2926B)


      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[PATH_MAX];
     30   if (snprintf(filename, sizeof(filename), "%s.gph", path) < 0) {
     31     err(1, "snprintf");
     32   }
     33   char out_path[PATH_MAX];
     34   path_concat(out_path, sizeof(out_path), "file", filename);
     35   char dir[PATH_MAX];
     36   estrlcpy(dir, out_path, sizeof(dir));
     37   const char* d = dirname(dir);
     38   if (!d) {
     39     err(1, "dirname");
     40   }
     41   mkdirp(d);
     42   blob->out = efopen(out_path, "w");
     43 
     44   // Compute the relative path.
     45   char relpath[PATH_MAX];
     46   estrlcpy(relpath, "../", sizeof(relpath));
     47   for (const char* p = d; *p != '\0'; p++) {
     48     if (*p == '/') {
     49       estrlcat(relpath, "../", sizeof(relpath));
     50     }
     51   }
     52   estrlcpy(filename, path, sizeof(filename));
     53   const char* title = basename(filename);
     54   if (!title) {
     55     err(1, "basename");
     56   }
     57   blob->page = gopher_page_create(blob->out, repo, title, relpath);
     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 }