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