diff --git a/README.md b/README.md index 0f608db..3a55adb 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ deployment 4. `cp hmml.a hmmlib.h ../cinera/` 5. `cd ../cinera/` -Note: For each parser update, remember to make and copy it into place +Note: For each parser update, remember to make and copy it into place. -### Install Dependencies +### Install the dependency 1. curl @@ -21,6 +21,20 @@ Note: For each parser update, remember to make and copy it into place 1. `$SHELL cinera.c` +### Configure the server + +If you enforce a strict Content Security Policy and X-Frame-Options in your +server configuration as recommended by [Security +Headers](https://securityheaders.com/), you may enable _Cinera_ to function by +making two small tweaks: + + add_header Content-Security-Policy "default-src … https://www.youtube.com https://s.ytimg.com"; + add_header X-Frame-Options "ALLOW-FROM https://www.youtube.com"; + +Note: For more information about these and other security headers, see Scott +Helme's articles [Content Security Policy - An Introduction](https://scotthelme.co.uk/content-security-policy-an-introduction/) +and [Hardening your HTTP response headers](https://scotthelme.co.uk/hardening-your-http-response-headers/#x-frame-options). + ### Run #### Single Edition operation @@ -28,14 +42,14 @@ Note: For each parser update, remember to make and copy it into place cinera test.hmml This simply generates an HTML file (and updates `cinera_topics.css` if needed) -from `test.hmml` and outputs to `out.html` +from `test.hmml` and outputs to `out.html` (configurable with -o). #### Project Edition operation cinera -p ProjectID Setting the ProjectID with the `-p` flag triggers Project Edition. In this -edition `cinera` monitors the Project Input Directory for new, edited and +edition _Cinera_ monitors the Project Input Directory for new, edited and deleted .hmml files, and generates one table of contents / search page and a player page each for valid sets of annotations (or removes them, if needed). @@ -93,7 +107,12 @@ directory. Typical operation will involve setting these flags: hold up to 255 characters 12 to 15 may hold up to 1023 characters_ Feel free to play with templates to your heart's content. If you do anything -invalid, `cinera` will tell you what's wrong. +invalid, _Cinera_ will tell you what's wrong. + +Note: There is currently an issue sizing the annotation marker container if your +HTML document contains ``. To work around this for now, please do +not declare a DOCTYPE and instead let the page be displayed in [quirks +mode](https://www.w3.org/International/articles/serving-xhtml/#quirks). #### Arguments @@ -123,6 +142,7 @@ invalid, `cinera` will tell you what's wrong. -m Override default default medium ("programming") Known project defaults: + bitwise: programming book: research pcalc: programming riscy: programming @@ -179,6 +199,10 @@ invalid, `cinera` will tell you what's wrong. Display (examine) index file and exit -f Force integration with an incomplete template + -g + Ignore video privacy status + NOTE: For use with projects whose videos are known to all be public, + to save us having to check their privacy status -w Force quote cache rebuild (memory aid: "wget") diff --git a/cinera/cinera.c b/cinera/cinera.c index 7641918..b58fc33 100644 --- a/cinera/cinera.c +++ b/cinera/cinera.c @@ -16,7 +16,7 @@ typedef struct version CINERA_APP_VERSION = { .Major = 0, .Minor = 5, - .Patch = 46 + .Patch = 47 }; // TODO(matt): Copy in the DB 3 stuff from cinera_working.c @@ -207,7 +207,7 @@ typedef struct char BaseURL[MAX_BASE_URL_LENGTH + 1]; char IndexLocation[MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1]; char PlayerLocation[MAX_RELATIVE_PAGE_LOCATION_LENGTH + 1]; - char PlayerURLPrefix[MAX_PLAYER_URL_PREFIX_LENGTH + 1]; + char PlayerURLPrefix[MAX_PLAYER_URL_PREFIX_LENGTH + 1]; // TODO(matt): Replace this with the OutputPath, when we add that } index_header; typedef struct @@ -417,8 +417,8 @@ typedef struct credential_info Credentials[] = { - { "/a_waterman", "Andrew Waterman", "https://www.linkedin.com/in/andrew-waterman-76805788", "", ""}, - { "/y_lee", "Yunsup Lee", "https://www.linkedin.com/in/yunsup-lee-385b692b/", "", ""}, + { "a_waterman", "Andrew Waterman", "https://www.linkedin.com/in/andrew-waterman-76805788", "", ""}, + { "y_lee", "Yunsup Lee", "https://www.linkedin.com/in/yunsup-lee-385b692b/", "", ""}, { "AndrewJDR", "Andrew Johnson", "", "", ""}, { "AsafGartner", "Asaf Gartner", "", "", ""}, { "BretHudson", "Bret Hudson", "http://www.brethudson.com/", "cinera_sprite_patreon.png", "https://www.patreon.com/indieFunction"}, @@ -726,33 +726,6 @@ CopyStringToBufferHTMLSafeBreakingOnSlash_(int LineNumber, buffer *Dest, char *S } } -#define CopyStringToBufferJSONSafe(Dest, String) CopyStringToBufferJSONSafe_(__LINE__, Dest, String) -void -CopyStringToBufferJSONSafe_(int LineNumber, buffer *Dest, char *String) -{ - char *Start = String; - int Length = StringLength(String); - while(*String) - { - switch(*String) - { - case '\\': - case '\"': - *Dest->Ptr++ = '\\'; - ++Length; - default: - *Dest->Ptr++ = *String++; - break; - } - } - if(Dest->Ptr - Dest->Location >= Dest->Size) - { - fprintf(stderr, "CopyStringToBufferJSONSafe(%s) call on line %d cannot accommodate %d(+1)-character JSON-sanitised string:\n" - "%s\n", Dest->ID, LineNumber, Length, Start); - __asm__("int3"); - } -} - #define CopyBuffer(Dest, Src) CopyBuffer_(__LINE__, Dest, Src) void CopyBuffer_(int LineNumber, buffer *Dest, buffer *Src) @@ -770,6 +743,22 @@ CopyBuffer_(int LineNumber, buffer *Dest, buffer *Src) *Dest->Ptr = '\0'; } +#define CopyBufferSized(Dest, Src, Size) CopyBufferSized_(__LINE__, Dest, Src, Size) +void +CopyBufferSized_(int LineNumber, buffer *Dest, buffer *Src, int Size) +{ + Src->Ptr = Src->Location; + while(Src->Ptr - Src->Location < Size) + { + *Dest->Ptr++ = *Src->Ptr++; + } + if(Dest->Ptr - Dest->Location >= Dest->Size) + { + fprintf(stderr, "CopyBufferNoNull(%s) call on line %d cannot accommodate %d(+1)-character %s\n", Dest->ID, LineNumber, StringLength(Src->Location), Src->ID); + __asm__("int3"); + } +} + int StringsDiffer(char *A, char *B) // NOTE(matt): Two null-terminated strings { @@ -2628,7 +2617,7 @@ MediumExists(char *Medium) int ReadFileIntoBuffer(file_buffer *File, int BufferPadding) { - if(!(File->Handle = fopen(File->Path, "r"))) + if(!(File->Handle = fopen(File->Path, "r"))) // TODO(matt): Fuller error handling { return RC_ERROR_FILE; } @@ -2954,6 +2943,7 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen RewindBuffer(&CollationBuffers->Player); RewindBuffer(&CollationBuffers->ScriptPlayer); RewindBuffer(&CollationBuffers->IncludesIndex); + RewindBuffer(&CollationBuffers->Search); *CollationBuffers->Custom0 = '\0'; *CollationBuffers->Custom1 = '\0'; *CollationBuffers->Custom2 = '\0'; @@ -3274,7 +3264,6 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen if(Config.Edition != EDITION_SINGLE && !PrivateVideo) { - RewindBuffer(&CollationBuffers->Search); CopyStringToBuffer(&CollationBuffers->Search, "name: \""); if(StringsDiffer(Config.PlayerURLPrefix, "")) { @@ -3288,7 +3277,7 @@ HMMLToBuffers(buffers *CollationBuffers, template **BespokeTemplate, char *Filen CopyStringToBuffer(&CollationBuffers->Search, "\"\n" "title: \""); - CopyStringToBufferJSONSafe(&CollationBuffers->Search, HMML.metadata.title); + CopyStringToBufferNoFormat(&CollationBuffers->Search, HMML.metadata.title); CopyStringToBuffer(&CollationBuffers->Search, "\"\n" "markers:\n"); } @@ -3681,12 +3670,12 @@ AppendedIdentifier: if(Anno->is_quote && !Anno->text[0]) { CopyStringToBuffer(&CollationBuffers->Search, "\u201C"); - CopyStringToBufferJSONSafe(&CollationBuffers->Search, QuoteInfo.Text); + CopyStringToBufferNoFormat(&CollationBuffers->Search, QuoteInfo.Text); CopyStringToBuffer(&CollationBuffers->Search, "\u201D"); } else { - CopyStringToBufferJSONSafe(&CollationBuffers->Search, Anno->text); + CopyStringToBufferNoFormat(&CollationBuffers->Search, Anno->text); } CopyStringToBuffer(&CollationBuffers->Search, "\"\n"); } @@ -4833,55 +4822,36 @@ GetNeighbourhood(index *Index, neighbourhood *N, int IndexEditType, bool *ThisIs int InsertIntoIndex(index *Index, neighbourhood *N, buffers *CollationBuffers, template **BespokeTemplate, char *BaseFilename, bool RecheckingPrivacy) { - int IndexMetadataFileReadCode = ReadFileIntoBuffer(&Index->Metadata, 0); - switch(IndexMetadataFileReadCode) - { - case RC_ERROR_MEMORY: - return RC_ERROR_MEMORY; - case RC_ERROR_FILE: - case RC_SUCCESS: - break; - } - - int IndexFileReadCode = ReadFileIntoBuffer(&Index->File, 0); - switch(IndexFileReadCode) - { - case RC_ERROR_MEMORY: - return RC_ERROR_MEMORY; - case RC_ERROR_FILE: - case RC_SUCCESS: - break; - } - int IndexEntryInsertionStart = -1; int IndexEntryInsertionEnd = -1; Index->Header.EntryCount = 0; ClearEntry(&Index->Entry); bool Reinserting = FALSE; - if(IndexMetadataFileReadCode == RC_SUCCESS && IndexFileReadCode == RC_SUCCESS) + index_metadata *Entry = { 0 }; + if(Index->Metadata.FileSize > 0 && Index->File.FileSize > 0) { // TODO(matt): Index validation? // Maybe at least if(!StringsDiffer(..., "name: \""); // and check that we won't crash through the end of the file when skipping to the next entry - Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location; - Index->Header = *(index_header *)Index->Metadata.Buffer.Ptr; + Index->Header = *(index_header *)Index->Metadata.Buffer.Location; Index->Header.CurrentDBVersion = CINERA_DB_VERSION; Index->Header.CurrentAppVersion = CINERA_APP_VERSION; Index->Header.CurrentHMMLVersion.Major = hmml_version.Major; Index->Header.CurrentHMMLVersion.Minor = hmml_version.Minor; Index->Header.CurrentHMMLVersion.Patch = hmml_version.Patch; - Index->Metadata.Buffer.Ptr += sizeof(index_header); - Index->File.Buffer.Ptr += StringLength("---\n"); + *(index_header *)Index->Metadata.Buffer.Location = Index->Header; + Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(Index->Header); + Index->File.Buffer.Ptr = Index->File.Buffer.Location + StringLength("---\n"); - index_metadata *Entry = { 0 }; N->ThisIndex = BinarySearchForMetadataEntry(Index, &Entry, BaseFilename); if(Entry) { // Reinsert 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; @@ -4911,17 +4881,11 @@ InsertIntoIndex(index *Index, neighbourhood *N, buffers *CollationBuffers, templ else { // NOTE(matt): Initialising new index_header - Index->Header.CurrentDBVersion = CINERA_DB_VERSION; - Index->Header.CurrentAppVersion = CINERA_APP_VERSION; - Index->Header.CurrentHMMLVersion.Major = hmml_version.Major; - Index->Header.CurrentHMMLVersion.Minor = hmml_version.Minor; - Index->Header.CurrentHMMLVersion.Patch = hmml_version.Patch; - - Index->Header.InitialDBVersion = CINERA_DB_VERSION; - Index->Header.InitialAppVersion = CINERA_APP_VERSION; - Index->Header.InitialHMMLVersion.Major = hmml_version.Major; - Index->Header.InitialHMMLVersion.Minor = hmml_version.Minor; - Index->Header.InitialHMMLVersion.Patch = hmml_version.Patch; + Index->Header.InitialDBVersion = Index->Header.CurrentDBVersion = CINERA_DB_VERSION; + Index->Header.InitialAppVersion = Index->Header.CurrentAppVersion = CINERA_APP_VERSION; + Index->Header.InitialHMMLVersion.Major = Index->Header.CurrentHMMLVersion.Major = hmml_version.Major; + Index->Header.InitialHMMLVersion.Minor = Index->Header.CurrentHMMLVersion.Minor = hmml_version.Minor; + Index->Header.InitialHMMLVersion.Patch = Index->Header.CurrentHMMLVersion.Patch = hmml_version.Patch; CopyStringNoFormat(Index->Header.ProjectID, sizeof(Index->Header.ProjectID), Config.ProjectID); @@ -4958,7 +4922,7 @@ InsertIntoIndex(index *Index, neighbourhood *N, buffers *CollationBuffers, templ switch(HMMLToBuffers(CollationBuffers, BespokeTemplate, InputFile, N)) { - // TODO(matt): Actually sort out the fatality of these cases, once we are always-on + // TODO(matt): Actually sort out the fatality of these cases case RC_ERROR_FILE: case RC_ERROR_FATAL: return RC_ERROR_FATAL; @@ -4971,75 +4935,69 @@ InsertIntoIndex(index *Index, neighbourhood *N, buffers *CollationBuffers, templ VideoIsPrivate = TRUE; case RC_SUCCESS: break; - }; + } 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(!Reinserting) { ++Index->Header.EntryCount; } - - 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); - } + if(N->This.Size > 0) { ClearCopyStringNoFormat(N->This.Title, sizeof(N->This.Title), CollationBuffers->Title); } 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 - 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, N->This.Size, 1, Index->File.Handle); - fwrite(Index->File.Buffer.Location + IndexEntryInsertionEnd, Index->File.FileSize - IndexEntryInsertionEnd, 1, Index->File.Handle); - - if(VideoIsPrivate) + if(!VideoIsPrivate) { - if(!RecheckingPrivacy) + if(!(Index->File.Handle = fopen(Index->File.Path, "w"))) { return RC_ERROR_FILE; } + fwrite(Index->File.Buffer.Location, IndexEntryInsertionStart, 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); + fclose(Index->File.Handle); + + if(N->This.Size == IndexEntryInsertionEnd - IndexEntryInsertionStart) { - LogError(LOG_NOTICE, "Privately Reinserted %s", BaseFilename); - fprintf(stderr, "\e[0;34mPrivately Reinserted\e[0m %s\n", BaseFilename); + Index->File.Buffer.Ptr = Index->File.Buffer.Location + IndexEntryInsertionStart; + CopyBufferSized(&Index->File.Buffer, &CollationBuffers->Search, N->This.Size); + } + else + { + FreeBuffer(&Index->File.Buffer); + ReadFileIntoBuffer(&Index->File, 0); } - } - 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(IndexEntryInsertionStart >= 0) - { - // Insert new - 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, N->This.Size, 1, Index->File.Handle); - fwrite(Index->File.Buffer.Location + IndexEntryInsertionStart, Index->File.FileSize - IndexEntryInsertionStart, 1, Index->File.Handle); - - if(VideoIsPrivate) + else if(!RecheckingPrivacy) { - if(!RecheckingPrivacy) + LogError(LOG_NOTICE, "Privately Reinserted %s", BaseFilename); + fprintf(stderr, "\e[0;34mPrivately Reinserted\e[0m %s\n", BaseFilename); + } + } + else + { + ++Index->Header.EntryCount; + + if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { return RC_ERROR_FILE; } + fwrite(&Index->Header, sizeof(index_header), 1, Index->Metadata.Handle); + + if(!(Index->File.Handle = fopen(Index->File.Path, "w"))) { return RC_ERROR_FILE; } + + if(IndexEntryInsertionStart >= 0) + { + // Insert new + 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, N->This.Size, 1, Index->File.Handle); + fwrite(Index->File.Buffer.Location + IndexEntryInsertionStart, Index->File.FileSize - IndexEntryInsertionStart, 1, Index->File.Handle); + + if(!VideoIsPrivate) + { + LogError(LOG_NOTICE, "Inserted %s - %s", BaseFilename, CollationBuffers->Title); + fprintf(stderr, "\e[1;32mInserted\e[0m %s - %s\n", BaseFilename, CollationBuffers->Title); + } + else if(!RecheckingPrivacy) { LogError(LOG_NOTICE, "Privately Inserted %s", BaseFilename); fprintf(stderr, "\e[0;34mPrivately Inserted\e[0m %s\n", BaseFilename); @@ -5047,46 +5005,42 @@ InsertIntoIndex(index *Index, neighbourhood *N, buffers *CollationBuffers, templ } 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.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(&N->This, sizeof(index_metadata), 1, Index->Metadata.Handle); - fwrite(CollationBuffers->Search.Location, N->This.Size, 1, Index->File.Handle); - if(VideoIsPrivate) - { - if(!RecheckingPrivacy) + // Append new + if(Index->Metadata.FileSize > 0) + { + 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(&N->This, sizeof(index_metadata), 1, Index->Metadata.Handle); + fwrite(CollationBuffers->Search.Location, N->This.Size, 1, Index->File.Handle); + + if(!VideoIsPrivate) + { + LogError(LOG_NOTICE, "Appended %s - %s", BaseFilename, CollationBuffers->Title); + fprintf(stderr, "\e[1;32mAppended\e[0m %s - %s\n", BaseFilename, CollationBuffers->Title); + } + else 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); + FreeBuffer(&Index->Metadata.Buffer); + ReadFileIntoBuffer(&Index->Metadata, 0); + + fclose(Index->File.Handle); + FreeBuffer(&Index->File.Buffer); + ReadFileIntoBuffer(&Index->File, 0); } - fclose(Index->Metadata.Handle); - fclose(Index->File.Handle); - - FreeBuffer(&Index->Metadata.Buffer); - FreeBuffer(&Index->File.Buffer); // 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; + return VideoIsPrivate ? RC_PRIVATE_VIDEO : RC_SUCCESS; } void @@ -5279,19 +5233,6 @@ DeleteNeighbourLinks(file_buffer *File, index_metadata *Metadata) 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, "LinkNeighbours(): %s", strerror(errno)); - return RC_ERROR_MEMORY; - case RC_SUCCESS: - break; - } - - Index->Header = *(index_header *)Index->Metadata.Buffer.Ptr; - if(N->PrevIndex == -1 && N->NextIndex == -1) { buffer LonePlayerPagePath; @@ -5304,9 +5245,6 @@ LinkNeighbours(index *Index, neighbourhood *N, char *BaseFilename, int LinkType) 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 { @@ -5335,15 +5273,11 @@ LinkNeighbours(index *Index, neighbourhood *N, char *BaseFilename, int LinkType) 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: { 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; + *(index_metadata *)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * N->PrevIndex) = N->Prev; } } @@ -5375,28 +5309,17 @@ LinkNeighbours(index *Index, neighbourhood *N, char *BaseFilename, int LinkType) 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; + *(index_metadata *)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * N->NextIndex) = N->Next; } } DeclaimBuffer(&NextPlayerPagePath); - } } - - 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; } @@ -5457,30 +5380,8 @@ int DeleteFromIndex(index *Index, neighbourhood *N, char *BaseFilename) { // TODO(matt): LogError() - switch(ReadFileIntoBuffer(&Index->Metadata, 0)) - { - case RC_ERROR_FILE: - return RC_ERROR_FILE; - case RC_ERROR_MEMORY: - LogError(LOG_ERROR, "DeleteFromIndex(): %s", strerror(errno)); - return RC_ERROR_MEMORY; - case RC_SUCCESS: - break; - } - - switch(ReadFileIntoBuffer(&Index->File, 0)) - { - case RC_ERROR_FILE: - return RC_ERROR_FILE; - case RC_ERROR_MEMORY: - LogError(LOG_ERROR, "DeleteFromIndex(): %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->Header = *(index_header *)Index->Metadata.Buffer.Location; + Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(Index->Header); index_metadata *Entry = { 0 }; int EntryIndex = BinarySearchForMetadataEntry(Index, &Entry, BaseFilename); @@ -5496,8 +5397,14 @@ DeleteFromIndex(index *Index, neighbourhood *N, char *BaseFilename) if(Index->Header.EntryCount == 0) { DeleteIndexPageFromFilesystem(); + remove(Index->Metadata.Path); + Index->Metadata.FileSize = 0; + FreeBuffer(&Index->Metadata.Buffer); + remove(Index->File.Path); + Index->File.FileSize = 0; + FreeBuffer(&Index->File.Buffer); } else { @@ -5511,24 +5418,27 @@ DeleteFromIndex(index *Index, neighbourhood *N, char *BaseFilename) 1, Index->Metadata.Handle); fclose(Index->Metadata.Handle); + FreeBuffer(&Index->Metadata.Buffer); + ReadFileIntoBuffer(&Index->Metadata, 0); + fwrite(Index->File.Buffer.Location, DeleteFileFrom, 1, Index->File.Handle); fwrite(Index->File.Buffer.Location + DeleteFileTo, Index->File.FileSize - DeleteFileTo, 1, Index->File.Handle); fclose(Index->File.Handle); + + FreeBuffer(&Index->File.Buffer); + ReadFileIntoBuffer(&Index->File, 0); } } - FreeBuffer(&Index->Metadata.Buffer); - FreeBuffer(&Index->File.Buffer); return Entry ? RC_SUCCESS : RC_NOOP; } int IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy malloc's CollationBuffers->Index { - if(ReadFileIntoBuffer(&Index->Metadata, 0) == RC_SUCCESS) + if(Index->Metadata.FileSize > 0) { - Index->Header = *(index_header*)Index->Metadata.Buffer.Ptr; - Index->Metadata.Buffer.Ptr += sizeof(Index->Header); + Index->Header = *(index_header*)Index->Metadata.Buffer.Location; bool ProjectFound = FALSE; int ProjectIndex; @@ -5544,7 +5454,6 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m if(!ProjectFound) { fprintf(stderr, "Missing Project Info for %s\n", Config.ProjectID); - FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_PROJECT; } @@ -5599,11 +5508,8 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m int EntryLength = StringLength(PlayerURL.Location) + sizeof(Text) + 82; CollationBuffers->Index.Size = StringLength(queryContainer) + (Index->Header.EntryCount * EntryLength) + StringLength(Script); - if(!(CollationBuffers->Index.Location = malloc(CollationBuffers->Index.Size))) - { - FreeBuffer(&Index->Metadata.Buffer); - return(RC_ERROR_MEMORY); - } + if(!(CollationBuffers->Index.Location = malloc(CollationBuffers->Index.Size))) { return RC_ERROR_MEMORY; } + CollationBuffers->Index.ID = "Index"; CollationBuffers->Index.Ptr = CollationBuffers->Index.Location; @@ -5612,6 +5518,7 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m int ProjectIDLength = StringLength(Config.ProjectID); bool IndexRequired = FALSE; + Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + sizeof(Index->Header); for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex, Index->Metadata.Buffer.Ptr += sizeof(index_metadata)) { This = (index_metadata *)Index->Metadata.Buffer.Ptr; @@ -5667,7 +5574,6 @@ IndexToBuffer(index *Index, buffers *CollationBuffers) // NOTE(matt): This guy m CopyStringToBuffer(&CollationBuffers->Index, "%s", Script); - FreeBuffer(&Index->Metadata.Buffer); if(!IndexRequired) { return RC_NOOP; } else { return RC_SUCCESS; } } @@ -5742,16 +5648,14 @@ GeneratePlayerPage(index *Index, neighbourhood *N, buffers *CollationBuffers, te break; } } + BuffersToHTML(CollationBuffers, PlayerTemplate, PlayerPagePath, PAGE_PLAYER, &N->This.LinkOffsets.PrevStart); - ReadFileIntoBuffer(&Index->Metadata, 0); + *(index_metadata *)(Index->Metadata.Buffer.Location + sizeof(index_header) + sizeof(index_metadata) * N->ThisIndex) = N->This; - if(!(Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"))) { FreeBuffer(&Index->Metadata.Buffer); return RC_ERROR_FILE; } - 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); + Index->Metadata.Handle = fopen(Index->Metadata.Path, "w"); + fwrite(Index->Metadata.Buffer.Location, Index->Metadata.Buffer.Size, 1, Index->Metadata.Handle); fclose(Index->Metadata.Handle); - FreeBuffer(&Index->Metadata.Buffer); if(IndexInTemplate) { @@ -5814,36 +5718,32 @@ DeleteEntry(index *Index, neighbourhood *Neighbourhood, char *BaseFilename) return RC_NOOP; } -void -InsertEntry(index *Index, neighbourhood *Neighbourhood, buffers *CollationBuffers, template *PlayerTemplate, template *BespokeTemplate, char *BaseFilename, bool *Inserted, bool RecheckingPrivacy) +int +InsertEntry(index *Index, neighbourhood *Neighbourhood, buffers *CollationBuffers, template *PlayerTemplate, template *BespokeTemplate, char *BaseFilename, bool RecheckingPrivacy) { - switch(InsertIntoIndex(Index, Neighbourhood, CollationBuffers, &BespokeTemplate, BaseFilename, RecheckingPrivacy)) + if(InsertIntoIndex(Index, Neighbourhood, CollationBuffers, &BespokeTemplate, BaseFilename, RecheckingPrivacy) == RC_SUCCESS) { - case RC_UNFOUND: - LinkNeighbours(Index, Neighbourhood, BaseFilename, LINK_INCLUDE); - *Inserted = TRUE; - case RC_SUCCESS: - { - if(StringsDiffer(BespokeTemplate->Metadata.Filename, "")) - { - GeneratePlayerPage(Index, Neighbourhood, CollationBuffers, BespokeTemplate, BaseFilename); - DeclaimTemplate(BespokeTemplate); - } - else - { - GeneratePlayerPage(Index, Neighbourhood, CollationBuffers, PlayerTemplate, BaseFilename); - } - *Inserted = TRUE; - } break; + LinkNeighbours(Index, Neighbourhood, BaseFilename, LINK_INCLUDE); + if(StringsDiffer(BespokeTemplate->Metadata.Filename, "")) + { + GeneratePlayerPage(Index, Neighbourhood, CollationBuffers, BespokeTemplate, BaseFilename); + DeclaimTemplate(BespokeTemplate); + } + else + { + GeneratePlayerPage(Index, Neighbourhood, CollationBuffers, PlayerTemplate, BaseFilename); + } + return RC_SUCCESS; } + return RC_NOOP; } int RecheckPrivacy(index *Index, buffers *CollationBuffers, template *IndexTemplate, template *PlayerTemplate, template *BespokeTemplate) { - if(ReadFileIntoBuffer(&Index->Metadata, 0) == RC_SUCCESS) + if(Index->Metadata.FileSize > 0) { - Index->Header = *(index_header*)Index->Metadata.Buffer.Ptr; + Index->Header = *(index_header*)Index->Metadata.Buffer.Location; index_metadata Entry = { }; int PrivateEntryIndex = 0; index_metadata PrivateEntries[Index->Header.EntryCount]; @@ -5857,7 +5757,6 @@ RecheckPrivacy(index *Index, buffers *CollationBuffers, template *IndexTemplate, ++PrivateEntryIndex; } } - FreeBuffer(&Index->Metadata.Buffer); for(int i = 0; i < PrivateEntryIndex; ++i) { @@ -5865,7 +5764,7 @@ RecheckPrivacy(index *Index, buffers *CollationBuffers, template *IndexTemplate, Neighbourhood.PrevIndex = -1; Neighbourhood.ThisIndex = -1; Neighbourhood.NextIndex = -1; - InsertEntry(Index, &Neighbourhood, CollationBuffers, PlayerTemplate, BespokeTemplate, PrivateEntries[i].BaseFilename, &Inserted, TRUE); + Inserted = (InsertEntry(Index, &Neighbourhood, CollationBuffers, PlayerTemplate, BespokeTemplate, PrivateEntries[i].BaseFilename, TRUE) == RC_SUCCESS); } if(Inserted) @@ -5895,9 +5794,10 @@ MonitorDirectory(index *Index, buffers *CollationBuffers, template *IndexTemplat int BytesRead = read(inotifyInstance, Events.Location, Events.Size); // TODO(matt): Handle error EINVAL if(inotifyInstance < 0) { perror("MonitorDirectory()"); } - bool Deleted = FALSE; - bool Inserted = FALSE; + struct inotify_event *FinalFileEvents[1024]; + int FinalFileEventsCount = 0; + // TODO(matt): Test this with longer update intervals, and combinations of events... for(Events.Ptr = Events.Location; Events.Ptr < Events.Location + BytesRead && Events.Ptr - Events.Location < Events.Size; Events.Ptr += sizeof(struct inotify_event) + Event->len) @@ -5908,23 +5808,37 @@ MonitorDirectory(index *Index, buffers *CollationBuffers, template *IndexTemplat if(!(StringsDiffer(Ptr, ".hmml"))) { *Ptr = '\0'; - neighbourhood Neighbourhood = { }; - 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) + bool FoundEvent = FALSE; + int FinalFileEventsIndex; + for(FinalFileEventsIndex = 0; FinalFileEventsIndex < FinalFileEventsCount; ++FinalFileEventsIndex) { - if(DeleteEntry(Index, &Neighbourhood, Event->name) == RC_SUCCESS) + if(!StringsDiffer(FinalFileEvents[FinalFileEventsIndex]->name, Event->name)) { - Deleted = TRUE; + FinalFileEvents[FinalFileEventsIndex] = Event; + FoundEvent = TRUE; } } - else - { - InsertEntry(Index, &Neighbourhood, CollationBuffers, PlayerTemplate, BespokeTemplate, Event->name, &Inserted, 0); - } + if(!FoundEvent) { FinalFileEvents[FinalFileEventsIndex] = Event; ++FinalFileEventsCount; } + } + } + + bool Deleted = FALSE; + bool Inserted = FALSE; + for(int FinalFileEventsIndex = 0; FinalFileEventsIndex < FinalFileEventsCount; ++FinalFileEventsIndex) + { + neighbourhood Neighbourhood = { }; + Neighbourhood.PrevIndex = -1; + Neighbourhood.ThisIndex = -1; + Neighbourhood.NextIndex = -1; + + // TODO(matt): Maybe handle IN_ALL_EVENTS + if(FinalFileEvents[FinalFileEventsIndex]->mask & IN_DELETE || FinalFileEvents[FinalFileEventsIndex]->mask & IN_MOVED_FROM) + { + Deleted = (DeleteEntry(Index, &Neighbourhood, FinalFileEvents[FinalFileEventsIndex]->name) == RC_SUCCESS); + } + else + { + Inserted = (InsertEntry(Index, &Neighbourhood, CollationBuffers, PlayerTemplate, BespokeTemplate, FinalFileEvents[FinalFileEventsIndex]->name, 0) == RC_SUCCESS); } } @@ -6057,17 +5971,6 @@ UpgradeDB(index *Index) fwrite(&Index->Header, sizeof(Index->Header), 1, Index->Metadata.Handle); Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location + OriginalHeaderSize; - - if(ReadFileIntoBuffer(&Index->File, 0) == RC_ERROR_FILE) - { - fprintf(stderr, "\e[1;31mUnable to open index file\e[0m %s: %s\n" - "Removing %s and starting afresh\n", Index->File.Path, strerror(errno), - Index->Metadata.Path); - fclose(Index->Metadata.Handle); - FreeBuffer(&Index->Metadata.Buffer); - remove(Index->Metadata.Path); - return RC_ERROR_FILE; - } Index->File.Buffer.Ptr += StringLength("---\n"); for(int EntryIndex = 0; EntryIndex < Index->Header.EntryCount; ++EntryIndex) @@ -6097,8 +6000,6 @@ UpgradeDB(index *Index) Index->File.Buffer.Ptr = IndexEntryStart; } - FreeBuffer(&Index->File.Buffer); - fclose(Index->Metadata.Handle); FreeBuffer(&Index->Metadata.Buffer); if(ReadFileIntoBuffer(&Index->Metadata, 0) == RC_ERROR_FILE) @@ -6112,9 +6013,7 @@ UpgradeDB(index *Index) { 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 + OriginalHeaderSize; - fwrite(Index->Metadata.Buffer.Ptr, Index->Metadata.FileSize - OriginalHeaderSize, 1, Index->Metadata.Handle); - Index->Metadata.Buffer.Ptr = Index->Metadata.Buffer.Location; + fwrite(Index->Metadata.Buffer.Location + OriginalHeaderSize, Index->Metadata.FileSize - OriginalHeaderSize, 1, Index->Metadata.Handle); fclose(Index->Metadata.Handle); FreeBuffer(&Index->Metadata.Buffer); if(ReadFileIntoBuffer(&Index->Metadata, 0) == RC_ERROR_FILE) @@ -6154,12 +6053,7 @@ DeleteDeadIndexEntries(index *Index) { // TODO(matt): More rigorously figure out who we should delete // Maybe compare the output directory and the input HMML names - if(ReadFileIntoBuffer(&Index->Metadata, 0) == RC_ERROR_FILE) - { - return RC_ERROR_FILE; - } - - Index->Header = *(index_header *)Index->Metadata.Buffer.Ptr; + Index->Header = *(index_header *)Index->Metadata.Buffer.Location; if(Index->Header.CurrentDBVersion < CINERA_DB_VERSION) { if(CINERA_DB_VERSION == 4) @@ -6167,8 +6061,7 @@ DeleteDeadIndexEntries(index *Index) fprintf(stderr, "\n\e[1;31mHandle conversion from CINERA_DB_VERSION %d to %d!\e[0m\n\n", Index->Header.CurrentDBVersion, CINERA_DB_VERSION); exit(RC_ERROR_FATAL); } - if(UpgradeDB(Index) == RC_ERROR_FILE) { return RC_NOOP; - } + if(UpgradeDB(Index) == RC_ERROR_FILE) { return RC_NOOP; } } else if(Index->Header.CurrentDBVersion > CINERA_DB_VERSION) { @@ -6209,6 +6102,7 @@ DeleteDeadIndexEntries(index *Index) DeclaimBuffer(&OldPlayerDirectory); ClearCopyStringNoFormat(Index->Header.PlayerLocation, sizeof(Index->Header.PlayerLocation), Config.PlayerLocation); + *(index_header *)Index->Metadata.Buffer.Location = Index->Header; NewPlayerLocation = TRUE; } if(StringsDiffer(Index->Header.IndexLocation, Config.IndexLocation)) @@ -6234,13 +6128,13 @@ DeleteDeadIndexEntries(index *Index) DeclaimBuffer(&OldIndexDirectory); ClearCopyStringNoFormat(Index->Header.IndexLocation, sizeof(Index->Header.IndexLocation), Config.IndexLocation); + *(index_header *)Index->Metadata.Buffer.Location = Index->Header; NewIndexLocation = TRUE; } 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); } @@ -6298,7 +6192,6 @@ DeleteDeadIndexEntries(index *Index) } } - FreeBuffer(&Index->Metadata.Buffer); return Deleted ? RC_SUCCESS : RC_NOOP; } @@ -6306,7 +6199,7 @@ int SyncIndexWithInput(index *Index, buffers *CollationBuffers, template *IndexTemplate, template *PlayerTemplate, template *BespokeTemplate) { bool Deleted = FALSE; - if(DeleteDeadIndexEntries(Index) == RC_SUCCESS) + if(Index->Metadata.FileSize > 0 && Index->File.FileSize > 0 && DeleteDeadIndexEntries(Index) == RC_SUCCESS) { Deleted = TRUE; } @@ -6332,7 +6225,7 @@ SyncIndexWithInput(index *Index, buffers *CollationBuffers, template *IndexTempl neighbourhood Neighbourhood = { }; Neighbourhood.PrevIndex = -1; Neighbourhood.NextIndex = -1; - InsertEntry(Index, &Neighbourhood, CollationBuffers, PlayerTemplate, BespokeTemplate, ProjectFiles->d_name, &Inserted, 0); + Inserted = (InsertEntry(Index, &Neighbourhood, CollationBuffers, PlayerTemplate, BespokeTemplate, ProjectFiles->d_name, 0) == RC_SUCCESS); } } closedir(ProjectDirHandle); @@ -6738,19 +6631,23 @@ main(int ArgC, char **Args) } index Index = { }; + Index.Metadata.Buffer.ID = "IndexMetadata"; CopyString(Index.Metadata.Path, sizeof(Index.Metadata.Path), "%s/%s.metadata", Config.BaseDir, Config.ProjectID); + ReadFileIntoBuffer(&Index.Metadata, 0); // NOTE(matt): Could we actually catch errors (permissions?) here and bail? + Index.File.Buffer.ID = "IndexFile"; CopyString(Index.File.Path, sizeof(Index.File.Path), "%s/%s.index", Config.BaseDir, Config.ProjectID); + ReadFileIntoBuffer(&Index.File, 0); // NOTE(matt): Could we actually catch errors (permissions?) here and bail? - printf("┌╼ Synchronising with annotation files in Project Input Directory ╾┐\n"); + printf("┌╼ Synchronising with Annotations Directory ╾┐\n"); SyncIndexWithInput(&Index, &CollationBuffers, IndexTemplate, PlayerTemplate, BespokeTemplate); if(Config.Mode & MODE_ONESHOT) { goto RIP; } - printf("\n┌╼ Monitoring Project Directory for \e[1;32mnew\e[0m, \e[1;33medited\e[0m and \e[1;30mdeleted\e[0m .hmml files ╾┐\n"); + printf("\n┌╼ Monitoring Annotations Directory for \e[1;32mnew\e[0m, \e[1;33medited\e[0m and \e[1;30mdeleted\e[0m .hmml files ╾┐\n"); int inotifyInstance = inotify_init1(IN_NONBLOCK); // 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); @@ -6795,7 +6692,7 @@ NextFile: CopyString(Config.SingleHMMLFilePath, sizeof(Config.SingleHMMLFilePath), "%s", Args[FileIndex]); switch(HMMLToBuffers(&CollationBuffers, &BespokeTemplate, Args[FileIndex], 0)) { - // TODO(matt): Actually sort out the fatality of these cases, once we are always-on + // TODO(matt): Actually sort out the fatality of these cases case RC_ERROR_FILE: case RC_ERROR_FATAL: goto RIP; @@ -6816,7 +6713,7 @@ NextFile: 0, PAGE_PLAYER, 0)) { - // TODO(matt): Actually sort out the fatality of these cases, once we are always-on + // TODO(matt): Actually sort out the fatality of these cases case RC_INVALID_TEMPLATE: if(HasBespokeTemplate) { DeclaimTemplate(BespokeTemplate); } if(FileIndex < (ArgC - 1)) { goto NextFile; } diff --git a/cinera/cinera_search.js b/cinera/cinera_search.js index 6612d46..5854ce6 100644 --- a/cinera/cinera_search.js +++ b/cinera/cinera_search.js @@ -233,7 +233,7 @@ xhr.addEventListener("load", function() { } else if (line.startsWith("name:")) { episode.name = line.slice(6); } else if (line.startsWith("title:")) { - episode.title = line.slice(7).replace(/"/g, ""); + episode.title = line.slice(7).trim().slice(1, -1); } else if (line.startsWith("markers")) { mode = "markers"; episode.markers = [];