};
} html_element;
+typedef struct html_tag
+{
+ struct html_tag* Prev;
+ struct html_tag* Next;
+
+ str Content;
+} html_tag;
+
+typedef struct
+{
+ html_tag* Tags;
+} html_meta;
+
typedef struct
{
html_element* Root;
html_element* Title;
html_element* Article;
+
+ html_meta* Meta;
} html;
static void
appendElement(html_element* Sentinel, html_element* Element)
{
- /* todo: create sentinel in html_create() */
+ /* todo: create sentinel in html_create() (inline and get rid of this function) */
Element->Prev = Sentinel->Prev;
Sentinel->Prev->Next = Element;
Element->Next = Sentinel;
{
Html->Title = Title;
+ /* todo: use static "nullstring"? */
html_appendContent(Title, STR_LITERAL("<untitled>"), Arena);
html_appendValue(Head, Title);
}
}
}
+ html_meta* Meta = ARENA_PUSH_STRUCT(Arena, html_meta);
+ {
+ Html->Meta = Meta;
+
+ html_tag* Sentinel = ARENA_PUSH_STRUCT(Arena, html_tag);
+ {
+ Sentinel->Next = Sentinel;
+ Sentinel->Prev = Sentinel;
+
+ Meta->Tags = Sentinel;
+ }
+ }
+
return Html;
}
return Content;
}
+static inline void
+insertTag(html_tag* Sentinel, str Stripped, arena* Arena)
+{
+ for (html_tag* Tag = Sentinel->Next;
+ (Tag != Sentinel) && (Tag->Next != Sentinel);
+ Tag = Tag->Next)
+ {
+ str Candidate = Tag->Content;
+ str Reference = Tag->Next->Content;
+
+ s32 Result = str_compare(Candidate, Reference);
+ if (0);
+ else if (Result < 0)
+ {
+ html_tag* Next = Tag->Next;
+
+ Tag->Next = Next;
+ Tag->Prev = Next->Prev;
+
+ Next->Prev->Next = Tag;
+ Next->Prev = Tag;
+
+ break;
+ }
+ else if (Result == 0)
+ {
+ /* tag already exists. stop insertion */
+ break;
+ }
+ else if (Result > 0)
+ {
+ /* continue insertion */
+ continue;
+ }
+ }
+}
+
static void
parseHeading(str Line, html_element* Html, arena* Arena)
{
static void
parseLine(str Line, html_element* Html, arena* Arena)
{
- str Stripped = str_stripTail(Line);
-
- switch (Stripped.Base[0])
- {
- case '#':
- {
- parseHeading(Stripped, Html, Arena);
- } break;
-
- case '-':
- case '*':
- {
- parseListItem(Stripped, Html, Arena);
- } break;
-
- default:
- {
- parseParagraph(Stripped, Html, Arena);
- } break;
- }
}
static html*
{
html* Html = html_createDefault(Arena);
- /* todo: parse front matter */
+ b32 Frontmatter = 0;
+ b32 ExpectShebang = 0;
for (str Line = str_getLine(Source);
str_isValid(Line);
Line = str_getLine(Source))
{
- parseLine(Line, Html->Article, Arena);
+ str Stripped = str_stripTail(Line);
+
+ if (!Frontmatter)
+ {
+ if (0);
+ else if (str_startsWith(Stripped, STR_LITERAL("---")))
+ {
+ Frontmatter = 1;
+ ExpectShebang = 1;
+ }
+ else if (str_startsWith(Stripped, STR_LITERAL("#")))
+ {
+ parseHeading(Stripped, Html->Root, Arena);
+ }
+ else if (str_startsWith(Line, STR_LITERAL("-")) ||
+ str_startsWith(Line, STR_LITERAL("*")))
+ {
+ parseListItem(Stripped, Html->Root, Arena);
+ }
+ else
+ {
+ parseParagraph(Stripped, Html->Root, Arena);
+ }
+ }
+ else
+ {
+ if (ExpectShebang)
+ {
+ ExpectShebang = 0;
+ /* could be used to differentiate between different frontmatter languages */
+ ASSERT(str_equals(Stripped, STR_LITERAL("#!ini")));
+ }
+ else
+ {
+ if (0);
+ else if (str_startsWith(Stripped, STR_LITERAL("[")))
+ {
+ ASSERT(str_equals(Stripped, STR_LITERAL("[tags]")));
+ /* skip section header */
+ }
+ else if (str_startsWith(Stripped, STR_LITERAL("---")))
+ {
+ Frontmatter = 0;
+ }
+ else
+ {
+ html_tag* Sentinel = Html->Meta->Tags;
+ insertTag(Sentinel, Stripped, Arena);
+ }
+ }
+ }
+
str_advance(Source, Line.Length);
}
}
}
+static void
+html_setTitle(html* Html, str TitleStr, arena* Arena)
+{
+ html_element* Title = Html->Title;
+ {
+ html_appendContent(Title, TitleStr, Arena);
+ }
+}
+
static str
html_toString(html* Html, arena* Arena)
{
str_unbounded Unbounded = str_startUnbounded(Arena);
{
str* Line = &Unbounded.Str;
- serializeElement(Line, Html);
+ serializeElement(Line, Html->Root);
}
str Result = str_endUnbounded(Unbounded);
return Result;
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
+#include <sys/stat.h>
+
+typedef struct node
+{
+ struct node* Next;
+ str Name;
+ html* Html;
+} recipe;
+
+static b32
+isValid_recipe(recipe* Recipe)
+{
+ b32 Result = 0;
+
+ if (Recipe != NULL)
+ {
+ Result = 1;
+ }
+
+ return Result;
+}
+
+enum /* Limits */
+{
+ MAX_PATH = 1024
+};
static u32
generateHtmlFile(str Filename, html* Html, arena* Arena)
{
u32 Error = 0;
- enum { MAX_PATH = 1024 };
char Path[MAX_PATH] = {0};
-
- /* todo: create path as c string */
str_toCString(Path, sizeof(Path), Filename);
int FileDescriptor = open(Path, O_WRONLY|O_CREAT);
return Error;
}
-typedef struct node
-{
- struct node* Next;
- str Name;
-} recipe, tag;
-
int
main(int ArgumentCount, char** Arguments)
{
char* OutputDir;
recipe* Recipes;
- tag* Tags;
+ //tag* Tags;
u64 RecipeCount;
arena MainArena;
- } Context;
+ } Context = {0};
/* handle commandline arguments */
if (ArgumentCount != 3)
struct dirent* Entry;
while ((Entry = readdir(Directory)) != NULL)
{
+ str Name = str_fromCString(&Context.MainArena, Entry->d_name);
+ if (str_equals(Name, STR_LITERAL(".") ) ||
+ str_equals(Name, STR_LITERAL("..")) ||
+ str_equals(Name, STR_LITERAL(".template.md.tmp")))
+ {
+ /* skip entry */
+ continue;
+ }
+
recipe* New = ARENA_PUSH_STRUCT(&Context.MainArena, recipe);
- New->Name = str_fromCString(&Context.MainArena, Entry->d_name);
+ New->Name = Name;
New->Next = Context.Recipes;
Context.Recipes = New;
}
}
- /* todo: parse recipe files (multithreaded) */
- Recipe->Html = html_parseMarkdown();
+ /* todo: parse recipe files multithreaded */
+ for (recipe* Recipe = Context.Recipes; isValid_recipe(Recipe); Recipe = Recipe->Next)
+ {
+ /* copy file */
+ str FileStr;
+ {
+ char Path[MAX_PATH] = {0};
+ str_toCString(Path, sizeof(Path), Recipe->Name);
+
+ int File = open(Path, O_RDONLY);
+ if (File == -1)
+ {
+ perror("open");
+ return -1;
+ }
+
+ /* read file */
+ {
+ struct stat FileStat;
+
+ if (stat(Path, &FileStat) == -1)
+ {
+ perror("stat");
+ return -1;
+ }
+
+ u8* Memory = ARENA_PUSH_ARRAY(&Context.MainArena, u8, FileStat.st_size);
+ if (read(File, Memory, FileStat.st_size) == -1)
+ {
+ perror("read");
+ return -1;
+ }
+
+ FileStr.Base = Memory;
+ FileStr.Length = FileStat.st_size;
+ FileStr.Capacity = FileStat.st_size;
+ }
+
+ if (close(File) == -1)
+ {
+ perror("close");
+ return -1;
+ }
+ }
+
+ Recipe->Html = html_parseMarkdown(FileStr, &Context.MainArena);
+ }
+#if 0
#if 0
/* todo: distill unique sorted tags */
html* MainPage = html_createDefault(&Context.MainArena);
{
/* todo: remove title from overview? */
- html_setTitle(MainPage, STR_LITERAL("Überblick"));
+ html_setTitle(MainPage, STR_LITERAL("Überblick"), &Context.MainArena);
/* tags */
{
}
}
}
+#endif
fprintf(stdout, "Done.\n");
{
Line.Base = Str.Base;
- memory_size Index = 0u;
for (memory_size i = 0u; i < Str.Length; i++)
{
if (Str.Base[i] == '\n')
return Result;
}
+static inline b32
+str_equals(str A, str B)
+{
+ b32 Result = 1;
+
+ if (A.Length != B.Length)
+ {
+ Result = 0;
+ }
+ else
+ {
+ for (memory_size i = 0u; i < A.Length; i++)
+ {
+ if (A.Base[i] != B.Base[i])
+ {
+ Result = 0;
+ break;
+ }
+ }
+ }
+
+ return Result;
+}
+
+static inline b32
+str_startsWith(str A, str B)
+{
+ b32 Result = 1;
+
+ if (A.Length < B.Length)
+ {
+ Result = 0;
+ }
+ else
+ {
+ for (memory_size i = 0u; i < B.Length; i++)
+ {
+ if (A.Base[i] != B.Base[i])
+ {
+ Result = 0;
+ break;
+ }
+ }
+ }
+
+ return Result;
+}
+
static inline b32
str_isWhitespaceOnly(str Str)
{
return Result;
}
+static inline s32
+str_compare(str A, str B)
+{
+ s32 Result = 0;
+
+ if (!str_equals(A, B))
+ {
+ Result = -1;
+
+ for (u32 i = 0u; i < A.Length; i++)
+ {
+ if (A.Base[i] > B.Base[i])
+ {
+ break;
+ }
+
+ if (B.Base[i] > A.Base[i])
+ {
+ Result = 1;
+ break;
+ }
+ }
+ }
+
+ return Result;
+}
+
typedef struct
{
{
char* Start = CStr;
- while (*CStr++ != '0');
+ while (*++CStr != '\0');
u64 Length = CStr - Start;
u8* Target = ARENA_PUSH_ARRAY(Arena, u8, Length);