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 }