From df5fe8de0323e66c2218c5d9ae9404ba8e1064fc Mon Sep 17 00:00:00 2001 From: Matt Mascarenhas Date: Wed, 18 Oct 2017 22:07:29 +0100 Subject: [PATCH] hmml_to_html.c: Full-featured always-on For the same input across either multiple runs or one long always-on run, we now produce the same output. This wasn't the case because our indexing did not take into account stuff processed in previous runs. --- hmml_to_html/hmml_to_html.c | 1833 +++++++++++++++++++++-------------- 1 file changed, 1117 insertions(+), 716 deletions(-) diff --git a/hmml_to_html/hmml_to_html.c b/hmml_to_html/hmml_to_html.c index 3c0b8c1..7d91aba 100644 --- a/hmml_to_html/hmml_to_html.c +++ b/hmml_to_html/hmml_to_html.c @@ -1,11 +1,15 @@ #if 0 ctime -begin ${0%.*}.ctm gcc -g -fsanitize=address -Wall -std=c99 -pipe $0 -o ${0%.*} hmml.a -lcurl +#gcc -Wall -std=c99 -pipe $0 -o ${0%.*} hmml.a -lcurl ctime -end ${0%.*}.ctm exit #endif +#define CINERA_VERSION "0.4.0" + #define DEBUG 0 +#define DEBUG_MEM 0 typedef unsigned int bool; @@ -81,21 +85,24 @@ enum typedef struct { - char *BaseDir; - char CacheDir[255]; - char *CSSDir; - int Edition; - char *ImagesDir; - char *JSDir; - int LogLevel; + int Edition; + int LogLevel; + int Mode; + int UpdateInterval; + bool ForceIntegration; + char *RootDir; + char *BaseDir; // Relative to RootDir + char *CSSDir; // Relative to RootDir + char *ImagesDir; // Relative to RootDir + char *ProjectID; + char *JSDir; // Relative to RootDir + char *TemplateIndexLocation; // Relative to RootDir + char *TemplatePlayerLocation; // Relative to RootDir + char CacheDir[255]; char *DefaultMedium; - int Mode; char *OutLocation; char *OutIntegratedLocation; - bool ForceIntegration; char *ProjectDir; - char *TemplateIndexLocation; - char *TemplatePlayerLocation; } config; typedef struct @@ -106,6 +113,10 @@ typedef struct int Size; } arena; +// NOTE(matt): Globals +config Config; +arena MemoryArena; + typedef struct { char *Location; @@ -114,15 +125,26 @@ typedef struct int Size; } buffer; +typedef struct +{ + buffer Buffer; + FILE *Handle; + char Path[255]; + int FileSize; +} file_buffer; + enum { + // Contents Page TAG_INDEX, + // Player Page TAG_INCLUDES, TAG_MENUS, TAG_PLAYER, TAG_SCRIPT, + // Anywhere TAG_TITLE } template_tags; @@ -153,6 +175,12 @@ typedef struct tag_offset Tag[16]; int Validity; // NOTE(matt): Bitmask describing which page the template is valid for, i.e. contents and / or player page int TagCount; +} template_metadata; + +typedef struct +{ + template_metadata Metadata; + buffer Buffer; } template; typedef struct @@ -164,7 +192,6 @@ typedef struct buffer Player; buffer Script; char Title[255]; - char Project[32]; } buffers; // TODO(matt): Consider putting the ref_info and quote_info into linked lists on the heap, just to avoid all the hardcoded sizes @@ -247,6 +274,7 @@ category_medium CategoryMedium[] = { "rant", "💢", "Rant"}, { "research", "📖", "Research"}, { "run", "🏃", "In-Game"}, // TODO(matt): Potentially make this configurable per project + { "speech", "🗩", "Speech"}, { "trivia", "🎲", "Trivia"}, }; @@ -449,11 +477,11 @@ MakeDir(char *Path) } void -LogUsage(buffer Buffer, char *CacheDir) +LogUsage(buffer *Buffer) { #if DEBUG char LogPath[255]; - CopyString(LogPath, "%s/%s", CacheDir, "buffers.log"); + CopyString(LogPath, "%s/%s", Config.CacheDir, "buffers.log"); FILE *LogFile; if(!(LogFile = fopen(LogPath, "a+"))) { @@ -466,16 +494,16 @@ LogUsage(buffer Buffer, char *CacheDir) } fprintf(LogFile, "%s,%ld,%d\n", - Buffer.ID, - Buffer.Ptr - Buffer.Location, - Buffer.Size); + Buffer->ID, + Buffer->Ptr - Buffer->Location, + Buffer->Size); fclose(LogFile); #endif } -__attribute__ ((format (printf, 3, 4))) +__attribute__ ((format (printf, 2, 3))) void -LogError(config Config, int LogLevel, char *Format, ...) +LogError(int LogLevel, char *Format, ...) { if(Config.LogLevel >= LogLevel) { @@ -506,6 +534,12 @@ void FreeBuffer(buffer *Buffer) { free(Buffer->Location); +#if DEBUG_MEM + FILE *MemLog = fopen("/home/matt/cinera_mem", "a+"); + fprintf(MemLog, " Freed %s\n", Buffer->ID); + fclose(MemLog); + printf(" Freed %s\n", Buffer->ID); +#endif } #if 0 @@ -519,35 +553,34 @@ FreeBuffer(buffer *Buffer) #endif int -ClaimBuffer(arena *MemoryArena, buffer *Buffer, char *ID, int Size) +ClaimBuffer(buffer *Buffer, char *ID, int Size) { - if(MemoryArena->Ptr - MemoryArena->Location + Size > MemoryArena->Size) + if(MemoryArena.Ptr - MemoryArena.Location + Size > MemoryArena.Size) { return RC_ARENA_FULL; } - Buffer->Location = (char *)MemoryArena->Ptr; + Buffer->Location = (char *)MemoryArena.Ptr; Buffer->Size = Size; Buffer->ID = ID; - MemoryArena->Ptr += Buffer->Size; + MemoryArena.Ptr += Buffer->Size; *Buffer->Location = '\0'; Buffer->Ptr = Buffer->Location; #if DEBUG - printf(" Claimed: %s: %d\n" - " Total ClaimedMemory: %ld\n\n", Buffer->ID, Buffer->Size, MemoryArena->Ptr - MemoryArena->Location); + float PercentageUsed = (float)(MemoryArena->Ptr - MemoryArena->Location) / MemoryArena->Size * 100; + printf(" ClaimBuffer(%s): %d\n" + " Total ClaimedMemory: %ld (%.2f%%, leaving %ld free)\n\n", Buffer->ID, Buffer->Size, MemoryArena->Ptr - MemoryArena->Location, PercentageUsed, MemoryArena->Size - (MemoryArena->Ptr - MemoryArena->Location)); #endif return RC_SUCCESS; } -#define DeclaimBuffer(MemoryArena, Buffer) __DeclaimBuffer(MemoryArena, Buffer, Config) - void -__DeclaimBuffer(arena *MemoryArena, buffer *Buffer, config Config) +DeclaimBuffer(buffer *Buffer) { *Buffer->Location = '\0'; - MemoryArena->Ptr -= Buffer->Size; + MemoryArena.Ptr -= Buffer->Size; float PercentageUsed = (float)(Buffer->Ptr - Buffer->Location) / Buffer->Size * 100; #if DEBUG - printf("Declaimed: %s\n" + printf("DeclaimBuffer(%s)\n" " Used: %ld / %d (%.2f%%)\n" "\n" " Total ClaimedMemory: %ld\n\n", @@ -555,43 +588,76 @@ __DeclaimBuffer(arena *MemoryArena, buffer *Buffer, config Config) Buffer->Ptr - Buffer->Location, Buffer->Size, PercentageUsed, - MemoryArena->Ptr - MemoryArena->Location); + MemoryArena.Ptr - MemoryArena.Location); #endif - LogUsage(*Buffer, Config.CacheDir); + LogUsage(Buffer); if(PercentageUsed >= 80.0f) { // TODO(matt): Implement either dynamically growing buffers, or phoning home to matt@handmadedev.org - LogError(Config, LOG_ERROR, "%s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed); + LogError(LOG_ERROR, "%s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed); fprintf(stderr, "Warning: %s used %.2f%% of its allotted memory\n", Buffer->ID, PercentageUsed); } } -int -ClaimTemplate(arena *MemoryArena, template **Template, char *ID) +void RewindBuffer(buffer *Buffer) { - if(MemoryArena->Ptr - MemoryArena->Location + sizeof(template) > MemoryArena->Size) +#if DEBUG + float PercentageUsed = (float)(Buffer->Ptr - Buffer->Location) / Buffer->Size * 100; + printf("Rewinding %s\n" + " Used: %ld / %d (%.2f%%)\n\n", + Buffer->ID, + Buffer->Ptr - Buffer->Location, + Buffer->Size, + PercentageUsed); +#endif + Buffer->Ptr = Buffer->Location; +} + +int +ClaimTemplate(template **Template, char *Location) +{ + *Template = (template *)MemoryArena.Ptr; + (*Template)->Buffer.Location = MemoryArena.Ptr + sizeof(template); + (*Template)->Buffer.Ptr = (*Template)->Buffer.Location; + (*Template)->Buffer.ID = Location; + CopyString((*Template)->Metadata.Filename, Location); + + FILE *File; + if(!(File = fopen((*Template)->Metadata.Filename, "r"))) + { + LogError(LOG_ERROR, "Unable to open template %s: %s", (*Template)->Metadata.Filename, strerror(errno)); + fprintf(stderr, "Unable to open template %s: %s\n", (*Template)->Metadata.Filename, strerror(errno)); + return RC_ERROR_FILE; + } + fseek(File, 0, SEEK_END); + (*Template)->Buffer.Size = ftell(File); + if(MemoryArena.Ptr - MemoryArena.Location + sizeof(template) + (*Template)->Buffer.Size > MemoryArena.Size) { return RC_ARENA_FULL; } - *Template = (template *)MemoryArena->Ptr; - CopyString((*Template)->Filename, ID); - MemoryArena->Ptr += sizeof(template); + + MemoryArena.Ptr += sizeof(template) + (*Template)->Buffer.Size; + + fseek(File, 0, SEEK_SET); + fread((*Template)->Buffer.Location, (*Template)->Buffer.Size, 1, File); + fclose(File); + #if DEBUG - printf(" Claimed: %s metadata: %ld\n" - " Total ClaimedMemory: %ld\n\n", (*Template)->Filename, sizeof(template), MemoryArena->Ptr - MemoryArena->Location); + printf(" ClaimTemplate(%s): %ld\n" + " Total ClaimedMemory: %ld\n\n", (*Template)->Metadata.Filename, sizeof(template) + (*Template)->Buffer.Size, MemoryArena->Ptr - MemoryArena->Location); #endif return RC_SUCCESS; } int -DeclaimTemplate(arena *MemoryArena, template **Template) +DeclaimTemplate(template *Template) { - MemoryArena->Ptr -= sizeof(template); + MemoryArena.Ptr -= (sizeof(template) + (*Template).Buffer.Size); #if DEBUG - printf("Declaimed: %s metadata\n" + printf("DeclaimTemplate(%s)\n" " Total ClaimedMemory: %ld\n\n", - (*Template)->Filename, - MemoryArena->Ptr - MemoryArena->Location); + (*Template).Metadata.Filename, + MemoryArena.Ptr - MemoryArena.Location); #endif return RC_SUCCESS; } @@ -711,7 +777,7 @@ enum }; int -SearchCredentials(config Config, buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, char *Person, char* Role) +SearchCredentials(buffer *CreditsMenu, bool *HasCreditsMenu, char *Person, char *Role) { bool Found = FALSE; for(int CredentialIndex = 0; CredentialIndex < ArrayCount(Credentials); ++CredentialIndex) @@ -761,7 +827,7 @@ SearchCredentials(config Config, buffer *CreditsMenu, bool *HasCreditsMenu, char CopyStringToBuffer(CreditsMenu, " \n", Credentials[CredentialIndex].SupportURL, - ImagesDir, + Config.ImagesDir, Credentials[CredentialIndex].SupportIcon); } else @@ -769,7 +835,7 @@ SearchCredentials(config Config, buffer *CreditsMenu, bool *HasCreditsMenu, char CopyStringToBuffer(CreditsMenu, " \n", Credentials[CredentialIndex].SupportURL, - ImagesDir, + Config.ImagesDir, Credentials[CredentialIndex].SupportIcon); } } @@ -782,17 +848,17 @@ SearchCredentials(config Config, buffer *CreditsMenu, bool *HasCreditsMenu, char } int -BuildCredits(config Config, buffer *CreditsMenu, bool *HasCreditsMenu, char *ImagesDir, HMML_VideoMetaData Metadata) +BuildCredits(buffer *CreditsMenu, bool *HasCreditsMenu, HMML_VideoMetaData *Metadata) // TODO(matt): Make this take the Credentials, once we are parsing them from a config { - if(Metadata.member) + if(Metadata->member) { - if(SearchCredentials(Config, CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.member, "Host")) + if(SearchCredentials(CreditsMenu, HasCreditsMenu, Metadata->member, "Host")) { printf("No credentials for %s. Please contact matt@handmadedev.org with their:\n" " Full name\n" " Homepage URL (optional)\n" - " Financial support info, e.g. Patreon URL (optional)\n", Metadata.member); + " Financial support info, e.g. Patreon URL (optional)\n", Metadata->member); } } else @@ -806,44 +872,44 @@ BuildCredits(config Config, buffer *CreditsMenu, bool *HasCreditsMenu, char *Ima return CreditsError_NoHost; } - if(Metadata.co_host_count > 0) + if(Metadata->co_host_count > 0) { - for(int i = 0; i < Metadata.co_host_count; ++i) + for(int i = 0; i < Metadata->co_host_count; ++i) { - if(SearchCredentials(Config, CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.co_hosts[i], "Co-host")) + if(SearchCredentials(CreditsMenu, HasCreditsMenu, Metadata->co_hosts[i], "Co-host")) { printf("No credentials for %s. Please contact matt@handmadedev.org with their:\n" " Full name\n" " Homepage URL (optional)\n" - " Financial support info, e.g. Patreon URL (optional)\n", Metadata.co_hosts[i]); + " Financial support info, e.g. Patreon URL (optional)\n", Metadata->co_hosts[i]); } } } - if(Metadata.guest_count > 0) + if(Metadata->guest_count > 0) { - for(int i = 0; i < Metadata.guest_count; ++i) + for(int i = 0; i < Metadata->guest_count; ++i) { - if(SearchCredentials(Config, CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.guests[i], "Guest")) + if(SearchCredentials(CreditsMenu, HasCreditsMenu, Metadata->guests[i], "Guest")) { printf("No credentials for %s. Please contact matt@handmadedev.org with their:\n" " Full name\n" " Homepage URL (optional)\n" - " Financial support info, e.g. Patreon URL (optional)\n", Metadata.guests[i]); + " Financial support info, e.g. Patreon URL (optional)\n", Metadata->guests[i]); } } } - if(Metadata.annotator_count > 0) + if(Metadata->annotator_count > 0) { - for(int i = 0; i < Metadata.annotator_count; ++i) + for(int i = 0; i < Metadata->annotator_count; ++i) { - if(SearchCredentials(Config, CreditsMenu, HasCreditsMenu, ImagesDir, Metadata.annotators[i], "Annotator")) + if(SearchCredentials(CreditsMenu, HasCreditsMenu, Metadata->annotators[i], "Annotator")) { printf("No credentials for %s. Please contact matt@handmadedev.org with their:\n" " Full name\n" " Homepage URL (optional)\n" - " Financial support info, e.g. Patreon URL (optional)\n", Metadata.annotators[i]); + " Financial support info, e.g. Patreon URL (optional)\n", Metadata->annotators[i]); } } } @@ -869,7 +935,7 @@ BuildCredits(config Config, buffer *CreditsMenu, bool *HasCreditsMenu, char *Ima } int -BuildReference(ref_info *ReferencesArray, int RefIdentifier, int UniqueRefs, HMML_Reference Ref, HMML_Annotation Anno) +BuildReference(ref_info *ReferencesArray, int RefIdentifier, int UniqueRefs, HMML_Reference *Ref, HMML_Annotation *Anno) { #define REF_SITE (1 << 0) @@ -884,96 +950,96 @@ BuildReference(ref_info *ReferencesArray, int RefIdentifier, int UniqueRefs, HMM int Mask = 0; - if(Ref.site) { Mask |= REF_SITE; } - if(Ref.page) { Mask |= REF_PAGE; } - if(Ref.url) { Mask |= REF_URL; } - if(Ref.title) { Mask |= REF_TITLE; } - if(Ref.article) { Mask |= REF_ARTICLE; } - if(Ref.author) { Mask |= REF_AUTHOR; } - if(Ref.editor) { Mask |= REF_EDITOR; } - if(Ref.publisher) { Mask |= REF_PUBLISHER; } - if(Ref.isbn) { Mask |= REF_ISBN; } + if(Ref->site) { Mask |= REF_SITE; } + if(Ref->page) { Mask |= REF_PAGE; } + if(Ref->url) { Mask |= REF_URL; } + if(Ref->title) { Mask |= REF_TITLE; } + if(Ref->article) { Mask |= REF_ARTICLE; } + if(Ref->author) { Mask |= REF_AUTHOR; } + if(Ref->editor) { Mask |= REF_EDITOR; } + if(Ref->publisher) { Mask |= REF_PUBLISHER; } + if(Ref->isbn) { Mask |= REF_ISBN; } if((REF_URL | REF_TITLE | REF_AUTHOR | REF_PUBLISHER | REF_ISBN) == Mask) { - CopyString(ReferencesArray[UniqueRefs].ID, Ref.isbn); - CopyString(ReferencesArray[UniqueRefs].Source, "%s (%s)", Ref.author, Ref.publisher); - CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref.title); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref.url); + CopyString(ReferencesArray[UniqueRefs].ID, Ref->isbn); + CopyString(ReferencesArray[UniqueRefs].Source, "%s (%s)", Ref->author, Ref->publisher); + CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->title); + CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } else if((REF_AUTHOR | REF_SITE | REF_PAGE | REF_URL) == Mask) { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref.url); - CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref.site); - CopyString(ReferencesArray[UniqueRefs].RefTitle, "%s: \"%s\"", Ref.author, Ref.page); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref.url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref->site); + CopyString(ReferencesArray[UniqueRefs].RefTitle, "%s: \"%s\"", Ref->author, Ref->page); + CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } else if((REF_PAGE | REF_URL | REF_TITLE) == Mask) { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref.url); - CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref.title); - CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref.page); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref.url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref->title); + CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->page); + CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } else if((REF_SITE | REF_PAGE | REF_URL) == Mask) { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref.url); - CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref.site); - CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref.page); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref.url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref->site); + CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->page); + CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } else if((REF_SITE | REF_URL | REF_TITLE) == Mask) { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref.url); - CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref.site); - CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref.title); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref.url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].Source, Ref->site); + CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->title); + CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } else if((REF_TITLE | REF_AUTHOR | REF_ISBN) == Mask) { - CopyString(ReferencesArray[UniqueRefs].ID, Ref.isbn); - CopyString(ReferencesArray[UniqueRefs].Source, Ref.author); - CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref.title); - CopyString(ReferencesArray[UniqueRefs].URL, "http://www.openisbn.com/isbn/%s", Ref.isbn); + CopyString(ReferencesArray[UniqueRefs].ID, Ref->isbn); + CopyString(ReferencesArray[UniqueRefs].Source, Ref->author); + CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->title); + CopyString(ReferencesArray[UniqueRefs].URL, "http://www.openisbn.com/isbn/%s", Ref->isbn); } else if((REF_URL | REF_ARTICLE | REF_AUTHOR) == Mask) { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref.url); - CopyString(ReferencesArray[UniqueRefs].Source, Ref.author); - CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref.article); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref.url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); + CopyString(ReferencesArray[UniqueRefs].Source, Ref->author); + CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->article); + CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } else if((REF_URL | REF_TITLE | REF_AUTHOR) == Mask) { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref.url); - CopyString(ReferencesArray[UniqueRefs].Source, Ref.author); - CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref.title); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref.url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); + CopyString(ReferencesArray[UniqueRefs].Source, Ref->author); + CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->title); + CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } else if((REF_URL | REF_TITLE) == Mask) { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref.url); - CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref.title); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref.url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->title); + CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } else if((REF_SITE | REF_URL) == Mask) { - CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref.url); - CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref.site); - CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref.url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].ID, Ref->url); + CopyStringNoFormat(ReferencesArray[UniqueRefs].RefTitle, Ref->site); + CopyStringNoFormat(ReferencesArray[UniqueRefs].URL, Ref->url); } else { return 1; } - CopyString(ReferencesArray[UniqueRefs].Identifier[ReferencesArray[UniqueRefs].IdentifierCount].Timecode, Anno.time); + CopyString(ReferencesArray[UniqueRefs].Identifier[ReferencesArray[UniqueRefs].IdentifierCount].Timecode, Anno->time); ReferencesArray[UniqueRefs].Identifier[ReferencesArray[UniqueRefs].IdentifierCount].Identifier = RefIdentifier; return 0; } void -InsertCategory(categories *Topics, categories *Media, char *Marker) +InsertCategory(categories *GlobalTopics, categories *LocalTopics, categories *GlobalMedia, categories *LocalMedia, char *Marker) { bool IsMedium = FALSE; @@ -990,88 +1056,143 @@ InsertCategory(categories *Topics, categories *Media, char *Marker) int CategoryIndex; if(IsMedium) { - for(CategoryIndex = 0; CategoryIndex < Media->Count; ++CategoryIndex) + for(CategoryIndex = 0; CategoryIndex < LocalMedia->Count; ++CategoryIndex) { - if(!StringsDiffer(CategoryMedium[CategoryMediumIndex].Medium, Media->Category[CategoryIndex].Marker)) + if(!StringsDiffer(CategoryMedium[CategoryMediumIndex].Medium, LocalMedia->Category[CategoryIndex].Marker)) { return; } - if((StringsDiffer(CategoryMedium[CategoryMediumIndex].WrittenName, Media->Category[CategoryIndex].WrittenText)) < 0) + if((StringsDiffer(CategoryMedium[CategoryMediumIndex].WrittenName, LocalMedia->Category[CategoryIndex].WrittenText)) < 0) { int CategoryCount; - for(CategoryCount = Media->Count; CategoryCount > CategoryIndex; --CategoryCount) + for(CategoryCount = LocalMedia->Count; CategoryCount > CategoryIndex; --CategoryCount) { - CopyString(Media->Category[CategoryCount].Marker, Media->Category[CategoryCount-1].Marker); - CopyString(Media->Category[CategoryCount].WrittenText, Media->Category[CategoryCount-1].WrittenText); + CopyString(LocalMedia->Category[CategoryCount].Marker, LocalMedia->Category[CategoryCount-1].Marker); + CopyString(LocalMedia->Category[CategoryCount].WrittenText, LocalMedia->Category[CategoryCount-1].WrittenText); } - CopyString(Media->Category[CategoryCount].Marker, CategoryMedium[CategoryMediumIndex].Medium); - CopyString(Media->Category[CategoryCount].WrittenText, CategoryMedium[CategoryMediumIndex].WrittenName); + CopyString(LocalMedia->Category[CategoryCount].Marker, CategoryMedium[CategoryMediumIndex].Medium); + CopyString(LocalMedia->Category[CategoryCount].WrittenText, CategoryMedium[CategoryMediumIndex].WrittenName); break; } } - if(CategoryIndex == Media->Count) + if(CategoryIndex == LocalMedia->Count) { - CopyString(Media->Category[CategoryIndex].Marker, CategoryMedium[CategoryMediumIndex].Medium); - CopyString(Media->Category[CategoryIndex].WrittenText, CategoryMedium[CategoryMediumIndex].WrittenName); + CopyString(LocalMedia->Category[CategoryIndex].Marker, CategoryMedium[CategoryMediumIndex].Medium); + CopyString(LocalMedia->Category[CategoryIndex].WrittenText, CategoryMedium[CategoryMediumIndex].WrittenName); } - ++Media->Count; + ++LocalMedia->Count; + + for(CategoryIndex = 0; CategoryIndex < GlobalMedia->Count; ++CategoryIndex) + { + if(!StringsDiffer(CategoryMedium[CategoryMediumIndex].Medium, GlobalMedia->Category[CategoryIndex].Marker)) + { + return; + } + if((StringsDiffer(CategoryMedium[CategoryMediumIndex].WrittenName, GlobalMedia->Category[CategoryIndex].WrittenText)) < 0) + { + int CategoryCount; + for(CategoryCount = GlobalMedia->Count; CategoryCount > CategoryIndex; --CategoryCount) + { + CopyString(GlobalMedia->Category[CategoryCount].Marker, GlobalMedia->Category[CategoryCount-1].Marker); + CopyString(GlobalMedia->Category[CategoryCount].WrittenText, GlobalMedia->Category[CategoryCount-1].WrittenText); + } + + CopyString(GlobalMedia->Category[CategoryCount].Marker, CategoryMedium[CategoryMediumIndex].Medium); + CopyString(GlobalMedia->Category[CategoryCount].WrittenText, CategoryMedium[CategoryMediumIndex].WrittenName); + break; + } + } + + if(CategoryIndex == GlobalMedia->Count) + { + CopyString(GlobalMedia->Category[CategoryIndex].Marker, CategoryMedium[CategoryMediumIndex].Medium); + CopyString(GlobalMedia->Category[CategoryIndex].WrittenText, CategoryMedium[CategoryMediumIndex].WrittenName); + } + + ++GlobalMedia->Count; } else { - for(CategoryIndex = 0; CategoryIndex < Topics->Count; ++CategoryIndex) + for(CategoryIndex = 0; CategoryIndex < LocalTopics->Count; ++CategoryIndex) { - if(!StringsDiffer(Marker, Topics->Category[CategoryIndex].Marker)) + if(!StringsDiffer(Marker, LocalTopics->Category[CategoryIndex].Marker)) { return; } - if((StringsDiffer(Marker, Topics->Category[CategoryIndex].Marker)) < 0) + if((StringsDiffer(Marker, LocalTopics->Category[CategoryIndex].Marker)) < 0) { int CategoryCount; - for(CategoryCount = Topics->Count; CategoryCount > CategoryIndex; --CategoryCount) + for(CategoryCount = LocalTopics->Count; CategoryCount > CategoryIndex; --CategoryCount) { - CopyString(Topics->Category[CategoryCount].Marker, Topics->Category[CategoryCount-1].Marker); + CopyString(LocalTopics->Category[CategoryCount].Marker, LocalTopics->Category[CategoryCount-1].Marker); } - CopyString(Topics->Category[CategoryCount].Marker, Marker); + CopyString(LocalTopics->Category[CategoryCount].Marker, Marker); break; } } - if(CategoryIndex == Topics->Count) + if(CategoryIndex == LocalTopics->Count) { - CopyString(Topics->Category[CategoryIndex].Marker, Marker); + CopyString(LocalTopics->Category[CategoryIndex].Marker, Marker); } - ++Topics->Count; + ++LocalTopics->Count; + + for(CategoryIndex = 0; CategoryIndex < GlobalTopics->Count; ++CategoryIndex) + { + if(!StringsDiffer(Marker, GlobalTopics->Category[CategoryIndex].Marker)) + { + return; + } + if((StringsDiffer(Marker, GlobalTopics->Category[CategoryIndex].Marker)) < 0) + { + int CategoryCount; + for(CategoryCount = GlobalTopics->Count; CategoryCount > CategoryIndex; --CategoryCount) + { + CopyString(GlobalTopics->Category[CategoryCount].Marker, GlobalTopics->Category[CategoryCount-1].Marker); + } + + CopyString(GlobalTopics->Category[CategoryCount].Marker, Marker); + break; + } + } + + if(CategoryIndex == GlobalTopics->Count) + { + CopyString(GlobalTopics->Category[CategoryIndex].Marker, Marker); + } + + ++GlobalTopics->Count; } return; } void -BuildCategories(buffer *AnnotationClass, buffer *TopicDots, categories LocalTopics, categories LocalMedia, int *MarkerIndex) +BuildCategories(buffer *AnnotationClass, buffer *TopicDots, categories *LocalTopics, categories *LocalMedia, int *MarkerIndex) { - if(LocalTopics.Count > 0) + if(LocalTopics->Count > 0) { CopyStringToBuffer(TopicDots, ""); - for(int i = 0; i < LocalTopics.Count; ++i) + for(int i = 0; i < LocalTopics->Count; ++i) { CopyStringToBuffer(TopicDots, "
", - SanitisePunctuation(LocalTopics.Category[i].Marker), - SanitisePunctuation(LocalTopics.Category[i].Marker)); + SanitisePunctuation(LocalTopics->Category[i].Marker), + SanitisePunctuation(LocalTopics->Category[i].Marker)); CopyStringToBuffer(AnnotationClass, " cat_%s", - SanitisePunctuation(LocalTopics.Category[i].Marker)); + SanitisePunctuation(LocalTopics->Category[i].Marker)); } CopyStringToBuffer(TopicDots, "
"); } - for(int i = 0; i < LocalMedia.Count; ++i) + for(int i = 0; i < LocalMedia->Count; ++i) { - CopyStringToBuffer(AnnotationClass, " %s", SanitisePunctuation(LocalMedia.Category[i].Marker)); + CopyStringToBuffer(AnnotationClass, " %s", SanitisePunctuation(LocalMedia->Category[i].Marker)); } CopyStringToBuffer(AnnotationClass, "\""); @@ -1120,21 +1241,21 @@ CurlQuotes(buffer *QuoteStaging, char *QuotesURL) } int -SearchQuotes(buffer QuoteStaging, int CacheSize, quote_info *Info, int ID) +SearchQuotes(buffer *QuoteStaging, int CacheSize, quote_info *Info, int ID) { - QuoteStaging.Ptr = QuoteStaging.Location; - while(QuoteStaging.Ptr - QuoteStaging.Location < CacheSize) + QuoteStaging->Ptr = QuoteStaging->Location; + while(QuoteStaging->Ptr - QuoteStaging->Location < CacheSize) { char InID[4] = { 0 }; char InTime[16] = { 0 }; char *OutPtr = InID; - QuoteStaging.Ptr += CopyStringNoFormatT(OutPtr, QuoteStaging.Ptr, ','); + QuoteStaging->Ptr += CopyStringNoFormatT(OutPtr, QuoteStaging->Ptr, ','); if(StringToInt(InID) == ID) { - QuoteStaging.Ptr += 1; + QuoteStaging->Ptr += 1; OutPtr = InTime; - QuoteStaging.Ptr += CopyStringNoFormatT(OutPtr, QuoteStaging.Ptr, ','); + QuoteStaging->Ptr += CopyStringNoFormatT(OutPtr, QuoteStaging->Ptr, ','); long int Time = StringToInt(InTime); char DayString[3]; @@ -1150,32 +1271,32 @@ SearchQuotes(buffer QuoteStaging, int CacheSize, quote_info *Info, int ID) strftime(MonthYear, 32, "%B, %Y", gmtime(&Time)); CopyString(Info->Date, "%d%s %s", Day, DaySuffix, MonthYear); - QuoteStaging.Ptr += 1; + QuoteStaging->Ptr += 1; OutPtr = Info->Text; - QuoteStaging.Ptr += CopyStringNoFormatT(OutPtr, QuoteStaging.Ptr, '\n'); + QuoteStaging->Ptr += CopyStringNoFormatT(OutPtr, QuoteStaging->Ptr, '\n'); - FreeBuffer(&QuoteStaging); + FreeBuffer(QuoteStaging); return RC_FOUND; } else { - while(*QuoteStaging.Ptr != '\n') + while(*QuoteStaging->Ptr != '\n') { - ++QuoteStaging.Ptr; + ++QuoteStaging->Ptr; } - ++QuoteStaging.Ptr; + ++QuoteStaging->Ptr; } } return RC_UNFOUND; } int -BuildQuote(quote_info *Info, char *Speaker, int ID, char *CacheDir) +BuildQuote(quote_info *Info, char *Speaker, int ID) { // TODO(matt): Rebuild cache option char QuoteCacheDir[255]; - CopyString(QuoteCacheDir, "%s/quotes", CacheDir); + CopyString(QuoteCacheDir, "%s/quotes", Config.CacheDir); char QuoteCachePath[255]; CopyString(QuoteCachePath, "%s/%s", QuoteCacheDir, Speaker); FILE *QuoteCache; @@ -1212,6 +1333,14 @@ BuildQuote(quote_info *Info, char *Speaker, int ID, char *CacheDir) fclose(QuoteCache); return RC_ERROR_MEMORY; } + +#if DEBUG_MEM + FILE *MemLog = fopen("/home/matt/cinera_mem", "a+"); + fprintf(MemLog, " Allocated QuoteStaging (%d)\n", QuoteStaging.Size); + fclose(MemLog); + printf(" Allocated QuoteStaging (%d)\n", QuoteStaging.Size); +#endif + QuoteStaging.Ptr = QuoteStaging.Location; if(CacheAvailable) @@ -1223,7 +1352,7 @@ BuildQuote(quote_info *Info, char *Speaker, int ID, char *CacheDir) fread(QuoteStaging.Location, FileSize, 1, QuoteCache); fclose(QuoteCache); - if(SearchQuotes(QuoteStaging, FileSize, Info, ID) == RC_UNFOUND) + if(SearchQuotes(&QuoteStaging, FileSize, Info, ID) == RC_UNFOUND) { CurlQuotes(&QuoteStaging, QuotesURL); @@ -1236,7 +1365,7 @@ BuildQuote(quote_info *Info, char *Speaker, int ID, char *CacheDir) int CacheSize = QuoteStaging.Ptr - QuoteStaging.Location; QuoteStaging.Ptr = QuoteStaging.Location; - if(SearchQuotes(QuoteStaging, CacheSize, Info, ID) == 1) + if(SearchQuotes(&QuoteStaging, CacheSize, Info, ID) == 1) { FreeBuffer(&QuoteStaging); return 1; @@ -1248,7 +1377,7 @@ BuildQuote(quote_info *Info, char *Speaker, int ID, char *CacheDir) CurlQuotes(&QuoteStaging, QuotesURL); int CacheSize = QuoteStaging.Ptr - QuoteStaging.Location; QuoteStaging.Ptr = QuoteStaging.Location; - if(SearchQuotes(QuoteStaging, CacheSize, Info, ID) == 1) + if(SearchQuotes(&QuoteStaging, CacheSize, Info, ID) == 1) { FreeBuffer(&QuoteStaging); return 1; @@ -1259,7 +1388,7 @@ BuildQuote(quote_info *Info, char *Speaker, int ID, char *CacheDir) } int -GenerateTopicColours(char *Topic, char *TopicsDir) +GenerateTopicColours(char *Topic) { for(int i = 0; i < ArrayCount(CategoryMedium); ++i) { @@ -1269,49 +1398,62 @@ GenerateTopicColours(char *Topic, char *TopicsDir) } } - FILE *TopicsFile; - char *TopicsBuffer; - // TODO(matt): Consider (optionally) pulling this path from the config - char TopicsPath[255]; - CopyString(TopicsPath, "%s/cinera_topics.css", TopicsDir); - if((TopicsFile = fopen(TopicsPath, "a+"))) - { - fseek(TopicsFile, 0, SEEK_END); - int TopicsLength = ftell(TopicsFile); - fseek(TopicsFile, 0, SEEK_SET); + file_buffer Topics; + Topics.Buffer.ID = "Topics"; - if(!(TopicsBuffer = malloc(TopicsLength))) + // TODO(matt): Correctly locate cinera_topics.css once the config takes everything we need + //char TopicsPath[255]; + CopyString(Topics.Path, "%s/cinera_topics.css", Config.CSSDir); +#if 0 + printf("BaseDir: %s\n" + "CSSDir relative to BaseDir: %s\n", Config->BaseDir, Config->CSSDir); +#endif + + if((Topics.Handle = fopen(Topics.Path, "a+"))) + { + fseek(Topics.Handle, 0, SEEK_END); + Topics.FileSize = ftell(Topics.Handle); + Topics.Buffer.Size = Topics.FileSize; + fseek(Topics.Handle, 0, SEEK_SET); + + if(!(Topics.Buffer.Location = malloc(Topics.Buffer.Size))) { return RC_ERROR_MEMORY; } - fread(TopicsBuffer, TopicsLength, 1, TopicsFile); +#if DEBUG_MEM + FILE *MemLog = fopen("/home/matt/cinera_mem", "a+"); + fprintf(MemLog, " Allocated Topics (%d)\n", Topics.Size); + fclose(MemLog); + printf(" Allocated Topics (%d)\n", Topics.Size); +#endif - char *TopicsPtr = TopicsBuffer; + Topics.Buffer.Ptr = Topics.Buffer.Location; + fread(Topics.Buffer.Location, Topics.Buffer.Size, 1, Topics.Handle); - while(TopicsPtr - TopicsBuffer < TopicsLength) + while(Topics.Buffer.Ptr - Topics.Buffer.Location < Topics.Buffer.Size) { - TopicsPtr += StringLength(".category."); - if(!StringsDifferT(SanitisePunctuation(Topic), TopicsPtr, ' ')) + Topics.Buffer.Ptr += StringLength(".category."); + if(!StringsDifferT(SanitisePunctuation(Topic), Topics.Buffer.Ptr, ' ')) { - free(TopicsBuffer); - fclose(TopicsFile); + FreeBuffer(&Topics.Buffer); + fclose(Topics.Handle); return RC_NOOP; } - while(TopicsPtr - TopicsBuffer < TopicsLength && *TopicsPtr != '\n') + while(Topics.Buffer.Ptr - Topics.Buffer.Location < Topics.Buffer.Size && *Topics.Buffer.Ptr != '\n') { - ++TopicsPtr; + ++Topics.Buffer.Ptr; } - ++TopicsPtr; + ++Topics.Buffer.Ptr; } hsl_colour Colour; StringToColourHash(&Colour, Topic); - fprintf(TopicsFile, ".category.%s { border: 1px solid hsl(%d, %d%%, %d%%); background: hsl(%d, %d%%, %d%%); }\n", + fprintf(Topics.Handle, ".category.%s { border: 1px solid hsl(%d, %d%%, %d%%); background: hsl(%d, %d%%, %d%%); }\n", SanitisePunctuation(Topic), Colour.Hue, Colour.Saturation, Colour.Lightness, Colour.Hue, Colour.Saturation, Colour.Lightness); - fclose(TopicsFile); - free(TopicsBuffer); + fclose(Topics.Handle); + FreeBuffer(&Topics.Buffer); return RC_SUCCESS; } else @@ -1321,35 +1463,44 @@ GenerateTopicColours(char *Topic, char *TopicsDir) } void -PrintUsage(char *BinaryLocation, config DefaultConfig) +PrintUsage(char *BinaryLocation, config *DefaultConfig) { fprintf(stderr, "Usage: %s [option(s)] filename(s)\n" "\n" "Options:\n" - " -b \n" - " Override default base output directory (\"%s\")\n" - " -c \n" - " Override default CSS directory (\"%s\")\n" + " Paths: \n" + " -r \n" + " Override default root directory (\"%s\")\n" + " -b \n" + " Override default base output directory (\"%s\"), relative to root\n" + " -c \n" + " Override default CSS directory (\"%s\"), relative to root\n" + " -i \n" + " Override default images directory (\"%s\"), relative to root\n" + " -j \n" + " Override default JS directory (\"%s\"), relative to root\n" + " -t \n" + " Override default player template location (\"%s\"), relative to root\n" + " and automatically enable integration\n" + " -x \n" + " Override default index template location (\"%s\"), relative to root\n" + " and automatically enable integration\n" + "\n" + " -o \n" + " Override default output player location for SINGLE_EDITION (\"%s\")\n" + " -p \n" + " Override default project directory (\"%s\")\n" + "\n" " -f\n" " Force integration with an incomplete template\n" - " -i \n" - " Override default images directory (\"%s\")\n" - " -j \n" - " Override default JS directory (\"%s\")\n" + " -I \n" + " Set the project ID, corresponding to the \"project\" field in the HMML files\n" " -l \n" " Override default log level (%d), where n is from 0 (terse) to 7 (verbose)\n" " -m \n" " Override default default medium (\"%s\")\n" - " -o \n" - " Override default output location (\"%s\")\n" - " -p \n" - " Override default project directory (\"%s\")\n" - " -t \n" - " Override default player template location (\"%s\")\n" - " and automatically enable integration\n" - " -x \n" - " Override default index template location (\"%s\")\n" - " and automatically enable integration\n" + " -u \n" + " Override default update interval (\"%d\")\n" //" -c config location\n" " -h\n" " display this help\n" @@ -1370,7 +1521,7 @@ PrintUsage(char *BinaryLocation, config DefaultConfig) "\n" "HMML Specification:\n" " https://git.handmade.network/Annotation-Pushers/Annotation-System/wikis/hmmlspec\n", - BinaryLocation, DefaultConfig.BaseDir, DefaultConfig.CSSDir, DefaultConfig.ImagesDir, DefaultConfig.JSDir, DefaultConfig.LogLevel, DefaultConfig.DefaultMedium, DefaultConfig.OutLocation, DefaultConfig.ProjectDir, DefaultConfig.TemplatePlayerLocation, DefaultConfig.TemplateIndexLocation); + BinaryLocation, DefaultConfig->RootDir, DefaultConfig->BaseDir, DefaultConfig->CSSDir, DefaultConfig->ImagesDir, DefaultConfig->JSDir, DefaultConfig->TemplatePlayerLocation, DefaultConfig->TemplateIndexLocation, DefaultConfig->OutLocation, DefaultConfig->ProjectDir, DefaultConfig->LogLevel, DefaultConfig->DefaultMedium, DefaultConfig->UpdateInterval); } void @@ -1394,54 +1545,33 @@ enum } pages; int -ValidateTemplate(buffer *Errors, template *TemplateMetadata, config Config, int PageType) +ValidateTemplate(buffer *Errors, template **Template, int PageType) { - Errors->Ptr = Errors->Location; + if(ClaimTemplate(Template, + PageType == PAGE_INDEX ? Config.TemplateIndexLocation : Config.TemplatePlayerLocation) == RC_ARENA_FULL){ return RC_ARENA_FULL; }; + + RewindBuffer(Errors); bool HaveErrors = FALSE; - FILE *TemplateFile; - if(!(TemplateFile = fopen(TemplateMetadata->Filename, "r"))) - { - LogError(Config, LOG_ERROR, "Unable to open template %s: %s", TemplateMetadata->Filename, strerror(errno)); - fprintf(stderr, "Unable to open template %s: %s\n", TemplateMetadata->Filename, strerror(errno)); - return RC_ERROR_FILE; - } - - buffer Template; - Template.ID = TemplateMetadata->Filename; - - fseek(TemplateFile, 0, SEEK_END); - Template.Size = ftell(TemplateFile); - fseek(TemplateFile, 0, SEEK_SET); - if(!(Template.Location = malloc(Template.Size))) - { - LogError(Config, LOG_ERROR, "ValidateTemplate(): %s", - strerror(errno)); - return RC_ERROR_MEMORY; - } - Template.Ptr = Template.Location; - fread(Template.Location, Template.Size, 1, TemplateFile); - fclose(TemplateFile); - bool FoundIncludes = FALSE; bool FoundMenus = FALSE; bool FoundPlayer = FALSE; bool FoundScript = FALSE; bool FoundIndex = FALSE; - char *Previous = Template.Location; + char *Previous = (*Template)->Buffer.Location; - while(Template.Ptr - Template.Location < Template.Size) + while((*Template)->Buffer.Ptr - (*Template)->Buffer.Location < (*Template)->Buffer.Size) { Here: - if(*Template.Ptr == '!' && (Template.Ptr > Template.Location && !StringsDifferT("", Template.Ptr, 0)) + char *CommentStart = &(*Template)->Buffer.Ptr[-1]; + while((*Template)->Buffer.Ptr - (*Template)->Buffer.Location < (*Template)->Buffer.Size && StringsDifferT("-->", (*Template)->Buffer.Ptr, 0)) { for(int i = 0; i < ArrayCount(Tags); ++i) { - if(!(StringsDifferT(Tags[i].Tag, Template.Ptr, 0))) + if(!(StringsDifferT(Tags[i].Tag, (*Template)->Buffer.Ptr, 0))) { // TODO(matt): Pack up this data for BuffersToHTML() to use /* @@ -1458,50 +1588,50 @@ Here: switch(Tags[i].Code) { case TAG_INDEX: - TemplateMetadata->Tag[TemplateMetadata->TagCount].Offset = CommentStart - Previous; - TemplateMetadata->Tag[TemplateMetadata->TagCount].TagCode = TAG_INDEX; - TemplateMetadata->TagCount++; - DepartComment(&Template); - Previous = Template.Ptr; + (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].Offset = CommentStart - Previous; + (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].TagCode = TAG_INDEX; + (*Template)->Metadata.TagCount++; + DepartComment(&(*Template)->Buffer); + Previous = (*Template)->Buffer.Ptr; FoundIndex = TRUE; goto Here; case TAG_INCLUDES: if(!Config.ForceIntegration && FoundIncludes == TRUE) { - CopyStringToBuffer(Errors, "Template contains more than one tag\n", Tags[i].Tag); + CopyStringToBuffer(Errors, "(*Template) contains more than one tag\n", Tags[i].Tag); HaveErrors = TRUE; } - TemplateMetadata->Tag[TemplateMetadata->TagCount].Offset = CommentStart - Previous; - TemplateMetadata->Tag[TemplateMetadata->TagCount].TagCode = TAG_INCLUDES; - TemplateMetadata->TagCount++; - DepartComment(&Template); - Previous = Template.Ptr; + (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].Offset = CommentStart - Previous; + (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].TagCode = TAG_INCLUDES; + (*Template)->Metadata.TagCount++; + DepartComment(&(*Template)->Buffer); + Previous = (*Template)->Buffer.Ptr; FoundIncludes = TRUE; goto Here; case TAG_MENUS: if(!Config.ForceIntegration && FoundMenus == TRUE) { - CopyStringToBuffer(Errors, "Template contains more than one tag\n", Tags[i].Tag); + CopyStringToBuffer(Errors, "(*Template) contains more than one tag\n", Tags[i].Tag); HaveErrors = TRUE; } - TemplateMetadata->Tag[TemplateMetadata->TagCount].Offset = CommentStart - Previous; - TemplateMetadata->Tag[TemplateMetadata->TagCount].TagCode = TAG_MENUS; - TemplateMetadata->TagCount++; - DepartComment(&Template); - Previous = Template.Ptr; + (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].Offset = CommentStart - Previous; + (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].TagCode = TAG_MENUS; + (*Template)->Metadata.TagCount++; + DepartComment(&(*Template)->Buffer); + Previous = (*Template)->Buffer.Ptr; FoundMenus = TRUE; goto Here; case TAG_PLAYER: if(!Config.ForceIntegration && FoundPlayer == TRUE) { - CopyStringToBuffer(Errors, "Template contains more than one tag\n", Tags[i].Tag); + CopyStringToBuffer(Errors, "(*Template) contains more than one tag\n", Tags[i].Tag); HaveErrors = TRUE; } - TemplateMetadata->Tag[TemplateMetadata->TagCount].Offset = CommentStart - Previous; - TemplateMetadata->Tag[TemplateMetadata->TagCount].TagCode = TAG_PLAYER; - TemplateMetadata->TagCount++; - DepartComment(&Template); - Previous = Template.Ptr; + (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].Offset = CommentStart - Previous; + (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].TagCode = TAG_PLAYER; + (*Template)->Metadata.TagCount++; + DepartComment(&(*Template)->Buffer); + Previous = (*Template)->Buffer.Ptr; FoundPlayer = TRUE; goto Here; case TAG_SCRIPT: @@ -1512,61 +1642,61 @@ Here: } if(!Config.ForceIntegration && FoundScript == TRUE) { - CopyStringToBuffer(Errors, "Template contains more than one tag\n", Tags[i].Tag); + CopyStringToBuffer(Errors, "(*Template) contains more than one tag\n", Tags[i].Tag); HaveErrors = TRUE; } - TemplateMetadata->Tag[TemplateMetadata->TagCount].Offset = CommentStart - Previous; - TemplateMetadata->Tag[TemplateMetadata->TagCount].TagCode = TAG_SCRIPT; - TemplateMetadata->TagCount++; - DepartComment(&Template); - Previous = Template.Ptr; + (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].Offset = CommentStart - Previous; + (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].TagCode = TAG_SCRIPT; + (*Template)->Metadata.TagCount++; + DepartComment(&(*Template)->Buffer); + Previous = (*Template)->Buffer.Ptr; FoundScript = TRUE; goto Here; case TAG_TITLE: - TemplateMetadata->Tag[TemplateMetadata->TagCount].Offset = CommentStart - Previous; - TemplateMetadata->Tag[TemplateMetadata->TagCount].TagCode = TAG_TITLE; - TemplateMetadata->TagCount++; - DepartComment(&Template); - Previous = Template.Ptr; + (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].Offset = CommentStart - Previous; + (*Template)->Metadata.Tag[(*Template)->Metadata.TagCount].TagCode = TAG_TITLE; + (*Template)->Metadata.TagCount++; + DepartComment(&(*Template)->Buffer); + Previous = (*Template)->Buffer.Ptr; goto Here; }; } } - ++Template.Ptr; + ++(*Template)->Buffer.Ptr; } } else { - ++Template.Ptr; + ++(*Template)->Buffer.Ptr; } } - FreeBuffer(&Template); + //FreeBuffer(&(*Template)->Buffer); if(FoundIndex) { - TemplateMetadata->Validity |= PAGE_INDEX; + (*Template)->Metadata.Validity |= PAGE_INDEX; } if(!HaveErrors && FoundIncludes && FoundMenus && FoundPlayer && FoundScript) { - TemplateMetadata->Validity |= PAGE_PLAYER; + (*Template)->Metadata.Validity |= PAGE_PLAYER; } if(!Config.ForceIntegration) { - if(PageType == PAGE_INDEX && !(TemplateMetadata->Validity & PAGE_INDEX)) + if(PageType == PAGE_INDEX && !((*Template)->Metadata.Validity & PAGE_INDEX)) { - CopyStringToBuffer(Errors, "Index template %s must include one tag\n", TemplateMetadata->Filename); + CopyStringToBuffer(Errors, "Index template %s must include one tag\n", (*Template)->Metadata.Filename); fprintf(stderr, "%s", Errors->Location); return RC_INVALID_TEMPLATE; } - else if(PageType == PAGE_PLAYER && !(TemplateMetadata->Validity & PAGE_PLAYER)) + else if(PageType == PAGE_PLAYER && !((*Template)->Metadata.Validity & PAGE_PLAYER)) { - if(!FoundIncludes){ CopyStringToBuffer(Errors, "Player template %s must include one tag\n", TemplateMetadata->Filename); }; - if(!FoundMenus){ CopyStringToBuffer(Errors, "Player template %s must include one tag\n", TemplateMetadata->Filename); }; - if(!FoundPlayer){ CopyStringToBuffer(Errors, "Player template %s must include one tag\n", TemplateMetadata->Filename); }; - if(!FoundScript){ CopyStringToBuffer(Errors, "Player template %s must include one tag\n", TemplateMetadata->Filename); }; + if(!FoundIncludes){ CopyStringToBuffer(Errors, "Player template %s must include one tag\n", (*Template)->Metadata.Filename); }; + if(!FoundMenus){ CopyStringToBuffer(Errors, "Player template %s must include one tag\n", (*Template)->Metadata.Filename); }; + if(!FoundPlayer){ CopyStringToBuffer(Errors, "Player template %s must include one tag\n", (*Template)->Metadata.Filename); }; + if(!FoundScript){ CopyStringToBuffer(Errors, "Player template %s must include one tag\n", (*Template)->Metadata.Filename); }; fprintf(stderr, "%s", Errors->Location); return RC_INVALID_TEMPLATE; } @@ -1574,22 +1704,8 @@ Here: return RC_SUCCESS; } -void RewindBuffer(buffer *Buffer) -{ -#if DEBUG - float PercentageUsed = (float)(Buffer->Ptr - Buffer->Location) / Buffer->Size * 100; - printf("Rewinding %s\n" - " Used: %ld / %d (%.2f%%)\n\n", - Buffer->ID, - Buffer->Ptr - Buffer->Location, - Buffer->Size, - PercentageUsed); -#endif - Buffer->Ptr = Buffer->Location; -} - int -HMMLToBuffers(arena *MemoryArena, buffers *CollationBuffers, config Config, char *Filename) +HMMLToBuffers(buffers *CollationBuffers, char *Filename) { RewindBuffer(&CollationBuffers->IncludesPlayer); RewindBuffer(&CollationBuffers->Menus); @@ -1610,7 +1726,7 @@ HMMLToBuffers(arena *MemoryArena, buffers *CollationBuffers, config Config, char FILE *InFile; if(!(InFile = fopen(Filepath, "r"))) { - LogError(Config, LOG_ERROR, "Unable to open (annotations file) %s: %s", Filename, strerror(errno)); + LogError(LOG_ERROR, "Unable to open (annotations file) %s: %s", Filename, strerror(errno)); fprintf(stderr, "Unable to open (annotations file) %s: %s\n", Filename, strerror(errno)); return RC_ERROR_FILE; } @@ -1621,7 +1737,6 @@ HMMLToBuffers(arena *MemoryArena, buffers *CollationBuffers, config Config, char if(HMML.well_formed) { CopyString(CollationBuffers->Title, HMML.metadata.title); - CopyString(CollationBuffers->Project, HMML.metadata.project); #if DEBUG printf( "================================================================================\n" @@ -1659,13 +1774,13 @@ HMMLToBuffers(arena *MemoryArena, buffers *CollationBuffers, config Config, char buffer FilterState; - if(ClaimBuffer(MemoryArena, &QuoteMenu, "QuoteMenu", Kilobytes(16)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; - if(ClaimBuffer(MemoryArena, &ReferenceMenu, "ReferenceMenu", Kilobytes(16)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; - if(ClaimBuffer(MemoryArena, &FilterMenu, "FilterMenu", Kilobytes(16)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; - if(ClaimBuffer(MemoryArena, &FilterTopics, "FilterTopics", Kilobytes(8)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; - if(ClaimBuffer(MemoryArena, &FilterMedia, "FilterMedia", Kilobytes(8)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; - if(ClaimBuffer(MemoryArena, &CreditsMenu, "CreditsMenu", Kilobytes(8)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; - if(ClaimBuffer(MemoryArena, &FilterState, "FilterState", Kilobytes(4)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&QuoteMenu, "QuoteMenu", Kilobytes(16)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&ReferenceMenu, "ReferenceMenu", Kilobytes(16)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&FilterMenu, "FilterMenu", Kilobytes(16)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&FilterTopics, "FilterTopics", Kilobytes(8)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&FilterMedia, "FilterMedia", Kilobytes(8)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&CreditsMenu, "CreditsMenu", Kilobytes(8)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&FilterState, "FilterState", Kilobytes(4)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; ref_info ReferencesArray[200] = { 0 }; categories Topics = { 0 }; @@ -1692,7 +1807,7 @@ HMMLToBuffers(arena *MemoryArena, buffers *CollationBuffers, config Config, char "
\n" "
\n", HMML.metadata.id, HMML.metadata.project); - int CreditsErrorCode = BuildCredits(Config, &CreditsMenu, &HasCreditsMenu, Config.ImagesDir, HMML.metadata); + int CreditsErrorCode = BuildCredits(&CreditsMenu, &HasCreditsMenu, &HMML.metadata); if(CreditsErrorCode) { switch(CreditsErrorCode) @@ -1734,12 +1849,12 @@ HMMLToBuffers(arena *MemoryArena, buffers *CollationBuffers, config Config, char // Text // TopicDots - if(ClaimBuffer(MemoryArena, &Annotation, "Annotation", Kilobytes(8)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; - if(ClaimBuffer(MemoryArena, &AnnotationHeader, "AnnotationHeader", 512) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; - if(ClaimBuffer(MemoryArena, &AnnotationClass, "AnnotationClass", 256) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; - if(ClaimBuffer(MemoryArena, &AnnotationData, "AnnotationData", 512) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; - if(ClaimBuffer(MemoryArena, &Text, "Text", Kilobytes(4)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; - if(ClaimBuffer(MemoryArena, &TopicDots, "TopicDots", 512) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&Annotation, "Annotation", Kilobytes(8)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&AnnotationHeader, "AnnotationHeader", 512) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&AnnotationClass, "AnnotationClass", 256) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&AnnotationData, "AnnotationData", 512) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&Text, "Text", Kilobytes(4)) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; + if(ClaimBuffer(&TopicDots, "TopicDots", 512) == RC_ARENA_FULL) { hmml_free(&HMML); return RC_ARENA_FULL; }; CopyStringToBuffer(&AnnotationHeader, "
author); if(Config.Edition == EDITION_NETWORK) @@ -1846,7 +1960,7 @@ HMMLToBuffers(arena *MemoryArena, buffers *CollationBuffers, config Config, char } else if(Anno->markers[MarkerIndex].type == HMML_CATEGORY) { - switch(GenerateTopicColours(Anno->markers[MarkerIndex].marker, Config.CSSDir)) + switch(GenerateTopicColours(Anno->markers[MarkerIndex].marker)) { case RC_SUCCESS: case RC_NOOP: @@ -1859,8 +1973,7 @@ HMMLToBuffers(arena *MemoryArena, buffers *CollationBuffers, config Config, char { HasFilterMenu = TRUE; } - InsertCategory(&Topics, &Media, Anno->markers[MarkerIndex].marker); // Global - InsertCategory(&LocalTopics, &LocalMedia, Anno->markers[MarkerIndex].marker); // Local + InsertCategory(&Topics, &LocalTopics, &Media, &LocalMedia, Anno->markers[MarkerIndex].marker); } } @@ -1876,9 +1989,9 @@ HMMLToBuffers(arena *MemoryArena, buffers *CollationBuffers, config Config, char "
\n" "
\n"); - if(BuildReference(ReferencesArray, RefIdentifier, UniqueRefs, *CurrentRef, *Anno) == 1) + if(BuildReference(ReferencesArray, RefIdentifier, UniqueRefs, CurrentRef, Anno) == 1) { - LogError(Config, LOG_ERROR, "Reference combination processing failed: %s:%d", Filename, Anno->line); + LogError(LOG_ERROR, "Reference combination processing failed: %s:%d", Filename, Anno->line); fprintf(stderr, "%s:%d: Cannot process new combination of reference info\n" "\n" "Either tweak your annotation, or contact matt@handmadedev.org\n" @@ -1898,7 +2011,7 @@ HMMLToBuffers(arena *MemoryArena, buffers *CollationBuffers, config Config, char { if(ReferencesArray[i].IdentifierCount == REF_MAX_IDENTIFIER) { - LogError(Config, LOG_EMERGENCY, "REF_MAX_IDENTIFIER (%d) reached. Contact matt@handmadedev.org", REF_MAX_IDENTIFIER); + LogError(LOG_EMERGENCY, "REF_MAX_IDENTIFIER (%d) reached. Contact matt@handmadedev.org", REF_MAX_IDENTIFIER); fprintf(stderr, "%s:%d: Too many timecodes associated with one reference (increase REF_MAX_IDENTIFIER)\n", Filename, Anno->line); hmml_free(&HMML); return RC_ERROR_MAX_REFS; @@ -1925,16 +2038,16 @@ HMMLToBuffers(arena *MemoryArena, buffers *CollationBuffers, config Config, char } else { - LogError(Config, LOG_ERROR, "Reference missing ISBN or URL: %s:%d", Filename, Anno->line); + LogError(LOG_ERROR, "Reference missing ISBN or URL: %s:%d", Filename, Anno->line); fprintf(stderr, "%s:%d: Reference must have an ISBN or URL\n", Filename, Anno->line); hmml_free(&HMML); return RC_INVALID_REFERENCE; } } - if(BuildReference(ReferencesArray, RefIdentifier, UniqueRefs, *CurrentRef, *Anno) == 1) + if(BuildReference(ReferencesArray, RefIdentifier, UniqueRefs, CurrentRef, Anno) == 1) { - LogError(Config, LOG_ERROR, "Reference combination processing failed: %s:%d", Filename, Anno->line); + LogError(LOG_ERROR, "Reference combination processing failed: %s:%d", Filename, Anno->line); fprintf(stderr, "%s:%d: Cannot process new combination of reference info\n" "\n" "Either tweak your annotation, or contact matt@handmadedev.org\n" @@ -1959,7 +2072,7 @@ AppendedIdentifier: } else { - LogError(Config, LOG_ERROR, "Reference missing ISBN or URL: %s:%d", Filename, Anno->line); + LogError(LOG_ERROR, "Reference missing ISBN or URL: %s:%d", Filename, Anno->line); fprintf(stderr, "%s:%d: Reference must have an ISBN or URL\n", Filename, Anno->line); hmml_free(&HMML); return RC_INVALID_REFERENCE; @@ -1979,7 +2092,7 @@ AppendedIdentifier: } else { - LogError(Config, LOG_ERROR, "Reference missing ISBN or URL: %s:%d", Filename, Anno->line); + LogError(LOG_ERROR, "Reference missing ISBN or URL: %s:%d", Filename, Anno->line); fprintf(stderr, "%s:%d: Reference must have an ISBN or URL", Filename, Anno->line); hmml_free(&HMML); return RC_INVALID_REFERENCE; @@ -2058,10 +2171,9 @@ AppendedIdentifier: char *Speaker = Anno->quote.author ? Anno->quote.author : HMML.metadata.stream_username ? HMML.metadata.stream_username : HMML.metadata.member; if(BuildQuote(&QuoteInfo, Speaker, - Anno->quote.id, - Config.CacheDir) == 1) + Anno->quote.id) == 1) { - LogError(Config, LOG_ERROR, "Quote #%s %d not found: %s:%d", Speaker, Anno->quote.id, Filename, Anno->line); + LogError(LOG_ERROR, "Quote #%s %d not found: %s:%d", Speaker, Anno->quote.id, Filename, Anno->line); fprintf(stderr, "%s:%d: Quote #%s %d not found. Skipping this file...\n", Filename, Anno->line, @@ -2109,7 +2221,7 @@ AppendedIdentifier: while(MarkerIndex < Anno->marker_count) { - switch(GenerateTopicColours(Anno->markers[MarkerIndex].marker, Config.CSSDir)) + switch(GenerateTopicColours(Anno->markers[MarkerIndex].marker)) { case RC_SUCCESS: case RC_NOOP: @@ -2122,21 +2234,16 @@ AppendedIdentifier: { HasFilterMenu = TRUE; } - if(Anno->markers[MarkerIndex].marker) - { - InsertCategory(&Topics, &Media, Anno->markers[MarkerIndex].marker); - } - InsertCategory(&LocalTopics, &LocalMedia, Anno->markers[MarkerIndex].marker); + InsertCategory(&Topics, &LocalTopics, &Media, &LocalMedia, Anno->markers[MarkerIndex].marker); ++MarkerIndex; } if(LocalMedia.Count == 0) { - InsertCategory(&Topics, &Media, Config.DefaultMedium); - InsertCategory(&LocalTopics, &LocalMedia, Config.DefaultMedium); + InsertCategory(&Topics, &LocalTopics, &Media, &LocalMedia, Config.DefaultMedium); } - BuildCategories(&AnnotationClass, &TopicDots, LocalTopics, LocalMedia, &MarkerIndex); + BuildCategories(&AnnotationClass, &TopicDots, &LocalTopics, &LocalMedia, &MarkerIndex); CopyBuffer(&AnnotationHeader, &AnnotationClass); if(HasQuote || HasReference) @@ -2187,12 +2294,12 @@ AppendedIdentifier: // AnnotationHeader // Annotation - DeclaimBuffer(MemoryArena, &TopicDots); - DeclaimBuffer(MemoryArena, &Text); - DeclaimBuffer(MemoryArena, &AnnotationData); - DeclaimBuffer(MemoryArena, &AnnotationClass); - DeclaimBuffer(MemoryArena, &AnnotationHeader); - DeclaimBuffer(MemoryArena, &Annotation); + DeclaimBuffer(&TopicDots); + DeclaimBuffer(&Text); + DeclaimBuffer(&AnnotationData); + DeclaimBuffer(&AnnotationClass); + DeclaimBuffer(&AnnotationHeader); + DeclaimBuffer(&Annotation); } #if DEBUG @@ -2909,18 +3016,18 @@ AppendedIdentifier: // QuoteMenu Cleanup: - DeclaimBuffer(MemoryArena, &FilterState); + DeclaimBuffer(&FilterState); - DeclaimBuffer(MemoryArena, &CreditsMenu); - DeclaimBuffer(MemoryArena, &FilterMedia); - DeclaimBuffer(MemoryArena, &FilterTopics); - DeclaimBuffer(MemoryArena, &FilterMenu); - DeclaimBuffer(MemoryArena, &ReferenceMenu); - DeclaimBuffer(MemoryArena, &QuoteMenu); + DeclaimBuffer(&CreditsMenu); + DeclaimBuffer(&FilterMedia); + DeclaimBuffer(&FilterTopics); + DeclaimBuffer(&FilterMenu); + DeclaimBuffer(&ReferenceMenu); + DeclaimBuffer(&QuoteMenu); } else { - LogError(Config, LOG_ERROR, "%s:%d: %s", Filename, HMML.error.line, HMML.error.message); + LogError(LOG_ERROR, "%s:%d: %s", Filename, HMML.error.line, HMML.error.message); fprintf(stderr, "%s:%d: %s\n", Filename, HMML.error.line, HMML.error.message); hmml_free(&HMML); return RC_ERROR_HMML; @@ -2930,110 +3037,107 @@ Cleanup: } int -BuffersToHTML(arena *MemoryArena, buffers CollationBuffers, template *TemplateMetadata, config Config, char *OutputPath, int PageType) +BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, int PageType) { #if DEBUG printf("\n\n --- Buffer Collation ---\n" - " %s\n\n\n", OutputPath); + " %s\n\n\n", OutputPath ? OutputPath : Config->OutLocation); +#endif + +#if DEBUG_MEM + FILE *MemLog = fopen("/home/matt/cinera_mem", "a+"); + fprintf(MemLog, "\nEntered BuffersToHTML(%s)\n", OutputPath ? OutputPath : Config->OutLocation); + fclose(MemLog); #endif if(Config.Mode == MODE_INTEGRATE) { - if(TemplateMetadata->Validity & PageType) + if(Template->Metadata.Validity & PageType) { - // TODO(matt): Maybe enable the Template(s) to stick around for the duration of RefreshProject() - buffer Template; - Template.ID = TemplateMetadata->Filename; - FILE *TemplateFile; - if(!(TemplateFile = fopen(TemplateMetadata->Filename, "r"))) - { - LogError(Config, LOG_ERROR, "Unable to open template %s: %s", TemplateMetadata->Filename, strerror(errno)); - return RC_ERROR_FILE; - } - fseek(TemplateFile, 0, SEEK_END); - Template.Size = ftell(TemplateFile); - fseek(TemplateFile, 0, SEEK_SET); - if(!(Template.Location = malloc(Template.Size))) - { - LogError(Config, LOG_ERROR, "BuffersToHTML(): %s", - strerror(errno)); - fclose(TemplateFile); - return RC_ERROR_MEMORY; - } - Template.Ptr = Template.Location; - fread(Template.Location, Template.Size, 1, TemplateFile); - fclose(TemplateFile); - buffer Output; - Output.Size = Template.Size + (Kilobytes(512)); + Output.Size = Template->Buffer.Size + (Kilobytes(512)); Output.ID = "Output"; if(!(Output.Location = malloc(Output.Size))) { - LogError(Config, LOG_ERROR, "BuffersToHTML(): %s", + LogError(LOG_ERROR, "BuffersToHTML(): %s", strerror(errno)); - FreeBuffer(&Template); return RC_ERROR_MEMORY; } + +#if DEBUG_MEM + MemLog = fopen("/home/matt/cinera_mem", "a+"); + fprintf(MemLog, " Allocated Output (%d)\n", Output.Size); + fclose(MemLog); + printf(" Allocated Output (%d)\n", Output.Size); +#endif + Output.Ptr = Output.Location; - Template.Ptr = Template.Location; - for(int i = 0; i < TemplateMetadata->TagCount; ++i) + Template->Buffer.Ptr = Template->Buffer.Location; + for(int i = 0; i < Template->Metadata.TagCount; ++i) { int j = 0; - while(TemplateMetadata->Tag[i].Offset > j) + while(Template->Metadata.Tag[i].Offset > j) { - *Output.Ptr++ = *Template.Ptr++; + *Output.Ptr++ = *Template->Buffer.Ptr++; ++j; } - switch(TemplateMetadata->Tag[i].TagCode) + switch(Template->Metadata.Tag[i].TagCode) { case TAG_TITLE: - CopyStringToBuffer(&Output, CollationBuffers.Title); + CopyStringToBuffer(&Output, CollationBuffers->Title); break; case TAG_INDEX: - CopyBuffer(&Output, &CollationBuffers.Index); + CopyBuffer(&Output, &CollationBuffers->Index); break; case TAG_INCLUDES: - if(PageType == PAGE_PLAYER) - { - CopyBuffer(&Output, &CollationBuffers.IncludesPlayer); - } - else - { - CopyBuffer(&Output, &CollationBuffers.IncludesIndex); - } + CopyBuffer(&Output, PageType == PAGE_PLAYER ? &CollationBuffers->IncludesPlayer : &CollationBuffers->IncludesIndex); break; case TAG_MENUS: - CopyBuffer(&Output, &CollationBuffers.Menus); + CopyBuffer(&Output, &CollationBuffers->Menus); break; case TAG_PLAYER: - CopyBuffer(&Output, &CollationBuffers.Player); + CopyBuffer(&Output, &CollationBuffers->Player); break; case TAG_SCRIPT: - CopyBuffer(&Output, &CollationBuffers.Script); + CopyBuffer(&Output, &CollationBuffers->Script); break; } - DepartComment(&Template); + DepartComment(&Template->Buffer); } - while(Template.Ptr - Template.Location < Template.Size) + while(Template->Buffer.Ptr - Template->Buffer.Location < Template->Buffer.Size) { - *Output.Ptr++ = *Template.Ptr++; + *Output.Ptr++ = *Template->Buffer.Ptr++; } - FreeBuffer(&Template); - FILE *OutFile; if(!(OutFile = fopen(Config.Edition == EDITION_PROJECT ? OutputPath : Config.OutIntegratedLocation, "w"))) { - LogError(Config, LOG_ERROR, "Unable to open output file %s: %s", Config.Edition == EDITION_PROJECT ? OutputPath : Config.OutIntegratedLocation, strerror(errno)); + LogError(LOG_ERROR, "Unable to open output file %s: %s", Config.Edition == EDITION_PROJECT ? OutputPath : Config.OutIntegratedLocation, strerror(errno)); free(Output.Location); + +#if DEBUG_MEM + MemLog = fopen("/home/matt/cinera_mem", "a+"); + fprintf(MemLog, " Freed Output\n"); + fclose(MemLog); + printf(" Freed Output\n"); +#endif + return RC_ERROR_FILE; } fwrite(Output.Location, Output.Ptr - Output.Location, 1, OutFile); fclose(OutFile); free(Output.Location); + +#if DEBUG_MEM + MemLog = fopen("/home/matt/cinera_mem", "a+"); + fprintf(MemLog, " Freed Output\n"); + fclose(MemLog); + printf(" Freed Output\n"); +#endif + return RC_SUCCESS; } else @@ -3044,13 +3148,13 @@ BuffersToHTML(arena *MemoryArena, buffers CollationBuffers, template *TemplateMe else { buffer Master; - if(ClaimBuffer(MemoryArena, &Master, "Master", Kilobytes(512)) == RC_ARENA_FULL) { return RC_ARENA_FULL; }; + if(ClaimBuffer(&Master, "Master", Kilobytes(512)) == RC_ARENA_FULL) { return RC_ARENA_FULL; }; CopyStringToBuffer(&Master, "\n" " \n"); - CopyBuffer(&Master, PageType == PAGE_PLAYER ? &CollationBuffers.IncludesPlayer : &CollationBuffers.IncludesIndex); + CopyBuffer(&Master, PageType == PAGE_PLAYER ? &CollationBuffers->IncludesPlayer : &CollationBuffers->IncludesIndex); CopyStringToBuffer(&Master, "\n"); CopyStringToBuffer(&Master, @@ -3058,16 +3162,16 @@ BuffersToHTML(arena *MemoryArena, buffers CollationBuffers, template *TemplateMe " \n"); if(PageType == PAGE_PLAYER) { - CopyBuffer(&Master, &CollationBuffers.Menus); + CopyBuffer(&Master, &CollationBuffers->Menus); CopyStringToBuffer(&Master, "\n"); - CopyBuffer(&Master, &CollationBuffers.Player); + CopyBuffer(&Master, &CollationBuffers->Player); CopyStringToBuffer(&Master, "\n"); - CopyBuffer(&Master, &CollationBuffers.Script); + CopyBuffer(&Master, &CollationBuffers->Script); CopyStringToBuffer(&Master, "\n"); } else { - CopyBuffer(&Master, &CollationBuffers.Index); + CopyBuffer(&Master, &CollationBuffers->Index); } CopyStringToBuffer(&Master, @@ -3077,281 +3181,390 @@ BuffersToHTML(arena *MemoryArena, buffers CollationBuffers, template *TemplateMe FILE *OutFile; if(!(OutFile = fopen(Config.Edition == EDITION_PROJECT ? OutputPath : Config.OutLocation, "w"))) { - LogError(Config, LOG_ERROR, "Unable to open output file %s: %s", Config.Edition == EDITION_PROJECT ? OutputPath : Config.OutLocation, strerror(errno)); - DeclaimBuffer(MemoryArena, &Master); + LogError(LOG_ERROR, "Unable to open output file %s: %s", Config.Edition == EDITION_PROJECT ? OutputPath : Config.OutLocation, strerror(errno)); + DeclaimBuffer(&Master); return RC_ERROR_FILE; } fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile); fclose(OutFile); - DeclaimBuffer(MemoryArena, &Master); + DeclaimBuffer(&Master); return RC_SUCCESS; } } int -RefreshIndex(arena *MemoryArena, buffers *CollationBuffers, config Config, char *BaseFilename) +ReadFileIntoBuffer(file_buffer *File, int BufferPadding) { - char IndexPath[255]; - CopyString(IndexPath, "%s/%s.index", Config.CacheDir, CollationBuffers->Project); - FILE *IndexFile; - if(!(IndexFile = fopen(IndexPath, "a+"))) + if(!(File->Handle = fopen(File->Path, "r"))) { - LogError(Config, LOG_ERROR, "Unable to open index file %s: %s", IndexPath, strerror(errno)); return RC_ERROR_FILE; } - buffer Index; - Index.ID = "Index"; - fseek(IndexFile, 0, SEEK_END); - int IndexFileSize = ftell(IndexFile); - Index.Size = IndexFileSize + StringLength(BaseFilename) + StringLength(CollationBuffers->Title) + 3; - fseek(IndexFile, 0, SEEK_SET); + fseek(File->Handle, 0, SEEK_END); + File->FileSize = ftell(File->Handle); + File->Buffer.Size = File->FileSize + 1 + BufferPadding; // NOTE(matt): +1 to accommodate a NULL terminator + fseek(File->Handle, 0, SEEK_SET); - if(!(Index.Location = malloc(Index.Size))) + // TODO(matt): Consider using the MemoryArena? Maybe have separate ReadFileIntoMemory() and ReadFileIntoArena() + if(!(File->Buffer.Location = malloc(File->Buffer.Size))) { - LogError(Config, LOG_ERROR, "RefreshIndex(): %s", strerror(errno)); + fclose(File->Handle); return RC_ERROR_MEMORY; } - Index.Ptr = Index.Location; - fread(Index.Location, IndexFileSize, 1, IndexFile); + File->Buffer.Ptr = File->Buffer.Location; - bool Found = FALSE; - bool Inserted = FALSE; - - int EntryCount = 0; - while(Index.Ptr - Index.Location < IndexFileSize) - { - char IndexedFile[32]; - char *Ptr = IndexedFile; - Index.Ptr += CopyStringNoFormatT(Ptr, Index.Ptr, ','); - if(!StringsDiffer(IndexedFile, BaseFilename)) - { - Found = TRUE; - break; - } - else if(StringsDiffer(IndexedFile, BaseFilename) > 0) - { - while(Index.Ptr > Index.Location && *Index.Ptr != '\n') - { - --Index.Ptr; - } - if(Index.Ptr > Index.Location) - { - ++Index.Ptr; - } - - int Head = Index.Ptr - Index.Location; - buffer Scratch; - Scratch.Size = IndexFileSize; - if(!(Scratch.Location = malloc(Scratch.Size + 1))) - { - LogError(Config, LOG_ERROR, "RefreshIndex(): %s", strerror(errno)); - free(Index.Location); - fclose(IndexFile); - return RC_ERROR_MEMORY; - } - Scratch.Ptr = Scratch.Location; - while(Index.Ptr - Index.Location < IndexFileSize) - { - *Scratch.Ptr++ = *Index.Ptr++; - } - *Scratch.Ptr = '\0'; - - Index.Ptr = Index.Location + Head; - CopyStringToBuffer(&Index, "%s,%s\n" - "%s", - BaseFilename, CollationBuffers->Title, - Scratch.Location); - - free(Scratch.Location); - Index.Size = Index.Ptr - Index.Location; - - fclose(IndexFile); - IndexFile = fopen(IndexPath, "w"); - fwrite(Index.Location, Index.Size, 1, IndexFile); - - Inserted = TRUE; - } - else - { - while(Index.Ptr - Index.Location < IndexFileSize && *Index.Ptr != '\n') - { - ++Index.Ptr; - } - ++Index.Ptr; - } - ++EntryCount; - } - - if(!Found) - { - ++EntryCount; - // TODO(matt): Write the EntryCount into the index - if(!Inserted) - { - CopyStringToBuffer(&Index, "%s,%s\n", BaseFilename, CollationBuffers->Title); - fprintf(IndexFile, "%s,%s\n", BaseFilename, CollationBuffers->Title); - } - - RewindBuffer(&CollationBuffers->Index); - Index.Size = Index.Ptr - Index.Location; - RewindBuffer(&Index); - - CopyStringToBuffer(&CollationBuffers->Index, "
\n"); - while(Index.Ptr - Index.Location < Index.Size) - { - char IndexedFile[32]; - char *Ptr = IndexedFile; - Index.Ptr += CopyStringNoFormatT(Ptr, Index.Ptr, ',') + 1; - - char Title[255]; - Ptr = Title; - Index.Ptr += CopyStringNoFormatT(Ptr, Index.Ptr, '\n') + 1; - - // TODO(matt): Fully figure out why the Table of Contents doesn't always get all the stuff from the index - // Steps to reproduce: - // 1. mv riscy04{4..6}.hmml out of the way - // 2. Call the program - // 3. mv riscy046.hmml into the project directory - // 4. See that riscy046 is in the index file, has its own player page, but is not mentioned in the Table of Contents - - CopyStringToBuffer(&CollationBuffers->Index, -"
\n" -" %s\n" -"
\n", - IndexedFile, Title); - - } - CopyStringToBuffer(&CollationBuffers->Index, "
"); - } - - fclose(IndexFile); - FreeBuffer(&Index); - return Found ? RC_NOOP : RC_REFRESHED; + fread(File->Buffer.Location, File->FileSize, 1, File->Handle); + File->Buffer.Location[File->FileSize] = '\0'; + fclose(File->Handle); + return RC_SUCCESS; } +// NOTE(matt): Currently unused int -RefreshProject(arena *MemoryArena, buffers *CollationBuffers, - template *IndexTemplateMetadata, template *PlayerTemplateMetadata, - config Config) +WriteBufferToFile(file_buffer *File, buffer *Buffer, int BytesToWrite, bool KeepFileHandleOpen) { - DIR *ProjectDirHandle; - if(!(ProjectDirHandle = opendir(Config.ProjectDir))) + if(!(File->Handle = fopen(File->Path, "w"))) { - LogError(Config, LOG_ERROR, "Unable to scan project directory %s: %s", Config.ProjectDir, strerror(errno)); - fprintf(stderr, "Unable to scan project directory %s: %s\n", Config.ProjectDir, strerror(errno)); - return RC_ERROR_DIRECTORY; + return RC_ERROR_FILE; } - struct dirent *ProjectFiles; - int FileIndex = 0; - -NextFile: - while((ProjectFiles = readdir(ProjectDirHandle))) + fwrite(Buffer->Location, BytesToWrite, 1, File->Handle); + if(!KeepFileHandleOpen) { - // TODO(matt): Loft out into a function, maybe? - char *Ptr; - Ptr = ProjectFiles->d_name; - Ptr += (StringLength(ProjectFiles->d_name) - StringLength(".hmml")); - if(!(StringsDiffer(Ptr, ".hmml"))) - { - *Ptr = '\0'; - char BaseFilename[255]; - CopyString(BaseFilename, ProjectFiles->d_name); - *Ptr = '.'; - - switch(HMMLToBuffers(MemoryArena, CollationBuffers, Config, ProjectFiles->d_name)) - { - // TODO(matt): Actually sort out the fatality of these cases, once we are always-on - case RC_ERROR_FILE: - case RC_ERROR_FATAL: - closedir(ProjectDirHandle); - return RC_ERROR_FATAL; - case RC_ERROR_HMML: - case RC_ERROR_MAX_REFS: - case RC_ERROR_QUOTE: - case RC_INVALID_REFERENCE: - goto NextFile; - case RC_SUCCESS: - break; - }; - - switch(RefreshIndex(MemoryArena, CollationBuffers, Config, BaseFilename)) - { - // TODO(matt): Actually sort out the fatality of these cases, once we are always-on - case RC_ERROR_FILE: - case RC_ERROR_MEMORY: - closedir(ProjectDirHandle); - return RC_ERROR_FATAL; - case RC_NOOP: - break; - case RC_REFRESHED: - { - char OutputDir[255]; - CopyString(OutputDir, "%s/%s", Config.BaseDir, BaseFilename); - char OutputPath[255]; - CopyString(OutputPath, "%s/index.html", OutputDir); - DIR *OutputDirectoryHandle; - if(!(OutputDirectoryHandle = opendir(OutputDir))) - { - if(MakeDir(OutputDir) == RC_ERROR_DIRECTORY) - { - LogError(Config, LOG_ERROR, "Unable to create directory %s: %s", OutputDir, strerror(errno)); - fprintf(stderr, "Unable to create directory %s: %s\n", OutputDir, strerror(errno)); - closedir(ProjectDirHandle); - return RC_ERROR_DIRECTORY; - }; - } - closedir(OutputDirectoryHandle); - // TODO(matt): Implement checksumming? - - char IndexPagePath[255]; - CopyString(IndexPagePath, "%s/index.html", Config.BaseDir); - switch(BuffersToHTML(MemoryArena, *CollationBuffers, IndexTemplateMetadata, Config, IndexPagePath, PAGE_INDEX)) - { - // TODO(matt): Actually sort out the fatality of these cases, once we are always-on - case RC_INVALID_TEMPLATE: - LogError(Config, LOG_ERROR, "Invalid index template: %s", IndexTemplateMetadata->Filename); - fprintf(stderr, "Invalid index template: %s\n", IndexTemplateMetadata->Filename); - closedir(ProjectDirHandle); - case RC_ERROR_MEMORY: - case RC_ERROR_FILE: - case RC_ARENA_FULL: - return RC_ERROR_FATAL; - case RC_SUCCESS: - break; - } - - switch(BuffersToHTML(MemoryArena, *CollationBuffers, PlayerTemplateMetadata, Config, OutputPath, PAGE_PLAYER)) - { - // TODO(matt): Actually sort out the fatality of these cases, once we are always-on - case RC_INVALID_TEMPLATE: - LogError(Config, LOG_ERROR, "Invalid player template: %s", PlayerTemplateMetadata->Filename); - fprintf(stderr, "Invalid player template: %s\n", PlayerTemplateMetadata->Filename); - closedir(ProjectDirHandle); - case RC_ERROR_MEMORY: - case RC_ERROR_FILE: - case RC_ARENA_FULL: - return RC_ERROR_FATAL; - case RC_SUCCESS: - break; - } - } - break; - } - - ++FileIndex; - } + fclose(File->Handle); } - closedir(ProjectDirHandle); return RC_SUCCESS; } int -MonitorDirectory(arena *MemoryArena, buffers *CollationBuffers, template *IndexTemplateMetadata, template *PlayerTemplateMetadata, config Config, int inotifyInstance, int WatchDescriptor) +InsertIntoIndex(buffers *CollationBuffers, char *BaseFilename) { + file_buffer Index; + Index.Buffer.ID = "Index"; + CopyString(Index.Path, "%s/%s.index", Config.CacheDir, Config.ProjectID); + int FileReadCode = ReadFileIntoBuffer(&Index, 0); + switch(FileReadCode) + { + case RC_ERROR_MEMORY: + return RC_ERROR_MEMORY; + case RC_ERROR_FILE: + case RC_SUCCESS: + break; + } + + int EntryInsertionOffset = -1; + int TitleInsertionOffset = -1; + int TitleInsertionEnd = -1; + int EntryCount = 0; + + char InputFile[255]; + CopyString(InputFile, "%s.hmml", BaseFilename); + HMMLToBuffers(CollationBuffers, InputFile); + + if(FileReadCode == RC_SUCCESS) + { + EntryCount = *(int *)Index.Buffer.Ptr; + Index.Buffer.Ptr += sizeof(int); + + while(Index.Buffer.Ptr - Index.Buffer.Location < Index.FileSize) + { + char IndexedFile[32]; + char *Ptr = IndexedFile; + Index.Buffer.Ptr += CopyStringNoFormatT(Ptr, Index.Buffer.Ptr, ',') + 1; + if(!StringsDiffer(IndexedFile, BaseFilename)) + { + if(!StringsDifferT(CollationBuffers->Title, Index.Buffer.Ptr, '\n')) + { + FreeBuffer(&Index.Buffer); + return RC_NOOP; + } + else + { + TitleInsertionOffset = Index.Buffer.Ptr - Index.Buffer.Location; + while(*Index.Buffer.Ptr != '\n' && Index.Buffer.Ptr - Index.Buffer.Location < Index.FileSize) + { + ++Index.Buffer.Ptr; + } + ++Index.Buffer.Ptr; + TitleInsertionEnd = Index.Buffer.Ptr - Index.Buffer.Location; + break; + } + } + else if(StringsDiffer(IndexedFile, BaseFilename) > 0) + { + while(*Index.Buffer.Ptr != '\n' && Index.Buffer.Ptr > Index.Buffer.Location + sizeof(int)) + { + --Index.Buffer.Ptr; + } + if(Index.Buffer.Ptr > Index.Buffer.Location + sizeof(int)) + { + ++Index.Buffer.Ptr; + } + EntryInsertionOffset = Index.Buffer.Ptr - Index.Buffer.Location; + break; + } + else + { + while(*Index.Buffer.Ptr != '\n' && Index.Buffer.Ptr - Index.Buffer.Location < Index.FileSize) + { + ++Index.Buffer.Ptr; + } + ++Index.Buffer.Ptr; + } + } + } + + if(!(Index.Handle = fopen(Index.Path, "w"))) { FreeBuffer(&Index.Buffer); return RC_ERROR_FILE; } + + if(FileReadCode == RC_ERROR_FILE) + { + Index.Buffer.Size = sizeof(int); + if(!(Index.Buffer.Location = malloc(Index.Buffer.Size))) + { + fclose(Index.Handle); + return RC_ERROR_MEMORY; + } + } + + if(TitleInsertionOffset < 0) { EntryCount++; } + + Index.Buffer.Ptr = Index.Buffer.Location; + *(int *)Index.Buffer.Ptr = EntryCount; + fwrite(Index.Buffer.Ptr, sizeof(int), 1, Index.Handle); + Index.Buffer.Ptr += sizeof(int); + + if(EntryInsertionOffset >= 0) + { + fwrite(Index.Buffer.Ptr, EntryInsertionOffset - sizeof(int), 1, Index.Handle); + fprintf(Index.Handle, "%s,%s\n", BaseFilename, CollationBuffers->Title); + fwrite(Index.Buffer.Ptr + EntryInsertionOffset - sizeof(int), Index.FileSize - EntryInsertionOffset, 1, Index.Handle); + LogError(LOG_NOTICE, "Inserted %s - %s", BaseFilename, CollationBuffers->Title); + fprintf(stderr, "Inserted %s - %s\n", BaseFilename, CollationBuffers->Title); + } + else if(TitleInsertionOffset >= 0) + { + fwrite(Index.Buffer.Ptr, TitleInsertionOffset - sizeof(int), 1, Index.Handle); + fprintf(Index.Handle, "%s\n", CollationBuffers->Title); + fwrite(Index.Buffer.Ptr + TitleInsertionEnd - sizeof(int), Index.FileSize - TitleInsertionEnd, 1, Index.Handle); + LogError(LOG_NOTICE, "Edited %s - %s", BaseFilename, CollationBuffers->Title); + fprintf(stderr, "Edited %s - %s\n", BaseFilename, CollationBuffers->Title); + } + else + { + if(FileReadCode == RC_SUCCESS) + { + fwrite(Index.Buffer.Ptr, Index.FileSize - sizeof(int), 1, Index.Handle); // Write existing stuff back out + } + fprintf(Index.Handle, "%s,%s\n", BaseFilename, CollationBuffers->Title); + LogError(LOG_NOTICE, "Inserted %s - %s", BaseFilename, CollationBuffers->Title); + fprintf(stderr, "Inserted %s - %s\n", BaseFilename, CollationBuffers->Title); + } + + fclose(Index.Handle); + FreeBuffer(&Index.Buffer); + return RC_SUCCESS; +} + +int +DeleteFromIndex(char *BaseFilename) +{ + // TODO(matt): LogError() + file_buffer Index; + Index.Buffer.ID = "Index"; + CopyString(Index.Path, "%s/%s.index", Config.CacheDir, Config.ProjectID); + + switch(ReadFileIntoBuffer(&Index, 0)) + { + case RC_ERROR_FILE: + return RC_ERROR_FILE; + break; + case RC_ERROR_MEMORY: + LogError(LOG_ERROR, "DeleteFromIndex(): %s", strerror(errno)); + return RC_ERROR_MEMORY; + break; + case RC_SUCCESS: + break; + } + + int EntryCount = *(int *)Index.Buffer.Ptr; + Index.Buffer.Ptr += sizeof(int); + + bool Found = FALSE; + int DeleteFrom = -1; + int DeleteTo = -1; + + while(Index.Buffer.Ptr - Index.Buffer.Location < Index.FileSize) + { + if(!StringsDifferT(BaseFilename, Index.Buffer.Ptr, ',')) + { + Found = TRUE; + --EntryCount; + DeleteFrom = Index.Buffer.Ptr - Index.Buffer.Location; + while(*Index.Buffer.Ptr != '\n' && Index.Buffer.Ptr - Index.Buffer.Location < Index.FileSize) + { + ++Index.Buffer.Ptr; + } + ++Index.Buffer.Ptr; + DeleteTo = Index.Buffer.Ptr - Index.Buffer.Location; + break; + } + ++Index.Buffer.Ptr; + } + + if(Found) + { + if(EntryCount == 0) + { + remove(Index.Path); + } + else + { + if(!(Index.Handle = fopen(Index.Path, "w"))) + { + free(Index.Buffer.Location); + return RC_ERROR_FILE; + } + Index.Buffer.Ptr = Index.Buffer.Location; + *(int *)Index.Buffer.Ptr = EntryCount; + fwrite(Index.Buffer.Ptr, DeleteFrom, 1, Index.Handle); + fwrite(Index.Buffer.Ptr + DeleteTo, Index.FileSize - DeleteTo, 1, Index.Handle); + fclose(Index.Handle); + } + } + + free(Index.Buffer.Location); + return Found ? RC_SUCCESS : RC_NOOP; +} + +int +IndexToBuffer(buffers *CollationBuffers) +{ + RewindBuffer(&CollationBuffers->Index); + // TODO(matt): Consider parsing the index into a linked list, or do something to save us having to iterate through the index + // file multiple times + + file_buffer Index; + Index.Buffer.ID = "Index"; + CopyString(Index.Path, "%s/%s.index", Config.CacheDir, Config.ProjectID); + ReadFileIntoBuffer(&Index, 0); + + //int EntryCount = *(int *)Index.Buffer.Ptr; + Index.Buffer.Ptr += sizeof(int); + + CopyStringToBuffer(&CollationBuffers->Index, "
\n"); + while(Index.Buffer.Ptr - Index.Buffer.Location < Index.FileSize) + { + char IndexedFile[32]; + char *Ptr = IndexedFile; + Index.Buffer.Ptr += CopyStringNoFormatT(Ptr, Index.Buffer.Ptr, ',') + 1; + + char Title[255]; + Ptr = Title; + Index.Buffer.Ptr += CopyStringNoFormatT(Ptr, Index.Buffer.Ptr, '\n') + 1; + + CopyStringToBuffer(&CollationBuffers->Index, + "
\n" + " %s\n" + "
\n", + IndexedFile, Title); + + } + CopyStringToBuffer(&CollationBuffers->Index, "
"); + + FreeBuffer(&Index.Buffer); + return RC_SUCCESS; +} + +int +GeneratePlayerPage(buffers *CollationBuffers, template *PlayerTemplate, char *BaseFilename) +{ + char OutputDir[255]; + CopyString(OutputDir, "%s/%s", Config.BaseDir, BaseFilename); + char PlayerPagePath[255]; + CopyString(PlayerPagePath, "%s/index.html", OutputDir); + + DIR *OutputDirectoryHandle; + if(!(OutputDirectoryHandle = opendir(OutputDir))) + { + if(MakeDir(OutputDir) == RC_ERROR_DIRECTORY) + { + LogError(LOG_ERROR, "Unable to create directory %s: %s", OutputDir, strerror(errno)); + fprintf(stderr, "Unable to create directory %s: %s\n", OutputDir, strerror(errno)); + return RC_ERROR_DIRECTORY; + }; + } + + closedir(OutputDirectoryHandle); + BuffersToHTML(CollationBuffers, PlayerTemplate, PlayerPagePath, PAGE_PLAYER); + return RC_SUCCESS; +} + +int +GenerateIndexPage(buffers *CollationBuffers, template *IndexTemplate) +{ + char IndexPagePath[255]; + CopyString(IndexPagePath, "%s/index.html", Config.BaseDir); + IndexToBuffer(CollationBuffers); + BuffersToHTML(CollationBuffers, IndexTemplate, IndexPagePath, PAGE_INDEX); + return RC_SUCCESS; +} + +int +DeletePlayerPageFromFilesystem(char *BaseFilename) +{ + // NOTE(matt): Once we have the notion of an output filename format, we'll need to use that here + char PlayerDirPath[255]; + CopyString(PlayerDirPath, "%s/%s", Config.BaseDir, BaseFilename); + DIR *PlayerDir; + + if((PlayerDir = opendir(PlayerDirPath))) // There is a directory for the Player, which there probably should be if not for manual intervention + { + char PlayerPagePath[255]; + CopyString(PlayerPagePath, "%s/index.html", PlayerDirPath); + FILE *PlayerPage; + if((PlayerPage = fopen(PlayerPagePath, "r"))) + { + fclose(PlayerPage); + remove(PlayerPagePath); + } + + closedir(PlayerDir); + if((remove(PlayerDirPath) == -1)) + { + LogError(LOG_NOTICE, "Mostly removed %s. Unable to remove directory %s: %s", BaseFilename, PlayerDirPath, strerror(errno)); + fprintf(stderr, "Mostly removed %s. Unable to remove directory %s: %s", BaseFilename, PlayerDirPath, strerror(errno)); + } + else + { + LogError(LOG_INFORMATIONAL, "Fully removed %s from the system", BaseFilename); + fprintf(stderr, "Fully removed %s from the system\n", BaseFilename); + } + } + return RC_SUCCESS; +} + +void +DeleteEntry(char *BaseFilename) +{ + if(DeleteFromIndex(BaseFilename) == RC_SUCCESS) + { + DeletePlayerPageFromFilesystem(BaseFilename); + } +} + +int +MonitorDirectory(buffers *CollationBuffers, template *IndexTemplate, template *PlayerTemplate, int inotifyInstance, int WatchDescriptor) +{ + +#if DEBUG_MEM + FILE *MemLog = fopen("/home/matt/cinera_mem", "a+"); + fprintf(MemLog, "\nCalled MonitorDirectory()\n"); + fclose(MemLog); +#endif + + // TODO(matt): Maybe straight up store the IndexPath in the Config to save us having to derive it near / at the usage site buffer Events; - if(ClaimBuffer(MemoryArena, &Events, "inotify Events", Kilobytes(4)) == RC_ARENA_FULL) { return RC_ARENA_FULL; }; + if(ClaimBuffer(&Events, "inotify Events", Kilobytes(4)) == RC_ARENA_FULL) { return RC_ARENA_FULL; }; struct inotify_event *Event; int BytesRead; @@ -3359,23 +3572,166 @@ MonitorDirectory(arena *MemoryArena, buffers *CollationBuffers, template *IndexT while((BytesRead = read(inotifyInstance, Events.Location, Events.Size)) != -1 && errno == EAGAIN && BytesRead > 0) { for(Events.Ptr = Events.Location; - Events.Ptr < Events.Location + BytesRead; + Events.Ptr < Events.Location + BytesRead && Events.Ptr - Events.Location < Events.Size; Events.Ptr += sizeof(struct inotify_event) + Event->len) { Event = (struct inotify_event *)Events.Ptr; - switch(RefreshProject(MemoryArena, CollationBuffers, IndexTemplateMetadata, PlayerTemplateMetadata, Config)) + + char *Ptr; + Ptr = Event->name; + Ptr += (StringLength(Event->name) - StringLength(".hmml")); + if(!(StringsDiffer(Ptr, ".hmml"))) { - case RC_ERROR_DIRECTORY: - case RC_ERROR_FATAL: - DeclaimBuffer(MemoryArena, &Events); - return RC_ERROR_FATAL; - case RC_SUCCESS: - break; + *Ptr = '\0'; + char BaseFilename[255]; + CopyString(BaseFilename, Event->name); + *Ptr = '.'; + + if(Event->mask & IN_DELETE || Event->mask & IN_MOVED_FROM) + { + // TODO(matt): Remove from Index and synchronise Table of Contents and player pages accordingly + DeleteEntry(BaseFilename); + GenerateIndexPage(CollationBuffers, IndexTemplate); + } + else + { + switch(InsertIntoIndex(CollationBuffers, BaseFilename)) + { + case RC_SUCCESS: + GeneratePlayerPage(CollationBuffers, PlayerTemplate, BaseFilename); + GenerateIndexPage(CollationBuffers, IndexTemplate); + break; + case RC_NOOP: + break; + } + } } } } - DeclaimBuffer(MemoryArena, &Events); + DeclaimBuffer(&Events); + return RC_NOOP; +} + +typedef struct +{ + bool Present; + char ID[32]; +} index_entry; + +int +DeleteDeadIndexEntries() +{ + file_buffer Index; + Index.Buffer.ID = "Index"; + CopyString(Index.Path, "%s/%s.index", Config.CacheDir, Config.ProjectID); + if(ReadFileIntoBuffer(&Index, 0) == RC_ERROR_FILE) + { + return RC_ERROR_FILE; + } + + // TODO(matt): Stuff entries into an array, through which we can iterate, marking off input files that are still present + int EntryCount = *(int *)Index.Buffer.Ptr; + Index.Buffer.Ptr += sizeof(int); + + index_entry Entries[EntryCount]; + + int i = 0; + while(Index.Buffer.Ptr - Index.Buffer.Location < Index.FileSize) + { + Index.Buffer.Ptr += CopyStringNoFormatT(Entries[i].ID, Index.Buffer.Ptr, ','); + Entries[i].Present = FALSE; + while(*Index.Buffer.Ptr != '\n') + { + ++Index.Buffer.Ptr; + } + ++Index.Buffer.Ptr, ++i; + } + + DIR *ProjectDirHandle; + if(!(ProjectDirHandle = opendir(Config.ProjectDir))) + { + LogError(LOG_ERROR, "Unable to scan project directory %s: %s", Config.ProjectDir, strerror(errno)); + fprintf(stderr, "Unable to scan project directory %s: %s\n", Config.ProjectDir, strerror(errno)); + return RC_ERROR_DIRECTORY; + } + + struct dirent *ProjectFiles; + + while((ProjectFiles = readdir(ProjectDirHandle))) + { + char *Ptr; + Ptr = ProjectFiles->d_name; + Ptr += (StringLength(ProjectFiles->d_name) - StringLength(".hmml")); + if(!(StringsDiffer(Ptr, ".hmml"))) + { + *Ptr = '\0'; + for(int i = 0; i < EntryCount; ++i) + { + if(!StringsDiffer(Entries[i].ID, ProjectFiles->d_name)) + { + Entries[i].Present = TRUE; + break; + } + } + } + } + closedir(ProjectDirHandle); + + bool Deleted = FALSE; + for(int i = 0; i < EntryCount; ++i) + { + if(Entries[i].Present == FALSE) + { + Deleted = TRUE; + DeleteEntry(Entries[i].ID); + } + } + + FreeBuffer(&Index.Buffer); + return Deleted ? RC_SUCCESS : RC_NOOP; +} + +int +SyncIndexWithInput(buffers *CollationBuffers, template *IndexTemplate, template *PlayerTemplate) +{ + DeleteDeadIndexEntries(); + + DIR *ProjectDirHandle; + if(!(ProjectDirHandle = opendir(Config.ProjectDir))) + { + LogError(LOG_ERROR, "Unable to scan project directory %s: %s", Config.ProjectDir, strerror(errno)); + fprintf(stderr, "Unable to scan project directory %s: %s\n", Config.ProjectDir, strerror(errno)); + return RC_ERROR_DIRECTORY; + } + + struct dirent *ProjectFiles; + bool Inserted = FALSE; + + while((ProjectFiles = readdir(ProjectDirHandle))) + { + char *Ptr = ProjectFiles->d_name; + Ptr += (StringLength(ProjectFiles->d_name) - StringLength(".hmml")); + if(!(StringsDiffer(Ptr, ".hmml"))) + { + *Ptr = '\0'; + switch(InsertIntoIndex(CollationBuffers, ProjectFiles->d_name)) + { + case RC_SUCCESS: + GeneratePlayerPage(CollationBuffers, PlayerTemplate, ProjectFiles->d_name); + Inserted = TRUE; + break; + case RC_NOOP: + break; + } + } + } + closedir(ProjectDirHandle); + + if(Inserted) + { + GenerateIndexPage(CollationBuffers, IndexTemplate); + } return RC_SUCCESS; } @@ -3389,15 +3745,18 @@ main(int ArgC, char **Args) .Edition = EDITION_SINGLE, .ImagesDir = ".", .JSDir = ".", - .LogLevel = LOG_DEBUG, + .LogLevel = LOG_EMERGENCY, .DefaultMedium = "programming", .Mode = getenv("CINERA_MODE") ? MODE_INTEGRATE : MODE_BARE, .OutLocation = "out.html", .OutIntegratedLocation = "out_integrated.html", .ForceIntegration = FALSE, .ProjectDir = ".", + .ProjectID = "", + .RootDir = ".", .TemplatePlayerLocation = "template_player.html", - .TemplateIndexLocation = "template_index.html" + .TemplateIndexLocation = "template_index.html", + .UpdateInterval = 4 }; if(getenv("XDG_CACHE_HOME")) @@ -3409,16 +3768,16 @@ main(int ArgC, char **Args) CopyString(DefaultConfig.CacheDir, "%s/.cache/cinera", getenv("HOME")); } - config Config = DefaultConfig; + Config = DefaultConfig; if(ArgC < 2) { - PrintUsage(Args[0], DefaultConfig); + PrintUsage(Args[0], &DefaultConfig); return RC_RIP; } char CommandLineArg; - while((CommandLineArg = getopt(ArgC, Args, "b:c:fi:j:l:m:o:p:q:t:x:h")) != -1) + while((CommandLineArg = getopt(ArgC, Args, "b:c:fhi:I:j:l:m:o:p:r:t:u:x:")) != -1) { switch(CommandLineArg) { @@ -3434,6 +3793,9 @@ main(int ArgC, char **Args) case 'i': Config.ImagesDir = optarg; break; + case 'I': + Config.ProjectID = optarg; + break; case 'j': Config.JSDir = optarg; break; @@ -3451,28 +3813,42 @@ main(int ArgC, char **Args) case 'p': Config.ProjectDir = optarg; break; + case 'r': + Config.RootDir = optarg; + break; case 't': Config.TemplatePlayerLocation = optarg; Config.Mode = MODE_INTEGRATE; break; + case 'u': + Config.UpdateInterval = StringToInt(optarg); + break; case 'x': Config.TemplateIndexLocation = optarg; Config.Mode = MODE_INTEGRATE; break; - //case 'c': - // Override config path, once we even have a default! case 'h': default: - PrintUsage(Args[0], DefaultConfig); + PrintUsage(Args[0], &DefaultConfig); return 1; } } + // TODO(matt): Sanitise this entry into Project Mode, probably once we switch to the config system if(StringsDiffer(Config.BaseDir, ".") || StringsDiffer(Config.ProjectDir, ".")) { if(StringsDiffer(Config.BaseDir, ".") && StringsDiffer(Config.ProjectDir, ".")) { - Config.Edition = EDITION_PROJECT; + if(StringsDiffer(Config.ProjectID, "")) + { + Config.Edition = EDITION_PROJECT; + } + else + { + fprintf(stderr, "%s: Project ID must be set using the -I flag in order for us to enter Project Mode\n", Args[0]); + return 1; + + } } else { @@ -3523,45 +3899,54 @@ main(int ArgC, char **Args) // In our case here, we just want to straight up validate a template if Config.Mode == MODE_INTEGRATE // And, in that same state, we gotta keep a Template buffer around - // NOTE(matt): Init MemoryArena - arena MemoryArena; + // NOTE(matt): Init MemoryArena (it is global) MemoryArena.Size = Megabytes(4); if(!(MemoryArena.Location = calloc(MemoryArena.Size, 1))) { - LogError(Config, LOG_EMERGENCY, "%s: %s", Args[0], strerror(errno)); + LogError(LOG_EMERGENCY, "%s: %s", Args[0], strerror(errno)); return RC_RIP; } MemoryArena.Ptr = MemoryArena.Location; - buffer Errors; - if(ClaimBuffer(&MemoryArena, &Errors, "Errors", Kilobytes(1)) == RC_ARENA_FULL) { goto RIP; }; +#if DEBUG_MEM + FILE *MemLog = fopen("/home/matt/cinera_mem", "a+"); + fprintf(MemLog, " Allocated MemoryArena (%d)\n", MemoryArena.Size); + fclose(MemLog); + printf(" Allocated MemoryArena (%d)\n", MemoryArena.Size); +#endif - // NOTE(matt): Setup buffers and ptrs - //char *InPtr; +#if DEBUG + printf("Allocated MemoryArena: %d\n\n", MemoryArena.Size); +#endif + + buffer Errors; + if(ClaimBuffer(&Errors, "Errors", Kilobytes(1)) == RC_ARENA_FULL) { goto RIP; }; // NOTE(matt): Tree structure of buffer dependencies // IncludesPlayer // Menus // Player // Script + // + // IncludesIndex + // Index buffers CollationBuffers; - if(ClaimBuffer(&MemoryArena, &CollationBuffers.IncludesPlayer, "IncludesPlayer", Kilobytes(1)) == RC_ARENA_FULL) { goto RIP; }; - if(ClaimBuffer(&MemoryArena, &CollationBuffers.Menus, "Menus", Kilobytes(24)) == RC_ARENA_FULL) { goto RIP; }; - if(ClaimBuffer(&MemoryArena, &CollationBuffers.Player, "Player", Kilobytes(256)) == RC_ARENA_FULL) { goto RIP; }; - if(ClaimBuffer(&MemoryArena, &CollationBuffers.Script, "Script", Kilobytes(8)) == RC_ARENA_FULL) { goto RIP; }; + if(ClaimBuffer(&CollationBuffers.IncludesPlayer, "IncludesPlayer", Kilobytes(1)) == RC_ARENA_FULL) { goto RIP; }; + if(ClaimBuffer(&CollationBuffers.Menus, "Menus", Kilobytes(24)) == RC_ARENA_FULL) { goto RIP; }; + if(ClaimBuffer(&CollationBuffers.Player, "Player", Kilobytes(256)) == RC_ARENA_FULL) { goto RIP; }; + if(ClaimBuffer(&CollationBuffers.Script, "Script", Kilobytes(8)) == RC_ARENA_FULL) { goto RIP; }; - if(ClaimBuffer(&MemoryArena, &CollationBuffers.IncludesIndex, "IncludesIndex", Kilobytes(1)) == RC_ARENA_FULL) { goto RIP; }; - if(ClaimBuffer(&MemoryArena, &CollationBuffers.Index, "Index", Kilobytes(8)) == RC_ARENA_FULL) { goto RIP; }; + if(ClaimBuffer(&CollationBuffers.IncludesIndex, "IncludesIndex", Kilobytes(1)) == RC_ARENA_FULL) { goto RIP; }; + if(ClaimBuffer(&CollationBuffers.Index, "Index", Kilobytes(8)) == RC_ARENA_FULL) { goto RIP; }; *CollationBuffers.Title = '\0'; - template *PlayerTemplateMetadata; - template *IndexTemplateMetadata; + template *PlayerTemplate; + template *IndexTemplate; if(Config.Mode == MODE_INTEGRATE) { - if(ClaimTemplate(&MemoryArena, &PlayerTemplateMetadata, Config.TemplatePlayerLocation) == RC_ARENA_FULL) { goto RIP; }; - switch(ValidateTemplate(&Errors, PlayerTemplateMetadata, Config, PAGE_PLAYER)) + switch(ValidateTemplate(&Errors, &PlayerTemplate, PAGE_PLAYER)) { case RC_INVALID_TEMPLATE: // Invalid template case RC_ERROR_FILE: // Could not load template @@ -3573,8 +3958,7 @@ main(int ArgC, char **Args) if(Config.Edition == EDITION_PROJECT) { - if(ClaimTemplate(&MemoryArena, &IndexTemplateMetadata, Config.TemplateIndexLocation) == RC_ARENA_FULL) { goto RIP; }; - switch(ValidateTemplate(&Errors, IndexTemplateMetadata, Config, PAGE_INDEX)) + switch(ValidateTemplate(&Errors, &IndexTemplate, PAGE_INDEX)) { case RC_INVALID_TEMPLATE: // Invalid template case RC_ERROR_MEMORY: // Could not allocate memory for template @@ -3595,22 +3979,31 @@ main(int ArgC, char **Args) if(Config.Edition == EDITION_PROJECT) { - switch(RefreshProject(&MemoryArena, &CollationBuffers, IndexTemplateMetadata, PlayerTemplateMetadata, Config)) - { - case RC_ERROR_DIRECTORY: - case RC_ERROR_FATAL: - goto RIP; - case RC_SUCCESS: - break; - } + +#if DEBUG_MEM + FILE *MemLog = fopen("/home/matt/cinera_mem", "w+"); + fprintf(MemLog, "Entered Project Edition\n"); + fclose(MemLog); +#endif + + printf("[Cinera %s]\n" + "\n" + "Project ID: %s\n" + "Project Directory: %s\n" + "\n" + "Synchronising with annotation files in Project Directory\n", CINERA_VERSION, Config.ProjectID, Config.ProjectDir); + + SyncIndexWithInput(&CollationBuffers, IndexTemplate, PlayerTemplate); + + printf("\nMonitoring Project Directory for new, edited or deleted .hmml files\n"); int inotifyInstance = inotify_init1(IN_NONBLOCK); - int WatchDescriptor = inotify_add_watch(inotifyInstance, Config.ProjectDir, IN_CLOSE_WRITE | IN_MOVED_TO); + // NOTE(matt): Do we want to also watch IN_DELETE_SELF events? + int WatchDescriptor = inotify_add_watch(inotifyInstance, Config.ProjectDir, IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO); - while(MonitorDirectory(&MemoryArena, &CollationBuffers, IndexTemplateMetadata, PlayerTemplateMetadata, Config, inotifyInstance, WatchDescriptor) == RC_SUCCESS) + while(MonitorDirectory(&CollationBuffers, IndexTemplate, PlayerTemplate, inotifyInstance, WatchDescriptor) != RC_ERROR_FATAL) { - // TODO(matt): Make this update frequency configurable - sleep(1); + sleep(Config.UpdateInterval); } } else @@ -3618,14 +4011,14 @@ main(int ArgC, char **Args) if(optind == ArgC) { fprintf(stderr, "%s: requires at least one input .hmml file\n", Args[0]); - PrintUsage(Args[0], DefaultConfig); + PrintUsage(Args[0], &DefaultConfig); goto RIP; } NextFile: for(int FileIndex = optind; FileIndex < ArgC; ++FileIndex) { - switch(HMMLToBuffers(&MemoryArena, &CollationBuffers, Config, Args[FileIndex])) + switch(HMMLToBuffers(&CollationBuffers, Args[FileIndex])) { // TODO(matt): Actually sort out the fatality of these cases, once we are always-on case RC_ERROR_FILE: @@ -3640,11 +4033,11 @@ NextFile: case RC_SUCCESS: break; }; - switch(BuffersToHTML(&MemoryArena, CollationBuffers, PlayerTemplateMetadata, Config, 0, PAGE_PLAYER)) + switch(BuffersToHTML(&CollationBuffers, PlayerTemplate, 0, PAGE_PLAYER)) { // TODO(matt): Actually sort out the fatality of these cases, once we are always-on case RC_INVALID_TEMPLATE: - LogError(Config, LOG_ERROR, "Invalid player template: %s", PlayerTemplateMetadata->Filename); + LogError(LOG_ERROR, "Invalid player template: %s", PlayerTemplate->Metadata.Filename); case RC_ERROR_MEMORY: case RC_ERROR_FILE: case RC_ARENA_FULL: @@ -3657,20 +4050,28 @@ NextFile: if(Config.Mode == MODE_INTEGRATE) { - DeclaimTemplate(&MemoryArena, &PlayerTemplateMetadata); + DeclaimTemplate(PlayerTemplate); if(Config.Edition == EDITION_PROJECT) { - DeclaimTemplate(&MemoryArena, &IndexTemplateMetadata); + DeclaimTemplate(IndexTemplate); } } - DeclaimBuffer(&MemoryArena, &CollationBuffers.Index); - DeclaimBuffer(&MemoryArena, &CollationBuffers.IncludesIndex); - DeclaimBuffer(&MemoryArena, &CollationBuffers.Script); - DeclaimBuffer(&MemoryArena, &CollationBuffers.Player); - DeclaimBuffer(&MemoryArena, &CollationBuffers.Menus); - DeclaimBuffer(&MemoryArena, &CollationBuffers.IncludesPlayer); - DeclaimBuffer(&MemoryArena, &Errors); + DeclaimBuffer(&CollationBuffers.Index); + DeclaimBuffer(&CollationBuffers.IncludesIndex); + DeclaimBuffer(&CollationBuffers.Script); + DeclaimBuffer(&CollationBuffers.Player); + DeclaimBuffer(&CollationBuffers.Menus); + DeclaimBuffer(&CollationBuffers.IncludesPlayer); + DeclaimBuffer(&Errors); RIP: free(MemoryArena.Location); + +#if DEBUG_MEM + MemLog = fopen("/home/matt/cinera_mem", "a+"); + fprintf(MemLog, " Freed MemoryArena\n"); + fclose(MemLog); + printf(" Freed MemoryArena\n"); +#endif + }