fileblob.c (3393B)
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 <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/html/page.h" 15 16 struct HtmlFileBlob { 17 const GitRepo* repo; 18 const FileSystem* fs; 19 FILE* out; 20 HtmlPage* page; 21 }; 22 23 HtmlFileBlob* html_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 HtmlFileBlob* blob = ecalloc(1, sizeof(HtmlFileBlob)); 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.html", 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 = html_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 html_fileblob_free(HtmlFileBlob* blob) { 74 if (!blob) { 75 return; 76 } 77 blob->fs->fclose(blob->out); 78 blob->out = NULL; 79 html_page_free(blob->page); 80 blob->page = NULL; 81 free(blob); 82 } 83 84 void html_fileblob_begin(HtmlFileBlob* blob) { 85 assert(blob != NULL); 86 html_page_begin(blob->page); 87 } 88 89 void html_fileblob_add_file(HtmlFileBlob* blob, const GitFile* file) { 90 assert(blob != NULL); 91 assert(file != NULL); 92 FILE* out = blob->out; 93 94 fprintf(out, "<p> "); 95 char path[PATH_MAX]; 96 estrlcpy(path, file->repo_path, sizeof(path)); 97 const char* filename = basename(path); 98 if (!filename) { 99 err(1, "basename"); 100 } 101 print_xml_encoded(out, filename); 102 fprintf(out, " (%zdB)", file->size_bytes); 103 fprintf(out, "</p><hr/>"); 104 105 ssize_t size_lines = file->size_lines; 106 if (size_lines == -1) { 107 fprintf(out, "<p>Binary file.</p>\n"); 108 return; 109 } 110 if (size_lines == -2) { 111 fprintf(out, "<p>File too large to display.</p>\n"); 112 return; 113 } 114 115 fprintf(out, "<pre id=\"blob\">\n"); 116 117 assert(file->content != NULL); 118 size_t i = 0; 119 const char* end = file->content + file->size_bytes; 120 const char* cur_line = file->content; 121 while (cur_line < end) { 122 const char* next_line = memchr(cur_line, '\n', end - cur_line); 123 size_t len = (next_line ? next_line : end) - cur_line; 124 125 i++; 126 fprintf(out, "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">", i, i); 127 fprintf(out, "%7zu</a> ", i); 128 print_xml_encoded_len(out, cur_line, len, false); 129 fprintf(out, "\n"); 130 131 if (next_line) { 132 cur_line = next_line + 1; 133 } else { 134 break; 135 } 136 } 137 fprintf(out, "</pre>\n"); 138 } 139 140 void html_fileblob_end(HtmlFileBlob* blob) { 141 assert(blob != NULL); 142 html_page_end(blob->page); 143 }