#include "md.h" #include "md.c" typedef struct SiteInfo SiteInfo; struct SiteInfo { MD_Node *title; MD_Node *desc; MD_Node *canonical_url; MD_Node *author; MD_Node *twitter_handle; MD_Node *link_dictionary; MD_Node *header; MD_Node *footer; MD_Node *style; }; typedef struct PageInfo PageInfo; struct PageInfo { MD_Node *title; MD_Node *desc; MD_Node *date; MD_Node *parent; MD_Node *header_image; }; static PageInfo ParsePageInfo(MD_Node *page); static SiteInfo ParseSiteInfo(MD_Node *site); static MD_String8 MakeDateString(MD_Node *date); static void GeneratePageContent(MD_Map *index_table, SiteInfo *site_info, PageInfo *page_info, FILE *file, MD_Node *node); int main(int argument_count, char **arguments) { //~ NOTE(rjf): Parse command line arguments. MD_String8 site_info_path = {0}; MD_String8 page_dir_path = {0}; MD_CommandLine cmdln = MD_CommandLine_Start(argument_count, arguments); if(!MD_CommandLine_FlagString(&cmdln, MD_S8Lit("--siteinfo"), &site_info_path) || !MD_CommandLine_FlagString(&cmdln, MD_S8Lit("--pagedir"), &page_dir_path)) { fprintf(stderr, "USAGE: %s --siteinfo --pagedir ...\n", arguments[0]); goto end; } //~ NOTE(rjf): Load JS. MD_String8 js_string = MD_LoadEntireFile(MD_PushStringF("%.*s/site.js", MD_StringExpand(page_dir_path))); //~ NOTE(rjf): Parse site info. SiteInfo site_info = {0}; { printf("Parsing site metadata at \"%.*s\"...\n", MD_StringExpand(site_info_path)); MD_Node *site_info_file = MD_ParseWholeFile(site_info_path).node; site_info = ParseSiteInfo(site_info_file); } //~ NOTE(rjf): Parse pages. MD_Node *first_root = MD_NilNode(); MD_Node *last_root = MD_NilNode(); { printf("Searching for site pages at \"%.*s\"...\n", MD_StringExpand(page_dir_path)); MD_FileInfo file_info = {0}; for(MD_FileIter it = {0}; MD_FileIterIncrement(&it, page_dir_path, &file_info);) { if(MD_StringMatch(MD_ExtensionFromPath(file_info.filename), MD_S8Lit("md"), MD_StringMatchFlag_CaseInsensitive) && !MD_StringMatch(MD_SkipFolder(MD_ChopExtension(file_info.filename)), MD_SkipFolder(MD_ChopExtension(site_info_path)), MD_StringMatchFlag_CaseInsensitive | MD_StringMatchFlag_SlashInsensitive)) { printf("Processing site page at \"%.*s\"...\n", MD_StringExpand(file_info.filename)); MD_String8 folder = MD_FolderFromPath(page_dir_path); MD_String8 path = MD_PushStringF("%.*s/%.*s", MD_StringExpand(folder), MD_StringExpand(file_info.filename)); MD_PushSibling(&first_root, &last_root, MD_NilNode(), MD_ParseWholeFile(path).node); } } } //~ NOTE(rjf): Generate index table. MD_Map index_table = {0}; { for(MD_EachNode(root, first_root)) { for(MD_EachNode(node, root->first_child)) { if(!MD_NodeIsNil(node->first_child) && MD_StringMatch(node->string, MD_S8Lit("index"), MD_StringMatchFlag_CaseInsensitive)) { for(MD_EachNode(index_string, node->first_child)) { MD_NodeTable_Insert(&index_table, MD_MapCollisionRule_Chain, index_string->string, root); } goto end_index_build; } } end_index_build:; } } //~ NOTE(rjf): Generate stylesheet. if(site_info.style) { FILE *file = fopen("style.css", "wb"); if(file) { fprintf(file, "%.*s", MD_StringExpand(site_info.style->string)); fclose(file); } } //~ NOTE(rjf): Generate JS. { FILE *file = fopen("site.js", "wb"); if(file) { fprintf(file, "%.*s", MD_StringExpand(js_string)); fclose(file); } } //~ NOTE(rjf): Generate files for all roots. for(MD_EachNode(root, first_root)) { PageInfo page_info = ParsePageInfo(root); MD_String8 name_without_extension = MD_SkipFolder(MD_ChopExtension(root->filename)); FILE *file = fopen(MD_PushStringF("%.*s.html", MD_StringExpand(name_without_extension)).str, "wb"); if(file) { fprintf(file, "\n"); fprintf(file, "\n"); MD_String8 site_title = !MD_NodeIsNil(site_info.title) ? site_info.title->string : MD_S8Lit(""); MD_String8 title = !MD_NodeIsNil(page_info.title) ? page_info.title->string : MD_S8Lit(""); MD_String8 url = !MD_NodeIsNil(site_info.canonical_url) ? site_info.canonical_url->string : MD_S8Lit(""); MD_String8 author = !MD_NodeIsNil(site_info.author) ? site_info.author->string : MD_S8Lit(""); MD_String8 twitter_handle = !MD_NodeIsNil(site_info.twitter_handle) ? site_info.twitter_handle->string : MD_S8Lit(""); // NOTE(rjf): Generate heading. { fprintf(file, "\n"); fprintf(file, "\n"); fprintf(file, "\n", MD_StringExpand(author)); fprintf(file, "\n", MD_StringExpand(title)); fprintf(file, "\n", MD_StringExpand(title)); fprintf(file, "\n", MD_StringExpand(url)); fprintf(file, "\n"); fprintf(file, "\n", MD_StringExpand(url)); fprintf(file, "\n", MD_StringExpand(site_title)); fprintf(file, "\n"); fprintf(file, "\n", MD_StringExpand(twitter_handle)); fprintf(file, "\n"); fprintf(file, "\n"); if(title.size > 0) { if(site_title.size > 0) { fprintf(file, "%.*s | %.*s\n", MD_StringExpand(title), MD_StringExpand(site_title)); } else { fprintf(file, "%.*s\n", MD_StringExpand(title)); } } else if(site_title.size > 0) { fprintf(file, "%.*s\n", MD_StringExpand(site_title)); } fprintf(file, "\n"); } // NOTE(rjf): Generate body. { fprintf(file, "\n"); // NOTE(rjf): Generate header. if(site_info.header) { fprintf(file, "%.*s", MD_StringExpand(site_info.header->string)); } fprintf(file, "
\n"); // NOTE(rjf): Parent page back button. if(page_info.parent) { fprintf(file, "", MD_StringExpand(page_info.parent->string)); } // NOTE(rjf): Banner. if(page_info.header_image) { fprintf(file, "
", MD_StringExpand(page_info.header_image->string)); } // NOTE(rjf): Title. if(title.size > 0) { fprintf(file, "

%.*s

", MD_StringExpand(title)); } // NOTE(rjf): Main description/subtitle. if(page_info.desc) { fprintf(file, "

%.*s

", MD_StringExpand(page_info.desc->string)); } // NOTE(rjf): Date. if(page_info.date) { MD_String8 date_string = MakeDateString(page_info.date); if(date_string.size > 0) { fprintf(file, "

%.*s

", MD_StringExpand(date_string)); } } // NOTE(rjf): The rest of the page content should be generated from the page nodes. for(MD_EachNode(node, root->first_child)) { GeneratePageContent(&index_table, &site_info, &page_info, file, node); } fprintf(file, "
\n"); // NOTE(rjf): Generate footer. if(site_info.footer) { fprintf(file, "%.*s", MD_StringExpand(site_info.footer->string)); } fprintf(file, "\n"); } fprintf(file, "\n"); fclose(file); } } end:; return 0; } static PageInfo ParsePageInfo(MD_Node *page) { PageInfo info = {0}; for(MD_EachNode(node, page->first_child)) { if(!MD_NodeIsNil(node->first_child)) { if(MD_StringMatch(node->string, MD_S8Lit("title"), MD_StringMatchFlag_CaseInsensitive)) { info.title = node->first_child; } else if(MD_StringMatch(node->string, MD_S8Lit("desc"), MD_StringMatchFlag_CaseInsensitive)) { info.desc = node->first_child; } else if(MD_StringMatch(node->string, MD_S8Lit("date"), MD_StringMatchFlag_CaseInsensitive)) { info.date = node; } else if(MD_StringMatch(node->string, MD_S8Lit("parent"), MD_StringMatchFlag_CaseInsensitive)) { info.parent = node->first_child; } else if(MD_StringMatch(node->string, MD_S8Lit("header_image"), MD_StringMatchFlag_CaseInsensitive)) { info.header_image = node->first_child; } } } return info; } static SiteInfo ParseSiteInfo(MD_Node *site) { SiteInfo info = {0}; for(MD_EachNode(node, site->first_child)) { if(!MD_NodeIsNil(node->first_child)) { if(MD_StringMatch(node->string, MD_S8Lit("title"), MD_StringMatchFlag_CaseInsensitive)) { info.title = node->first_child; } else if(MD_StringMatch(node->string, MD_S8Lit("desc"), MD_StringMatchFlag_CaseInsensitive)) { info.desc = node->first_child; } else if(MD_StringMatch(node->string, MD_S8Lit("canonical_url"), MD_StringMatchFlag_CaseInsensitive)) { info.canonical_url = node->first_child; } else if(MD_StringMatch(node->string, MD_S8Lit("author"), MD_StringMatchFlag_CaseInsensitive)) { info.author = node->first_child; } else if(MD_StringMatch(node->string, MD_S8Lit("twitter_handle"), MD_StringMatchFlag_CaseInsensitive)) { info.twitter_handle = node->first_child; } else if(MD_StringMatch(node->string, MD_S8Lit("link_dictionary"), MD_StringMatchFlag_CaseInsensitive)) { info.link_dictionary = node; } else if(MD_StringMatch(node->string, MD_S8Lit("header"), MD_StringMatchFlag_CaseInsensitive)) { info.header = node->first_child; } else if(MD_StringMatch(node->string, MD_S8Lit("footer"), MD_StringMatchFlag_CaseInsensitive)) { info.footer = node->first_child; } else if(MD_StringMatch(node->string, MD_S8Lit("style"), MD_StringMatchFlag_CaseInsensitive)) { info.style = node->first_child; } } } return info; } static MD_String8 MakeDateString(MD_Node *date) { MD_String8 result = {0}; if(date) { MD_Node *year = 0; MD_Node *month = 0; MD_Node *day = 0; for(MD_EachNode(child, date->first_child)) { if(child->flags & MD_NodeFlag_Numeric) { if (year == 0) year = child; else if (month == 0) month = child; else if (day == 0) day = child; else { break; } } } if(year && month && day) { char *month_names[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", }; int month_idx = MD_I64FromString(month->string, 10)-1; if(month_idx >= 0 && month_idx < sizeof(month_names)/sizeof(month_names[0])) { result = MD_PushStringF("%.*s %s %.*s", MD_StringExpand(day->string), month_names[month_idx], MD_StringExpand(year->string)); } } } return result; } static void GeneratePageContent(MD_Map *index_table, SiteInfo *site_info, PageInfo *page_info, FILE *file, MD_Node *node) { //~ NOTE(rjf): Text blobs if(MD_NodeIsNil(node->first_child) && (node->flags & MD_NodeFlag_StringLiteral || node->flags & MD_NodeFlag_CharLiteral)) { char *html_tag = "p"; char *style = "paragraph"; if(MD_NodeHasTag(node, MD_S8Lit("title"))) { html_tag = "h1"; style = "title"; } else if(MD_NodeHasTag(node, MD_S8Lit("subtitle"))) { html_tag = "h2"; style = "subtitle"; } else if(MD_NodeHasTag(node, MD_S8Lit("code"))) { html_tag = "pre"; style = "code"; } MD_String8 splits[] = { MD_S8Lit("\n\n"), }; MD_String8List strlist = MD_SplitString(node->string, sizeof(splits)/sizeof(splits[0]), splits); for(MD_String8Node *strnode = strlist.first; strnode; strnode = strnode->next) { fprintf(file, "<%s class=\"%s\">", html_tag, style); for(MD_u64 i = 0; i < strnode->string.size; i += 1) { if(strnode->string.str[i] == '@') { MD_ParseResult parse = MD_ParseOneNode(node->filename, MD_StringSubstring(strnode->string, i, strnode->string.size)); if(!MD_NodeIsNil(parse.node)) { if(MD_NodeHasTag(node, MD_S8Lit("i"))) { fprintf(file, "%.*s", MD_StringExpand(parse.node->string)); } else if(MD_NodeHasTag(node, MD_S8Lit("b"))) { fprintf(file, "%.*s", MD_StringExpand(parse.node->string)); } else if(MD_NodeHasTag(node, MD_S8Lit("code"))) { fprintf(file, "%.*s", MD_StringExpand(parse.node->string)); } else if(MD_NodeHasTag(node, MD_S8Lit("link"))) { MD_Node *text = MD_ChildFromIndex(parse.node, 0); MD_Node *link = MD_ChildFromIndex(parse.node, 1); fprintf(file, "%.*s", MD_StringExpand(link->string), MD_StringExpand(text->string)); } } i += parse.bytes_parsed - 1; } else { MD_b32 dict_word = 0; if(site_info->link_dictionary) { MD_Node *text = MD_NilNode(); MD_Node *link = MD_NilNode(); for(MD_EachNode(dict_link, site_info->link_dictionary->first_child)) { text = MD_ChildFromIndex(dict_link, 0); link = MD_ChildFromIndex(dict_link, 1); MD_String8 substring = MD_StringSubstring(strnode->string, i, i+text->string.size); if(MD_StringMatch(substring, text->string, 0)) { fprintf(file, "%.*s", MD_StringExpand(link->string), MD_StringExpand(text->string)); dict_word = 1; i += text->string.size-1; break; } } } if(!dict_word) { fprintf(file, "%c", strnode->string.str[i]); } } } fprintf(file, "\n", html_tag); } } if(!MD_NodeIsNil(node->first_child)) { if(MD_NodeHasTag(node, MD_S8Lit("list"))) { fprintf(file, "\n"); } else if(MD_NodeHasTag(node, MD_S8Lit("img"))) { MD_Node *src = MD_ChildFromIndex(node, 0); MD_Node *alt = MD_ChildFromIndex(node, 1); fprintf(file, "
\n", MD_StringExpand(src->string)); } else if(MD_NodeHasTag(node, MD_S8Lit("youtube"))) { MD_Node *id = MD_ChildFromIndex(node, 0); fprintf(file, "\n", MD_StringExpand(id->string)); } else if(MD_NodeHasTag(node, MD_S8Lit("lister"))) { static int lister_idx = 0; fprintf(file, "", lister_idx, lister_idx, lister_idx); fprintf(file, "\n"); } } }