From 33cbb5b05f809b492e3343ac821fc3ba1c83698c Mon Sep 17 00:00:00 2001 From: Matt Mascarenhas Date: Sun, 1 Apr 2018 21:58:53 +0100 Subject: [PATCH] cinera.c: Support private videos It merely checks the video's privacy status and, if not public, records its corresponding HMML base filename privately in the .metadata without generating a player page or a search / table of contents entry for it. Every four hours it will loop over the .metadata to recheck the privacy status of any privately recorded entries, and process newly public ones. Fix template validation to check that the script comes after both the player and menus (previously it only specified the player). Fix relocation code to try and remove only the child directories of the BaseDir, and no shallower (previously it would try and "recursively" remove directories all the way back to /, obviously not actually getting there because it would encounter a non-empty directory along the way). Add "Modes" to the startup printout. Flags: -g Ignore privacy status --- cinera/cinera.c | 1572 +++++++++++++++++++++++++++-------------------- 1 file changed, 899 insertions(+), 673 deletions(-) diff --git a/cinera/cinera.c b/cinera/cinera.c index de2bfd1..6f79ba1 100644 --- a/cinera/cinera.c +++ b/cinera/cinera.c @@ -14,21 +14,12 @@ typedef struct version CINERA_APP_VERSION = { .Major = 0, .Minor = 5, - .Patch = 39 + .Patch = 40 }; // TODO(matt): Copy in the DB 3 stuff from cinera_working.c #define CINERA_DB_VERSION 3 -typedef unsigned int bool; -#define TRUE 1 -#define FALSE 0 - -#define DEBUG 0 -#define DEBUG_MEM 0 -#define BINARY_SEARCH 1 -bool PROFILING = 0; - #include // NOTE(matt): varargs #include // NOTE(matt): printf, sprintf, vsprintf, fprintf, perror #include // NOTE(matt): calloc, malloc, free @@ -45,6 +36,14 @@ bool PROFILING = 0; #include // NOTE(matt): inotify #include // NOTE(matt): sleep() +typedef unsigned int bool; +#define TRUE 1 +#define FALSE 0 + +#define DEBUG 0 +#define DEBUG_MEM 0 + +bool PROFILING = 0; clock_t TIMING_START; #define START_TIMING_BLOCK(...) if(PROFILING) { printf(__VA_ARGS__); TIMING_START = clock(); } #define END_TIMING_BLOCK() if(PROFILING) { printf("\e[1;34m%ld\e[0m\n", clock() - TIMING_START);} @@ -86,9 +85,11 @@ enum enum { - MODE_ONESHOT = 1 << 0, - MODE_EXAMINE = 1 << 1, - MODE_NOCACHE = 1 << 2, + MODE_FORCEINTEGRATION = 1 << 0, + MODE_ONESHOT = 1 << 1, + MODE_EXAMINE = 1 << 2, + MODE_NOCACHE = 1 << 3, + MODE_NOPRIVACY = 1 << 4, } modes; enum @@ -107,6 +108,7 @@ enum RC_UNFOUND, RC_INVALID_REFERENCE, RC_INVALID_TEMPLATE, + RC_PRIVATE_VIDEO, RC_NOOP, RC_RIP, RC_SUCCESS @@ -128,7 +130,6 @@ typedef struct int LogLevel; int Mode; int UpdateInterval; - bool ForceIntegration; // Advisedly universal, although could be per-project char *RootDir; // Absolute @@ -166,6 +167,7 @@ typedef struct // NOTE(matt): Globals config Config = {}; arena MemoryArena; +time_t LastPrivacyCheck; time_t LastQuoteFetch; // @@ -2120,6 +2122,10 @@ PrintUsage(char *BinaryLocation, config *DefaultConfig) " Display (examine) index file and exit\n" " -f\n" " Force integration with an incomplete template\n" + " -g\n" + " Ignore video privacy status\n" + " NOTE: For use with projects whose videos are known to all be public,\n" + " to save us having to check their privacy status\n" " -w\n" " Force quote cache rebuild \e[1;30m(memory aid: \"wget\")\e[0m\n" "\n" @@ -2257,7 +2263,7 @@ NextTagSearch: FoundIndex = TRUE; goto RecordTag; case TAG_INCLUDES: - if(!Config.ForceIntegration && FoundIncludes == TRUE) + if(!(Config.Mode & MODE_FORCEINTEGRATION) && FoundIncludes == TRUE) { CopyStringToBuffer(&Errors, "Template contains more than one tag\n", ThisTagName); HaveErrors = TRUE; @@ -2265,7 +2271,7 @@ NextTagSearch: FoundIncludes = TRUE; goto RecordTag; case TAG_MENUS: - if(!Config.ForceIntegration && FoundMenus == TRUE) + if(!(Config.Mode & MODE_FORCEINTEGRATION) && FoundMenus == TRUE) { CopyStringToBuffer(&Errors, "Template contains more than one tag\n", Tags[i].Tag); HaveErrors = TRUE; @@ -2273,7 +2279,7 @@ NextTagSearch: FoundMenus = TRUE; goto RecordTag; case TAG_PLAYER: - if(!Config.ForceIntegration && FoundPlayer == TRUE) + if(!(Config.Mode & MODE_FORCEINTEGRATION) && FoundPlayer == TRUE) { CopyStringToBuffer(&Errors, "Template contains more than one tag\n", Tags[i].Tag); HaveErrors = TRUE; @@ -2281,12 +2287,12 @@ NextTagSearch: FoundPlayer = TRUE; goto RecordTag; case TAG_SCRIPT: - if(!Config.ForceIntegration && FoundPlayer == FALSE) + if(!(Config.Mode & MODE_FORCEINTEGRATION) && (FoundMenus == FALSE || FoundPlayer == FALSE)) { - CopyStringToBuffer(&Errors, " must come after \n", Tags[i].Tag); + CopyStringToBuffer(&Errors, " must come after and \n", Tags[i].Tag); HaveErrors = TRUE; } - if(!Config.ForceIntegration && FoundScript == TRUE) + if(!(Config.Mode & MODE_FORCEINTEGRATION) && FoundScript == TRUE) { CopyStringToBuffer(&Errors, "Template contains more than one tag\n", Tags[i].Tag); HaveErrors = TRUE; @@ -2323,7 +2329,7 @@ RecordTag: (*Template)->Metadata.Validity |= PAGE_PLAYER; } - if(!Config.ForceIntegration) + if(!(Config.Mode & MODE_FORCEINTEGRATION)) { if(TemplateType == TEMPLATE_INDEX && !((*Template)->Metadata.Validity & PAGE_INDEX)) { @@ -2434,6 +2440,7 @@ ReadFileIntoBuffer(file_buffer *File, int BufferPadding) fread(File->Buffer.Location, File->FileSize, 1, File->Handle); File->Buffer.Location[File->FileSize] = '\0'; fclose(File->Handle); + File->Buffer.ID = File->Path; return RC_SUCCESS; } @@ -2542,6 +2549,87 @@ ExamineIndex(index *Index) return RC_SUCCESS; } +enum +{ + C_SEEK_FORWARDS, + C_SEEK_BACKWARDS +} seek_directions; + +enum +{ + C_SEEK_START, // First character of string + C_SEEK_BEFORE, // Character before first character + C_SEEK_END, // Last character of string + C_SEEK_AFTER // Character after last character +} seek_positions; + +int +SeekBufferForString(buffer *Buffer, char *String, + int Direction, /* seek_directions */ + int Position /* seek_positions */) +{ + // TODO(matt): Optimise? Some means of analysing the String to increment + // the pointer in bigger strides + + // Perhaps count up runs of consecutive chars and seek for the char with + // the longest run, in strides of that run-length + + char *InitialLocation = Buffer->Ptr; + if(Direction == C_SEEK_FORWARDS) + { + while(Buffer->Ptr - Buffer->Location < Buffer->Size - StringLength(String) + && StringsDifferT(String, Buffer->Ptr, 0)) + { + ++Buffer->Ptr; + } + } + else + { + while(Buffer->Ptr > Buffer->Location + && StringsDifferT(String, Buffer->Ptr, 0)) + { + --Buffer->Ptr; + } + } + + if(StringsDifferT(String, Buffer->Ptr, 0)) + { + Buffer->Ptr = InitialLocation; + return RC_UNFOUND; + } + + switch(Position) + { + case C_SEEK_START: + break; + case C_SEEK_BEFORE: + if(Buffer->Ptr > Buffer->Location) + { + --Buffer->Ptr; + break; + } + else + { + return RC_ERROR_SEEK; // Ptr remains at string start + } + case C_SEEK_END: + Buffer->Ptr += StringLength(String) - 1; + break; + case C_SEEK_AFTER: + if(Buffer->Size >= Buffer->Ptr - Buffer->Location + StringLength(String)) + { + Buffer->Ptr += StringLength(String); + break; + } + else + { + return RC_ERROR_SEEK; // Ptr remains at string start + // NOTE(matt): Should it, however, be left at the end of the string? + } + } + return RC_SUCCESS; +} + #define HMMLCleanup() \ DeclaimBuffer(&FilterState); \ DeclaimBuffer(&CreditsMenu); \ @@ -2552,8 +2640,80 @@ ExamineIndex(index *Index) DeclaimBuffer(&QuoteMenu); \ hmml_free(&HMML); +void +ClearTerminalRow(int Length) +{ + fprintf(stderr, "\r"); + for(int i = 0; i < Length; ++i) + { + fprintf(stderr, " "); + } + fprintf(stderr, "\r"); +} + +bool +VideoIsPrivate(char *VideoID) +{ + // NOTE(matt): Currently only supports YouTube + char Message[128]; + CopyString(Message, "\e[0;35mChecking\e[0m privacy status of: https://youtube.com/watch?v=%s", VideoID); + fprintf(stderr, Message); + int MessageLength = StringLength(Message); + buffer VideoAPIResponse; + ClaimBuffer(&VideoAPIResponse, "VideoAPIResponse", Kilobytes(1)); + + CURL *curl = curl_easy_init(); + if(curl) { + LastPrivacyCheck = time(0); +#define APIKey "AIzaSyAdV2U8ivPk8PHMaPMId0gynksw_gdzr9k" + char URL[1024] = {0}; + CopyString(URL, "https://www.googleapis.com/youtube/v3/videos?key=%s&part=status&id=%s", APIKey, VideoID); + CURLcode CurlReturnCode; + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &VideoAPIResponse.Ptr); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlIntoBuffer); + curl_easy_setopt(curl, CURLOPT_URL, URL); + if((CurlReturnCode = curl_easy_perform(curl))) + { + fprintf(stderr, "%s\n", curl_easy_strerror(CurlReturnCode)); + } + curl_easy_cleanup(curl); + + VideoAPIResponse.Ptr = VideoAPIResponse.Location; + SeekBufferForString(&VideoAPIResponse, "{", C_SEEK_FORWARDS, C_SEEK_AFTER); + SeekBufferForString(&VideoAPIResponse, "\"totalResults\": ", C_SEEK_FORWARDS, C_SEEK_AFTER); + if(*VideoAPIResponse.Ptr == '0') + { + DeclaimBuffer(&VideoAPIResponse); + // printf("Private video: https://youtube.com/watch?v=%s\n", VideoID); + ClearTerminalRow(MessageLength); + return TRUE; + } + SeekBufferForString(&VideoAPIResponse, "{", C_SEEK_FORWARDS, C_SEEK_AFTER); + SeekBufferForString(&VideoAPIResponse, "\"privacyStatus\": \"", C_SEEK_FORWARDS, C_SEEK_AFTER); + char Status[16]; + CopyStringNoFormatT(Status, VideoAPIResponse.Ptr, '\"'); + if(!StringsDiffer(Status, "public")) + { + DeclaimBuffer(&VideoAPIResponse); + ClearTerminalRow(MessageLength); + return FALSE; + } + } + DeclaimBuffer(&VideoAPIResponse); + // printf("Unlisted video: https://youtube.com/watch?v=%s\n", VideoID); + ClearTerminalRow(MessageLength); + return TRUE; +} + +typedef struct +{ + index_metadata Prev, This, Next; + bool PrevIsFirst, NextIsFinal; + short int PrevIndex, ThisIndex, NextIndex; +} neighbourhood; + int -HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filename, link_insertion_offsets *LinkOffsets, neighbours *Neighbours) +HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filename, neighbourhood *N) { RewindBuffer(&CollationBuffers->IncludesPlayer); RewindBuffer(&CollationBuffers->Menus); @@ -2750,12 +2910,12 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen return RC_ERROR_HMML; } - if(LinkOffsets) + if(N) { - LinkOffsets->PrevStart = 0; - LinkOffsets->NextStart = 0; - LinkOffsets->PrevEnd = 0; - LinkOffsets->NextEnd = 0; + N->This.LinkOffsets.PrevStart = 0; + N->This.LinkOffsets.PrevEnd = 0; + N->This.LinkOffsets.NextStart = 0; + N->This.LinkOffsets.NextEnd = 0; } #if DEBUG @@ -2828,21 +2988,21 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen "
\n" "
\n", HMML.metadata.id, StringsDiffer(Config.Theme, "") ? Config.Theme : HMML.metadata.project); - if(LinkOffsets) + if(N) { - LinkOffsets->PrevStart = (CollationBuffers->Player.Ptr - CollationBuffers->Player.Location); - if(Neighbours->Prev.BaseFilename || Neighbours->Next.BaseFilename) + N->This.LinkOffsets.PrevStart = (CollationBuffers->Player.Ptr - CollationBuffers->Player.Location); + if(N->Prev.Size || N->Next.Size) { - if(Neighbours->Prev.BaseFilename) + if(N->Prev.Size) { // TODO(matt): Once we have a more rigorous notion of "Day Numbers", perhaps also use them here buffer PreviousPlayerURL; ClaimBuffer(&PreviousPlayerURL, "PreviousPlayerURL", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH); - ConstructPlayerURL(&PreviousPlayerURL, Neighbours->Prev.BaseFilename); + ConstructPlayerURL(&PreviousPlayerURL, N->Prev.BaseFilename); CopyStringToBuffer(&CollationBuffers->Player, "
Previous: '%s'
\n", PreviousPlayerURL.Location, - Neighbours->Prev.Title); + N->Prev.Title); DeclaimBuffer(&PreviousPlayerURL); } @@ -2852,7 +3012,7 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen "
Welcome to %s
\n", CollationBuffers->ProjectName); } } - LinkOffsets->PrevEnd = (CollationBuffers->Player.Ptr - CollationBuffers->Player.Location - LinkOffsets->PrevStart); + N->This.LinkOffsets.PrevEnd = (CollationBuffers->Player.Ptr - CollationBuffers->Player.Location - N->This.LinkOffsets.PrevStart); } CopyStringToBuffer(&CollationBuffers->Player, @@ -2868,7 +3028,21 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen return RC_ERROR_HMML; } - if(Config.Edition != EDITION_SINGLE) + bool PrivateVideo = FALSE; + if(Config.Edition != EDITION_SINGLE && N->This.Size == 0 && !(Config.Mode & MODE_NOPRIVACY)) + { + if(VideoIsPrivate(HMML.metadata.id)) + { + // TODO(matt): Actually generate these guys, just putting them in a secret location + N->This.LinkOffsets.PrevStart = 0; + N->This.LinkOffsets.PrevEnd = 0; + PrivateVideo = TRUE; + HMMLCleanup(); + return RC_PRIVATE_VIDEO; + } + } + + if(Config.Edition != EDITION_SINGLE && !PrivateVideo) { RewindBuffer(&CollationBuffers->Search); CopyStringToBuffer(&CollationBuffers->Search, "name: \"%s\"\n" @@ -2902,7 +3076,7 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen } PreviousTimecode = TimecodeToSeconds(Anno->time); - if(Config.Edition != EDITION_SINGLE) + if(Config.Edition != EDITION_SINGLE && !PrivateVideo) { CopyStringToBuffer(&CollationBuffers->Search, "\"%d\": \"", TimecodeToSeconds(Anno->time)); CopyStringToBufferJSONSafe(&CollationBuffers->Search, Anno->text); @@ -3418,9 +3592,10 @@ AppendedIdentifier: DeclaimBuffer(&Annotation); } - if(Config.Edition != EDITION_SINGLE) + if(Config.Edition != EDITION_SINGLE && !PrivateVideo) { CopyStringToBuffer(&CollationBuffers->Search, "---\n"); + N->This.Size = CollationBuffers->Search.Ptr - CollationBuffers->Search.Location; } #if DEBUG @@ -3932,21 +4107,21 @@ AppendedIdentifier: CopyStringToBuffer(&CollationBuffers->Player, "
\n"); - if(LinkOffsets) + if(N) { - LinkOffsets->NextStart = (CollationBuffers->Player.Ptr - CollationBuffers->Player.Location - (LinkOffsets->PrevStart + LinkOffsets->PrevEnd)); - if(Neighbours->Prev.BaseFilename || Neighbours->Next.BaseFilename) + N->This.LinkOffsets.NextStart = (CollationBuffers->Player.Ptr - CollationBuffers->Player.Location - (N->This.LinkOffsets.PrevStart + N->This.LinkOffsets.PrevEnd)); + if(N->Prev.Size || N->Next.Size) { - if(Neighbours->Next.BaseFilename) + if(N->Next.Size > 0) { // TODO(matt): Once we have a more rigorous notion of "Day Numbers", perhaps also use them here buffer NextPlayerURL; ClaimBuffer(&NextPlayerURL, "NextPlayerURL", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH); - ConstructPlayerURL(&NextPlayerURL, Neighbours->Next.BaseFilename); + ConstructPlayerURL(&NextPlayerURL, N->Next.BaseFilename); CopyStringToBuffer(&CollationBuffers->Player, "
Next: '%s'
\n", NextPlayerURL.Location, - Neighbours->Next.Title); + N->Next.Title); DeclaimBuffer(&NextPlayerURL); } @@ -3956,7 +4131,7 @@ AppendedIdentifier: "
You have arrived at the (current) end of %s
\n", CollationBuffers->ProjectName); } } - LinkOffsets->NextEnd = (CollationBuffers->Player.Ptr - CollationBuffers->Player.Location - (LinkOffsets->PrevStart + LinkOffsets->PrevEnd + LinkOffsets->NextStart)); + N->This.LinkOffsets.NextEnd = (CollationBuffers->Player.Ptr - CollationBuffers->Player.Location - (N->This.LinkOffsets.PrevStart + N->This.LinkOffsets.PrevEnd + N->This.LinkOffsets.NextStart)); } CopyStringToBuffer(&CollationBuffers->Player, @@ -4079,7 +4254,7 @@ AppendedIdentifier: } int -BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, int PageType, int *PlayerOffset) +BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, int PageType, unsigned int *PlayerOffset) { #if DEBUG printf("\n\n --- Buffer Collation ---\n" @@ -4095,7 +4270,7 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i if(Template->Metadata.Filename && StringsDiffer(Template->Metadata.Filename, "")) { if((Template->Metadata.Filename && StringsDiffer(Template->Metadata.Filename, "")) - && ((Template->Metadata.Validity & PageType) || Config.ForceIntegration)) + && ((Template->Metadata.Validity & PageType) || Config.Mode & MODE_FORCEINTEGRATION)) { buffer Output; Output.Size = Template->Buffer.Size + (Kilobytes(512)); @@ -4182,7 +4357,7 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i CopyBuffer(&Output, &CollationBuffers->Menus); break; case TAG_PLAYER: - if(NeedPlayerOffset) { *PlayerOffset = (Output.Ptr - Output.Location); NeedPlayerOffset = !NeedPlayerOffset; } + if(NeedPlayerOffset) { *PlayerOffset += (Output.Ptr - Output.Location); NeedPlayerOffset = !NeedPlayerOffset; } CopyBuffer(&Output, &CollationBuffers->Player); break; case TAG_SCRIPT: @@ -4272,7 +4447,7 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i CopyStringToBuffer(&Master, "\n" " "); - if(PlayerOffset) { *PlayerOffset = Master.Ptr - Master.Location; } + if(PlayerOffset) { *PlayerOffset += Master.Ptr - Master.Location; } CopyBuffer(&Master, &CollationBuffers->Player); CopyStringToBuffer(&Master, "\n" @@ -4305,93 +4480,12 @@ BuffersToHTML(buffers *CollationBuffers, template *Template, char *OutputPath, i } } -enum -{ - C_SEEK_FORWARDS, - C_SEEK_BACKWARDS -} seek_directions; - -enum -{ - C_SEEK_START, // First character of string - C_SEEK_BEFORE, // Character before first character - C_SEEK_END, // Last character of string - C_SEEK_AFTER // Character after last character -} seek_positions; - int -SeekBufferForString(buffer *Buffer, char *String, - int Direction, /* seek_directions */ - int Position /* seek_positions */) -{ - // TODO(matt): Optimise? Some means of analysing the String to increment - // the pointer in bigger strides - - // Perhaps count up runs of consecutive chars and seek for the char with - // the longest run, in strides of that run-length - - char *InitialLocation = Buffer->Ptr; - if(Direction == C_SEEK_FORWARDS) - { - while(Buffer->Ptr - Buffer->Location < Buffer->Size - StringLength(String) - && StringsDifferT(String, Buffer->Ptr, 0)) - { - ++Buffer->Ptr; - } - } - else - { - while(Buffer->Ptr > Buffer->Location - && StringsDifferT(String, Buffer->Ptr, 0)) - { - --Buffer->Ptr; - } - } - - if(StringsDifferT(String, Buffer->Ptr, 0)) - { - Buffer->Ptr = InitialLocation; - return RC_UNFOUND; - } - - switch(Position) - { - case C_SEEK_START: - break; - case C_SEEK_BEFORE: - if(Buffer->Ptr > Buffer->Location) - { - --Buffer->Ptr; - break; - } - else - { - return RC_ERROR_SEEK; // Ptr remains at string start - } - case C_SEEK_END: - Buffer->Ptr += StringLength(String) - 1; - break; - case C_SEEK_AFTER: - if(Buffer->Size >= Buffer->Ptr - Buffer->Location + StringLength(String)) - { - Buffer->Ptr += StringLength(String); - break; - } - else - { - return RC_ERROR_SEEK; // Ptr remains at string start - // NOTE(matt): Should it, however, be left at the end of the string? - } - } - return RC_SUCCESS; -} - -int -BinarySearchForMetadataEntry(index *Index, index_metadata **Test, char *SearchTerm) +BinarySearchForMetadataEntry(index *Index, index_metadata **Entry, char *SearchTerm) { int Lower = 0; index_metadata *LowerEntry = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * Lower); - if(StringsDiffer(SearchTerm, LowerEntry->BaseFilename) < 0 ) { return Lower; } + if(StringsDiffer(SearchTerm, LowerEntry->BaseFilename) < 0 ) { return -1; } int Upper = Index->Header.EntryCount - 1; int Pivot = Upper - ((Upper - Lower) >> 1); @@ -4403,9 +4497,9 @@ BinarySearchForMetadataEntry(index *Index, index_metadata **Test, char *SearchTe PivotEntry = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * Pivot); UpperEntry = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * Upper); - if(!StringsDiffer(SearchTerm, LowerEntry->BaseFilename)) { *Test = LowerEntry; return Lower; } - if(!StringsDiffer(SearchTerm, PivotEntry->BaseFilename)) { *Test = PivotEntry; return Pivot; } - if(!StringsDiffer(SearchTerm, UpperEntry->BaseFilename)) { *Test = UpperEntry; return Upper; } + if(!StringsDiffer(SearchTerm, LowerEntry->BaseFilename)) { *Entry = LowerEntry; return Lower; } + if(!StringsDiffer(SearchTerm, PivotEntry->BaseFilename)) { *Entry = PivotEntry; return Pivot; } + if(!StringsDiffer(SearchTerm, UpperEntry->BaseFilename)) { *Entry = UpperEntry; return Upper; } if((StringsDiffer(SearchTerm, PivotEntry->BaseFilename) < 0)) { Upper = Pivot; } else { Lower = Pivot; } @@ -4440,8 +4534,167 @@ AccumulateIndexEntryInsertionOffset(index *Index, int EntryIndex) return Result; } +void +ClearEntry(index_metadata *Entry) +{ + Entry->LinkOffsets.PrevStart = 0; + Entry->LinkOffsets.NextStart = 0; + Entry->LinkOffsets.PrevEnd = 0; + Entry->LinkOffsets.NextEnd = 0; + Entry->Size = 0; + Clear(Entry->BaseFilename, sizeof(Entry->BaseFilename)); + Clear(Entry->Title, sizeof(Entry->Title)); +} + +enum +{ + EDIT_DELETION, + EDIT_ADDITION, + EDIT_REINSERTION +} index_edits; + +void +GetNeighbourhood(index *Index, neighbourhood *N, int IndexEditType, bool *ThisIsPrev) +{ + index_metadata Entry = { 0 }; + int EntryIndex; + int IncomingIndex = N->ThisIndex; + + if(IndexEditType == EDIT_DELETION) + { + bool FoundThis = FALSE; + for(EntryIndex = IncomingIndex + 1; EntryIndex < Index->Header.EntryCount; ++EntryIndex) + { + Entry = *(index_metadata *)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * EntryIndex); + if(Entry.Size > 0) + { + FoundThis = TRUE; + break; + } + } + + if(!FoundThis) + { + for(EntryIndex = IncomingIndex - 1; EntryIndex >= 0; --EntryIndex) + { + Entry = *(index_metadata *)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * EntryIndex); + if(Entry.Size > 0) + { + FoundThis = TRUE; + *ThisIsPrev = TRUE; + break; + } + } + } + + if(FoundThis) + { + N->ThisIndex = EntryIndex; + N->This.Size = Entry.Size; + N->This.LinkOffsets.PrevStart = Entry.LinkOffsets.PrevStart; + N->This.LinkOffsets.PrevEnd = Entry.LinkOffsets.PrevEnd; + N->This.LinkOffsets.NextStart = Entry.LinkOffsets.NextStart; + N->This.LinkOffsets.NextEnd = Entry.LinkOffsets.NextEnd; + CopyString(N->This.BaseFilename, Entry.BaseFilename); + CopyString(N->This.Title, Entry.Title); + } + else + { + return; // NOTE(matt): We were evidently the last public entry, until now + } + } + + N->PrevIsFirst = TRUE; + N->NextIsFinal = TRUE; + + if(IndexEditType == EDIT_DELETION && *ThisIsPrev == FALSE) + { + EntryIndex = IncomingIndex - 1; + } + else + { + EntryIndex = N->ThisIndex - 1; + } + bool FoundPrev = FALSE; + for(; EntryIndex >= 0; --EntryIndex) + { + Entry = *(index_metadata*)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * EntryIndex); + if(Entry.Size > 0) + { + if(!FoundPrev) + { + N->PrevIndex = EntryIndex; + N->Prev.Size = Entry.Size; + N->Prev.LinkOffsets.PrevStart = Entry.LinkOffsets.PrevStart; + N->Prev.LinkOffsets.PrevEnd = Entry.LinkOffsets.PrevEnd; + N->Prev.LinkOffsets.NextStart = Entry.LinkOffsets.NextStart; + N->Prev.LinkOffsets.NextEnd = Entry.LinkOffsets.NextEnd; + CopyString(N->Prev.BaseFilename, Entry.BaseFilename); + CopyString(N->Prev.Title, Entry.Title); + FoundPrev = TRUE; + } + else + { + N->PrevIsFirst = FALSE; + break; + } + } + } + + switch(IndexEditType) + { + case EDIT_DELETION: + if(*ThisIsPrev == TRUE) { EntryIndex = Index->Header.EntryCount; break; } // NOTE(matt): No need to enter the loop, else fallthrough + case EDIT_REINSERTION: + EntryIndex = N->ThisIndex + 1; + break; + case EDIT_ADDITION: + EntryIndex = N->ThisIndex; + break; + } + bool FoundNext = FALSE; + for(; EntryIndex < Index->Header.EntryCount; + ++EntryIndex) + { + Entry = *(index_metadata*)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * EntryIndex); + if(Entry.Size > 0) + { + if(!FoundNext) + { + N->NextIndex = EntryIndex; + N->Next.Size = Entry.Size; + N->Next.LinkOffsets.PrevStart = Entry.LinkOffsets.PrevStart; + N->Next.LinkOffsets.PrevEnd = Entry.LinkOffsets.PrevEnd; + N->Next.LinkOffsets.NextStart = Entry.LinkOffsets.NextStart; + N->Next.LinkOffsets.NextEnd = Entry.LinkOffsets.NextEnd; + CopyString(N->Next.BaseFilename, Entry.BaseFilename); + CopyString(N->Next.Title, Entry.Title); + FoundNext = TRUE; + } + else + { + N->NextIsFinal = FALSE; + break; + } + } + } + + switch(IndexEditType) + { + case EDIT_REINSERTION: + break; + case EDIT_DELETION: + if(*ThisIsPrev == FALSE) { --N->ThisIndex; } + if(FoundNext) { --N->NextIndex; } + break; + case EDIT_ADDITION: + if(FoundNext) { ++N->NextIndex; } + break; + } +} + int -InsertIntoIndex(index *Index, int *EntryIndex, buffers *CollationBuffers, template **BespokeTemplate, char *BaseFilename) +InsertIntoIndex(index *Index, neighbourhood *N, buffers *CollationBuffers, template **BespokeTemplate, char *BaseFilename, bool RecheckingPrivacy) { int IndexMetadataFileReadCode = ReadFileIntoBuffer(&Index->Metadata, 0); switch(IndexMetadataFileReadCode) @@ -4463,15 +4716,12 @@ InsertIntoIndex(index *Index, int *EntryIndex, buffers *CollationBuffers, templa break; } - int MetadataInsertionOffset = -1; int IndexEntryInsertionStart = -1; int IndexEntryInsertionEnd = -1; Index->Header.EntryCount = 0; - bool Found = FALSE; + ClearEntry(&Index->Entry); + bool Reinserting = FALSE; - neighbours Neighbours = { 0 }; - index_metadata *This = { 0 }; - index_metadata *Next = { 0 }; if(IndexMetadataFileReadCode == RC_SUCCESS && IndexFileReadCode == RC_SUCCESS) { // TODO(matt): Index validation? @@ -4486,110 +4736,44 @@ InsertIntoIndex(index *Index, int *EntryIndex, buffers *CollationBuffers, templa Index->Header.CurrentHMMLVersion.Minor = hmml_version.Minor; Index->Header.CurrentHMMLVersion.Patch = hmml_version.Patch; - Index->Metadata.Buffer.Ptr += sizeof(Index->Header); + Index->Metadata.Buffer.Ptr += sizeof(index_header); Index->File.Buffer.Ptr += StringLength("---\n"); -#if BINARY_SEARCH -START_TIMING_BLOCK(" InsertIntoIndex(%s) BinarySearch: ", BaseFilename); - index_metadata *Prev = { 0 }; - *EntryIndex = BinarySearchForMetadataEntry(Index, &This, BaseFilename); - if(This) + index_metadata *Entry = { 0 }; + N->ThisIndex = BinarySearchForMetadataEntry(Index, &Entry, BaseFilename); + if(Entry) { // Reinsert - Found = TRUE; - MetadataInsertionOffset = sizeof(index_header) + sizeof(index_metadata) * *EntryIndex; - IndexEntryInsertionStart = AccumulateIndexEntryInsertionOffset(Index, *EntryIndex); - IndexEntryInsertionEnd = IndexEntryInsertionStart + This->Size; - if(*EntryIndex > 0) - { - Prev = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (*EntryIndex - 1)); - Neighbours.Prev.BaseFilename = Prev->BaseFilename; - Neighbours.Prev.Title = Prev->Title; - } - if(*EntryIndex < Index->Header.EntryCount - 1) - { - Next = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (*EntryIndex + 1)); - Neighbours.Next.BaseFilename = Next->BaseFilename; - Neighbours.Next.Title = Next->Title; - } + Reinserting = TRUE; + N->This.Size = Entry->Size; + N->This.LinkOffsets.PrevStart = Entry->LinkOffsets.PrevStart; + N->This.LinkOffsets.PrevEnd = Entry->LinkOffsets.PrevEnd; + N->This.LinkOffsets.NextStart = Entry->LinkOffsets.NextStart; + N->This.LinkOffsets.NextEnd = Entry->LinkOffsets.NextEnd; + + IndexEntryInsertionStart = AccumulateIndexEntryInsertionOffset(Index, N->ThisIndex); + IndexEntryInsertionEnd = IndexEntryInsertionStart + N->This.Size; } else { - This = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * *EntryIndex); - if(StringsDiffer(BaseFilename, This->BaseFilename) < 0) + if(N->ThisIndex == -1) { ++N->ThisIndex; } // NOTE(matt): BinarySearchForMetadataEntry returns -1 if search term precedes the set + Entry = (index_metadata*)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * N->ThisIndex); + if(StringsDiffer(BaseFilename, Entry->BaseFilename) < 0) { // Insert - MetadataInsertionOffset = sizeof(index_header) + sizeof(index_metadata) * *EntryIndex; - IndexEntryInsertionStart = AccumulateIndexEntryInsertionOffset(Index, *EntryIndex); - if(*EntryIndex > 0) - { - Prev = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (*EntryIndex - 1)); - Neighbours.Prev.BaseFilename = Prev->BaseFilename; - Neighbours.Prev.Title = Prev->Title; - } - Neighbours.Next.BaseFilename = This->BaseFilename; - Neighbours.Next.Title = This->Title; + IndexEntryInsertionStart = AccumulateIndexEntryInsertionOffset(Index, N->ThisIndex); + } else { // Append - if(*EntryIndex > 0) - { - Neighbours.Prev.BaseFilename = This->BaseFilename; - Neighbours.Prev.Title = This->Title; - } - ++*EntryIndex; + ++N->ThisIndex; } } -#else -START_TIMING_BLOCK(" InsertIntoIndex(%s) LinearSearch: ", BaseFilename); - char *IndexEntryStart; - IndexEntryStart = Index->File.Buffer.Ptr; - for(*EntryIndex = 0; *EntryIndex < Index->Header.EntryCount; ++*EntryIndex) - { - This = (index_metadata *)Index->Metadata.Buffer.Ptr; - if(!StringsDiffer(This->BaseFilename, BaseFilename)) - { - // Reinsert - if(*EntryIndex < (Index->Header.EntryCount - 1)) - { - Next = (index_metadata *)(Index->Metadata.Buffer.Ptr + sizeof(Index->Entry)); - Neighbours.Next.BaseFilename = Next->BaseFilename; - Neighbours.Next.Title = Next->Title; - } - - MetadataInsertionOffset = Index->Metadata.Buffer.Ptr - Index->Metadata.Buffer.Location; - IndexEntryInsertionStart = IndexEntryStart - Index->File.Buffer.Location; - IndexEntryInsertionEnd = IndexEntryInsertionStart + This->Size; - Found = TRUE; - break; - } - else if(StringsDiffer(This->BaseFilename, BaseFilename) > 0) - { - // Insert - Neighbours.Next.BaseFilename = This->BaseFilename; - Neighbours.Next.Title = This->Title; - - MetadataInsertionOffset = Index->Metadata.Buffer.Ptr - Index->Metadata.Buffer.Location; - IndexEntryInsertionStart = IndexEntryStart - Index->File.Buffer.Location; - break; - } - else - { - Index->Metadata.Buffer.Ptr += sizeof(Index->Entry); - - IndexEntryStart += This->Size; - Index->File.Buffer.Ptr = IndexEntryStart; - - Neighbours.Prev.BaseFilename = This->BaseFilename; - Neighbours.Prev.Title = This->Title; - } - } -#endif + GetNeighbourhood(Index, N, Reinserting ? EDIT_REINSERTION : EDIT_ADDITION, 0); } else { -START_TIMING_BLOCK(" InsertIntoIndex(%s) NoSearch: ", BaseFilename); // NOTE(matt): Initialising new index_header Index->Header.CurrentDBVersion = CINERA_DB_VERSION; Index->Header.CurrentAppVersion = CINERA_APP_VERSION; @@ -4631,11 +4815,12 @@ START_TIMING_BLOCK(" InsertIntoIndex(%s) NoSearch: ", BaseFilename); } closedir(OutputDirectoryHandle); } -END_TIMING_BLOCK(); char InputFile[StringLength(BaseFilename) + StringLength(".hmml")]; CopyString(InputFile, "%s.hmml", BaseFilename); - switch(HMMLToBuffers(CollationBuffers, BespokeTemplate, InputFile, &Index->Entry.LinkOffsets, &Neighbours)) + bool VideoIsPrivate = FALSE; + + switch(HMMLToBuffers(CollationBuffers, BespokeTemplate, InputFile, N)) { // TODO(matt): Actually sort out the fatality of these cases, once we are always-on case RC_ERROR_FILE: @@ -4646,81 +4831,126 @@ END_TIMING_BLOCK(); case RC_ERROR_QUOTE: case RC_INVALID_REFERENCE: return RC_ERROR_HMML; + case RC_PRIVATE_VIDEO: + VideoIsPrivate = TRUE; case RC_SUCCESS: break; }; - Index->Entry.Size = CollationBuffers->Search.Ptr - CollationBuffers->Search.Location; - ClearCopyStringNoFormat(Index->Entry.BaseFilename, sizeof(Index->Entry.BaseFilename), BaseFilename); - ClearCopyStringNoFormat(Index->Entry.Title, sizeof(Index->Entry.Title), CollationBuffers->Title); + ClearCopyStringNoFormat(N->This.BaseFilename, sizeof(N->This.BaseFilename), BaseFilename); + if(N->This.Size > 0) + { + ClearCopyStringNoFormat(N->This.Title, sizeof(N->This.Title), CollationBuffers->Title); + } if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; } if(!(Index->File.Handle = fopen(Index->File.Path, "w"))) { FreeBuffer(&Index->File.Buffer); return RC_ERROR_FILE; } - if(!Found) { ++Index->Header.EntryCount; } + if(!Reinserting) { ++Index->Header.EntryCount; } - fwrite(&Index->Header, sizeof(Index->Header), 1, Index->Metadata.Handle); + fwrite(&Index->Header, sizeof(index_header), 1, Index->Metadata.Handle); if(IndexMetadataFileReadCode == RC_SUCCESS) { - Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(Index->Header); + Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(index_header); } - if(Found) + if(Reinserting) { // NOTE(matt): We hit this during the start-up sync and when copying in a .hmml file over an already existing one, but // would need to fool about with the inotify event processing to get to this branch in the case that saving // a file triggers an IN_DELETE followed by an IN_CLOSE_WRITE event + // + // We will almost definitely need to handle our inotify events differently once we are outputting the player + // pages of private videos to "secret locations", because we will need to preserve the randomly generated + // location stored in the .metadata // Reinsert - fwrite(Index->Metadata.Buffer.Ptr, MetadataInsertionOffset - sizeof(Index->Header), 1, Index->Metadata.Handle); - fwrite(&Index->Entry, sizeof(Index->Entry), 1, Index->Metadata.Handle); - fwrite(Index->Metadata.Buffer.Ptr - sizeof(Index->Header) + MetadataInsertionOffset + sizeof(Index->Entry), Index->Metadata.FileSize - MetadataInsertionOffset - sizeof(Index->Entry), 1, Index->Metadata.Handle); + Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * N->ThisIndex; + *(index_metadata *)Index->Metadata.Buffer.Ptr = N->This; + fwrite(Index->Metadata.Buffer.Location + sizeof(index_header), Index->Metadata.FileSize - sizeof(index_header), 1, Index->Metadata.Handle); fwrite(Index->File.Buffer.Location, IndexEntryInsertionStart, 1, Index->File.Handle); - fwrite(CollationBuffers->Search.Location, Index->Entry.Size, 1, Index->File.Handle); + fwrite(CollationBuffers->Search.Location, N->This.Size, 1, Index->File.Handle); fwrite(Index->File.Buffer.Location + IndexEntryInsertionEnd, Index->File.FileSize - IndexEntryInsertionEnd, 1, Index->File.Handle); - LogError(LOG_NOTICE, "Reinserted %s - %s", BaseFilename, CollationBuffers->Title); - fprintf(stderr, "\e[1;33mReinserted\e[0m %s - %s\n", BaseFilename, CollationBuffers->Title); + if(VideoIsPrivate) + { + if(!RecheckingPrivacy) + { + LogError(LOG_NOTICE, "Privately Reinserted %s", BaseFilename); + fprintf(stderr, "\e[0;34mPrivately Reinserted\e[0m %s\n", BaseFilename); + } + } + else + { + LogError(LOG_NOTICE, "Reinserted %s - %s", BaseFilename, CollationBuffers->Title); + fprintf(stderr, "\e[1;33mReinserted\e[0m %s - %s\n", BaseFilename, CollationBuffers->Title); + } } - else if(MetadataInsertionOffset >= 0 && IndexEntryInsertionStart >= 0) + else if(IndexEntryInsertionStart >= 0) { // Insert new - fwrite(Index->Metadata.Buffer.Ptr, MetadataInsertionOffset - sizeof(Index->Header), 1, Index->Metadata.Handle); - fwrite(&Index->Entry, sizeof(Index->Entry), 1, Index->Metadata.Handle); - fwrite(Index->Metadata.Buffer.Ptr - sizeof(Index->Header) + MetadataInsertionOffset, Index->Metadata.FileSize - MetadataInsertionOffset, 1, Index->Metadata.Handle); + fwrite(Index->Metadata.Buffer.Location + sizeof(index_header), sizeof(index_metadata) * N->ThisIndex, 1, Index->Metadata.Handle); + fwrite(&N->This, sizeof(index_metadata), 1, Index->Metadata.Handle); + fwrite(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * N->ThisIndex, + Index->Metadata.FileSize - sizeof(index_header) - sizeof(index_metadata) * N->ThisIndex, + 1, Index->Metadata.Handle); fwrite(Index->File.Buffer.Location, IndexEntryInsertionStart, 1, Index->File.Handle); - fwrite(CollationBuffers->Search.Location, Index->Entry.Size, 1, Index->File.Handle); + fwrite(CollationBuffers->Search.Location, N->This.Size, 1, Index->File.Handle); fwrite(Index->File.Buffer.Location + IndexEntryInsertionStart, Index->File.FileSize - IndexEntryInsertionStart, 1, Index->File.Handle); - LogError(LOG_NOTICE, "Inserted %s - %s", BaseFilename, CollationBuffers->Title); - fprintf(stderr, "\e[1;32mInserted\e[0m %s - %s\n", BaseFilename, CollationBuffers->Title); + if(VideoIsPrivate) + { + if(!RecheckingPrivacy) + { + LogError(LOG_NOTICE, "Privately Inserted %s", BaseFilename); + fprintf(stderr, "\e[0;34mPrivately Inserted\e[0m %s\n", BaseFilename); + } + } + else + { + LogError(LOG_NOTICE, "Inserted %s - %s", BaseFilename, CollationBuffers->Title); + fprintf(stderr, "\e[1;32mInserted\e[0m %s - %s\n", BaseFilename, CollationBuffers->Title); + } } else { // Append new if(IndexMetadataFileReadCode == RC_SUCCESS) { - fwrite(Index->Metadata.Buffer.Ptr, Index->Metadata.FileSize - sizeof(Index->Header), 1, Index->Metadata.Handle); + fwrite(Index->Metadata.Buffer.Location + sizeof(index_header), Index->Metadata.FileSize - sizeof(index_header), 1, Index->Metadata.Handle); fwrite(Index->File.Buffer.Location, Index->File.FileSize, 1, Index->File.Handle); } else { fprintf(Index->File.Handle, "---\n"); } - fwrite(&Index->Entry, sizeof(Index->Entry), 1, Index->Metadata.Handle); - fwrite(CollationBuffers->Search.Location, Index->Entry.Size, 1, Index->File.Handle); - LogError(LOG_NOTICE, "Appended %s - %s", BaseFilename, CollationBuffers->Title); - fprintf(stderr, "\e[1;32mAppended\e[0m %s - %s\n", BaseFilename, CollationBuffers->Title); + fwrite(&N->This, sizeof(index_metadata), 1, Index->Metadata.Handle); + fwrite(CollationBuffers->Search.Location, N->This.Size, 1, Index->File.Handle); + if(VideoIsPrivate) + { + if(!RecheckingPrivacy) + { + LogError(LOG_NOTICE, "Privately Appended %s", BaseFilename); + fprintf(stderr, "\e[0;34mPrivately Appended\e[0m %s\n", BaseFilename); + } + } + else + { + LogError(LOG_NOTICE, "Appended %s - %s", BaseFilename, CollationBuffers->Title); + fprintf(stderr, "\e[1;32mAppended\e[0m %s - %s\n", BaseFilename, CollationBuffers->Title); + } } fclose(Index->Metadata.Handle); fclose(Index->File.Handle); + FreeBuffer(&Index->Metadata.Buffer); FreeBuffer(&Index->File.Buffer); - return Found ? RC_SUCCESS : RC_UNFOUND; + // TODO(matt): Remove VideoIsPrivate in favour of generating a player page in a random location + return VideoIsPrivate ? RC_PRIVATE_VIDEO : Reinserting ? RC_SUCCESS : RC_UNFOUND; } void @@ -4741,14 +4971,17 @@ ConstructDirectoryPath(buffer *DirectoryPath, int PageType, char *PageLocation, { CopyStringToBuffer(DirectoryPath, "/%s", PageLocation); } - if(StringsDiffer(Config.PlayerURLPrefix, "")) + if(BaseFilename) { - char *Ptr = BaseFilename + StringLength(Config.ProjectID); - CopyStringToBuffer(DirectoryPath, "/%s%s", Config.PlayerURLPrefix, Ptr); - } - else - { - CopyStringToBuffer(DirectoryPath, "/%s", BaseFilename); + if(StringsDiffer(Config.PlayerURLPrefix, "")) + { + char *Ptr = BaseFilename + StringLength(Config.ProjectID); + CopyStringToBuffer(DirectoryPath, "/%s%s", Config.PlayerURLPrefix, Ptr); + } + else + { + CopyStringToBuffer(DirectoryPath, "/%s", BaseFilename); + } } break; } @@ -4902,201 +5135,187 @@ int DeleteNeighbourLinks(file_buffer *File, index_metadata *Metadata) FreeBuffer(&File->Buffer); return RC_SUCCESS; } - else - { - return RC_ERROR_FILE; - } + else { return RC_ERROR_FILE; } } -int LinkNeighbours(index *Index, int EntryIndex, char *BaseFilename, int LinkType) +int LinkNeighbours(index *Index, neighbourhood *N, char *BaseFilename, int LinkType) { switch(ReadFileIntoBuffer(&Index->Metadata, 0)) { case RC_ERROR_FILE: return RC_ERROR_FILE; case RC_ERROR_MEMORY: - LogError(LOG_ERROR, "DeleteFromIndex(): %s", strerror(errno)); + LogError(LOG_ERROR, "LinkNeighbours(): %s", strerror(errno)); return RC_ERROR_MEMORY; case RC_SUCCESS: break; } Index->Header = *(index_header *)Index->Metadata.Buffer.Ptr; - Index->Metadata.Buffer.Ptr += sizeof(Index->Header); - index_metadata *Prev = { 0 }; - index_metadata *This = { 0 }; - index_metadata *Next = { 0 }; -#if BINARY_SEARCH -START_TIMING_BLOCK(" LinkNeighbours(%s) [passing in EntryIndex]: ", BaseFilename); - This = (index_metadata *)Index->Metadata.Buffer.Ptr; - if(Index->Header.EntryCount != 1) + if(N->PrevIndex == -1 && N->NextIndex == -1) { - This = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * EntryIndex); - if(EntryIndex > 0) - { - Prev = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (EntryIndex - 1)); - } - if(EntryIndex < Index->Header.EntryCount - 1) - { - Next = (index_metadata*)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata) * (EntryIndex + 1)); - } + buffer LonePlayerPagePath; + ClaimBuffer(&LonePlayerPagePath, "LonePlayerPagePath", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10); + ConstructDirectoryPath(&LonePlayerPagePath, PAGE_PLAYER, Config.PlayerLocation, N->This.BaseFilename); + CopyStringToBuffer(&LonePlayerPagePath, "/index.html"); + + file_buffer LonePlayerPage; + CopyStringNoFormat(LonePlayerPage.Path, LonePlayerPagePath.Location); + + DeleteNeighbourLinks(&LonePlayerPage, &N->This); + DeclaimBuffer(&LonePlayerPagePath); + + Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * N->ThisIndex; + *(index_metadata *)Index->Metadata.Buffer.Ptr = N->This; } -#else -START_TIMING_BLOCK(" LinkNeighbours(%s) LinearSearch: ", BaseFilename); - switch(LinkType) + else { - case LINK_INCLUDE: + if(N->PrevIndex >= 0) + { + buffer PreviousPlayerPagePath; + ClaimBuffer(&PreviousPlayerPagePath, "PreviousPlayerPagePath", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10); + ConstructDirectoryPath(&PreviousPlayerPagePath, PAGE_PLAYER, Config.PlayerLocation, N->Prev.BaseFilename); + CopyStringToBuffer(&PreviousPlayerPagePath, "/index.html"); + + file_buffer PreviousPlayerPage; + CopyStringNoFormat(PreviousPlayerPage.Path, PreviousPlayerPagePath.Location); + + switch(LinkType) { - for(EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex) - { - This = (index_metadata *)Index->Metadata.Buffer.Ptr; - if(!StringsDiffer(This->BaseFilename, BaseFilename)) + case LINK_EXCLUDE: { - if(EntryIndex < (Index->Header.EntryCount - 1)) - { - Next = (index_metadata *)(Index->Metadata.Buffer.Ptr + sizeof(index_metadata)); - } - break; - } - Prev = This; - Index->Metadata.Buffer.Ptr += sizeof(index_metadata); - } - } break; - case LINK_EXCLUDE: - { - This = (index_metadata *)Index->Metadata.Buffer.Ptr; - if(Index->Header.EntryCount != 1) - { - Index->Metadata.Buffer.Ptr += sizeof(index_metadata); - Next = (index_metadata *)Index->Metadata.Buffer.Ptr; + buffer ThisPlayerPagePath; + ClaimBuffer(&ThisPlayerPagePath, "ThisPlayerPagePath", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10); + ConstructDirectoryPath(&ThisPlayerPagePath, PAGE_PLAYER, Config.PlayerLocation, N->This.BaseFilename); + CopyStringToBuffer(&ThisPlayerPagePath, "/index.html"); - for(EntryIndex = 1; EntryIndex < Index->Header.EntryCount; ++EntryIndex) + file_buffer ThisPlayerPage; + CopyStringNoFormat(ThisPlayerPage.Path, ThisPlayerPagePath.Location); + + InsertNeighbourLink(&ThisPlayerPage, &N->This, &N->Prev, LINK_PREV, Index->Header.ProjectName, N->NextIndex == -1 ? TRUE : FALSE); + + DeclaimBuffer(&ThisPlayerPagePath); + + Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * N->ThisIndex; + *(index_metadata *)Index->Metadata.Buffer.Ptr = N->This; + } + case LINK_INCLUDE: { - Prev = This; - This = Next; - if(EntryIndex < (Index->Header.EntryCount - 1)) - { - Index->Metadata.Buffer.Ptr += sizeof(index_metadata); - Next = (index_metadata *)Index->Metadata.Buffer.Ptr; - } - else { Next = 0; } - if(StringsDiffer(BaseFilename, This->BaseFilename) < 0) - { - break; - } + InsertNeighbourLink(&PreviousPlayerPage, &N->Prev, &N->This, LINK_NEXT, Index->Header.ProjectName, N->PrevIsFirst); + Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * N->PrevIndex; + *(index_metadata *)Index->Metadata.Buffer.Ptr = N->Prev; } - } - } break; - } -#endif -END_TIMING_BLOCK(); + } - if(!Prev && !Next) - { - buffer ThisPlayerPagePath; - ClaimBuffer(&ThisPlayerPagePath, "ThisPlayerPagePath", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10); - ConstructDirectoryPath(&ThisPlayerPagePath, PAGE_PLAYER, Config.PlayerLocation, This->BaseFilename); - CopyStringToBuffer(&ThisPlayerPagePath, "/index.html"); - - file_buffer ThisPlayerPage; - CopyStringNoFormat(ThisPlayerPage.Path, ThisPlayerPagePath.Location); - - DeleteNeighbourLinks(&ThisPlayerPage, This); - - DeclaimBuffer(&ThisPlayerPagePath); - - Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"); - fwrite(Index->Metadata.Buffer.Location, ((char *)This - Index->Metadata.Buffer.Location), 1, Index->Metadata.Handle); - fwrite(This, sizeof(index_metadata), 1, Index->Metadata.Handle); - fwrite(Index->Metadata.Buffer.Location + ((char *)This - Index->Metadata.Buffer.Location) + sizeof(index_metadata), - Index->Metadata.FileSize - (((char *)This - Index->Metadata.Buffer.Location) + sizeof(index_metadata)), - 1, Index->Metadata.Handle); - fclose(Index->Metadata.Handle); - } - - if(Prev) - { - buffer PreviousPlayerPagePath; - ClaimBuffer(&PreviousPlayerPagePath, "PreviousPlayerPagePath", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10); - ConstructDirectoryPath(&PreviousPlayerPagePath, PAGE_PLAYER, Config.PlayerLocation, Prev->BaseFilename); - CopyStringToBuffer(&PreviousPlayerPagePath, "/index.html"); - - file_buffer PreviousPlayerPage; - CopyStringNoFormat(PreviousPlayerPage.Path, PreviousPlayerPagePath.Location); - - switch(LinkType) - { - case LINK_EXCLUDE: - { - buffer ThisPlayerPagePath; - ClaimBuffer(&ThisPlayerPagePath, "ThisPlayerPagePath", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10); - ConstructDirectoryPath(&ThisPlayerPagePath, PAGE_PLAYER, Config.PlayerLocation, This->BaseFilename); - CopyStringToBuffer(&ThisPlayerPagePath, "/index.html"); - - file_buffer ThisPlayerPage; - CopyStringNoFormat(ThisPlayerPage.Path, ThisPlayerPagePath.Location); - - InsertNeighbourLink(&ThisPlayerPage, This, Prev, LINK_PREV, Index->Header.ProjectName, Next ? FALSE : TRUE); - - DeclaimBuffer(&ThisPlayerPagePath); - - Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"); - fwrite(Index->Metadata.Buffer.Location, ((char* )This - Index->Metadata.Buffer.Location), 1, Index->Metadata.Handle); - fwrite(This, sizeof(index_metadata), 1, Index->Metadata.Handle); - fwrite(Index->Metadata.Buffer.Location + ((char* )This - Index->Metadata.Buffer.Location) + sizeof(index_metadata), - Index->Metadata.FileSize - (((char* )This - Index->Metadata.Buffer.Location) + sizeof(index_metadata)), - 1, Index->Metadata.Handle); - fclose(Index->Metadata.Handle); - - if(EntryIndex == Index->Header.EntryCount) { break; } - } - case LINK_INCLUDE: - { - InsertNeighbourLink(&PreviousPlayerPage, Prev, This, LINK_NEXT, Index->Header.ProjectName, EntryIndex == 1 ? TRUE : FALSE); - } break; + DeclaimBuffer(&PreviousPlayerPagePath); } - DeclaimBuffer(&PreviousPlayerPagePath); + if(N->NextIndex >= 0) + { + buffer NextPlayerPagePath; + ClaimBuffer(&NextPlayerPagePath, "NextPlayerPagePath", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10); + ConstructDirectoryPath(&NextPlayerPagePath, PAGE_PLAYER, Config.PlayerLocation, N->Next.BaseFilename); + CopyStringToBuffer(&NextPlayerPagePath, "/index.html"); - Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"); - fwrite(Index->Metadata.Buffer.Location, ((char *)Prev - Index->Metadata.Buffer.Location), 1, Index->Metadata.Handle); - fwrite(Prev, sizeof(index_metadata), 1, Index->Metadata.Handle); - fwrite(Index->Metadata.Buffer.Location + ((char *)Prev - Index->Metadata.Buffer.Location) + sizeof(index_metadata), - Index->Metadata.FileSize - (((char *)Prev - Index->Metadata.Buffer.Location) + sizeof(index_metadata)), - 1, Index->Metadata.Handle); - fclose(Index->Metadata.Handle); + file_buffer NextPlayerPage; + CopyStringNoFormat(NextPlayerPage.Path, NextPlayerPagePath.Location); + + switch(LinkType) + { + case LINK_EXCLUDE: + { + buffer ThisPlayerPagePath; + ClaimBuffer(&ThisPlayerPagePath, "ThisPlayerPagePath", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10); + ConstructDirectoryPath(&ThisPlayerPagePath, PAGE_PLAYER, Config.PlayerLocation, N->This.BaseFilename); + CopyStringToBuffer(&ThisPlayerPagePath, "/index.html"); + + file_buffer ThisPlayerPage; + CopyStringNoFormat(ThisPlayerPage.Path, ThisPlayerPagePath.Location); + + InsertNeighbourLink(&ThisPlayerPage, &N->This, &N->Next, LINK_NEXT, Index->Header.ProjectName, N->PrevIndex == -1 ? TRUE : FALSE); + + DeclaimBuffer(&ThisPlayerPagePath); + + Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * N->ThisIndex; + *(index_metadata *)Index->Metadata.Buffer.Ptr = N->This; + } + case LINK_INCLUDE: + { + InsertNeighbourLink(&NextPlayerPage, &N->Next, &N->This, LINK_PREV, Index->Header.ProjectName, N->NextIsFinal); + Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * N->NextIndex; + *(index_metadata *)Index->Metadata.Buffer.Ptr = N->Next; + } + } + + DeclaimBuffer(&NextPlayerPagePath); + + } } - if(Next && LinkType == LINK_INCLUDE) - { - buffer NextPlayerPagePath; - ClaimBuffer(&NextPlayerPagePath, "NextPlayerPagePath", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH + 1 + 10); - ConstructDirectoryPath(&NextPlayerPagePath, PAGE_PLAYER, Config.PlayerLocation, Next->BaseFilename); - CopyStringToBuffer(&NextPlayerPagePath, "/index.html"); - - file_buffer NextPlayerPage; - CopyStringNoFormat(NextPlayerPage.Path, NextPlayerPagePath.Location); - - InsertNeighbourLink(&NextPlayerPage, Next, This, LINK_PREV, Index->Header.ProjectName, EntryIndex == (Index->Header.EntryCount - 2) ? TRUE : FALSE); - - DeclaimBuffer(&NextPlayerPagePath); - - Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"); - fwrite(Index->Metadata.Buffer.Location, ((char* )Next - Index->Metadata.Buffer.Location), 1, Index->Metadata.Handle); - fwrite(Next, sizeof(index_metadata), 1, Index->Metadata.Handle); - fwrite(Index->Metadata.Buffer.Location + ((char* )Next - Index->Metadata.Buffer.Location) + sizeof(index_metadata), - Index->Metadata.FileSize - (((char* )Next - Index->Metadata.Buffer.Location) + sizeof(index_metadata)), - 1, Index->Metadata.Handle); - fclose(Index->Metadata.Handle); - } + Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"); + fwrite(Index->Metadata.Buffer.Location, Index->Metadata.FileSize, 1, Index->Metadata.Handle); + fclose(Index->Metadata.Handle); FreeBuffer(&Index->Metadata.Buffer); return RC_SUCCESS; } +void +DeleteIndexPageFromFilesystem() // NOTE(matt): Do we need to handle relocating, like the PlayerPage function? +{ + buffer IndexDirectory; + ClaimBuffer(&IndexDirectory, "IndexDirectory", 1024); + ConstructDirectoryPath(&IndexDirectory, PAGE_INDEX, Config.IndexLocation, ""); + char IndexPagePath[1024]; + CopyString(IndexPagePath, "%s/index.html", IndexDirectory.Location); + remove(IndexPagePath); + remove(IndexDirectory.Location); + DeclaimBuffer(&IndexDirectory); +} + int -DeleteFromIndex(index *Index, int *EntryIndex, char *BaseFilename) +DeletePlayerPageFromFilesystem(char *BaseFilename, char *PlayerLocation, bool Relocating) +{ + // NOTE(matt): Once we have the notion of an output filename format, we'll need to use that here + buffer OutputDirectoryPath; + ClaimBuffer(&OutputDirectoryPath, "OutputDirectoryPath", 1024); + ConstructDirectoryPath(&OutputDirectoryPath, PAGE_PLAYER, PlayerLocation, BaseFilename); + DIR *PlayerDir; + + if((PlayerDir = opendir(OutputDirectoryPath.Location))) // There is a directory for the Player, which there probably should be if not for manual intervention + { + char PlayerPagePath[256]; + CopyString(PlayerPagePath, "%s/index.html", OutputDirectoryPath.Location); + FILE *PlayerPage; + if((PlayerPage = fopen(PlayerPagePath, "r"))) + { + fclose(PlayerPage); + remove(PlayerPagePath); + } + + closedir(PlayerDir); + if((remove(OutputDirectoryPath.Location) == -1)) + { + LogError(LOG_NOTICE, "Mostly deleted %s. Unable to remove directory %s: %s", BaseFilename, OutputDirectoryPath.Location, strerror(errno)); + fprintf(stderr, "\e[1;30mMostly deleted\e[0m %s. \e[1;31mUnable to remove directory\e[0m %s: %s", BaseFilename, OutputDirectoryPath.Location, strerror(errno)); + } + else + { + if(!Relocating) + { + LogError(LOG_INFORMATIONAL, "Deleted %s", BaseFilename); + fprintf(stderr, "\e[1;30mDeleted\e[0m %s\n", BaseFilename); + } + + } + } + DeclaimBuffer(&OutputDirectoryPath); + return RC_SUCCESS; +} + +int +DeleteFromIndex(index *Index, neighbourhood *N, char *BaseFilename) { // TODO(matt): LogError() switch(ReadFileIntoBuffer(&Index->Metadata, 0)) @@ -5124,74 +5343,33 @@ DeleteFromIndex(index *Index, int *EntryIndex, char *BaseFilename) Index->Header = *(index_header *)Index->Metadata.Buffer.Ptr; Index->Metadata.Buffer.Ptr += sizeof(Index->Header); - bool Found = FALSE; - int DeleteMetadataFrom = -1; - int DeleteFileFrom = -1; - int DeleteFileTo = -1; - -#if BINARY_SEARCH -START_TIMING_BLOCK(" DeleteFromIndex(%s) BinarySearch: ", BaseFilename); - index_metadata *This = { 0 }; - *EntryIndex = BinarySearchForMetadataEntry(Index, &This, BaseFilename); - if(This) + index_metadata *Entry = { 0 }; + int EntryIndex = BinarySearchForMetadataEntry(Index, &Entry, BaseFilename); + if(Entry) { - Found = TRUE; - DeleteMetadataFrom = (char *)This - Index->Metadata.Buffer.Location; - DeleteFileFrom = AccumulateIndexEntryInsertionOffset(Index, *EntryIndex); - DeleteFileTo = DeleteFileFrom + This->Size; + int DeleteFileFrom = AccumulateIndexEntryInsertionOffset(Index, EntryIndex); + int DeleteFileTo = DeleteFileFrom + Entry->Size; + bool ThisIsPrev = FALSE; + N->ThisIndex = EntryIndex; + GetNeighbourhood(Index, N, EDIT_DELETION, &ThisIsPrev); --Index->Header.EntryCount; - } -#else -START_TIMING_BLOCK(" DeleteFromIndex(%s) LinearSearch: ", BaseFilename); - int SizeAcc = 0; - for(*EntryIndex = 0; *EntryIndex < Index->Header.EntryCount; ++*EntryIndex, Index->Metadata.Buffer.Ptr += sizeof(index_metadata)) - { - index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr; - if(!StringsDiffer(This.BaseFilename, BaseFilename)) - { - Found = TRUE; - --Index->Header.EntryCount; - DeleteMetadataFrom = Index->Metadata.Buffer.Ptr - Index->Metadata.Buffer.Location; - DeleteFileFrom = StringLength("---\n") + SizeAcc; - DeleteFileTo = DeleteFileFrom + This.Size; - break; - } - SizeAcc += This.Size; - } -#endif -END_TIMING_BLOCK(); - - if(Found) - { if(Index->Header.EntryCount == 0) { - buffer IndexDirectory; - ClaimBuffer(&IndexDirectory, "IndexDirectory", 1024); - ConstructDirectoryPath(&IndexDirectory, PAGE_INDEX, Config.IndexLocation, ""); - char IndexPagePath[1024]; - CopyString(IndexPagePath, "%s/index.html", IndexDirectory.Location); - remove(IndexPagePath); - remove(IndexDirectory.Location); - DeclaimBuffer(&IndexDirectory); + DeleteIndexPageFromFilesystem(); remove(Index->Metadata.Path); remove(Index->File.Path); } else { - if(*EntryIndex == Index->Header.EntryCount) // NOTE(matt): LinkNeighbours() requires this - { - --*EntryIndex; - } - if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; } if(!(Index->File.Handle = fopen(Index->File.Path, "w"))) { FreeBuffer(&Index->File.Buffer); return RC_ERROR_FILE; } fwrite(&Index->Header, sizeof(Index->Header), 1, Index->Metadata.Handle); - Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(Index->Header); - - fwrite(Index->Metadata.Buffer.Ptr, DeleteMetadataFrom - sizeof(Index->Header), 1, Index->Metadata.Handle); - fwrite(Index->Metadata.Buffer.Ptr + DeleteMetadataFrom - sizeof(Index->Header) + sizeof(Index->Entry), Index->Metadata.FileSize - DeleteMetadataFrom - sizeof(Index->Entry), 1, Index->Metadata.Handle); + fwrite(Index->Metadata.Buffer.Location + sizeof(index_header), sizeof(index_metadata) * EntryIndex, 1, Index->Metadata.Handle); + fwrite(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * (EntryIndex + 1), + Index->Metadata.FileSize - sizeof(index_header) - sizeof(index_metadata) * (EntryIndex + 1), + 1, Index->Metadata.Handle); fclose(Index->Metadata.Handle); fwrite(Index->File.Buffer.Location, DeleteFileFrom, 1, Index->File.Handle); @@ -5202,7 +5380,7 @@ END_TIMING_BLOCK(); FreeBuffer(&Index->Metadata.Buffer); FreeBuffer(&Index->File.Buffer); - return Found ? RC_SUCCESS : RC_NOOP; + return Entry ? RC_SUCCESS : RC_NOOP; } int @@ -5253,14 +5431,13 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m Theme, Theme); - buffer URLPrefix; - ClaimBuffer(&URLPrefix, "URLPrefix", 1024); - ConstructURLPrefix(&URLPrefix, INCLUDE_JS, PAGE_INDEX); - buffer PlayerURL; ClaimBuffer(&PlayerURL, "PlayerURL", MAX_BASE_URL_LENGTH + 1 + MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1 + MAX_PLAYER_URL_PREFIX_LENGTH + MAX_BASE_FILENAME_LENGTH); ConstructPlayerURL(&PlayerURL, ""); + buffer URLPrefix; + ClaimBuffer(&URLPrefix, "URLPrefix", 1024); + ConstructURLPrefix(&URLPrefix, INCLUDE_JS, PAGE_INDEX); char Script[532 + StringLength(URLPrefix.Location) + (StringLength(Config.ProjectID) * 2)]; CopyString(Script, " \n" @@ -5291,6 +5468,7 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m FreeBuffer(&Index->Metadata.Buffer); return(RC_ERROR_MEMORY); } + CollationBuffers->Index.ID = "Index"; CollationBuffers->Index.Ptr = CollationBuffers->Index.Location; CopyStringToBuffer(&CollationBuffers->Index, queryContainer); @@ -5304,51 +5482,56 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m char Number[16]; char Text[1024]; // NOTE(matt): Surely this will be big enough index_metadata *This; + bool IndexRequired = FALSE; for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex, Index->Metadata.Buffer.Ptr += sizeof(index_metadata)) { This = (index_metadata *)Index->Metadata.Buffer.Ptr; - CopyString(Number, This->BaseFilename + ProjectIDLength); - if(ProjectInfo[ProjectIndex].NumberingScheme == NS_LINEAR) + if(This->Size > 0) { - for(int i = 0; Number[i]; ++i) + IndexRequired = TRUE; + CopyString(Number, This->BaseFilename + ProjectIDLength); + if(ProjectInfo[ProjectIndex].NumberingScheme == NS_LINEAR) { - if(Number[i] == '_') + for(int i = 0; Number[i]; ++i) { - Number[i] = '.'; + if(Number[i] == '_') + { + Number[i] = '.'; + } } } - } - ConstructPlayerURL(&PlayerURL, This->BaseFilename); + ConstructPlayerURL(&PlayerURL, This->BaseFilename); - if(ProjectUnit) - { - CopyStringToBuffer(&CollationBuffers->Index, - "
\n" - " ", PlayerURL.Location); + if(ProjectUnit) + { + CopyStringToBuffer(&CollationBuffers->Index, + " \n"); - } - else - { - CopyStringToBuffer(&CollationBuffers->Index, - " \n"); + } + else + { + CopyStringToBuffer(&CollationBuffers->Index, + " \n"); + CopyStringToBufferHTMLSafe(&CollationBuffers->Index, This->Title); + CopyStringToBuffer(&CollationBuffers->Index, + "\n" + "
\n"); + } } } @@ -5357,7 +5540,8 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m CopyStringToBuffer(&CollationBuffers->Index, Script); FreeBuffer(&Index->Metadata.Buffer); - return RC_SUCCESS; + if(!IndexRequired) { return RC_NOOP; } + else { return RC_SUCCESS; } } else { @@ -5398,7 +5582,7 @@ StripSurroundingSlashes(char *String) // NOTE(matt): For relative paths } int -GeneratePlayerPage(index *Index, int EntryIndex, buffers *CollationBuffers, template *PlayerTemplate, char *BaseFilename) +GeneratePlayerPage(index *Index, neighbourhood *N, buffers *CollationBuffers, template *PlayerTemplate, char *BaseFilename) { buffer OutputDirectoryPath; ClaimBuffer(&OutputDirectoryPath, "OutputDirectoryPath", 1024); @@ -5430,35 +5614,14 @@ GeneratePlayerPage(index *Index, int EntryIndex, buffers *CollationBuffers, temp break; } } - int PlayerOffset = 0; // NOTE(matt): Could just straight up pass the LinkOffsets.PrevStart directly... - BuffersToHTML(CollationBuffers, PlayerTemplate, PlayerPagePath, PAGE_PLAYER, &PlayerOffset); - Index->Entry.LinkOffsets.PrevStart += PlayerOffset; + BuffersToHTML(CollationBuffers, PlayerTemplate, PlayerPagePath, PAGE_PLAYER, &N->This.LinkOffsets.PrevStart); ReadFileIntoBuffer(&Index->Metadata, 0); - Index->Metadata.Buffer.Ptr += sizeof(index_header); - int MetadataInsertionOffset = 0; -#if BINARY_SEARCH -START_TIMING_BLOCK(" GeneratePlayerPage(%s) [passing in EntryIndex]: ", BaseFilename); - MetadataInsertionOffset = sizeof(index_header) + sizeof(index_metadata) * EntryIndex; -#else -START_TIMING_BLOCK(" GeneratePlayerPage(%s) LinearSearch: ", BaseFilename); - for(EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex) - { - index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr; - if(!StringsDiffer(This.BaseFilename, Index->Entry.BaseFilename)) - { - MetadataInsertionOffset = (Index->Metadata.Buffer.Ptr - Index->Metadata.Buffer.Location); - break; - } - Index->Metadata.Buffer.Ptr += sizeof(index_metadata); - } -#endif -END_TIMING_BLOCK(); if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; } - fwrite(Index->Metadata.Buffer.Location, MetadataInsertionOffset, 1, Index->Metadata.Handle); - fwrite(&Index->Entry, sizeof(Index->Entry), 1, Index->Metadata.Handle); - fwrite(Index->Metadata.Buffer.Location + MetadataInsertionOffset + sizeof(Index->Entry), Index->Metadata.FileSize - MetadataInsertionOffset - sizeof(Index->Entry), 1, Index->Metadata.Handle); + Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * N->ThisIndex; + *(index_metadata *)Index->Metadata.Buffer.Ptr = N->This; + fwrite(Index->Metadata.Buffer.Location, Index->Metadata.FileSize, 1, Index->Metadata.Handle); fclose(Index->Metadata.Handle); FreeBuffer(&Index->Metadata.Buffer); @@ -5491,65 +5654,102 @@ GenerateIndexPage(index *Index, buffers *CollationBuffers, template *IndexTempla char IndexPagePath[1024]; CopyString(IndexPagePath, "%s/index.html", OutputDirectoryPath.Location); DeclaimBuffer(&OutputDirectoryPath); - IndexToBuffer(Index, CollationBuffers); - BuffersToHTML(CollationBuffers, IndexTemplate, IndexPagePath, PAGE_INDEX, 0); + switch(IndexToBuffer(Index, CollationBuffers)) + { + case RC_SUCCESS: + { + BuffersToHTML(CollationBuffers, IndexTemplate, IndexPagePath, PAGE_INDEX, 0); + break; + } + case RC_NOOP: + { + DeleteIndexPageFromFilesystem(); + break; + } + } FreeBuffer(&CollationBuffers->Index); return RC_SUCCESS; } int -DeletePlayerPageFromFilesystem(char *BaseFilename, char *PlayerLocation, bool Relocating) +DeleteEntry(index *Index, neighbourhood *Neighbourhood, char *BaseFilename) { - // NOTE(matt): Once we have the notion of an output filename format, we'll need to use that here - buffer OutputDirectoryPath; - ClaimBuffer(&OutputDirectoryPath, "OutputDirectoryPath", 1024); - ConstructDirectoryPath(&OutputDirectoryPath, PAGE_PLAYER, PlayerLocation, BaseFilename); - DIR *PlayerDir; - - if((PlayerDir = opendir(OutputDirectoryPath.Location))) // There is a directory for the Player, which there probably should be if not for manual intervention + if(DeleteFromIndex(Index, Neighbourhood, BaseFilename) == RC_SUCCESS) { - char PlayerPagePath[256]; - CopyString(PlayerPagePath, "%s/index.html", OutputDirectoryPath.Location); - FILE *PlayerPage; - if((PlayerPage = fopen(PlayerPagePath, "r"))) + if(Neighbourhood->This.Size > 0) { - fclose(PlayerPage); - remove(PlayerPagePath); + LinkNeighbours(Index, Neighbourhood, BaseFilename, LINK_EXCLUDE); } - - closedir(PlayerDir); - if((remove(OutputDirectoryPath.Location) == -1)) - { - LogError(LOG_NOTICE, "Mostly deleted %s. Unable to remove directory %s: %s", BaseFilename, OutputDirectoryPath.Location, strerror(errno)); - fprintf(stderr, "\e[1;30mMostly deleted\e[0m %s. \e[1;31mUnable to remove directory\e[0m %s: %s", BaseFilename, OutputDirectoryPath.Location, strerror(errno)); - } - else - { - if(!Relocating) - { - LogError(LOG_INFORMATIONAL, "Deleted %s", BaseFilename); - fprintf(stderr, "\e[1;30mDeleted\e[0m %s\n", BaseFilename); - } - - } - } - DeclaimBuffer(&OutputDirectoryPath); - return RC_SUCCESS; -} - -int -DeleteEntry(index *Index, char *BaseFilename) -{ - int EntryIndex = 0; - if(DeleteFromIndex(Index, &EntryIndex, BaseFilename) == RC_SUCCESS) - { - LinkNeighbours(Index, EntryIndex, BaseFilename, LINK_EXCLUDE); DeletePlayerPageFromFilesystem(BaseFilename, Config.PlayerLocation, FALSE); return RC_SUCCESS; } return RC_NOOP; } +void +InsertEntry(index *Index, neighbourhood *Neighbourhood, buffers *CollationBuffers, template *PlayerTemplate, template *BespokeTemplate, char *BaseFilename, bool *Inserted, bool RecheckingPrivacy) +{ + switch(InsertIntoIndex(Index, Neighbourhood, CollationBuffers, &BespokeTemplate, BaseFilename, RecheckingPrivacy)) + { + case RC_UNFOUND: + LinkNeighbours(Index, Neighbourhood, BaseFilename, LINK_INCLUDE); + *Inserted = TRUE; + case RC_SUCCESS: + { + if(BespokeTemplate->Metadata.Filename && StringsDiffer(BespokeTemplate->Metadata.Filename, "")) + { + GeneratePlayerPage(Index, Neighbourhood, CollationBuffers, BespokeTemplate, BaseFilename); + DeclaimTemplate(BespokeTemplate); + } + else + { + GeneratePlayerPage(Index, Neighbourhood, CollationBuffers, PlayerTemplate, BaseFilename); + } + *Inserted = TRUE; + } break; + } +} + +int +RecheckPrivacy(index *Index, buffers *CollationBuffers, template *IndexTemplate, template *PlayerTemplate, template *BespokeTemplate) +{ + if(ReadFileIntoBuffer(&Index->Metadata, 0) == RC_SUCCESS) + { + Index->Header = *(index_header*)Index->Metadata.Buffer.Ptr; + index_metadata Entry = { 0 }; + int PrivateEntryIndex = 0; + index_metadata PrivateEntries[Index->Header.EntryCount]; + bool Inserted = FALSE; + for(int IndexEntry = 0; IndexEntry < Index->Header.EntryCount; ++IndexEntry) + { + Entry = *(index_metadata *)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * IndexEntry); + if(Entry.Size == 0) + { + PrivateEntries[PrivateEntryIndex] = Entry; + ++PrivateEntryIndex; + } + } + FreeBuffer(&Index->Metadata.Buffer); + + for(int i = 0; i < PrivateEntryIndex; ++i) + { + neighbourhood Neighbourhood = { 0 }; + Neighbourhood.PrevIndex = -1; + Neighbourhood.ThisIndex = -1; + Neighbourhood.NextIndex = -1; + InsertEntry(Index, &Neighbourhood, CollationBuffers, PlayerTemplate, BespokeTemplate, PrivateEntries[i].BaseFilename, &Inserted, TRUE); + } + + if(Inserted) + { + GenerateIndexPage(Index, CollationBuffers, IndexTemplate); + } + + LastPrivacyCheck = time(0); + } + return RC_SUCCESS; +} + int MonitorDirectory(index *Index, buffers *CollationBuffers, template *IndexTemplate, template *PlayerTemplate, template *BespokeTemplate, int inotifyInstance, int WatchDescriptor) { @@ -5561,62 +5761,52 @@ MonitorDirectory(index *Index, buffers *CollationBuffers, template *IndexTemplat #endif buffer Events; - if(ClaimBuffer(&Events, "inotify Events", Kilobytes(1)) == 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 = read(inotifyInstance, Events.Location, Events.Size); + int BytesRead = read(inotifyInstance, Events.Location, Events.Size); // TODO(matt): Handle error EINVAL if(inotifyInstance < 0) { perror("MonitorDirectory()"); } + bool Deleted = FALSE; + bool Inserted = FALSE; + for(Events.Ptr = Events.Location; 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; - char *Ptr; - Ptr = Event->name; + char *Ptr = Event->name; Ptr += (StringLength(Event->name) - StringLength(".hmml")); if(!(StringsDiffer(Ptr, ".hmml"))) { *Ptr = '\0'; - char BaseFilename[256]; - CopyString(BaseFilename, Event->name); - *Ptr = '.'; + neighbourhood Neighbourhood = { 0 }; + Neighbourhood.PrevIndex = -1; + Neighbourhood.ThisIndex = -1; + Neighbourhood.NextIndex = -1; // TODO(matt): Maybe handle IN_ALL_EVENTS if(Event->mask & IN_DELETE || Event->mask & IN_MOVED_FROM) { - if(DeleteEntry(Index, BaseFilename) == RC_SUCCESS) + if(DeleteEntry(Index, &Neighbourhood, Event->name) == RC_SUCCESS) { - GenerateIndexPage(Index, CollationBuffers, IndexTemplate); + Deleted = TRUE; } } else { - int EntryIndex = 0; - switch(InsertIntoIndex(Index, &EntryIndex, CollationBuffers, &BespokeTemplate, BaseFilename)) - { - case RC_SUCCESS: - case RC_UNFOUND: - LinkNeighbours(Index, EntryIndex, BaseFilename, LINK_INCLUDE); - { - if(BespokeTemplate->Metadata.Filename && StringsDiffer(BespokeTemplate->Metadata.Filename, "")) - { - GeneratePlayerPage(Index, EntryIndex, CollationBuffers, BespokeTemplate, BaseFilename); - DeclaimTemplate(BespokeTemplate); - } - else - { - GeneratePlayerPage(Index, EntryIndex, CollationBuffers, PlayerTemplate, BaseFilename); - } - GenerateIndexPage(Index, CollationBuffers, IndexTemplate); - } break; - } + InsertEntry(Index, &Neighbourhood, CollationBuffers, PlayerTemplate, BespokeTemplate, Event->name, &Inserted, 0); } } } + if(Deleted || Inserted) + { + GenerateIndexPage(Index, CollationBuffers, IndexTemplate); + } + DeclaimBuffer(&Events); - return RC_NOOP; + return RC_SUCCESS; } int @@ -5815,6 +6005,22 @@ typedef struct char ID[32]; } index_entry; // Metadata, unless we actually want to bolster this? +void +RemoveChildDirectories(buffer FullPath, char *ParentDirectory) +{ + char *Ptr = FullPath.Location + StringLength(ParentDirectory); + RemoveDirectory(FullPath.Location); + while(FullPath.Ptr > Ptr) + { + if(*FullPath.Ptr == '/') + { + *FullPath.Ptr = '\0'; + RemoveDirectory(FullPath.Location); + } + --FullPath.Ptr; + } +} + int DeleteDeadIndexEntries(index *Index) { @@ -5842,65 +6048,82 @@ DeleteDeadIndexEntries(index *Index) exit(RC_ERROR_FATAL); } + bool NewPlayerLocation = FALSE; + bool NewIndexLocation = FALSE; if(StringsDiffer(Index->Header.PlayerLocation, Config.PlayerLocation)) { - buffer PlayerDirectory; - ClaimBuffer(&PlayerDirectory, "PlayerDirectory", 1024); + buffer OldPlayerDirectory; + ClaimBuffer(&OldPlayerDirectory, "OldPlayerDirectory", 1024); + ConstructDirectoryPath(&OldPlayerDirectory, PAGE_PLAYER, Index->Header.PlayerLocation, 0); + buffer NewPlayerDirectory; + ClaimBuffer(&NewPlayerDirectory, "NewPlayerDirectory", 1024); + ConstructDirectoryPath(&NewPlayerDirectory, PAGE_PLAYER, Config.PlayerLocation, 0); printf("\e[1;33mRelocating Player Page%s from %s to %s\e[0m\n", Index->Header.EntryCount > 1 ? "s" : "", - (StringsDiffer(Index->Header.PlayerLocation, "") ? Index->Header.PlayerLocation : (StringsDiffer(Config.BaseDir, ".") ? Config.BaseDir : "\"Base Directory\"")), - (StringsDiffer(Config.PlayerLocation, "") ? Config.PlayerLocation : (StringsDiffer(Config.BaseDir, ".") ? Config.BaseDir : "\"Base Directory\""))); - Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(Index->Header); + OldPlayerDirectory.Location, + NewPlayerDirectory.Location); + DeclaimBuffer(&NewPlayerDirectory); + for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex) { - index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr; - ConstructDirectoryPath(&PlayerDirectory, PAGE_PLAYER, Index->Header.PlayerLocation, This.BaseFilename); + index_metadata This = *(index_metadata *)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * EntryIndex); + ConstructDirectoryPath(&OldPlayerDirectory, PAGE_PLAYER, Index->Header.PlayerLocation, This.BaseFilename); DeletePlayerPageFromFilesystem(This.BaseFilename, Index->Header.PlayerLocation, TRUE); - Index->Metadata.Buffer.Ptr += sizeof(This); } - DeclaimBuffer(&PlayerDirectory); - RemoveDirectoryRecursively(Index->Header.PlayerLocation); + + ConstructDirectoryPath(&OldPlayerDirectory, PAGE_PLAYER, Index->Header.PlayerLocation, 0); + + if(StringLength(Index->Header.PlayerLocation) > 0) + { + RemoveChildDirectories(OldPlayerDirectory, Config.BaseDir); + } + + DeclaimBuffer(&OldPlayerDirectory); + ClearCopyStringNoFormat(Index->Header.PlayerLocation, sizeof(Index->Header.PlayerLocation), Config.PlayerLocation); - if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; } - fwrite(&Index->Header, sizeof(Index->Header), 1, Index->Metadata.Handle); - Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(Index->Header); - fwrite(Index->Metadata.Buffer.Ptr, Index->Metadata.FileSize - sizeof(Index->Header), 1, Index->Metadata.Handle); - Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location; - fclose(Index->Metadata.Handle); + NewPlayerLocation = TRUE; } if(StringsDiffer(Index->Header.IndexLocation, Config.IndexLocation)) { + buffer OldIndexDirectory; + ClaimBuffer(&OldIndexDirectory, "OldIndexDirectory", 1024); + ConstructDirectoryPath(&OldIndexDirectory, PAGE_INDEX, Index->Header.IndexLocation, 0); + buffer NewIndexDirectory; + ClaimBuffer(&NewIndexDirectory, "NewIndexDirectory", 1024); + ConstructDirectoryPath(&NewIndexDirectory, PAGE_INDEX, Config.IndexLocation, 0); printf("\e[1;33mRelocating Index Page from %s to %s\e[0m\n", - (StringsDiffer(Index->Header.IndexLocation, "") ? Index->Header.IndexLocation : (StringsDiffer(Config.BaseDir, ".") ? Config.BaseDir : "\"Base Directory\"")), - (StringsDiffer(Config.IndexLocation, "") ? Config.IndexLocation : (StringsDiffer(Config.BaseDir, ".") ? Config.BaseDir : "\"Base Directory\""))); - buffer IndexDirectory; - ClaimBuffer(&IndexDirectory, "IndexDirectory", 1024); - ConstructDirectoryPath(&IndexDirectory, PAGE_INDEX, Index->Header.IndexLocation, ""); + OldIndexDirectory.Location, + NewIndexDirectory.Location); + DeclaimBuffer(&NewIndexDirectory); + char IndexPagePath[2048] = { 0 }; - CopyString(IndexPagePath, "%s/index.html", IndexDirectory.Location); + CopyString(IndexPagePath, "%s/index.html", OldIndexDirectory.Location); remove(IndexPagePath); - RemoveDirectoryRecursively(IndexDirectory.Location); - DeclaimBuffer(&IndexDirectory); + if(StringLength(Index->Header.IndexLocation) > 0) + { + RemoveChildDirectories(OldIndexDirectory, Config.BaseDir); + } + DeclaimBuffer(&OldIndexDirectory); ClearCopyStringNoFormat(Index->Header.IndexLocation, sizeof(Index->Header.IndexLocation), Config.IndexLocation); - if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; } - fwrite(&Index->Header, sizeof(Index->Header), 1, Index->Metadata.Handle); - Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(Index->Header); - fwrite(Index->Metadata.Buffer.Ptr, Index->Metadata.FileSize - sizeof(Index->Header), 1, Index->Metadata.Handle); - Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location; - fclose(Index->Metadata.Handle); + NewIndexLocation = TRUE; } - Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(Index->Header); + if(NewPlayerLocation || NewIndexLocation) + { + if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; } + *(index_header *)Index->Metadata.Buffer.Location = Index->Header; + fwrite(Index->Metadata.Buffer.Location, Index->Metadata.FileSize, 1, Index->Metadata.Handle); + fclose(Index->Metadata.Handle); + } index_entry Entries[Index->Header.EntryCount]; for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex) { - index_metadata This = *(index_metadata *)Index->Metadata.Buffer.Ptr; + index_metadata This = *(index_metadata *)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * EntryIndex); CopyStringNoFormat(Entries[EntryIndex].ID, This.BaseFilename); Entries[EntryIndex].Present = FALSE; - Index->Metadata.Buffer.Ptr += sizeof(This); } DIR *ProjectDirHandle; @@ -5939,7 +6162,11 @@ DeleteDeadIndexEntries(index *Index) if(Entries[i].Present == FALSE) { Deleted = TRUE; - DeleteEntry(Index, Entries[i].ID); + neighbourhood Neighbourhood = { 0 }; + Neighbourhood.PrevIndex = -1; + Neighbourhood.ThisIndex = -1; + Neighbourhood.NextIndex = -1; + DeleteEntry(Index, &Neighbourhood, Entries[i].ID); } } @@ -5974,25 +6201,10 @@ SyncIndexWithInput(index *Index, buffers *CollationBuffers, template *IndexTempl if(!(StringsDiffer(Ptr, ".hmml"))) { *Ptr = '\0'; - int EntryIndex = 0; - switch(InsertIntoIndex(Index, &EntryIndex, CollationBuffers, &BespokeTemplate, ProjectFiles->d_name)) - { - case RC_UNFOUND: - LinkNeighbours(Index, EntryIndex, ProjectFiles->d_name, LINK_INCLUDE); - case RC_SUCCESS: - { - if(BespokeTemplate->Metadata.Filename && StringsDiffer(BespokeTemplate->Metadata.Filename, "")) - { - GeneratePlayerPage(Index, EntryIndex, CollationBuffers, BespokeTemplate, ProjectFiles->d_name); - DeclaimTemplate(BespokeTemplate); - } - else - { - GeneratePlayerPage(Index, EntryIndex, CollationBuffers, PlayerTemplate, ProjectFiles->d_name); - } - Inserted = TRUE; - } - } + neighbourhood Neighbourhood = { 0 }; + Neighbourhood.PrevIndex = -1; + Neighbourhood.NextIndex = -1; + InsertEntry(Index, &Neighbourhood, CollationBuffers, PlayerTemplate, BespokeTemplate, ProjectFiles->d_name, &Inserted, 0); } } closedir(ProjectDirHandle); @@ -6021,10 +6233,6 @@ PrintVersions() int main(int ArgC, char **Args) { -#if 0 - printf("%lu\n", sizeof(index_metadata)); - exit(1); -#endif // TODO(matt): Read all defaults from the config config DefaultConfig = { .RootDir = ".", @@ -6045,7 +6253,6 @@ main(int ArgC, char **Args) .Mode = 0, .OutLocation = "out.html", .OutIntegratedLocation = "out_integrated.html", - .ForceIntegration = FALSE, .ProjectDir = ".", .ProjectID = "", .Theme = "", @@ -6071,7 +6278,7 @@ main(int ArgC, char **Args) } char CommandLineArg; - while((CommandLineArg = getopt(ArgC, Args, "a:b:B:c:d:efhi:j:l:m:n:o:p:qr:R:s:t:u:vwx:y:")) != -1) + while((CommandLineArg = getopt(ArgC, Args, "a:b:B:c:d:efghi:j:l:m:n:o:p:qr:R:s:t:u:vwx:y:")) != -1) { switch(CommandLineArg) { @@ -6094,7 +6301,10 @@ main(int ArgC, char **Args) Config.Mode |= MODE_EXAMINE; break; case 'f': - Config.ForceIntegration = TRUE; + Config.Mode |= MODE_FORCEINTEGRATION; + break; + case 'g': + Config.Mode |= MODE_NOPRIVACY; break; case 'i': Config.ImagesDir = StripSurroundingSlashes(optarg); @@ -6354,6 +6564,12 @@ main(int ArgC, char **Args) /* NOTE(matt): Here, I think, is where we'll split into sub-projects (...really?...) */ " Player Page(s): \e[1;30m(-a)\e[0m\t\t%s\n" " Player Page Prefix: \e[1;30m(hardcoded)\e[0m\t%s\n" + "\n" + "Modes\n" + " Force template integration: \e[1;30m(-f)\e[0m\t%s\n" + " Ignore video privacy status: \e[1;30m(-g)\e[0m\t%s\n" + " Quit after sync: \e[1;30m(-q)\e[0m\t\t%s\n" + " Force quote cache rebuild: \e[1;30m(-w)\e[0m\t%s\n" "\n", Config.CacheDir, @@ -6378,7 +6594,12 @@ main(int ArgC, char **Args) StringsDiffer(Config.BaseURL, "") ? Config.BaseURL : "[empty]", StringsDiffer(Config.IndexLocation, "") ? Config.IndexLocation : "(same as base)", StringsDiffer(Config.PlayerLocation, "") ? Config.PlayerLocation : "(directly descended from base)", - StringsDiffer(Config.PlayerURLPrefix, "") ? Config.PlayerURLPrefix : Config.ProjectID); + StringsDiffer(Config.PlayerURLPrefix, "") ? Config.PlayerURLPrefix : Config.ProjectID, + + Config.Mode & MODE_FORCEINTEGRATION ? "on" : "off", + Config.Mode & MODE_NOPRIVACY ? "on" : "off", + Config.Mode & MODE_ONESHOT ? "on" : "off", + Config.Mode & MODE_NOCACHE ? "on" : "off"); if((StringsDiffer(Config.IndexLocation, "") || StringsDiffer(Config.PlayerLocation, "")) && StringLength(Config.BaseURL) == 0) @@ -6406,7 +6627,7 @@ main(int ArgC, char **Args) // 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(&Index, &CollationBuffers, IndexTemplate, PlayerTemplate, BespokeTemplate, inotifyInstance, WatchDescriptor) != RC_ERROR_FATAL) + while(MonitorDirectory(&Index, &CollationBuffers, IndexTemplate, PlayerTemplate, BespokeTemplate, inotifyInstance, WatchDescriptor) != RC_ARENA_FULL) { // TODO(matt): Refetch the quotes and rebuild player pages if needed // @@ -6417,6 +6638,11 @@ main(int ArgC, char **Args) // The most ideal solution is possibly that we store quote numbers in the Metadata->Entry, listen for and handle a // REST PUT request from insobot when a quote changes (unless we're supposed to poll insobot for them?), and rebuild // the player page(s) accordingly. + // + if(!(Config.Mode & MODE_NOPRIVACY) && time(0) - LastPrivacyCheck > 60 * 60 * 4) + { + RecheckPrivacy(&Index, &CollationBuffers, IndexTemplate, PlayerTemplate, BespokeTemplate); + } sleep(Config.UpdateInterval); } } @@ -6439,7 +6665,7 @@ NextFile: if(!(StringsDiffer(Ptr, ".hmml"))) { CopyString(Config.SingleHMMLFilePath, Args[FileIndex]); - switch(HMMLToBuffers(&CollationBuffers, &BespokeTemplate, Args[FileIndex], 0, 0)) + switch(HMMLToBuffers(&CollationBuffers, &BespokeTemplate, Args[FileIndex], 0)) { // TODO(matt): Actually sort out the fatality of these cases, once we are always-on case RC_ERROR_FILE: