diff --git a/cinera/cinera.c b/cinera/cinera.c index a40d9d0..f86587e 100644 --- a/cinera/cinera.c +++ b/cinera/cinera.c @@ -23,7 +23,7 @@ typedef struct version CINERA_APP_VERSION = { .Major = 0, .Minor = 7, - .Patch = 13 + .Patch = 14 }; #include // NOTE(matt): varargs @@ -4238,14 +4238,20 @@ BinarySearchForMetadataLandmark(db_asset *Asset, landmark_range ProjectRange, in void PrintEntryIndex(db_project_index Project, int64_t EntryIndex) { - fprintf(stderr, "%s%i:%i%s %s%3li%s", + fprintf(stderr, "%s%2i:%2i%s %s%3li%s", ColourStrings[CS_MAGENTA], Project.Generation, Project.Index, ColourStrings[CS_END], ColourStrings[CS_BLUE_BOLD], EntryIndex, ColourStrings[CS_END]); } void -PrintLandmark(db_landmark *L) +PrintLandmark(db_landmark *L, uint16_t *Index) { + if(Index) + { + Colourise(CS_BLACK_BOLD); + fprintf(stderr, "[%4u] ", *Index); + Colourise(CS_END); + } PrintEntryIndex(L->Project, L->EntryIndex); fprintf(stderr, " %6u", L->Position); } @@ -4256,7 +4262,7 @@ PrintAsset(db_asset *A, uint16_t *Index) if(Index) { Colourise(CS_BLACK_BOLD); - fprintf(stderr, "[%i]", *Index); + fprintf(stderr, "[%4u]", *Index); Colourise(CS_END); } string FilenameL = Wrap0i(A->Filename, sizeof(A->Filename)); @@ -4301,7 +4307,7 @@ PrintAssetAndLandmarks(db_asset *A, uint16_t *Index) db_landmark *FirstLandmark = LocateFirstLandmark(A); //Colourise(CS_BLACK_BOLD); fprintf(stderr, "%4u ", 0); Colourise(CS_END); //PrintLandmark(FirstLandmark); - for(int i = 0; i < A->LandmarkCount; ++i) + for(uint16_t i = 0; i < A->LandmarkCount; ++i) { db_landmark *This = FirstLandmark + i; if((i % 8) == 0) @@ -4312,8 +4318,8 @@ PrintAssetAndLandmarks(db_asset *A, uint16_t *Index) { PrintC(CS_BLACK_BOLD, " │ "); } - Colourise(CS_BLACK_BOLD); fprintf(stderr, "%4u ", i); Colourise(CS_END); - PrintLandmark(This); + //Colourise(CS_BLACK_BOLD); fprintf(stderr, "%4u ", i); Colourise(CS_END); + PrintLandmark(This, &i); } fprintf(stderr, "\n"); } @@ -4789,7 +4795,7 @@ SnipeChecksumIntoHTML(db_asset *Asset, buffer *Checksum) { PrintC(CS_ERROR, "\nInvalid landmark (aborting update): "); PrintAsset(Asset, 0); - PrintLandmark(Landmark); + PrintLandmark(Landmark, 0); Result = RC_FAILURE; break; } @@ -6904,9 +6910,10 @@ BuildQuote(quote_info *Info, string Speaker, int ID, bool ShouldFetchQuotes) return Result; } -int +rc GenerateTopicColours(neighbourhood *N, string Topic) { + // TODO(matt): Maybe straighten out the return code situation? // NOTE(matt): Stack-string char SanitisedTopic[Topic.Length + 1]; CopyString(SanitisedTopic, sizeof(SanitisedTopic), "%.*s", (int)Topic.Length, Topic.Base); @@ -6949,43 +6956,27 @@ GenerateTopicColours(neighbourhood *N, string Topic) closedir(CSSDirHandle); *Ptr = '/'; - if((Topics.Handle = fopen(Topics.Path, "a+"))) + ReadFileIntoBuffer(&Topics); + + bool Exists = FALSE; + while(Topics.Buffer.Ptr - Topics.Buffer.Location < Topics.Buffer.Size) { - fseek(Topics.Handle, 0, SEEK_END); - Topics.Buffer.Size = ftell(Topics.Handle); - fseek(Topics.Handle, 0, SEEK_SET); - - if(!(Topics.Buffer.Location = malloc(Topics.Buffer.Size))) + Topics.Buffer.Ptr += StringLength(".category."); + if(!StringsDifferT(SanitisedTopic, Topics.Buffer.Ptr, ' ')) { - return RC_ERROR_MEMORY; + Exists = TRUE; + break; } - -#if DEBUG_MEM - FILE *MemLog = fopen("/home/matt/cinera_mem", "a+"); - fprintf(MemLog, " Allocated Topics (%ld)\n", Topics.Buffer.Size); - fclose(MemLog); - printf(" Allocated Topics (%ld)\n", Topics.Buffer.Size); -#endif - - Topics.Buffer.Ptr = Topics.Buffer.Location; - fread(Topics.Buffer.Location, Topics.Buffer.Size, 1, Topics.Handle); - - while(Topics.Buffer.Ptr - Topics.Buffer.Location < Topics.Buffer.Size) + while(Topics.Buffer.Ptr - Topics.Buffer.Location < Topics.Buffer.Size && *Topics.Buffer.Ptr != '\n') { - Topics.Buffer.Ptr += StringLength(".category."); - if(!StringsDifferT(SanitisedTopic, Topics.Buffer.Ptr, ' ')) - { - FreeBuffer(&Topics.Buffer); - fclose(Topics.Handle); - return RC_NOOP; - } - while(Topics.Buffer.Ptr - Topics.Buffer.Location < Topics.Buffer.Size && *Topics.Buffer.Ptr != '\n') - { - ++Topics.Buffer.Ptr; - } ++Topics.Buffer.Ptr; } + ++Topics.Buffer.Ptr; + } + if(!Exists) + { + Topics.Handle = fopen(Topics.Path, "a+"); if(!StringsDifferLv0(Topic, "nullTopic")) { fprintf(Topics.Handle, ".category.%s { border: 1px solid transparent; background: transparent; }\n", @@ -6999,14 +6990,15 @@ GenerateTopicColours(neighbourhood *N, string Topic) SanitisedTopic, Colour.Hue, Colour.Saturation, Colour.Lightness, Colour.Hue, Colour.Saturation, Colour.Lightness); } - fclose(Topics.Handle); #if DEBUG_MEM MemLog = fopen("/home/matt/cinera_mem", "a+"); fprintf(MemLog, " Freed Topics (%ld)\n", Topics.Buffer.Size); fclose(MemLog); printf(" Freed Topics (%ld)\n", Topics.Buffer.Size); #endif - FreeBuffer(&Topics.Buffer); + + fclose(Topics.Handle); + FreeFile(&Topics); asset *Asset = GetPlaceInBook(&Assets, ASSET_CSS_TOPICS); if(Asset->Known) @@ -7020,14 +7012,8 @@ GenerateTopicColours(neighbourhood *N, string Topic) asset *CSSTopics = BuiltinAssets + ASSET_CSS_TOPICS; PlaceAsset(Wrap0(CSSTopics->Filename), CSSTopics->Type, CSSTopics->Variants, CSSTopics->Associated, ASSET_CSS_TOPICS); } - return RC_SUCCESS; - } - else - { - // NOTE(matt): Maybe it shouldn't be possible to hit this case now that we MakeDir the actual dir... - perror(Topics.Path); - return RC_ERROR_FILE; } + return RC_SUCCESS; } void @@ -7166,8 +7152,8 @@ enable syntax highlighting:")); // Defaults // NewSection("Defaults", &IndentationLevel); - scope_tree *ScopeTree = calloc(1, sizeof(scope_tree)); - InitScopeBooks(ScopeTree); + + scope_tree *ScopeTree = InitRootScopeTree(); SetTypeSpec(ScopeTree, &TypeSpecs); SetDefaults(ScopeTree, &TypeSpecs); PrintScopeTree(ScopeTree, IndentationLevel); @@ -9303,6 +9289,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF case RC_ERROR_MEMORY: HMMLCleanup(); return RC_ERROR_FATAL; + default: break; }; if(!HasFilterMenu) { @@ -9587,6 +9574,7 @@ AppendedIdentifier: case RC_ERROR_MEMORY: HMMLCleanup(); return RC_ERROR_FATAL; + default: break; } if(!HasFilterMenu) { @@ -9607,6 +9595,7 @@ AppendedIdentifier: case RC_ERROR_MEMORY: HMMLCleanup(); return RC_ERROR_FATAL; + default: break; }; InsertCategory(&Topics, &LocalTopics, &Media, &LocalMedia, Wrap0("nullTopic")); } @@ -10424,9 +10413,48 @@ GenerateNavigation(config *C, project *Target, navigation_buffer *NavBuffer) } } -int +void +SortLandmarks(memory_book *A) +{ + for(int AssetIndex = 0; AssetIndex < A->ItemCount; ++AssetIndex) + { + asset *This = GetPlaceInBook(A, AssetIndex); + for(int SearchLandmarkIndex = 0; SearchLandmarkIndex < This->SearchLandmarkCount; ++SearchLandmarkIndex) + { + landmark *A = This->Search + SearchLandmarkIndex; + for(int TestIndex = SearchLandmarkIndex + 1; TestIndex < This->SearchLandmarkCount; ++TestIndex) + { + landmark *B = This->Search + TestIndex; + if(A->Offset > B->Offset) + { + landmark Temp = *A; + *A = *B; + *B = Temp; + } + } + } + + for(int PlayerLandmarkIndex = 0; PlayerLandmarkIndex < This->PlayerLandmarkCount; ++PlayerLandmarkIndex) + { + landmark *A = This->Player + PlayerLandmarkIndex; + for(int TestIndex = PlayerLandmarkIndex + 1; TestIndex < This->PlayerLandmarkCount; ++TestIndex) + { + landmark *B = This->Player + TestIndex; + if(A->Offset > B->Offset) + { + landmark Temp = *A; + *A = *B; + *B = Temp; + } + } + } + } +} + +rc BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template *Template, char *OutputPath, page_type PageType, unsigned int *PlayerOffset) { + rc Result = RC_SUCCESS; MEM_TEST_INITIAL(); #if DEBUG printf("\n\n --- Buffer Collation ---\n" @@ -10465,7 +10493,8 @@ BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template * LogError(LOG_ERROR, "BuffersToHTML(): %s", strerror(errno)); MEM_TEST_AFTER("BuffersToHTML"); - return RC_ERROR_MEMORY; + Result = RC_ERROR_MEMORY; + goto End; } #if DEBUG_MEM @@ -10619,7 +10648,8 @@ BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template * #endif MEM_TEST_AFTER("BuffersToHTML"); - return RC_ERROR_FILE; + Result = RC_ERROR_FILE; + goto End; } fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile); fclose(OutFile); @@ -10634,16 +10664,14 @@ BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template * #endif MEM_TEST_AFTER("BuffersToHTML"); - return RC_SUCCESS; } else { MEM_TEST_AFTER("BuffersToHTML"); - return RC_INVALID_TEMPLATE; + Result = RC_INVALID_TEMPLATE; } #endif // AFE MEM_TEST_AFTER("BuffersToHTML"); - return RC_SUCCESS; // NOTE(matt): We added this simply to squash the "end of non-void function" warning } else { @@ -10657,7 +10685,8 @@ BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template * LogError(LOG_ERROR, "BuffersToHTML(): %s", strerror(errno)); MEM_TEST_AFTER("BuffersToHTML"); - return RC_ERROR_MEMORY; + Result = RC_ERROR_MEMORY; + goto End; } MEM_TEST_MID("BuffersToHTML3"); Master.Ptr = Master.Location; @@ -10700,7 +10729,8 @@ BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template * LogError(LOG_ERROR, "Unable to open output file %s: %s", OutputPath, strerror(errno)); DeclaimBuffer(&Master); MEM_TEST_AFTER("BuffersToHTML"); - return RC_ERROR_FILE; + Result = RC_ERROR_FILE; + goto End; } MEM_TEST_MID("BuffersToHTML13"); fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile); @@ -10710,8 +10740,11 @@ BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template * OutFile = 0; FreeBuffer(&Master); MEM_TEST_AFTER("BuffersToHTML"); - return RC_SUCCESS; + Result = RC_SUCCESS; } +End: + SortLandmarks(&Assets); + return Result; } int @@ -11042,14 +11075,11 @@ GetNeighbourhoodForAddition(neighbourhood *N, edit_type_id EditType) void GetNeighbourhoodForDeletion(neighbourhood *N) { - char *Ptr = (char *)N->Project; - Ptr += sizeof(*N->Project); - db_entry *FirstEntry = (db_entry *)Ptr; - db_entry *Entry = FirstEntry + N->ThisIndex; + db_entry *FirstEntry = LocateFirstEntry(N->Project); N->PreDeletionThisIndex = N->ThisIndex; - N->This = Entry; + db_entry *Entry; int EntryIndex; N->DeletedEntryWasFirst = TRUE; @@ -11608,7 +11638,6 @@ AddLandmarks(neighbourhood *N, project *P, edit_type_id EditType) for(int j = 0; j < AssetInMemory->PlayerLandmarkCount; ++j) { db_landmark Landmark = {}; - // TODO(matt): Actually make sure that the correct project_index is set! Landmark.Project = P->Index; Landmark.EntryIndex = N->ThisIndex; Landmark.Position = AssetInMemory->Player[j].Offset; @@ -11718,31 +11747,45 @@ VerifyLandmarks_(neighbourhood *N, int LineNumber) db_block_assets *Block = LocateBlock(B_ASET); db_asset *Asset = LocateFirstAsset(Block); - for(int i = 0; i < Block->Count; ++i) + bool Malformed = FALSE; + for(uint16_t i = 0; i < Block->Count; ++i) { + bool Titled = FALSE; db_landmark *Landmark = LocateFirstLandmark(Asset); - for(int j = 0; j < Asset->LandmarkCount; ++j, ++Landmark) + for(uint16_t j = 0; j < Asset->LandmarkCount; ++j, ++Landmark) { if(j + 1 < Asset->LandmarkCount) { db_landmark *Next = Landmark + 1; if((ProjectIndicesDiffer(Next->Project, Landmark->Project) < 0) || - (ProjectIndicesMatch(Next->Project, Landmark->Project) && Next->EntryIndex < Landmark->EntryIndex)) + (ProjectIndicesMatch(Next->Project, Landmark->Project) && Next->EntryIndex < Landmark->EntryIndex) || + (ProjectIndicesMatch(Next->Project, Landmark->Project) && Next->EntryIndex == Landmark->EntryIndex && Next->Position < Landmark->Position)) { - PrintC(CS_ERROR, "We fail, sadly\n"); - PrintAssetsBlock(0); - PrintLandmark(Landmark); - fprintf(stderr, " vs "); - PrintLandmark(Next); + if(!Titled) + { + fprintf(stderr, "\nOut-of-order landmarks\n"); + PrintAsset(Asset, &i); + Titled = TRUE; + } + //PrintAssetsBlock(0); + PrintLandmark(Landmark, &j); + PrintC(CS_YELLOW, " vs "); + PrintLandmark(Next, 0); fprintf(stderr, "\n"); - PrintNeighbourhood(N); - _exit(1); + //PrintNeighbourhood(N); + //_exit(1); + Malformed = TRUE; } } } Asset = SkipAsset(Asset); } + if(Malformed) + { + _exit(1); + } + #if 0 DB.Metadata.Buffer.Ptr = DB.Metadata.Buffer.Location; DB.Header = *(db_header *)DB.Metadata.Buffer.Ptr; @@ -12263,57 +12306,97 @@ LinkOverDeletedEntry(neighbourhood *N) { if(N->DeletedEntryWasFirst) { - N->PreLinkNextOffsetTotal = N->Next->LinkOffsets.PrevEnd - + N->Next->LinkOffsets.NextStart - + N->Next->LinkOffsets.NextEnd; + if(N->Next) // NOTE(matt): Should be impossible to fail this test + { + N->PreLinkNextOffsetTotal = N->Next->LinkOffsets.PrevEnd + + N->Next->LinkOffsets.NextStart + + N->Next->LinkOffsets.NextEnd; - MarkNextAsFirst(N); + MarkNextAsFirst(N); - N->NextOffsetModifier = N->Next->LinkOffsets.PrevEnd - + N->Next->LinkOffsets.NextStart - + N->Next->LinkOffsets.NextEnd - - N->PreLinkNextOffsetTotal; + N->NextOffsetModifier = N->Next->LinkOffsets.PrevEnd + + N->Next->LinkOffsets.NextStart + + N->Next->LinkOffsets.NextEnd + - N->PreLinkNextOffsetTotal; + } + else + { + PrintC(CS_ERROR, "Error: Malformed neighbourhood"); + PrintNeighbourhood(N); + } return; } else if(N->DeletedEntryWasFinal) { - N->PreLinkPrevOffsetTotal = N->Prev->LinkOffsets.PrevEnd - + N->Prev->LinkOffsets.NextStart - + N->Prev->LinkOffsets.NextEnd; + if(N->Prev) // NOTE(matt): Should be impossible to fail this test + { + N->PreLinkPrevOffsetTotal = N->Prev->LinkOffsets.PrevEnd + + N->Prev->LinkOffsets.NextStart + + N->Prev->LinkOffsets.NextEnd; - MarkPrevAsFinal(N); + MarkPrevAsFinal(N); - N->PrevOffsetModifier = N->Prev->LinkOffsets.PrevEnd - + N->Prev->LinkOffsets.NextStart - + N->Prev->LinkOffsets.NextEnd - - N->PreLinkPrevOffsetTotal; + N->PrevOffsetModifier = N->Prev->LinkOffsets.PrevEnd + + N->Prev->LinkOffsets.NextStart + + N->Prev->LinkOffsets.NextEnd + - N->PreLinkPrevOffsetTotal; + } + else + { + PrintC(CS_ERROR, "Error: Malformed neighbourhood"); + PrintNeighbourhood(N); + } return; } else { // Assert(N->PrevIndex >= 0 && N->NextIndex >= 0) - N->PreLinkPrevOffsetTotal = N->Prev->LinkOffsets.PrevEnd - + N->Prev->LinkOffsets.NextStart - + N->Prev->LinkOffsets.NextEnd; + if(N->Prev) + { + N->PreLinkPrevOffsetTotal = N->Prev->LinkOffsets.PrevEnd + + N->Prev->LinkOffsets.NextStart + + N->Prev->LinkOffsets.NextEnd; + } + else + { + PrintC(CS_ERROR, "Error: Malformed neighbourhood"); + PrintNeighbourhood(N); + } - N->PreLinkNextOffsetTotal = N->Next->LinkOffsets.PrevEnd - + N->Next->LinkOffsets.NextStart - + N->Next->LinkOffsets.NextEnd; + if(N->Next) + { + N->PreLinkNextOffsetTotal = N->Next->LinkOffsets.PrevEnd + + N->Next->LinkOffsets.NextStart + + N->Next->LinkOffsets.NextEnd; + } + else + { + PrintC(CS_ERROR, "Error: Malformed neighbourhood"); + PrintNeighbourhood(N); + } } - InsertNeighbourLink(N->Project, N->Prev, N->Next, LINK_FORWARDS, N->FormerIsFirst); - InsertNeighbourLink(N->Project, N->Next, N->Prev, LINK_BACKWARDS, N->LatterIsFinal); + if(N->Prev && N->Next) + { + InsertNeighbourLink(N->Project, N->Prev, N->Next, LINK_FORWARDS, N->FormerIsFirst); + InsertNeighbourLink(N->Project, N->Next, N->Prev, LINK_BACKWARDS, N->LatterIsFinal); - N->PrevOffsetModifier = N->Prev->LinkOffsets.PrevEnd - + N->Prev->LinkOffsets.NextStart - + N->Prev->LinkOffsets.NextEnd - - N->PreLinkPrevOffsetTotal; + N->PrevOffsetModifier = N->Prev->LinkOffsets.PrevEnd + + N->Prev->LinkOffsets.NextStart + + N->Prev->LinkOffsets.NextEnd + - N->PreLinkPrevOffsetTotal; - N->NextOffsetModifier = N->Next->LinkOffsets.PrevEnd - + N->Next->LinkOffsets.NextStart - + N->Next->LinkOffsets.NextEnd - - N->PreLinkNextOffsetTotal; + N->NextOffsetModifier = N->Next->LinkOffsets.PrevEnd + + N->Next->LinkOffsets.NextStart + + N->Next->LinkOffsets.NextEnd + - N->PreLinkNextOffsetTotal; + } + else + { + PrintC(CS_ERROR, "Error: Malformed neighbourhood"); + PrintNeighbourhood(N); + } } } } @@ -12334,6 +12417,7 @@ LinkNeighbours(neighbourhood *N, enum8(link_types) LinkType) rc DeleteFromDB(neighbourhood *N, string BaseFilename) { + ResetNeighbourhood(N); // TODO(matt): LogError() db_entry *Entry = 0; int EntryIndex = BinarySearchForMetadataEntry(N->Project, &Entry, BaseFilename); @@ -14813,49 +14897,76 @@ SyncProject(project *P, neighbourhood *N, buffers *CollationBuffers, template *B PushWatchHandle(P->HMMLDir, EXT_HMML, WT_HMML, P, 0); } +char *inotifyEventStrings[] = +{ + /* 0x00000001 */ "IN_ACCESS", + /* 0x00000002 */ "IN_MODIFY", + /* 0x00000004 */ "IN_ATTRIB", + /* 0x00000008 */ "IN_CLOSE_WRITE", + /* 0x00000010 */ "IN_CLOSE_NOWRITE", + /* 0x00000020 */ "IN_OPEN", + /* 0x00000040 */ "IN_MOVED_FROM", + /* 0x00000080 */ "IN_MOVED_TO", + /* 0x00000100 */ "IN_CREATE", + /* 0x00000200 */ "IN_DELETE", + /* 0x00000400 */ "IN_DELETE_SELF", + /* 0x00000800 */ "IN_MOVE_SELF", + /* 0x00001000 */ "", // NOTE(matt): Apparently doesn't exist + /* 0x00002000 */ "IN_UNMOUNT", + /* 0x00004000 */ "IN_Q_OVERFLOW", + /* 0x00008000 */ "IN_IGNORED", + /* 0x00010000 */ "", + /* 0x00020000 */ "", + /* 0x00040000 */ "", + /* 0x00080000 */ "", + /* 0x00100000 */ "", + /* 0x00200000 */ "", + /* 0x00400000 */ "", + /* 0x00800000 */ "", + /* 0x01000000 */ "IN_ONLYDIR", + /* 0x02000000 */ "IN_DONT_FOLLOW", + /* 0x04000000 */ "IN_EXCL_UNLINK", + /* 0x08000000 */ "", // NOTE(matt): Apparently doesn't exist + /* 0x10000000 */ "IN_MASK_CREATE", + /* 0x20000000 */ "IN_MASK_ADD", + /* 0x40000000 */ "IN_ISDIR", + /* 0x80000000 */ "IN_ONESHOT", +}; + #define DEBUG_EVENTS 0 #if DEBUG_EVENTS void -PrintEvent(struct inotify_event *Event, int EventIndex) +PrintEvent(struct inotify_event *Event, int EventIndex, int Indentation) { + fprintf(stderr, "\n\n"); + Indent(Indentation); + fprintf(stderr, "Event[%d]\n", EventIndex); + Indent(Indentation); + fprintf(stderr, " wd: %d\n", Event->wd); + Indent(Indentation); + fprintf(stderr, " mask: 0x%08X", Event->mask); - printf("\nEvent[%d]\n" - " wd: %d\n" - " mask: %d\n", - EventIndex, - Event->wd, - Event->mask); + for(int i = 0; i < ArrayCount(inotifyEventStrings); ++i) + { + if(Event->mask & (1 << i)) + { + fprintf(stderr, "\n"); + Indent(Indentation); + fprintf(stderr, " %s", inotifyEventStrings[i]); + } + } - if(Event->mask & IN_ACCESS) { printf(" IN_ACCESS\n"); } - if(Event->mask & IN_ATTRIB) { printf(" IN_ATTRIB\n"); } - if(Event->mask & IN_CLOSE_WRITE) { printf(" IN_CLOSE_WRITE\n"); } - if(Event->mask & IN_CLOSE_NOWRITE) { printf(" IN_CLOSE_NOWRITE\n"); } - if(Event->mask & IN_CREATE) { printf(" IN_CREATE\n"); } - if(Event->mask & IN_DELETE) { printf(" IN_DELETE\n"); } - if(Event->mask & IN_DELETE_SELF) { printf(" IN_DELETE_SELF\n"); } - if(Event->mask & IN_MODIFY) { printf(" IN_MODIFY\n"); } - if(Event->mask & IN_MOVE_SELF) { printf(" IN_MOVE_SELF\n"); } - if(Event->mask & IN_MOVED_FROM) { printf(" IN_MOVED_FROM\n"); } - if(Event->mask & IN_MOVED_TO) { printf(" IN_MOVED_TO\n"); } - if(Event->mask & IN_OPEN) { printf(" IN_OPEN\n"); } - if(Event->mask & IN_MOVE) { printf(" IN_MOVE\n"); } - if(Event->mask & IN_CLOSE) { printf(" IN_CLOSE\n"); } - if(Event->mask & IN_DONT_FOLLOW) { printf(" IN_DONT_FOLLOW\n"); } - if(Event->mask & IN_EXCL_UNLINK) { printf(" IN_EXCL_UNLINK\n"); } - if(Event->mask & IN_MASK_ADD) { printf(" IN_MASK_ADD\n"); } - if(Event->mask & IN_ONESHOT) { printf(" IN_ONESHOT\n"); } - if(Event->mask & IN_ONLYDIR) { printf(" IN_ONLYDIR\n"); } - if(Event->mask & IN_IGNORED) { printf(" IN_IGNORED\n"); } - if(Event->mask & IN_ISDIR) { printf(" IN_ISDIR\n"); } - if(Event->mask & IN_Q_OVERFLOW) { printf(" IN_Q_OVERFLOW\n"); } - if(Event->mask & IN_UNMOUNT) { printf(" IN_UNMOUNT\n"); } + fprintf(stderr, "\n"); + Indent(Indentation); + fprintf(stderr, " cookie: %d", Event->cookie); - printf( " cookie: %d\n" - " len: %d\n" - " name: %s\n", - Event->cookie, - Event->len, - Event->name); + fprintf(stderr, "\n"); + Indent(Indentation); + fprintf(stderr, " len: %d", Event->len); + + fprintf(stderr, "\n"); + Indent(Indentation); + fprintf(stderr, " name: %s", Event->name); } #endif @@ -14995,6 +15106,56 @@ ParseAndEitherPrintConfigOrInitAll(string ConfigPath, memory_book *TokensList, n } } +#if DEBUG_EVENTS +void +SquashEventsD(buffer *Events, int BytesRead, struct inotify_event **Event, int *DebugEventIndex) +{ + char *PeekPtr = Events->Ptr + sizeof(struct inotify_event) + (*Event)->len; + struct inotify_event *Peek = (struct inotify_event *)PeekPtr; + bool Squashed = FALSE; + while(PeekPtr - Events->Location < BytesRead && (*Event)->wd == Peek->wd && StringsMatch(Wrap0((*Event)->name), Wrap0(Peek->name))) + { + if(!Squashed) + { + fprintf(stderr, "\n\n" + " Squashing events:"); + + } + + PrintEvent(Peek, ++*DebugEventIndex, 1); + + Squashed = TRUE; + + *Event = Peek; + Events->Ptr = PeekPtr; + + PeekPtr = Events->Ptr + sizeof(struct inotify_event) + (*Event)->len; + Peek = (struct inotify_event *)PeekPtr; + } + + if(Squashed) + { + fprintf(stderr, "\n\n" + " Finished squashing\n"); + } +} +#endif + +void +SquashEvents(buffer *Events, int BytesRead, struct inotify_event **Event) +{ + char *PeekPtr = Events->Ptr + sizeof(struct inotify_event) + (*Event)->len; + struct inotify_event *Peek = (struct inotify_event *)PeekPtr; + while(PeekPtr - Events->Location < BytesRead && (*Event)->wd == Peek->wd && StringsMatch(Wrap0((*Event)->name), Wrap0(Peek->name))) + { + *Event = Peek; + Events->Ptr = PeekPtr; + + PeekPtr = Events->Ptr + sizeof(struct inotify_event) + (*Event)->len; + Peek = (struct inotify_event *)PeekPtr; + } +} + int MonitorFilesystem(neighbourhood *N, buffers *CollationBuffers, template *BespokeTemplate, string ConfigPath, memory_book *TokensList) { @@ -15004,9 +15165,6 @@ MonitorFilesystem(neighbourhood *N, buffers *CollationBuffers, template *Bespoke struct inotify_event *Event; int BytesRead = read(inotifyInstance, Events.Location, Events.Size); // TODO(matt): Handle error EINVAL - if(inotifyInstance < 0) { perror("MonitorFilesystem()"); } - - // TODO(matt): Test this with longer update intervals, and combinations of events... #if DEBUG_EVENTS if(BytesRead > 0) { @@ -15031,46 +15189,56 @@ MonitorFilesystem(neighbourhood *N, buffers *CollationBuffers, template *Bespoke Event = (struct inotify_event *)Events.Ptr; #if DEBUG_EVENTS - PrintEvent(Event, DebugEventIndex); + PrintEvent(Event, DebugEventIndex, 0); //PrintWatchHandles(); #endif watch_file *WatchFile = GetWatchFileForEvent(Event); if(WatchFile) { - char *PeekPtr = Events.Ptr + sizeof(struct inotify_event) + Event->len; - struct inotify_event *Peek = (struct inotify_event *)PeekPtr; - while(PeekPtr - Events.Location < BytesRead && Event->wd == Peek->wd && StringsMatch(Wrap0i(Event->name, Event->len), Wrap0i(Peek->name, Peek->len))) - { #if DEBUG_EVENTS - fprintf(stderr, "Squashing events:\n" - " "); - PrintEvent(Event, DebugEventIndex++); - fprintf(stderr, "\n" - " "); - PrintEvent(Peek, DebugEventIndex); - fprintf(stderr, "\n"); + SquashEventsD(&Events, BytesRead, &Event, &DebugEventIndex); +#else + SquashEvents(&Events, BytesRead, &Event); #endif + bool WouldHaveDeleted = (Event->mask & (IN_DELETE | IN_MOVED_FROM)) ? TRUE : FALSE; - Event = Peek; - Events.Ptr = PeekPtr; + if(WouldHaveDeleted && (Events.Ptr + sizeof(struct inotify_event) + Event->len) - Events.Location == BytesRead) + { + fprintf(stderr, "Sleeping before polling for more filesystem events"); + sleep(1); - PeekPtr = Events.Ptr + sizeof(struct inotify_event) + Event->len; - Peek = (struct inotify_event *)PeekPtr; + struct inotify_event EventN = *Event; + char EventNName[Event->len + 1]; + ClearCopyStringNoFormat(EventNName, sizeof(EventNName), Wrap0(Event->name)); + + char *EventSlot1 = Events.Location + sizeof(struct inotify_event) + Event->len; + + int NewBytesRead = read(inotifyInstance, EventSlot1, Events.Size - (EventSlot1 - Events.Location)); // TODO(matt): Handle error EINVAL + if(NewBytesRead >= 0) + { + BytesRead = sizeof(struct inotify_event) + EventN.len + NewBytesRead; + struct inotify_event *Event0 = (struct inotify_event *)Events.Location; + *Event0 = EventN; + CopyStringNoFormat(Event0->name, Event0->len, Wrap0(EventNName)); + Event = Event0; + Events.Ptr = Events.Location; +#if DEBUG_EVENTS + SquashEventsD(&Events, BytesRead, &Event, &DebugEventIndex); +#else + SquashEvents(&Events, BytesRead, &Event); +#endif + } } switch(WatchFile->Type) { - // TODO(matt): We're probably watching for too many events when the target directory exists case WT_HMML: { SetCurrentProject(WatchFile->Project, N); - //PrintLineage(CurrentProject->Lineage, TRUE); - string BaseFilename = GetBaseFilename(Wrap0(Event->name), WatchFile->Extension); if(Event->mask & (IN_DELETE | IN_MOVED_FROM)) { - // TODO(matt): Why are we getting here after editing a .hmml file? We should just reinsert Deleted |= (DeleteEntry(N, BaseFilename) == RC_SUCCESS); } else if(Event->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)) @@ -15080,8 +15248,6 @@ MonitorFilesystem(neighbourhood *N, buffers *CollationBuffers, template *Bespoke } break; case WT_ASSET: { - // TODO(matt): Why are we getting here after editing a .hmml file? - //PrintAssetsBlock(0); UpdateAsset(WatchFile->Asset, FALSE); UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts); #if DEBUG_LANDMARKS @@ -15210,6 +15376,7 @@ main(int ArgC, char **Args) InitWatchHandles((IN_CREATE | IN_MOVED_FROM | IN_MOVED_TO | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF), Kilobytes(4)); inotifyInstance = inotify_init1(IN_NONBLOCK); + int inotifyError = errno; string ConfigPathL = Wrap0(ConfigPath); PushWatchHandle(ConfigPathL, EXT_NULL, WT_CONFIG, 0, 0); @@ -15250,24 +15417,31 @@ main(int ArgC, char **Args) } } - //PrintWatchHandles(); - while(MonitorFilesystem(&Neighbourhood, &CollationBuffers, &BespokeTemplate, ConfigPathL, &TokensList) != RC_ARENA_FULL) + if(inotifyInstance) { - // TODO(matt): Refetch the quotes and rebuild player pages if needed - // - // Every sixty mins, redownload the quotes and, I suppose, SyncDBWithInput(). But here we still don't even know - // who the speaker is. To know, we'll probably have to store all quoted speakers in the project's .metadata. Maybe - // postpone this for now, but we will certainly need this to happen - // - // 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(!(Mode & MODE_DRYRUN) && Config && Config->RespectingPrivacy && time(0) - LastPrivacyCheck > Config->PrivacyCheckInterval) + while(MonitorFilesystem(&Neighbourhood, &CollationBuffers, &BespokeTemplate, ConfigPathL, &TokensList) != RC_ARENA_FULL) { - RecheckPrivacy(&Neighbourhood, &CollationBuffers, &BespokeTemplate); + // TODO(matt): Refetch the quotes and rebuild player pages if needed + // + // Every sixty mins, redownload the quotes and, I suppose, SyncDBWithInput(). But here we still don't even know + // who the speaker is. To know, we'll probably have to store all quoted speakers in the project's .metadata. Maybe + // postpone this for now, but we will certainly need this to happen + // + // 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(!(Mode & MODE_DRYRUN) && Config && Config->RespectingPrivacy && time(0) - LastPrivacyCheck > Config->PrivacyCheckInterval) + { + RecheckPrivacy(&Neighbourhood, &CollationBuffers, &BespokeTemplate); + } + sleep(GLOBAL_UPDATE_INTERVAL); } - sleep(GLOBAL_UPDATE_INTERVAL); + } + else + { + fprintf(stderr, "Error: Quitting because inotify initialisation failed with message\n" + " %s\n", strerror(inotifyError)); } DiscardAllAndFreeConfig(); diff --git a/cinera/cinera_config.c b/cinera/cinera_config.c index 2b89285..e7bed48 100644 --- a/cinera/cinera_config.c +++ b/cinera/cinera_config.c @@ -1543,6 +1543,14 @@ InitScopeBooks(scope_tree *Scope) InitBook(&Scope->BoolPairs, sizeof(config_bool_pair), 4, MBT_CONFIG_BOOL_PAIR); } +scope_tree * +InitRootScopeTree(void) +{ + scope_tree *Result = calloc(1, sizeof(scope_tree)); + InitScopeBooks(Result); + return Result; +} + scope_tree * PushScope(memory_book *TypeSpecs, scope_tree *Parent, config_pair *ID) { @@ -4583,8 +4591,7 @@ ParseConfig(string Path, memory_book *TokensList) if(T) { - scope_tree *ScopeTree = calloc(1, sizeof(scope_tree)); - InitScopeBooks(ScopeTree); + scope_tree *ScopeTree = InitRootScopeTree(); SetTypeSpec(ScopeTree, &TypeSpecs); SetDefaults(ScopeTree, &TypeSpecs); ScopeTree = ScopeTokens(ScopeTree, TokensList, T, &TypeSpecs, 0); @@ -4597,8 +4604,7 @@ ParseConfig(string Path, memory_book *TokensList) FreeTokensList(TokensList); MEM_LOOP_PRE_WORK() T = Tokenise(TokensList, Path); - ScopeTree = calloc(1, sizeof(scope_tree)); - InitScopeBooks(ScopeTree); + ScopeTree = InitRootScopeTree(); SetTypeSpec(ScopeTree, &TypeSpecs); SetDefaults(ScopeTree, &TypeSpecs); ScopeTree = ScopeTokens(ScopeTree, TokensList, T, &TypeSpecs, 0);