gout

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

fileblob.c (3065B)


      1 #include "writer/html/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/html/page.h"
     14 
     15 struct HtmlFileBlob {
     16   const GitRepo* repo;
     17   FILE* out;
     18   HtmlPage* page;
     19 };
     20 
     21 HtmlFileBlob* html_fileblob_create(const GitRepo* repo, const char* path) {
     22   if (!is_safe_repo_path(path)) {
     23     errx(1, "unsafe path: %s", path);
     24   }
     25   HtmlFileBlob* blob = ecalloc(1, sizeof(HtmlFileBlob));
     26   blob->repo = repo;
     27 
     28   // Create directories.
     29   char filename[PATH_MAX];
     30   if (snprintf(filename, sizeof(filename), "%s.html", 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 = html_page_create(blob->out, repo, title, relpath);
     58   return blob;
     59 }
     60 
     61 void html_fileblob_free(HtmlFileBlob* blob) {
     62   if (!blob) {
     63     return;
     64   }
     65   fclose(blob->out);
     66   blob->out = NULL;
     67   html_page_free(blob->page);
     68   blob->page = NULL;
     69   free(blob);
     70 }
     71 
     72 void html_fileblob_begin(HtmlFileBlob* blob) {
     73   html_page_begin(blob->page);
     74 }
     75 
     76 void html_fileblob_add_file(HtmlFileBlob* blob, const GitFile* file) {
     77   FILE* out = blob->out;
     78 
     79   fprintf(out, "<p> ");
     80   char path[PATH_MAX];
     81   estrlcpy(path, gitfile_repo_path(file), sizeof(path));
     82   const char* filename = basename(path);
     83   if (!filename) {
     84     err(1, "basename");
     85   }
     86   print_xml_encoded(out, filename);
     87   fprintf(out, " (%zdB)", gitfile_size_bytes(file));
     88   fprintf(out, "</p><hr/>");
     89 
     90   ssize_t size_lines = gitfile_size_lines(file);
     91   if (size_lines == -1) {
     92     fprintf(out, "<p>Binary file.</p>\n");
     93     return;
     94   }
     95   if (size_lines == -2) {
     96     fprintf(out, "<p>File too large to display.</p>\n");
     97     return;
     98   }
     99 
    100   fprintf(out, "<pre id=\"blob\">\n");
    101 
    102   size_t i = 0;
    103   const char* content = gitfile_content(file);
    104   const char* end = content + gitfile_size_bytes(file);
    105   const char* cur_line = content;
    106   while (cur_line < end) {
    107     const char* next_line = memchr(cur_line, '\n', end - cur_line);
    108     size_t len = (next_line ? next_line : end) - cur_line;
    109 
    110     i++;
    111     fprintf(out, "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">", i, i);
    112     fprintf(out, "%7zu</a> ", i);
    113     print_xml_encoded_len(out, cur_line, len, false);
    114     fprintf(out, "\n");
    115 
    116     if (next_line) {
    117       cur_line = next_line + 1;
    118     } else {
    119       break;
    120     }
    121   }
    122   fprintf(out, "</pre>\n");
    123 }
    124 
    125 void html_fileblob_end(HtmlFileBlob* blob) {
    126   html_page_end(blob->page);
    127 }