cinera.c: Fix segfault and event handling

• Segfault was due to a read access violation on an unset entry pointer,
  which in turn was due to stale neighbourhood data. To fix it we simply
  reset the neighbourhood when starting to delete an entry. Additionally
  we now check that those entry pointers are set before accessing them.

• Event handling of the trio of events triggered when vim saves a file.
  We now read in a second set of events while processing the first if we
  were on the verge of processing a deletion. If we get any more events,
  we continue to squash those ones if possible, to always end up having
  seen the entire trio of events associated with a file save, and then
  process it as an insertion / reinsertion, not a deletion.

• Sort the asset landmarks by their offset.

• Change GenerateTopicColours() to initially open cinera_topics.css as
  "r" to search it for the incoming topic, and only if that topic is
  absent reopen it as "a+", thus triggering an IN_CLOSE_WRITE event.
This commit is contained in:
Matt Mascarenhas 2020-05-30 18:12:54 +01:00
parent 7edcecfd40
commit 0959fa2774
2 changed files with 367 additions and 187 deletions

View File

@ -23,7 +23,7 @@ typedef struct
version CINERA_APP_VERSION = { version CINERA_APP_VERSION = {
.Major = 0, .Major = 0,
.Minor = 7, .Minor = 7,
.Patch = 13 .Patch = 14
}; };
#include <stdarg.h> // NOTE(matt): varargs #include <stdarg.h> // NOTE(matt): varargs
@ -4238,14 +4238,20 @@ BinarySearchForMetadataLandmark(db_asset *Asset, landmark_range ProjectRange, in
void void
PrintEntryIndex(db_project_index Project, int64_t EntryIndex) 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_MAGENTA], Project.Generation, Project.Index, ColourStrings[CS_END],
ColourStrings[CS_BLUE_BOLD], EntryIndex, ColourStrings[CS_END]); ColourStrings[CS_BLUE_BOLD], EntryIndex, ColourStrings[CS_END]);
} }
void 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); PrintEntryIndex(L->Project, L->EntryIndex);
fprintf(stderr, " %6u", L->Position); fprintf(stderr, " %6u", L->Position);
} }
@ -4256,7 +4262,7 @@ PrintAsset(db_asset *A, uint16_t *Index)
if(Index) if(Index)
{ {
Colourise(CS_BLACK_BOLD); Colourise(CS_BLACK_BOLD);
fprintf(stderr, "[%i]", *Index); fprintf(stderr, "[%4u]", *Index);
Colourise(CS_END); Colourise(CS_END);
} }
string FilenameL = Wrap0i(A->Filename, sizeof(A->Filename)); string FilenameL = Wrap0i(A->Filename, sizeof(A->Filename));
@ -4301,7 +4307,7 @@ PrintAssetAndLandmarks(db_asset *A, uint16_t *Index)
db_landmark *FirstLandmark = LocateFirstLandmark(A); db_landmark *FirstLandmark = LocateFirstLandmark(A);
//Colourise(CS_BLACK_BOLD); fprintf(stderr, "%4u ", 0); Colourise(CS_END); //Colourise(CS_BLACK_BOLD); fprintf(stderr, "%4u ", 0); Colourise(CS_END);
//PrintLandmark(FirstLandmark); //PrintLandmark(FirstLandmark);
for(int i = 0; i < A->LandmarkCount; ++i) for(uint16_t i = 0; i < A->LandmarkCount; ++i)
{ {
db_landmark *This = FirstLandmark + i; db_landmark *This = FirstLandmark + i;
if((i % 8) == 0) if((i % 8) == 0)
@ -4312,8 +4318,8 @@ PrintAssetAndLandmarks(db_asset *A, uint16_t *Index)
{ {
PrintC(CS_BLACK_BOLD, ""); PrintC(CS_BLACK_BOLD, "");
} }
Colourise(CS_BLACK_BOLD); fprintf(stderr, "%4u ", i); Colourise(CS_END); //Colourise(CS_BLACK_BOLD); fprintf(stderr, "%4u ", i); Colourise(CS_END);
PrintLandmark(This); PrintLandmark(This, &i);
} }
fprintf(stderr, "\n"); fprintf(stderr, "\n");
} }
@ -4789,7 +4795,7 @@ SnipeChecksumIntoHTML(db_asset *Asset, buffer *Checksum)
{ {
PrintC(CS_ERROR, "\nInvalid landmark (aborting update): "); PrintC(CS_ERROR, "\nInvalid landmark (aborting update): ");
PrintAsset(Asset, 0); PrintAsset(Asset, 0);
PrintLandmark(Landmark); PrintLandmark(Landmark, 0);
Result = RC_FAILURE; Result = RC_FAILURE;
break; break;
} }
@ -6904,9 +6910,10 @@ BuildQuote(quote_info *Info, string Speaker, int ID, bool ShouldFetchQuotes)
return Result; return Result;
} }
int rc
GenerateTopicColours(neighbourhood *N, string Topic) GenerateTopicColours(neighbourhood *N, string Topic)
{ {
// TODO(matt): Maybe straighten out the return code situation?
// NOTE(matt): Stack-string // NOTE(matt): Stack-string
char SanitisedTopic[Topic.Length + 1]; char SanitisedTopic[Topic.Length + 1];
CopyString(SanitisedTopic, sizeof(SanitisedTopic), "%.*s", (int)Topic.Length, Topic.Base); CopyString(SanitisedTopic, sizeof(SanitisedTopic), "%.*s", (int)Topic.Length, Topic.Base);
@ -6949,43 +6956,27 @@ GenerateTopicColours(neighbourhood *N, string Topic)
closedir(CSSDirHandle); closedir(CSSDirHandle);
*Ptr = '/'; *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.Ptr += StringLength(".category.");
Topics.Buffer.Size = ftell(Topics.Handle); if(!StringsDifferT(SanitisedTopic, Topics.Buffer.Ptr, ' '))
fseek(Topics.Handle, 0, SEEK_SET);
if(!(Topics.Buffer.Location = malloc(Topics.Buffer.Size)))
{ {
return RC_ERROR_MEMORY; Exists = TRUE;
break;
} }
while(Topics.Buffer.Ptr - Topics.Buffer.Location < Topics.Buffer.Size && *Topics.Buffer.Ptr != '\n')
#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)
{ {
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;
} }
++Topics.Buffer.Ptr;
}
if(!Exists)
{
Topics.Handle = fopen(Topics.Path, "a+");
if(!StringsDifferLv0(Topic, "nullTopic")) if(!StringsDifferLv0(Topic, "nullTopic"))
{ {
fprintf(Topics.Handle, ".category.%s { border: 1px solid transparent; background: transparent; }\n", 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); SanitisedTopic, Colour.Hue, Colour.Saturation, Colour.Lightness, Colour.Hue, Colour.Saturation, Colour.Lightness);
} }
fclose(Topics.Handle);
#if DEBUG_MEM #if DEBUG_MEM
MemLog = fopen("/home/matt/cinera_mem", "a+"); MemLog = fopen("/home/matt/cinera_mem", "a+");
fprintf(MemLog, " Freed Topics (%ld)\n", Topics.Buffer.Size); fprintf(MemLog, " Freed Topics (%ld)\n", Topics.Buffer.Size);
fclose(MemLog); fclose(MemLog);
printf(" Freed Topics (%ld)\n", Topics.Buffer.Size); printf(" Freed Topics (%ld)\n", Topics.Buffer.Size);
#endif #endif
FreeBuffer(&Topics.Buffer);
fclose(Topics.Handle);
FreeFile(&Topics);
asset *Asset = GetPlaceInBook(&Assets, ASSET_CSS_TOPICS); asset *Asset = GetPlaceInBook(&Assets, ASSET_CSS_TOPICS);
if(Asset->Known) if(Asset->Known)
@ -7020,14 +7012,8 @@ GenerateTopicColours(neighbourhood *N, string Topic)
asset *CSSTopics = BuiltinAssets + ASSET_CSS_TOPICS; asset *CSSTopics = BuiltinAssets + ASSET_CSS_TOPICS;
PlaceAsset(Wrap0(CSSTopics->Filename), CSSTopics->Type, CSSTopics->Variants, CSSTopics->Associated, 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 void
@ -7166,8 +7152,8 @@ enable syntax highlighting:"));
// Defaults // Defaults
// //
NewSection("Defaults", &IndentationLevel); NewSection("Defaults", &IndentationLevel);
scope_tree *ScopeTree = calloc(1, sizeof(scope_tree));
InitScopeBooks(ScopeTree); scope_tree *ScopeTree = InitRootScopeTree();
SetTypeSpec(ScopeTree, &TypeSpecs); SetTypeSpec(ScopeTree, &TypeSpecs);
SetDefaults(ScopeTree, &TypeSpecs); SetDefaults(ScopeTree, &TypeSpecs);
PrintScopeTree(ScopeTree, IndentationLevel); PrintScopeTree(ScopeTree, IndentationLevel);
@ -9303,6 +9289,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
case RC_ERROR_MEMORY: case RC_ERROR_MEMORY:
HMMLCleanup(); HMMLCleanup();
return RC_ERROR_FATAL; return RC_ERROR_FATAL;
default: break;
}; };
if(!HasFilterMenu) if(!HasFilterMenu)
{ {
@ -9587,6 +9574,7 @@ AppendedIdentifier:
case RC_ERROR_MEMORY: case RC_ERROR_MEMORY:
HMMLCleanup(); HMMLCleanup();
return RC_ERROR_FATAL; return RC_ERROR_FATAL;
default: break;
} }
if(!HasFilterMenu) if(!HasFilterMenu)
{ {
@ -9607,6 +9595,7 @@ AppendedIdentifier:
case RC_ERROR_MEMORY: case RC_ERROR_MEMORY:
HMMLCleanup(); HMMLCleanup();
return RC_ERROR_FATAL; return RC_ERROR_FATAL;
default: break;
}; };
InsertCategory(&Topics, &LocalTopics, &Media, &LocalMedia, Wrap0("nullTopic")); 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) BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template *Template, char *OutputPath, page_type PageType, unsigned int *PlayerOffset)
{ {
rc Result = RC_SUCCESS;
MEM_TEST_INITIAL(); MEM_TEST_INITIAL();
#if DEBUG #if DEBUG
printf("\n\n --- Buffer Collation ---\n" printf("\n\n --- Buffer Collation ---\n"
@ -10465,7 +10493,8 @@ BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template *
LogError(LOG_ERROR, "BuffersToHTML(): %s", LogError(LOG_ERROR, "BuffersToHTML(): %s",
strerror(errno)); strerror(errno));
MEM_TEST_AFTER("BuffersToHTML"); MEM_TEST_AFTER("BuffersToHTML");
return RC_ERROR_MEMORY; Result = RC_ERROR_MEMORY;
goto End;
} }
#if DEBUG_MEM #if DEBUG_MEM
@ -10619,7 +10648,8 @@ BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template *
#endif #endif
MEM_TEST_AFTER("BuffersToHTML"); MEM_TEST_AFTER("BuffersToHTML");
return RC_ERROR_FILE; Result = RC_ERROR_FILE;
goto End;
} }
fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile); fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile);
fclose(OutFile); fclose(OutFile);
@ -10634,16 +10664,14 @@ BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template *
#endif #endif
MEM_TEST_AFTER("BuffersToHTML"); MEM_TEST_AFTER("BuffersToHTML");
return RC_SUCCESS;
} }
else else
{ {
MEM_TEST_AFTER("BuffersToHTML"); MEM_TEST_AFTER("BuffersToHTML");
return RC_INVALID_TEMPLATE; Result = RC_INVALID_TEMPLATE;
} }
#endif // AFE #endif // AFE
MEM_TEST_AFTER("BuffersToHTML"); MEM_TEST_AFTER("BuffersToHTML");
return RC_SUCCESS; // NOTE(matt): We added this simply to squash the "end of non-void function" warning
} }
else else
{ {
@ -10657,7 +10685,8 @@ BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template *
LogError(LOG_ERROR, "BuffersToHTML(): %s", LogError(LOG_ERROR, "BuffersToHTML(): %s",
strerror(errno)); strerror(errno));
MEM_TEST_AFTER("BuffersToHTML"); MEM_TEST_AFTER("BuffersToHTML");
return RC_ERROR_MEMORY; Result = RC_ERROR_MEMORY;
goto End;
} }
MEM_TEST_MID("BuffersToHTML3"); MEM_TEST_MID("BuffersToHTML3");
Master.Ptr = Master.Location; 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)); LogError(LOG_ERROR, "Unable to open output file %s: %s", OutputPath, strerror(errno));
DeclaimBuffer(&Master); DeclaimBuffer(&Master);
MEM_TEST_AFTER("BuffersToHTML"); MEM_TEST_AFTER("BuffersToHTML");
return RC_ERROR_FILE; Result = RC_ERROR_FILE;
goto End;
} }
MEM_TEST_MID("BuffersToHTML13"); MEM_TEST_MID("BuffersToHTML13");
fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile); fwrite(Master.Location, Master.Ptr - Master.Location, 1, OutFile);
@ -10710,8 +10740,11 @@ BuffersToHTML(config *C, project *Project, buffers *CollationBuffers, template *
OutFile = 0; OutFile = 0;
FreeBuffer(&Master); FreeBuffer(&Master);
MEM_TEST_AFTER("BuffersToHTML"); MEM_TEST_AFTER("BuffersToHTML");
return RC_SUCCESS; Result = RC_SUCCESS;
} }
End:
SortLandmarks(&Assets);
return Result;
} }
int int
@ -11042,14 +11075,11 @@ GetNeighbourhoodForAddition(neighbourhood *N, edit_type_id EditType)
void void
GetNeighbourhoodForDeletion(neighbourhood *N) GetNeighbourhoodForDeletion(neighbourhood *N)
{ {
char *Ptr = (char *)N->Project; db_entry *FirstEntry = LocateFirstEntry(N->Project);
Ptr += sizeof(*N->Project);
db_entry *FirstEntry = (db_entry *)Ptr;
db_entry *Entry = FirstEntry + N->ThisIndex;
N->PreDeletionThisIndex = N->ThisIndex; N->PreDeletionThisIndex = N->ThisIndex;
N->This = Entry;
db_entry *Entry;
int EntryIndex; int EntryIndex;
N->DeletedEntryWasFirst = TRUE; N->DeletedEntryWasFirst = TRUE;
@ -11608,7 +11638,6 @@ AddLandmarks(neighbourhood *N, project *P, edit_type_id EditType)
for(int j = 0; j < AssetInMemory->PlayerLandmarkCount; ++j) for(int j = 0; j < AssetInMemory->PlayerLandmarkCount; ++j)
{ {
db_landmark Landmark = {}; db_landmark Landmark = {};
// TODO(matt): Actually make sure that the correct project_index is set!
Landmark.Project = P->Index; Landmark.Project = P->Index;
Landmark.EntryIndex = N->ThisIndex; Landmark.EntryIndex = N->ThisIndex;
Landmark.Position = AssetInMemory->Player[j].Offset; Landmark.Position = AssetInMemory->Player[j].Offset;
@ -11718,31 +11747,45 @@ VerifyLandmarks_(neighbourhood *N, int LineNumber)
db_block_assets *Block = LocateBlock(B_ASET); db_block_assets *Block = LocateBlock(B_ASET);
db_asset *Asset = LocateFirstAsset(Block); 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); 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) if(j + 1 < Asset->LandmarkCount)
{ {
db_landmark *Next = Landmark + 1; db_landmark *Next = Landmark + 1;
if((ProjectIndicesDiffer(Next->Project, Landmark->Project) < 0) || 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"); if(!Titled)
PrintAssetsBlock(0); {
PrintLandmark(Landmark); fprintf(stderr, "\nOut-of-order landmarks\n");
fprintf(stderr, " vs "); PrintAsset(Asset, &i);
PrintLandmark(Next); Titled = TRUE;
}
//PrintAssetsBlock(0);
PrintLandmark(Landmark, &j);
PrintC(CS_YELLOW, " vs ");
PrintLandmark(Next, 0);
fprintf(stderr, "\n"); fprintf(stderr, "\n");
PrintNeighbourhood(N); //PrintNeighbourhood(N);
_exit(1); //_exit(1);
Malformed = TRUE;
} }
} }
} }
Asset = SkipAsset(Asset); Asset = SkipAsset(Asset);
} }
if(Malformed)
{
_exit(1);
}
#if 0 #if 0
DB.Metadata.Buffer.Ptr = DB.Metadata.Buffer.Location; DB.Metadata.Buffer.Ptr = DB.Metadata.Buffer.Location;
DB.Header = *(db_header *)DB.Metadata.Buffer.Ptr; DB.Header = *(db_header *)DB.Metadata.Buffer.Ptr;
@ -12263,57 +12306,97 @@ LinkOverDeletedEntry(neighbourhood *N)
{ {
if(N->DeletedEntryWasFirst) if(N->DeletedEntryWasFirst)
{ {
N->PreLinkNextOffsetTotal = N->Next->LinkOffsets.PrevEnd if(N->Next) // NOTE(matt): Should be impossible to fail this test
+ N->Next->LinkOffsets.NextStart {
+ N->Next->LinkOffsets.NextEnd; 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->NextOffsetModifier = N->Next->LinkOffsets.PrevEnd
+ N->Next->LinkOffsets.NextStart + N->Next->LinkOffsets.NextStart
+ N->Next->LinkOffsets.NextEnd + N->Next->LinkOffsets.NextEnd
- N->PreLinkNextOffsetTotal; - N->PreLinkNextOffsetTotal;
}
else
{
PrintC(CS_ERROR, "Error: Malformed neighbourhood");
PrintNeighbourhood(N);
}
return; return;
} }
else if(N->DeletedEntryWasFinal) else if(N->DeletedEntryWasFinal)
{ {
N->PreLinkPrevOffsetTotal = N->Prev->LinkOffsets.PrevEnd if(N->Prev) // NOTE(matt): Should be impossible to fail this test
+ N->Prev->LinkOffsets.NextStart {
+ N->Prev->LinkOffsets.NextEnd; 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->PrevOffsetModifier = N->Prev->LinkOffsets.PrevEnd
+ N->Prev->LinkOffsets.NextStart + N->Prev->LinkOffsets.NextStart
+ N->Prev->LinkOffsets.NextEnd + N->Prev->LinkOffsets.NextEnd
- N->PreLinkPrevOffsetTotal; - N->PreLinkPrevOffsetTotal;
}
else
{
PrintC(CS_ERROR, "Error: Malformed neighbourhood");
PrintNeighbourhood(N);
}
return; return;
} }
else else
{ {
// Assert(N->PrevIndex >= 0 && N->NextIndex >= 0) // 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 if(N->Next)
+ N->Next->LinkOffsets.NextStart {
+ N->Next->LinkOffsets.NextEnd; 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); if(N->Prev && N->Next)
InsertNeighbourLink(N->Project, N->Next, N->Prev, LINK_BACKWARDS, N->LatterIsFinal); {
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->PrevOffsetModifier = N->Prev->LinkOffsets.PrevEnd
+ N->Prev->LinkOffsets.NextStart + N->Prev->LinkOffsets.NextStart
+ N->Prev->LinkOffsets.NextEnd + N->Prev->LinkOffsets.NextEnd
- N->PreLinkPrevOffsetTotal; - N->PreLinkPrevOffsetTotal;
N->NextOffsetModifier = N->Next->LinkOffsets.PrevEnd N->NextOffsetModifier = N->Next->LinkOffsets.PrevEnd
+ N->Next->LinkOffsets.NextStart + N->Next->LinkOffsets.NextStart
+ N->Next->LinkOffsets.NextEnd + N->Next->LinkOffsets.NextEnd
- N->PreLinkNextOffsetTotal; - N->PreLinkNextOffsetTotal;
}
else
{
PrintC(CS_ERROR, "Error: Malformed neighbourhood");
PrintNeighbourhood(N);
}
} }
} }
} }
@ -12334,6 +12417,7 @@ LinkNeighbours(neighbourhood *N, enum8(link_types) LinkType)
rc rc
DeleteFromDB(neighbourhood *N, string BaseFilename) DeleteFromDB(neighbourhood *N, string BaseFilename)
{ {
ResetNeighbourhood(N);
// TODO(matt): LogError() // TODO(matt): LogError()
db_entry *Entry = 0; db_entry *Entry = 0;
int EntryIndex = BinarySearchForMetadataEntry(N->Project, &Entry, BaseFilename); 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); 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 #define DEBUG_EVENTS 0
#if DEBUG_EVENTS #if DEBUG_EVENTS
void 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" for(int i = 0; i < ArrayCount(inotifyEventStrings); ++i)
" wd: %d\n" {
" mask: %d\n", if(Event->mask & (1 << i))
EventIndex, {
Event->wd, fprintf(stderr, "\n");
Event->mask); Indent(Indentation);
fprintf(stderr, " %s", inotifyEventStrings[i]);
}
}
if(Event->mask & IN_ACCESS) { printf(" IN_ACCESS\n"); } fprintf(stderr, "\n");
if(Event->mask & IN_ATTRIB) { printf(" IN_ATTRIB\n"); } Indent(Indentation);
if(Event->mask & IN_CLOSE_WRITE) { printf(" IN_CLOSE_WRITE\n"); } fprintf(stderr, " cookie: %d", Event->cookie);
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"); }
printf( " cookie: %d\n" fprintf(stderr, "\n");
" len: %d\n" Indent(Indentation);
" name: %s\n", fprintf(stderr, " len: %d", Event->len);
Event->cookie,
Event->len, fprintf(stderr, "\n");
Event->name); Indent(Indentation);
fprintf(stderr, " name: %s", Event->name);
} }
#endif #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 int
MonitorFilesystem(neighbourhood *N, buffers *CollationBuffers, template *BespokeTemplate, string ConfigPath, memory_book *TokensList) 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; struct inotify_event *Event;
int BytesRead = read(inotifyInstance, Events.Location, Events.Size); // TODO(matt): Handle error EINVAL 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 DEBUG_EVENTS
if(BytesRead > 0) if(BytesRead > 0)
{ {
@ -15031,46 +15189,56 @@ MonitorFilesystem(neighbourhood *N, buffers *CollationBuffers, template *Bespoke
Event = (struct inotify_event *)Events.Ptr; Event = (struct inotify_event *)Events.Ptr;
#if DEBUG_EVENTS #if DEBUG_EVENTS
PrintEvent(Event, DebugEventIndex); PrintEvent(Event, DebugEventIndex, 0);
//PrintWatchHandles(); //PrintWatchHandles();
#endif #endif
watch_file *WatchFile = GetWatchFileForEvent(Event); watch_file *WatchFile = GetWatchFileForEvent(Event);
if(WatchFile) 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 #if DEBUG_EVENTS
fprintf(stderr, "Squashing events:\n" SquashEventsD(&Events, BytesRead, &Event, &DebugEventIndex);
" "); #else
PrintEvent(Event, DebugEventIndex++); SquashEvents(&Events, BytesRead, &Event);
fprintf(stderr, "\n"
" ");
PrintEvent(Peek, DebugEventIndex);
fprintf(stderr, "\n");
#endif #endif
bool WouldHaveDeleted = (Event->mask & (IN_DELETE | IN_MOVED_FROM)) ? TRUE : FALSE;
Event = Peek; if(WouldHaveDeleted && (Events.Ptr + sizeof(struct inotify_event) + Event->len) - Events.Location == BytesRead)
Events.Ptr = PeekPtr; {
fprintf(stderr, "Sleeping before polling for more filesystem events");
sleep(1);
PeekPtr = Events.Ptr + sizeof(struct inotify_event) + Event->len; struct inotify_event EventN = *Event;
Peek = (struct inotify_event *)PeekPtr; 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) switch(WatchFile->Type)
{ {
// TODO(matt): We're probably watching for too many events when the target directory exists
case WT_HMML: case WT_HMML:
{ {
SetCurrentProject(WatchFile->Project, N); SetCurrentProject(WatchFile->Project, N);
//PrintLineage(CurrentProject->Lineage, TRUE);
string BaseFilename = GetBaseFilename(Wrap0(Event->name), WatchFile->Extension); string BaseFilename = GetBaseFilename(Wrap0(Event->name), WatchFile->Extension);
if(Event->mask & (IN_DELETE | IN_MOVED_FROM)) 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); Deleted |= (DeleteEntry(N, BaseFilename) == RC_SUCCESS);
} }
else if(Event->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)) else if(Event->mask & (IN_CLOSE_WRITE | IN_MOVED_TO))
@ -15080,8 +15248,6 @@ MonitorFilesystem(neighbourhood *N, buffers *CollationBuffers, template *Bespoke
} break; } break;
case WT_ASSET: case WT_ASSET:
{ {
// TODO(matt): Why are we getting here after editing a .hmml file?
//PrintAssetsBlock(0);
UpdateAsset(WatchFile->Asset, FALSE); UpdateAsset(WatchFile->Asset, FALSE);
UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts); UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts);
#if DEBUG_LANDMARKS #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)); 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); inotifyInstance = inotify_init1(IN_NONBLOCK);
int inotifyError = errno;
string ConfigPathL = Wrap0(ConfigPath); string ConfigPathL = Wrap0(ConfigPath);
PushWatchHandle(ConfigPathL, EXT_NULL, WT_CONFIG, 0, 0); PushWatchHandle(ConfigPathL, EXT_NULL, WT_CONFIG, 0, 0);
@ -15250,24 +15417,31 @@ main(int ArgC, char **Args)
} }
} }
//PrintWatchHandles(); if(inotifyInstance)
while(MonitorFilesystem(&Neighbourhood, &CollationBuffers, &BespokeTemplate, ConfigPathL, &TokensList) != RC_ARENA_FULL)
{ {
// TODO(matt): Refetch the quotes and rebuild player pages if needed while(MonitorFilesystem(&Neighbourhood, &CollationBuffers, &BespokeTemplate, ConfigPathL, &TokensList) != RC_ARENA_FULL)
//
// 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); // 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(); DiscardAllAndFreeConfig();

View File

@ -1543,6 +1543,14 @@ InitScopeBooks(scope_tree *Scope)
InitBook(&Scope->BoolPairs, sizeof(config_bool_pair), 4, MBT_CONFIG_BOOL_PAIR); 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 * scope_tree *
PushScope(memory_book *TypeSpecs, scope_tree *Parent, config_pair *ID) PushScope(memory_book *TypeSpecs, scope_tree *Parent, config_pair *ID)
{ {
@ -4583,8 +4591,7 @@ ParseConfig(string Path, memory_book *TokensList)
if(T) if(T)
{ {
scope_tree *ScopeTree = calloc(1, sizeof(scope_tree)); scope_tree *ScopeTree = InitRootScopeTree();
InitScopeBooks(ScopeTree);
SetTypeSpec(ScopeTree, &TypeSpecs); SetTypeSpec(ScopeTree, &TypeSpecs);
SetDefaults(ScopeTree, &TypeSpecs); SetDefaults(ScopeTree, &TypeSpecs);
ScopeTree = ScopeTokens(ScopeTree, TokensList, T, &TypeSpecs, 0); ScopeTree = ScopeTokens(ScopeTree, TokensList, T, &TypeSpecs, 0);
@ -4597,8 +4604,7 @@ ParseConfig(string Path, memory_book *TokensList)
FreeTokensList(TokensList); FreeTokensList(TokensList);
MEM_LOOP_PRE_WORK() MEM_LOOP_PRE_WORK()
T = Tokenise(TokensList, Path); T = Tokenise(TokensList, Path);
ScopeTree = calloc(1, sizeof(scope_tree)); ScopeTree = InitRootScopeTree();
InitScopeBooks(ScopeTree);
SetTypeSpec(ScopeTree, &TypeSpecs); SetTypeSpec(ScopeTree, &TypeSpecs);
SetDefaults(ScopeTree, &TypeSpecs); SetDefaults(ScopeTree, &TypeSpecs);
ScopeTree = ScopeTokens(ScopeTree, TokensList, T, &TypeSpecs, 0); ScopeTree = ScopeTokens(ScopeTree, TokensList, T, &TypeSpecs, 0);