refs.c (4907B)
1 #include "writer/gemini/refs.h" 2 3 #include <assert.h> 4 #include <err.h> 5 #include <stdbool.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 9 #include "format.h" 10 #include "git/commit.h" 11 #include "third_party/openbsd/reallocarray.h" 12 #include "utils.h" 13 #include "writer/gemini/page.h" 14 15 typedef struct { 16 char* title; 17 FILE* out; 18 } GeminiRefsTable; 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 GeminiRefs { 33 const GitRepo* repo; 34 const FileSystem* fs; 35 FILE* out; 36 GeminiPage* page; 37 RefList branches; 38 RefList tags; 39 }; 40 41 static GeminiRefsTable* gemini_refstable_create(const char* title, FILE* out); 42 static void gemini_refstable_free(GeminiRefsTable* table); 43 static void gemini_refstable_begin(GeminiRefsTable* table); 44 static void gemini_refstable_add_ref(GeminiRefsTable* table, 45 const RefData* ref); 46 static void gemini_refstable_end(GeminiRefsTable* table); 47 48 static GeminiRefsTable* gemini_refstable_create(const char* title, FILE* out) { 49 assert(title != NULL); 50 assert(out != NULL); 51 GeminiRefsTable* table = ecalloc(1, sizeof(GeminiRefsTable)); 52 table->title = estrdup(title); 53 table->out = out; 54 return table; 55 } 56 57 static void gemini_refstable_free(GeminiRefsTable* table) { 58 if (!table) { 59 return; 60 } 61 free(table->title); 62 free(table); 63 } 64 65 static void gemini_refstable_begin(GeminiRefsTable* table) { 66 assert(table != NULL); 67 fprintf(table->out, "## %s\n\n", table->title); 68 fprintf(table->out, "```\n"); 69 print_utf8_padded(table->out, "Name", 32, ' '); 70 fprintf(table->out, " "); 71 print_utf8_padded(table->out, "Last commit date", 16, ' '); 72 fprintf(table->out, " Author\n"); 73 } 74 75 static void gemini_refstable_add_ref(GeminiRefsTable* table, 76 const RefData* ref) { 77 assert(table != NULL); 78 assert(ref != NULL); 79 FILE* out = table->out; 80 81 print_utf8_padded(out, ref->shorthand, 32, ' '); 82 fprintf(out, " "); 83 print_time_short(out, ref->author_time); 84 fprintf(out, " %s\n", ref->author_name); 85 } 86 87 static void gemini_refstable_end(GeminiRefsTable* table) { 88 assert(table != NULL); 89 fprintf(table->out, "```\n\n"); 90 } 91 92 static void reflist_add(RefList* list, const GitReference* ref) { 93 if (list->count >= list->capacity) { 94 list->capacity = list->capacity == 0 ? 16 : list->capacity * 2; 95 list->items = reallocarray(list->items, list->capacity, sizeof(RefData)); 96 if (!list->items) { 97 err(1, "reallocarray"); 98 } 99 } 100 RefData* item = &list->items[list->count++]; 101 item->shorthand = estrdup(ref->shorthand); 102 if (ref->commit) { 103 item->author_time = ref->commit->author_time; 104 item->author_name = estrdup(ref->commit->author_name); 105 } else { 106 item->author_time = 0; 107 item->author_name = estrdup(""); 108 } 109 } 110 111 static void reflist_clear(RefList* list) { 112 for (size_t i = 0; i < list->count; i++) { 113 free(list->items[i].shorthand); 114 free(list->items[i].author_name); 115 } 116 free(list->items); 117 list->items = NULL; 118 list->count = 0; 119 list->capacity = 0; 120 } 121 122 GeminiRefs* gemini_refs_create(const GitRepo* repo, const FileSystem* fs) { 123 assert(repo != NULL); 124 assert(fs != NULL); 125 GeminiRefs* refs = ecalloc(1, sizeof(GeminiRefs)); 126 refs->repo = repo; 127 refs->fs = fs; 128 refs->out = fs->fopen("refs.gmi", "w"); 129 if (!refs->out) { 130 err(1, "fopen: refs.gmi"); 131 } 132 refs->page = gemini_page_create(refs->out, repo, fs, "Refs", ""); 133 return refs; 134 } 135 136 void gemini_refs_free(GeminiRefs* refs) { 137 if (!refs) { 138 return; 139 } 140 refs->fs->fclose(refs->out); 141 gemini_page_free(refs->page); 142 reflist_clear(&refs->branches); 143 reflist_clear(&refs->tags); 144 free(refs); 145 } 146 147 void gemini_refs_begin(GeminiRefs* refs) { 148 assert(refs != NULL); 149 gemini_page_begin(refs->page); 150 } 151 152 void gemini_refs_add_ref(GeminiRefs* refs, const GitReference* ref) { 153 assert(refs != NULL); 154 assert(ref != NULL); 155 switch (ref->type) { 156 case kReftypeBranch: 157 reflist_add(&refs->branches, ref); 158 break; 159 case kReftypeTag: 160 reflist_add(&refs->tags, ref); 161 break; 162 } 163 } 164 165 void gemini_refs_end(GeminiRefs* refs) { 166 assert(refs != NULL); 167 if (refs->branches.count > 0) { 168 GeminiRefsTable* table = gemini_refstable_create("Branches", refs->out); 169 gemini_refstable_begin(table); 170 for (size_t i = 0; i < refs->branches.count; i++) { 171 gemini_refstable_add_ref(table, &refs->branches.items[i]); 172 } 173 gemini_refstable_end(table); 174 gemini_refstable_free(table); 175 } 176 if (refs->tags.count > 0) { 177 GeminiRefsTable* table = gemini_refstable_create("Tags", refs->out); 178 gemini_refstable_begin(table); 179 for (size_t i = 0; i < refs->tags.count; i++) { 180 gemini_refstable_add_ref(table, &refs->tags.items[i]); 181 } 182 gemini_refstable_end(table); 183 gemini_refstable_free(table); 184 } 185 gemini_page_end(refs->page); 186 }