Mercurial
diff postdog/main.c @ 114:e2a73e64e8e6
[Postdog] Got history working.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Tue, 06 Jan 2026 08:15:37 -0800 |
| parents | d6d578b49a19 |
| children | 96db6c3f38d6 |
line wrap: on
line diff
--- a/postdog/main.c Sun Jan 04 14:42:54 2026 -0800 +++ b/postdog/main.c Tue Jan 06 08:15:37 2026 -0800 @@ -3,7 +3,6 @@ #include <string.h> #include <time.h> #include <sys/stat.h> -#include <dirent.h> #include "dowa/dowa.h" #include <curl/curl.h> @@ -11,6 +10,10 @@ #define RAYGUI_IMPLEMENTATION #include "third_party/raylib/include/raygui.h" +#ifndef POSTDOG_PATHS + #define POSTDOG_PATHS "/Users/mrjunejune/zenbu/postdog/history" +#endif + #define SCREEN_WIDTH 1280 #define SCREEN_HEIGHT 780 #define TEXT_SIZE 10 @@ -27,6 +30,19 @@ #define RESULT_BUFFER_LENGTH 1024 * 1025 * 5 #define AREANA_BUFFER_LENGTH 1024 * 1025 * 15 + +#ifdef _WIN32 + #include <direct.h> + #include <io.h> + #define mkdir(path, mode) _mkdir(path) + #define access _access + #define F_OK 0 +#else + #include <sys/stat.h> + #include <dirent.h> + #include <unistd.h> +#endif + typedef Dowa_KV(char*, char*) INPUT_HASHMAP; typedef struct { @@ -35,12 +51,114 @@ } ResponseBuffer; typedef struct { - char filename[256]; - char displayName[128]; - char method[16]; - time_t timestamp; + char *filename; + Rectangle rect; + long time_modified; } HistoryItem; +typedef struct { + Rectangle rectangle; + char *label; + bool active; +} TabItem; + +typedef enum { + TAB_HEADER = 0, + TAB_BODY, + TAB_GET_PARAMS, + TAB_BAR, + TAB_LENGTH +} PostDog_Tab_Enum; + +static uint32 counter = 0; +HistoryItem *history_items = NULL; +HistoryItem *new_history_items = NULL; + +int CompareHistoryItemsByDate(const void *a, const void *b) { + HistoryItem *itemA = (HistoryItem *)a; + HistoryItem *itemB = (HistoryItem *)b; + return (itemB->time_modified - itemA->time_modified); +} + +// TODO: Make this into generic fucntion so I can use it across different thing. +void PostDog_List_Directory(const char *path, HistoryItem **p_file_arr) +{ + HistoryItem *file_arr = *p_file_arr; +#ifdef _WIN32 + struct _finddata_t fileinfo; + intptr_t handle; + char search_path[256]; + sprintf(search_path, "%s\\*", path); + + if ((handle = _findfirst(search_path, &fileinfo)) == -1L) { + printf("Directory is empty or cannot be read.\n"); + } else { + do { + HistoryItem item = {0}; + item.filename = strdup(fileinfo.name); + item.rect = (Rectangle){0}; + item.time_modified = fileinfo.time_write; + Dowa_Array_Push(file_arr, item); + } while (_findnext(handle, &fileinfo) == 0); + _findclose(handle); + } +#else + struct dirent *entry; + struct stat file_stat; + DIR *dp = opendir(path); + if (dp == NULL) return; + + char full_path[256]; + while ((entry = readdir(dp))) + { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + snprintf(full_path, sizeof(full_path), "%s/%s", path, entry->d_name); + if (stat(full_path, &file_stat) == 0) + { + HistoryItem item = {0}; + item.filename = strdup(entry->d_name); + item.time_modified = file_stat.st_mtime; + Dowa_Array_Push(file_arr, item); + } + } + closedir(dp); +#endif + int count = Dowa_Array_Length(file_arr); + if (count > 1) { + qsort(file_arr, count, sizeof(HistoryItem), CompareHistoryItemsByDate); + } +} + +int PostDog_History_Load(HistoryItem **p_history_files) +{ + if (access(POSTDOG_PATHS, F_OK) == -1) + { + printf("Directory '%s' not found. Creating it...\n", POSTDOG_PATHS); + if (mkdir(POSTDOG_PATHS, 0777) != 0) + return -1; + return 0; + } + printf("Directory '%s' already exists.\n", POSTDOG_PATHS); + PostDog_List_Directory(POSTDOG_PATHS, p_history_files); + return 0; +} + +bool InArea(Vector2 mouse_position, Rectangle area) +{ + return ( + mouse_position.x >= area.x && + mouse_position.x < area.x + area.width && + mouse_position.y >= area.y && + mouse_position.y < area.y + area.height + ); +} + +bool Clicked(Vector2 mouse_position, Rectangle area) +{ + return (InArea(mouse_position, area) && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)); +} + char *PostDog_Enum_To_String(int active_enum) { switch(active_enum) @@ -50,6 +168,74 @@ case 2: return "PUT"; case 3: return "DELETE"; } + return 0; +} + +void PostDog_History_CreateFile(char *filename, char* values) +{ + char full_file_path[512] = {0}; + snprintf(full_file_path, 512, "%s/%s", POSTDOG_PATHS, filename); + FILE *file = fopen(full_file_path, "w"); + if (!file) + { + printf("Failed to create a file: %s\n", full_file_path); + return; + } + fwrite(values, 1, strlen(values), file); + fclose(file); +} + +void PostDog_Request_SaveFile( + const char *url, + const char *method, + const char *headers, + const char *body, + const char *response) +{ + size_t new_file_size = 1024 * 1024; + Dowa_Arena *arena = Dowa_Arena_Create(1024 * 1024 * 2); + char *new_file = Dowa_Arena_Allocate(arena, 1024 * 1024); + snprintf( + new_file, + new_file_size, + "%s\n" + "---\n" + "%s\n" + "---\n" + "%s\n" + "---\n" + "%s\n" + "---\n" + "%s\n", + url, + method, + headers, + body, + response + ); + char *filename = Dowa_Arena_Allocate(arena, 1024); + if (!filename) + { + perror("Error opening file"); + exit(EXIT_FAILURE); + } + char *uuid4 = (char *)Dowa_Arena_Allocate(arena, 37); + if (!uuid4) + { + perror("Error uuid"); + exit(EXIT_FAILURE); + } + + int32 seed = (uint32)time(NULL) ^ counter++; + Dowa_String_UUID(seed, uuid4); + snprintf(filename, 1024, "%s.txt", uuid4); + PostDog_History_CreateFile(filename, new_file); + + HistoryItem item = (HistoryItem){ .filename = malloc(sizeof(char) * strlen(filename) + 1), .rect = (Rectangle){0} }; + memcpy(item.filename, filename, strlen(filename) + 1); + Dowa_Array_Push(new_history_items, item); + + Dowa_Arena_Free(arena); } static size_t Postdog_Curl_Callback(void *contents, size_t size, size_t nmemb, void *userp) @@ -72,7 +258,7 @@ return real_size; } -int PostDog_Make_HttpRequest( +int PostDog_Http_Request( const char *url, const char *method, const char *headers, @@ -106,16 +292,14 @@ if (strcmp(method, "POST") == 0) { curl_easy_setopt(curl, CURLOPT_POST, 1L); - if (body && strlen(body) > 0) { + if (body && strlen(body) > 0) curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body); - } } else if (strcmp(method, "PUT") == 0) { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); - if (body && strlen(body) > 0) { + if (body && strlen(body) > 0) curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body); - } } else if (strcmp(method, "DELETE") == 0) { @@ -131,9 +315,8 @@ while (line != NULL) { // Trim whitespace while (*line == ' ' || *line == '\t') line++; - if (strlen(line) > 0) { + if (strlen(line) > 0) headerList = curl_slist_append(headerList, line); - } line = strtok(NULL, "\n"); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerList); @@ -146,10 +329,8 @@ // Follow redirects curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - // Set timeout curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); - // Perform request res = curl_easy_perform(curl); @@ -174,27 +355,20 @@ free(buffer.data); curl_global_cleanup(); + PostDog_Request_SaveFile( + url, + method, + headers, + body, + response); return 0; } -typedef struct { - Rectangle rectangle; - char *label; - bool active; -} TabItem; - -typedef enum { - TAB_HEADER = 0, - TAB_BODY, - TAB_GET_PARAMS, - TAB_BAR, -} PostDog_Tab_Enum; - void PostDog_Update_URL(char **p_url_input_text, char* get_params) { char *url_input_text = *p_url_input_text; - // Reset + // Reset char *question_mark = strchr(url_input_text, '?'); if (question_mark) *question_mark = '\0'; @@ -207,43 +381,23 @@ Dowa_Arena *arena = Dowa_Arena_Create(1024*1024); char **lines = Dowa_String_Split(get_params, "\n", get_params_length, 1, arena); - - size_t url_len = strlen(url_input_text); - size_t url_capacity = URL_TEXT_BUFFER; - for (int i = 0; i < Dowa_Array_Length(lines); i++) { char *line = lines[i]; char **key_value = Dowa_String_Split(line, " ", (int)strlen(line), 1, arena); if (Dowa_Array_Length(key_value) < 2) - continue; // Skip this line, not break entire loop - - // Check buffer capacity before each append - size_t needed = url_len + strlen(separator) + strlen(key_value[0]) + 1 + 10; // +10 for "=" and safety - if (needed >= url_capacity) break; + break; strcat(url_input_text, separator); strcat(url_input_text, key_value[0]); strcat(url_input_text, "="); - url_len = strlen(url_input_text); - for (int i = 1; i < Dowa_Array_Length(key_value); i++) { if (!key_value[i] || key_value[i][0] == '\0') break; - - size_t value_len = strlen(key_value[i]); - needed = url_len + value_len + 4; // +4 for "%20" if needed - if (needed >= url_capacity) break; - - printf("\n\n------\n\n"); - printf("key_value[%i]: %s, p = %p\n", i, key_value[i], key_value[i]); - printf("\n\n------\n\n"); - if (i > 1) strcat(url_input_text, "%20"); strcat(url_input_text, key_value[i]); - url_len = strlen(url_input_text); } separator = "&"; } @@ -251,6 +405,100 @@ Dowa_Arena_Free(arena); } +int PostDog_String_To_MethodEnum(char *value) +{ + if (strstr(value, "GET")) + return 0; + if (strstr(value, "POST")) + return 1; + if (strstr(value, "PUT")) + return 2; + if (strstr(value, "DELETE")) + return 3; + return 0; +} + +void PostDog_Params_Reset( + char **p_url_input_text, + int *p_active_method_dropdown, + char **url_body_map, + char **p_url_result_text +) +{ + char *url_input_text = *p_url_input_text; + char *url_result_text = *p_url_result_text; + int active_method_dropdown = *p_active_method_dropdown; + + url_input_text = ""; + url_result_text = ""; + active_method_dropdown = 0; + for (int i = 0; i < Dowa_Array_Length(url_body_map); i++) + url_body_map[i] = ""; +} + +void PostDog_Load_File( + const char *filename, + char **p_url_input_text, + int *p_active_method_dropdown, + char **url_body_map, + char **p_url_result_text +) { + char *url_input_text = *p_url_input_text; + char *url_result_text = *p_url_result_text; + int active_method_dropdown = *p_active_method_dropdown; + + char full_file_path[512] = {0}; + snprintf(full_file_path, 512, "%s/%s", POSTDOG_PATHS, filename); + FILE *file = fopen(full_file_path, "r"); + if (!file) + return; + + fseek(file, 0, SEEK_END); + size_t file_size = ftell(file); + fseek(file, 0, SEEK_SET); + + Dowa_Arena *init_arena = Dowa_Arena_Create(file_size + 2); + Dowa_Arena *split_arena = Dowa_Arena_Create(file_size * 2); + char *file_buffer = Dowa_Arena_Allocate(init_arena, file_size+1); + fread(file_buffer, 1, file_size, file); + char **values = Dowa_String_Split(file_buffer, "---\n", file_size, 4, split_arena); + Dowa_Arena_Free(init_arena); + for (int i = 0; i < Dowa_Array_Length(values); i++) + { + if (i == 0) + { + snprintf(url_input_text, strlen(values[i]) + 1, "%s", values[i]); + url_input_text[strcspn(url_input_text, "\n")] = '\0'; + } + else if (i == 1) + active_method_dropdown = PostDog_String_To_MethodEnum(values[i]); + else if (i <= 3) + { + snprintf(url_body_map[i-2], strlen(values[i]) + 1, "%s", values[i]); + for (int j = strlen(values[i]); j > 0; j--) + { + if (url_body_map[i-2][j] == '\n') + { + url_body_map[i-2][j] = '\0'; + break; + } + } + } + else + snprintf(url_result_text, strlen(values[i]) + 1, "%s", values[i]); + } +} + +Rectangle AddPadding(Rectangle rect, float padding) +{ + return (Rectangle){ + rect.x + padding, + rect.y + padding, + rect.width - (2 * padding), + rect.height - (2 * padding) + }; +} + int main() { // -- initizlied --// @@ -260,31 +508,39 @@ Dowa_Arena *arena = Dowa_Arena_Create(AREANA_BUFFER_LENGTH); + Font customFont = LoadFontEx("postdog/Roboto-Regular.ttf", 20, 0, 0); + GuiSetFont(customFont); + GuiSetStyle(DEFAULT, TEXT_SIZE, 20); + // -- Starting pos ---// + // Everyhting is relative to sidebar at this point lol float side_bar_x = 10; float side_bar_y = 10; - Rectangle history_sidebar = { .x = side_bar_x, .y = side_bar_y, .width = 0, .height = 0 }; + Dowa_Array_Reserve(history_items, 10); + Dowa_Array_Reserve(new_history_items, 10); + PostDog_History_Load(&history_items); Rectangle url_area = { 0 }; Rectangle textBounds = { 0 }; Rectangle url_input_bounds = { 0 }; - bool url_input_bool = false; + bool url_input_edit = false; Rectangle url_text_bounds = { 0 }; Rectangle url_enter_button = { 0 }; Rectangle method_dropdown = { 0 }; + char *url_input_text = (char *)Dowa_Arena_Allocate(arena, URL_TEXT_BUFFER); snprintf(url_input_text, URL_TEXT_BUFFER, "https://httpbin.org/get"); - INPUT_HASHMAP *url_body_map = NULL; - Dowa_HashMap_Push_Arena(url_body_map, "Header", (char *)Dowa_Arena_Allocate(arena, HEADER_BUFFER_LENGTH), arena); - Dowa_HashMap_Push_Arena(url_body_map, "Body", (char *)Dowa_Arena_Allocate(arena, BODY_BUFFER_LENGTH), arena); - Dowa_HashMap_Push_Arena(url_body_map, "Get Param", (char *)Dowa_Arena_Allocate(arena, DEFAULT_TEXT_BUFFER_LENGTH), arena); - Dowa_HashMap_Push_Arena(url_body_map, "Bar", (char *)Dowa_Arena_Allocate(arena, DEFAULT_TEXT_BUFFER_LENGTH), arena); + char **url_body_map = NULL; + Dowa_Array_Push_Arena(url_body_map, (char *)Dowa_Arena_Allocate(arena, HEADER_BUFFER_LENGTH), arena); + Dowa_Array_Push_Arena(url_body_map, (char *)Dowa_Arena_Allocate(arena, BODY_BUFFER_LENGTH), arena); + Dowa_Array_Push_Arena(url_body_map, (char *)Dowa_Arena_Allocate(arena, DEFAULT_TEXT_BUFFER_LENGTH), arena); + Dowa_Array_Push_Arena(url_body_map, (char *)Dowa_Arena_Allocate(arena, DEFAULT_TEXT_BUFFER_LENGTH), arena); - snprintf(url_body_map[0].value, HEADER_BUFFER_LENGTH, "Content-Type: application/json"); - snprintf(url_body_map[1].value, HEADER_BUFFER_LENGTH, ""); + snprintf(url_body_map[TAB_HEADER], HEADER_BUFFER_LENGTH, "Content-Type: application/json"); + snprintf(url_body_map[TAB_BODY], HEADER_BUFFER_LENGTH, ""); char *url_result_text = (char *)Dowa_Arena_Allocate(arena, RESULT_BUFFER_LENGTH); @@ -305,7 +561,7 @@ Rectangle result_body = { 0 }; // General styling. - int padding = 10; // TODO make it % based? + float padding = 10; // TODO make it % based? int active_input_tab = 0; while (!WindowShouldClose()) @@ -314,7 +570,19 @@ int screen_height = GetScreenHeight(); history_sidebar.width = screen_width * 0.15; - history_sidebar.height = screen_width - 10; + history_sidebar.height = screen_width - 10; + + int32 new_history_items_length = Dowa_Array_Length(new_history_items); + int32 history_item_length = Dowa_Array_Length(history_items); + int32 total = new_history_items_length + history_item_length; + for (int i = 0; i < total; i++) + { + HistoryItem *curr_history_items = i < new_history_items_length ? &new_history_items[i] : &history_items[i - new_history_items_length]; + curr_history_items->rect.x = history_sidebar.x + padding; + curr_history_items->rect.y = history_sidebar.y + (padding * 2 * (i+1)) + (i * history_sidebar.height * 0.05); + curr_history_items->rect.width = history_sidebar.width - (padding * 2); + curr_history_items->rect.height = history_sidebar.height * 0.05; + } // -- URL Area --// url_area.x = (side_bar_x + history_sidebar.width); @@ -371,24 +639,32 @@ result_body.width = url_area.width * 0.49 - padding; result_body.height = result_area.height - input_tab.height - padding; + Vector2 mouse_position = GetMousePosition(); + BeginDrawing(); ClearBackground(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); - // Sidebar Rect DrawRectangleRec(history_sidebar, Fade(GRAY, 0.1f)); + for (int i = 0; i < total; i++) + { + HistoryItem *curr_history_items = i < new_history_items_length ? &new_history_items[i] : &history_items[i - new_history_items_length]; + DrawRectangleRec(curr_history_items->rect, Fade(RED, 0.1f)); + GuiDrawText(curr_history_items->filename, AddPadding(curr_history_items->rect, padding), TEXT_ALIGN_CENTER, RED); + } // URL area Rect GuiDrawText("URL: ", url_text_bounds, TEXT_ALIGN_CENTER, RED); DrawRectangleRec(url_area, Fade(RED, 0.1f)); - if (GuiTextBox(url_input_bounds, url_input_text, DEFAULT_TEXT_BUFFER_LENGTH, url_input_bool)) - url_input_bool = !url_input_bool; + if (GuiTextBox(url_input_bounds, url_input_text, DEFAULT_TEXT_BUFFER_LENGTH, url_input_edit)) + url_input_edit = !url_input_edit; + sendRequest = GuiButton(url_enter_button, "ENTER"); if (sendRequest) - PostDog_Make_HttpRequest( + PostDog_Http_Request( url_input_text, PostDog_Enum_To_String(active_method_dropdown), - url_body_map[0].value, - url_body_map[1].value, + url_body_map[TAB_HEADER], + url_body_map[TAB_BODY], url_result_text, RESULT_BUFFER_LENGTH ); @@ -399,15 +675,43 @@ DrawRectangleRec(input_area, Fade(BLUE, 0.1f)); DrawRectangleRec(input_tab, Fade(DARKBLUE, 0.1f)); GuiSetStyle(TOGGLE, GROUP_PADDING, 0); - if (JUNE_GuiTextBox(input_body, url_body_map[active_input_tab].value, DEFAULT_TEXT_BUFFER_LENGTH, input_body_bool)) + if (JUNE_GuiTextBox(input_body, url_body_map[active_input_tab], DEFAULT_TEXT_BUFFER_LENGTH, input_body_bool)) input_body_bool = !input_body_bool; GuiToggleGroup(input_tab_item, "Header;Body;Get Param;Bar", &active_input_tab); - PostDog_Update_URL(&url_input_text, url_body_map[TAB_GET_PARAMS].value); + + PostDog_Update_URL(&url_input_text, url_body_map[TAB_GET_PARAMS]); // Result Rect DrawRectangleRec(result_area, Fade(GREEN, 0.1f)); DrawRectangleRec(result_body, Fade(DARKGREEN, 0.1f)); GuiTextBoxMulti(result_body, url_result_text, RESULT_BUFFER_LENGTH, false); + + if (url_input_edit && (IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C)) + { + SetClipboardText(url_input_text); + } + else if (input_body_bool && (IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C)) + { + SetClipboardText(url_body_map[active_input_tab]); + } + else if (InArea(mouse_position, result_body)) + { + DrawRectangleRec(result_body, Fade(GREEN, 0.3f)); + if ((IsKeyDown(KEY_LEFT_SUPER) || IsKeyDown(KEY_LEFT_CONTROL)) && IsKeyPressed(KEY_C)) + SetClipboardText(url_result_text); + } + + for (int i = 0; i < Dowa_Array_Length(history_items); i++) + { + if (Clicked(mouse_position, history_items[i].rect)) + PostDog_Load_File( + history_items[i].filename, + &url_input_text, + &active_method_dropdown, + url_body_map, + &url_result_text + ); + } EndDrawing(); } CloseWindow();