Mercurial
diff mrjunejune/create_html_from_md.c @ 169:295ac2e5ec00
[MrJuneJune] Created separate target for generating html from md.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 19 Jan 2026 17:33:18 -0800 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/create_html_from_md.c Mon Jan 19 17:33:18 2026 -0800 @@ -0,0 +1,147 @@ +#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); + char *final_body = Dowa_Arena_Allocate(arena, buffer_sizes); + snprintf(ssr_body, buffer_sizes, BLOG_HTML, meta.description, meta.title, md); + + int open_flags = O_RDWR | O_CREAT | O_EXCL; + 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) + return; + printf("Saving to %s...\n", new_file_path); + fwrite(ssr_body, 1, buffer_sizes, new_file_fd); + fclose(new_file_fd); + } + return 0; +} +