X(ul) \
X(li) \
X(a) \
+ X(img) \
\
X(content)
html_prependAttribute(Article, STR_LITERAL("class"), ClassName, Arena);
}
+static inline void
+html_appendImageToArticle(html* Html, str ImageDir, str Filename, arena* Arena)
+{
+ html_element* ImageSection = html_createElement(HTML_ELEMENT_NAME_h2, Arena);
+ html_appendContent(ImageSection, STR_LITERAL("Bild"), Arena);
+ html_appendArticleSection(Html, ImageSection);
+
+ html_element* Image = html_createElement(HTML_ELEMENT_NAME_img, Arena);
+ html_prependAttribute(Image, STR_LITERAL("class"), STR_LITERAL("fit-picture"), Arena);
+ {
+ str_unbounded uPath = str_startUnbounded(Arena);
+ {
+ str_append(&uPath.Str, ImageDir);
+ str_append(&uPath.Str, Filename);
+ }
+ str Path = str_endUnbounded(uPath);
+
+ html_prependAttribute(Image, STR_LITERAL("src"), Path, Arena);
+ }
+ html_appendArticleSection(Html, Image);
+}
+
static inline void
html_appendTagListToArticle(html* Html, str TagsDir, arena* Arena)
{
str_append(Target, STR_LITERAL(">"));
if ((Child->ElementName == HTML_ELEMENT_NAME_meta) ||
- (Child->ElementName == HTML_ELEMENT_NAME_link))
+ (Child->ElementName == HTML_ELEMENT_NAME_link) ||
+ (Child->ElementName == HTML_ELEMENT_NAME_img))
{
/* void elements don't have a closing tag */
}
* - output to html directory
* - sortable table? (sort by most recent, by name, ...)
* - navigation (return to main page from recipe)
- *
- * - combine connected foods (lasagne)
*/
#define _POSIX_C_SOURCE 200112L
struct recipe* Next;
struct recipe* Prev;
+ struct image* Image;
html* Html;
str Filename;
str Name;
} recipe;
+typedef struct image
+{
+ struct image* Next;
+ struct image* Prev;
+
+ struct recipe* Recipe;
+
+ str Filename;
+} image;
+
static int
provideDirectory(str Path)
{
}
}
+static inline image*
+searchImage(recipe* Recipe, image* Images)
+{
+ image* Image = 0;
+
+ for (image* At = Images->Next; At != Images; At = At->Next)
+ {
+ if (str_equals(str_stripExtension(At->Filename),
+ str_stripExtension(Recipe->Filename)))
+ {
+ Image = At;
+ Image->Recipe = Recipe;
+ Recipe->Image = Image;
+
+ break;
+ }
+ }
+
+ return Image;
+}
+
static u32
generateHtmlFile(str Filename, html* Html, arena* Arena)
{
recipe* Recipes;
tag* Tags;
+ image* Images;
arena MainArena;
} Context = {0};
}
}
+ /* allocate image sentinel */
+ {
+ image* ImageSentinel = ARENA_PUSH_STRUCT(&Context.MainArena, image);
+ {
+ ImageSentinel->Next = ImageSentinel;
+ ImageSentinel->Prev = ImageSentinel;
+
+ Context.Images = ImageSentinel;
+ }
+ }
+
+ /* enumerate image files */
+ {
+ str_unbounded uPath = str_startUnbounded(&Context.MainArena);
+ {
+ appendTerminatedDirectory(&uPath.Str, Context.OutputDir);
+ appendTerminatedDirectory(&uPath.Str, STR_LITERAL("images"));
+ }
+ str ImagePath = str_endUnbounded(uPath);
+
+ char PathCStr[MAX_PATH] = {0};
+ str_toCString(PathCStr, sizeof(PathCStr), ImagePath);
+
+ DIR* Directory = opendir(PathCStr);
+ if (Directory == NULL)
+ {
+ printError("opendir");
+ return -1;
+ }
+
+ 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("..")))
+ {
+ /* skip entry */
+ continue;
+ }
+
+ image* New = ARENA_PUSH_STRUCT(&Context.MainArena, image);
+ {
+ New->Filename = Name;
+
+ image* Sentinel = Context.Images;
+ New->Next = Sentinel;
+ New->Prev = Sentinel->Prev;
+ Sentinel->Prev->Next = New;
+ Sentinel->Prev = New;
+ }
+ }
+
+ if (closedir(Directory) == -1)
+ {
+ printError("closedir");
+ return -1;
+ }
+ }
+
/* ensure existence of output directories */
{
if (provideDirectory(Context.OutputDir) == -1)
Recipe->Html = html_parseMarkdown(FileStr, &Context.MainArena);
Recipe->Name = html_getTitleStr(Recipe->Html);
+ Recipe->Image = searchImage(Recipe, Context.Images);
}
/* allocate tag sentinel */
recipe* Sentinel = Context.Recipes;
for (recipe* Recipe = Sentinel->Next; Recipe != Sentinel; Recipe = Recipe->Next)
{
- /* append tag list */
+ if (Recipe->Image)
+ {
+ html_appendImageToArticle(Recipe->Html, STR_LITERAL("/images/"), Recipe->Image->Filename, &Context.MainArena);
+ }
html_appendTagListToArticle(Recipe->Html, STR_LITERAL("/tags/"), &Context.MainArena);
/* serialize html tree */
}
}
+ /* look for unused images */
+ {
+ image* Sentinel = Context.Images;
+ for (image* At = Sentinel->Next; At != Sentinel; At = At->Next)
+ {
+ if (!At->Recipe)
+ {
+ str_unbounded uMsg = str_startUnbounded(&Context.MainArena);
+ {
+ str_append(&uMsg.Str, STR_LITERAL("image '"));
+ str_append(&uMsg.Str, At->Filename);
+ str_append(&uMsg.Str, STR_LITERAL("' is not linked to any recipe\n"));
+ }
+ str Message = str_endUnbounded(uMsg);
+
+ write(STDOUT_FILENO, Message.Base, Message.Length);
+ }
+ }
+ }
+
fprintf(stdout, "Done.\n");
/* todo: output stats (memory consumption, runtime, ...) */