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:
parent
7edcecfd40
commit
0959fa2774
436
cinera/cinera.c
436
cinera/cinera.c
|
@ -23,7 +23,7 @@ typedef struct
|
|||
version CINERA_APP_VERSION = {
|
||||
.Major = 0,
|
||||
.Minor = 7,
|
||||
.Patch = 13
|
||||
.Patch = 14
|
||||
};
|
||||
|
||||
#include <stdarg.h> // 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,35 +6956,16 @@ GenerateTopicColours(neighbourhood *N, string Topic)
|
|||
closedir(CSSDirHandle);
|
||||
*Ptr = '/';
|
||||
|
||||
if((Topics.Handle = fopen(Topics.Path, "a+")))
|
||||
{
|
||||
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)))
|
||||
{
|
||||
return RC_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
#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);
|
||||
ReadFileIntoBuffer(&Topics);
|
||||
|
||||
bool Exists = FALSE;
|
||||
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;
|
||||
Exists = TRUE;
|
||||
break;
|
||||
}
|
||||
while(Topics.Buffer.Ptr - Topics.Buffer.Location < Topics.Buffer.Size && *Topics.Buffer.Ptr != '\n')
|
||||
{
|
||||
|
@ -6986,6 +6974,9 @@ GenerateTopicColours(neighbourhood *N, string Topic)
|
|||
++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,15 +7012,9 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ResetConfigIdentifierDescriptionDisplayedBools(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;
|
||||
|
@ -12262,6 +12305,8 @@ LinkOverDeletedEntry(neighbourhood *N)
|
|||
else
|
||||
{
|
||||
if(N->DeletedEntryWasFirst)
|
||||
{
|
||||
if(N->Next) // NOTE(matt): Should be impossible to fail this test
|
||||
{
|
||||
N->PreLinkNextOffsetTotal = N->Next->LinkOffsets.PrevEnd
|
||||
+ N->Next->LinkOffsets.NextStart
|
||||
|
@ -12273,9 +12318,17 @@ LinkOverDeletedEntry(neighbourhood *N)
|
|||
+ N->Next->LinkOffsets.NextStart
|
||||
+ N->Next->LinkOffsets.NextEnd
|
||||
- N->PreLinkNextOffsetTotal;
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintC(CS_ERROR, "Error: Malformed neighbourhood");
|
||||
PrintNeighbourhood(N);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(N->DeletedEntryWasFinal)
|
||||
{
|
||||
if(N->Prev) // NOTE(matt): Should be impossible to fail this test
|
||||
{
|
||||
N->PreLinkPrevOffsetTotal = N->Prev->LinkOffsets.PrevEnd
|
||||
+ N->Prev->LinkOffsets.NextStart
|
||||
|
@ -12287,21 +12340,45 @@ LinkOverDeletedEntry(neighbourhood *N)
|
|||
+ 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)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -12315,6 +12392,12 @@ LinkOverDeletedEntry(neighbourhood *N)
|
|||
+ 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,7 +15417,8 @@ 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
|
||||
|
@ -15269,6 +15437,12 @@ main(int ArgC, char **Args)
|
|||
}
|
||||
sleep(GLOBAL_UPDATE_INTERVAL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Error: Quitting because inotify initialisation failed with message\n"
|
||||
" %s\n", strerror(inotifyError));
|
||||
}
|
||||
|
||||
DiscardAllAndFreeConfig();
|
||||
RemoveAndFreeWatchHandles(&WatchHandles);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue