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 = {
.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,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();

View File

@ -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);