refs.c (5250B)
1 #include "writer/gopher/refs.h" 2 3 #include <assert.h> 4 #include <err.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 8 #include "format.h" 9 #include "git/commit.h" 10 #include "third_party/openbsd/reallocarray.h" 11 #include "utils.h" 12 #include "writer/gopher/page.h" 13 14 typedef struct { 15 char* title; 16 char* id; 17 FILE* out; 18 } GopherRefsTable; 19 20 typedef struct { 21 char* shorthand; 22 time_t author_time; 23 char* author_name; 24 } RefData; 25 26 typedef struct { 27 RefData* items; 28 size_t count; 29 size_t capacity; 30 } RefList; 31 32 struct GopherRefs { 33 const GitRepo* repo; 34 const FileSystem* fs; 35 FILE* out; 36 GopherPage* page; 37 RefList branches; 38 RefList tags; 39 }; 40 41 static GopherRefsTable* gopher_refstable_create(const char* title, 42 const char* id, 43 FILE* out); 44 static void gopher_refstable_free(GopherRefsTable* table); 45 static void gopher_refstable_begin(GopherRefsTable* table); 46 static void gopher_refstable_add_ref(GopherRefsTable* table, 47 const RefData* ref); 48 static void gopher_refstable_end(GopherRefsTable* table); 49 50 static GopherRefsTable* gopher_refstable_create(const char* title, 51 const char* id, 52 FILE* out) { 53 assert(title != NULL); 54 assert(id != NULL); 55 assert(out != NULL); 56 GopherRefsTable* table = ecalloc(1, sizeof(GopherRefsTable)); 57 table->title = estrdup(title); 58 table->id = estrdup(id); 59 table->out = out; 60 return table; 61 } 62 63 static void gopher_refstable_free(GopherRefsTable* table) { 64 if (!table) { 65 return; 66 } 67 free(table->title); 68 free(table->id); 69 free(table); 70 } 71 72 static void gopher_refstable_begin(GopherRefsTable* table) { 73 assert(table != NULL); 74 FILE* out = table->out; 75 fprintf(out, "%s\n", table->title); 76 fprintf(out, " %-32.32s", "Name"); 77 fprintf(out, " %-16.16s", "Last commit date"); 78 fprintf(out, " %s\n", "Author"); 79 } 80 81 static void gopher_refstable_add_ref(GopherRefsTable* table, 82 const RefData* ref) { 83 assert(table != NULL); 84 assert(ref != NULL); 85 FILE* out = table->out; 86 87 fprintf(out, " "); 88 print_gopher_link_padded(out, ref->shorthand, 32, ' '); 89 fprintf(out, " "); 90 print_time_short(out, ref->author_time); 91 fprintf(out, " "); 92 print_gopher_link_padded(out, ref->author_name, 25, '\0'); 93 fprintf(out, "\n"); 94 } 95 96 static void gopher_refstable_end(GopherRefsTable* table) { 97 assert(table != NULL); 98 FILE* out = table->out; 99 fprintf(out, "\n"); 100 } 101 102 static void reflist_add(RefList* list, const GitReference* ref) { 103 if (list->count >= list->capacity) { 104 list->capacity = list->capacity == 0 ? 16 : list->capacity * 2; 105 list->items = reallocarray(list->items, list->capacity, sizeof(RefData)); 106 if (!list->items) { 107 err(1, "reallocarray"); 108 } 109 } 110 RefData* item = &list->items[list->count++]; 111 item->shorthand = estrdup(ref->shorthand); 112 if (ref->commit) { 113 item->author_time = ref->commit->author_time; 114 item->author_name = estrdup(ref->commit->author_name); 115 } else { 116 item->author_time = 0; 117 item->author_name = estrdup(""); 118 } 119 } 120 121 static void reflist_clear(RefList* list) { 122 for (size_t i = 0; i < list->count; i++) { 123 free(list->items[i].shorthand); 124 free(list->items[i].author_name); 125 } 126 free(list->items); 127 list->items = NULL; 128 list->count = 0; 129 list->capacity = 0; 130 } 131 132 GopherRefs* gopher_refs_create(const GitRepo* repo, const FileSystem* fs) { 133 assert(repo != NULL); 134 assert(fs != NULL); 135 GopherRefs* refs = ecalloc(1, sizeof(GopherRefs)); 136 refs->repo = repo; 137 refs->fs = fs; 138 refs->out = fs->fopen("refs.gph", "w"); 139 if (!refs->out) { 140 err(1, "fopen: refs.gph"); 141 } 142 refs->page = gopher_page_create(refs->out, repo, fs, "Refs", ""); 143 return refs; 144 } 145 146 void gopher_refs_free(GopherRefs* refs) { 147 if (!refs) { 148 return; 149 } 150 refs->fs->fclose(refs->out); 151 gopher_page_free(refs->page); 152 reflist_clear(&refs->branches); 153 reflist_clear(&refs->tags); 154 free(refs); 155 } 156 157 void gopher_refs_begin(GopherRefs* refs) { 158 assert(refs != NULL); 159 gopher_page_begin(refs->page); 160 } 161 162 void gopher_refs_add_ref(GopherRefs* refs, const GitReference* ref) { 163 assert(refs != NULL); 164 assert(ref != NULL); 165 switch (ref->type) { 166 case kReftypeBranch: 167 reflist_add(&refs->branches, ref); 168 break; 169 case kReftypeTag: 170 reflist_add(&refs->tags, ref); 171 break; 172 } 173 } 174 175 void gopher_refs_end(GopherRefs* refs) { 176 assert(refs != NULL); 177 if (refs->branches.count > 0) { 178 GopherRefsTable* table = 179 gopher_refstable_create("Branches", "branches", refs->out); 180 gopher_refstable_begin(table); 181 for (size_t i = 0; i < refs->branches.count; i++) { 182 gopher_refstable_add_ref(table, &refs->branches.items[i]); 183 } 184 gopher_refstable_end(table); 185 gopher_refstable_free(table); 186 } 187 if (refs->tags.count > 0) { 188 GopherRefsTable* table = gopher_refstable_create("Tags", "tags", refs->out); 189 gopher_refstable_begin(table); 190 for (size_t i = 0; i < refs->tags.count; i++) { 191 gopher_refstable_add_ref(table, &refs->tags.items[i]); 192 } 193 gopher_refstable_end(table); 194 gopher_refstable_free(table); 195 } 196 gopher_page_end(refs->page); 197 }