fileblob.c (3331B)
1 #include "writer/html/fileblob.h" 2 3 #include <assert.h> 4 #include <err.h> 5 #include <libgen.h> 6 #include <limits.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 const FileSystem* fs; 18 FILE* out; 19 HtmlPage* page; 20 }; 21 22 HtmlFileBlob* html_fileblob_create(const GitRepo* repo, 23 const FileSystem* fs, 24 const char* path) { 25 assert(repo != NULL); 26 assert(fs != NULL); 27 assert(path != NULL); 28 if (!is_safe_repo_path(path)) { 29 errx(1, "unsafe path: %s", path); 30 } 31 HtmlFileBlob* blob = ecalloc(1, sizeof(HtmlFileBlob)); 32 blob->repo = repo; 33 blob->fs = fs; 34 35 // Create directories. 36 char filename_buffer[PATH_MAX]; 37 int r = snprintf(filename_buffer, sizeof(filename_buffer), "%s.html", path); 38 if (r < 0 || (size_t)r >= sizeof(filename_buffer)) { 39 errx(1, "snprintf: filename truncated or error"); 40 } 41 42 char* out_path = path_concat("file", filename_buffer); 43 char* dir_copy = estrdup(out_path); 44 const char* d = dirname(dir_copy); 45 if (!d) { 46 err(1, "dirname"); 47 } 48 fs->mkdirp(d); 49 blob->out = fs->fopen(out_path, "w"); 50 if (!blob->out) { 51 err(1, "fopen: %s", out_path); 52 } 53 54 // Compute the relative path. 55 char* relpath = relpath_from_dir(d); 56 free(dir_copy); 57 58 char* path_copy = estrdup(path); 59 const char* title = basename(path_copy); 60 if (!title) { 61 err(1, "basename"); 62 } 63 blob->page = html_page_create(blob->out, repo, fs, title, relpath); 64 65 free(out_path); 66 free(path_copy); 67 free(relpath); 68 69 return blob; 70 } 71 72 void html_fileblob_free(HtmlFileBlob* blob) { 73 if (!blob) { 74 return; 75 } 76 blob->fs->fclose(blob->out); 77 html_page_free(blob->page); 78 free(blob); 79 } 80 81 void html_fileblob_begin(HtmlFileBlob* blob) { 82 assert(blob != NULL); 83 html_page_begin(blob->page); 84 } 85 86 void html_fileblob_add_file(HtmlFileBlob* blob, const GitFile* file) { 87 assert(blob != NULL); 88 assert(file != NULL); 89 FILE* out = blob->out; 90 91 fprintf(out, "<p> "); 92 char path[PATH_MAX]; 93 estrlcpy(path, file->repo_path, sizeof(path)); 94 const char* filename = basename(path); 95 if (!filename) { 96 err(1, "basename"); 97 } 98 print_xml_encoded(out, filename); 99 fprintf(out, " (%zdB)", file->size_bytes); 100 fprintf(out, "</p><hr/>"); 101 102 ssize_t size_lines = file->size_lines; 103 if (size_lines == -1) { 104 fprintf(out, "<p>Binary file.</p>\n"); 105 return; 106 } 107 if (size_lines == -2) { 108 fprintf(out, "<p>File too large to display.</p>\n"); 109 return; 110 } 111 112 fprintf(out, "<pre id=\"blob\">\n"); 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, "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">", i, i); 124 fprintf(out, "%7zu</a> ", i); 125 print_xml_encoded_len(out, cur_line, len, false); 126 fprintf(out, "\n"); 127 128 if (next_line) { 129 cur_line = next_line + 1; 130 } else { 131 break; 132 } 133 } 134 fprintf(out, "</pre>\n"); 135 } 136 137 void html_fileblob_end(HtmlFileBlob* blob) { 138 assert(blob != NULL); 139 html_page_end(blob->page); 140 }