Mercurial
view mrjunejune/create_html_from_md.c @ 279:b3b547563ec7
Add Google connector service and agent wiki
Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:22:36 -0700 |
| parents | 5ec271d612ae |
| children |
line wrap: on
line source
#include "markdown_converter/markdown_to_html.h" #include "dowa/dowa.h" #include <fcntl.h> #define BLOG_PATH "/home/june/zenbu/mrjunejune/src/blog/" #define BLOG_HTML "<!DOCTYPE html>" \ "<html lang=\"en\">" \ "<head>" \ " <meta charset=\"UTF-8\">" \ " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" \ " <meta name=\"description\" content=\"%s\">" \ " <title>%s - June's Blog</title>" \ " {{/parts/base_head.html}}" \ " <link rel=\"stylesheet\" href=\"/public/highlight/a11y-dark.min.css\" media=\"(prefers-color-scheme: dark)\">" \ " <link rel=\"stylesheet\" href=\"/public/highlight/a11y-light.min.css\" media=\"(prefers-color-scheme: light)\">" \ " <script src=\"/public/highlight/highlight.min.js\"></script>" \ "</head>" \ "<body>" \ " {{/parts/header.html}}" \ " <main>" \ "%s" \ " </main>" \ " {{/parts/footer.html}}" \ " <script>hljs.highlightAll();</script>"\ "</body>" typedef struct { char title[256]; char description[512]; char *content; // markdown content after frontmatter } Blog_Metadata; // Parse frontmatter from markdown content // Expected format: // --- // title: My Title // description: My description // --- // markdown static Blog_Metadata Parse_Blog_Frontmatter(const char *md_content) { Blog_Metadata meta = {0}; strcpy(meta.title, "Untitled"); strcpy(meta.description, "A blog post by June"); meta.content = (char *)md_content; if (!md_content) return meta; if (strncmp(md_content, "---", 3) != 0) return meta; const char *end_delim = strstr(md_content + 3, "\n---"); if (!end_delim) return meta; const char *cursor = md_content + 4; // skip "---\n" while (cursor < end_delim) { // whitespaces while (cursor < end_delim && (*cursor == ' ' || *cursor == '\t')) cursor++; const char *line_end = cursor; while (line_end < end_delim && *line_end != '\n') line_end++; const char *colon = cursor; while (colon < line_end && *colon != ':') colon++; if (colon < line_end) { size_t key_len = colon - cursor; const char *value_start = colon + 1; while (value_start < line_end && (*value_start == ' ' || *value_start == '\t')) value_start++; size_t value_len = line_end - value_start; if (key_len == 5 && strncmp(cursor, "title", 5) == 0) { size_t copy_len = value_len < sizeof(meta.title) - 1 ? value_len : sizeof(meta.title) - 1; strncpy(meta.title, value_start, copy_len); meta.title[copy_len] = '\0'; } else if (key_len == 11 && strncmp(cursor, "description", 11) == 0) { size_t copy_len = value_len < sizeof(meta.description) - 1 ? value_len : sizeof(meta.description) - 1; strncpy(meta.description, value_start, copy_len); meta.description[copy_len] = '\0'; } } cursor = line_end + 1; } meta.content = (char *)(end_delim + 4); if (*meta.content == '\n') meta.content++; return meta; } int main() { Dowa_Arena *arena = Dowa_Arena_Create(1024 * 1024 * 5); size_t buffer_sizes = 100 * 1024; char *files[] = { BLOG_PATH"thoughts-on-ide/index.md", BLOG_PATH"multithread-in-js/index.md", BLOG_PATH"thoughts-on-tdd/index.md", BLOG_PATH"my-seobeo-journey/index.md", BLOG_PATH"wasm-bunny/index.md", BLOG_PATH"optimizing-data-structures/index.md", BLOG_PATH"websocket-demystified/index.md", BLOG_PATH"optimizing-grass-rendering/index.md", BLOG_PATH"wsl2-ssh/index.md" }; for (int i = 0; i < 9; i++) { char *md_file_path = files[i]; char *md_file = Dowa_Arena_Allocate(arena, buffer_sizes); FILE *file = fopen(md_file_path, "ro"); fseek(file, 0, SEEK_END); size_t file_length = ftell(file); fseek(file, 0, SEEK_SET); fread(md_file, 1, file_length, file); Blog_Metadata meta = Parse_Blog_Frontmatter(md_file); char *md = markdown_to_html(meta.content); char *ssr_body = Dowa_Arena_Allocate(arena, buffer_sizes); snprintf(ssr_body, buffer_sizes, BLOG_HTML, meta.description, meta.title, md); markdown_free(md); size_t md_file_path_length = strlen(md_file_path); md_file_path_length -= 3; // html char *new_file_path = Dowa_Arena_Allocate(arena, 1024); snprintf(new_file_path, 1024, "%.*s.html", (int)md_file_path_length, md_file_path); FILE *new_file_fd = fopen(new_file_path, "w+"); if (!new_file_fd) { Dowa_Arena_Free(arena); return 1; } printf("Saving to %s...\n", new_file_path); fwrite(ssr_body, 1, buffer_sizes, new_file_fd); fclose(new_file_fd); } Dowa_Arena_Free(arena); return 0; }