cinera: Lock database and config files
This commit aims to help Cinera instances coordinate themselves by throwing an error upon being instructed to use a database or config file currently in use by another instance.
This commit is contained in:
parent
1cf703e346
commit
f60cf3087f
314
cinera/cinera.c
314
cinera/cinera.c
|
@ -23,15 +23,22 @@ typedef struct
|
|||
version CINERA_APP_VERSION = {
|
||||
.Major = 0,
|
||||
.Minor = 10,
|
||||
.Patch = 12
|
||||
.Patch = 13
|
||||
};
|
||||
|
||||
#define __USE_XOPEN2K8 // NOTE(matt): O_NOFOLLOW
|
||||
#include <fcntl.h> // NOTE(matt): open()
|
||||
#undef __USE_XOPEN2K8
|
||||
|
||||
#include <stdarg.h> // NOTE(matt): varargs
|
||||
#define __USE_POSIX // NOTE(matt): fileno()
|
||||
#include <stdio.h> // NOTE(matt): printf, sprintf, vsprintf, fprintf, perror
|
||||
#undef __USE_POSIX
|
||||
#include <stdlib.h> // NOTE(matt): calloc, malloc, free
|
||||
#include <getopt.h> // NOTE(matt): getopts
|
||||
#include <curl/curl.h>
|
||||
#include <time.h>
|
||||
#include <sys/file.h> // NOTE(matt): flock
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
|
@ -41,10 +48,6 @@ version CINERA_APP_VERSION = {
|
|||
#include <sys/ioctl.h> // NOTE(matt): ioctl and TIOCGWINSZ
|
||||
#include <wordexp.h>
|
||||
|
||||
#define __USE_XOPEN2K8 // NOTE(matt): O_NOFOLLOW
|
||||
#include <fcntl.h> // NOTE(matt): open()
|
||||
#undef __USE_XOPEN2K8
|
||||
|
||||
#define __USE_XOPEN2K // NOTE(matt): readlink()
|
||||
#include <unistd.h> // NOTE(matt): sleep()
|
||||
#undef __USE_XOPEN2K
|
||||
|
@ -67,6 +70,7 @@ version CINERA_APP_VERSION = {
|
|||
typedef uint64_t bool;
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define NA 0
|
||||
|
||||
#define SECONDS_PER_HOUR 3600
|
||||
#define SECONDS_PER_MINUTE 60
|
||||
|
@ -279,6 +283,7 @@ typedef enum
|
|||
RC_ERROR_DIRECTORY,
|
||||
RC_ERROR_FATAL,
|
||||
RC_ERROR_FILE,
|
||||
RC_ERROR_FILE_LOCKED,
|
||||
RC_ERROR_HMML,
|
||||
RC_ERROR_MAX_REFS,
|
||||
RC_ERROR_MEMORY,
|
||||
|
@ -464,6 +469,7 @@ typedef struct
|
|||
buffer Buffer;
|
||||
char *Path;
|
||||
FILE *Handle;
|
||||
bool Locking;
|
||||
} file;
|
||||
|
||||
typedef struct
|
||||
|
@ -1064,7 +1070,7 @@ ExtendString0(char **Dest, string Src)
|
|||
}
|
||||
|
||||
file
|
||||
InitFile(string *Directory, string *Filename, extension_id Extension)
|
||||
InitFile(string *Directory, string *Filename, extension_id Extension, bool Locking)
|
||||
{
|
||||
file Result = {};
|
||||
if(Directory)
|
||||
|
@ -1079,6 +1085,36 @@ InitFile(string *Directory, string *Filename, extension_id Extension)
|
|||
{
|
||||
ExtendString0(&Result.Path, ExtensionStrings[Extension]);
|
||||
}
|
||||
Result.Locking = Locking;
|
||||
return Result;
|
||||
}
|
||||
|
||||
void
|
||||
CloseFile(file *F, bool CloseLocking)
|
||||
{
|
||||
if(F->Handle)
|
||||
{
|
||||
fclose(F->Handle);
|
||||
F->Handle = 0;
|
||||
if(F->Locking && !CloseLocking)
|
||||
{
|
||||
F->Handle = fopen(F->Path, "r");
|
||||
flock(fileno(F->Handle), LOCK_EX | LOCK_NB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TryLock(file *F) // USAGE: File shall already be open, if possible
|
||||
{
|
||||
bool Result = TRUE;
|
||||
if(F->Locking && F->Handle)
|
||||
{
|
||||
if(flock(fileno(F->Handle), LOCK_EX | LOCK_NB) == -1)
|
||||
{
|
||||
Result = FALSE;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
@ -1088,7 +1124,9 @@ ReadFileIntoBuffer(file *F)
|
|||
rc Result = RC_ERROR_FILE;
|
||||
if(F->Path)
|
||||
{
|
||||
if((F->Handle = fopen(F->Path, "r")))
|
||||
if(F->Handle || (F->Handle = fopen(F->Path, "r")))
|
||||
{
|
||||
if(TryLock(F))
|
||||
{
|
||||
fseek(F->Handle, 0, SEEK_END);
|
||||
F->Buffer.Size = ftell(F->Handle);
|
||||
|
@ -1096,13 +1134,13 @@ ReadFileIntoBuffer(file *F)
|
|||
F->Buffer.Ptr = F->Buffer.Location;
|
||||
fseek(F->Handle, 0, SEEK_SET);
|
||||
fread(F->Buffer.Location, F->Buffer.Size, 1, F->Handle);
|
||||
fclose(F->Handle);
|
||||
F->Handle = 0;
|
||||
CloseFile(F, FALSE);
|
||||
Result = RC_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
perror(F->Path);
|
||||
Result = RC_ERROR_FILE_LOCKED;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
|
@ -1914,16 +1952,6 @@ ConfigErrorUnset(config_identifier_id FieldID)
|
|||
"Unset %s\n", ConfigIdentifiers[FieldID].String);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigFileIncludeError(string *Filename, uint64_t LineNumber, string Path)
|
||||
{
|
||||
ErrorFilenameAndLineNumber(Filename, LineNumber, S_WARNING, ED_CONFIG);
|
||||
fprintf(stderr,
|
||||
"Included file could not be opened (%s): ", strerror(errno));
|
||||
PrintStringC(CS_MAGENTA_BOLD, Path);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
ConfigErrorSizing(string *Filename, uint64_t LineNumber, config_identifier_id FieldID, string *Received, uint64_t MaxSize)
|
||||
{
|
||||
|
@ -1933,6 +1961,27 @@ ConfigErrorSizing(string *Filename, uint64_t LineNumber, config_identifier_id Fi
|
|||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
ConfigErrorLockedConfigLocation(string *Filename, uint64_t LineNumber, string *Received)
|
||||
{
|
||||
ErrorFilenameAndLineNumber(Filename, LineNumber, Filename ? S_WARNING : S_ERROR, ED_CONFIG);
|
||||
fprintf(stderr, "%s file %s%.*s%s is in use by another Cinera instance\n", Filename ? "Included" : "Config", ColourStrings[CS_MAGENTA_BOLD], (int)Received->Length, Received->Base, ColourStrings[CS_END]);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigErrorUnopenableConfigLocation(string *Filename, uint64_t LineNumber, string *Received)
|
||||
{
|
||||
ErrorFilenameAndLineNumber(Filename, LineNumber, Filename ? S_WARNING : S_ERROR, ED_CONFIG);
|
||||
fprintf(stderr, "%s file %s%.*s%s could not be opened: %s\n", Filename ? "Included" : "Config", ColourStrings[CS_MAGENTA_BOLD], (int)Received->Length, Received->Base, ColourStrings[CS_END], strerror(errno));
|
||||
}
|
||||
|
||||
void
|
||||
ConfigErrorLockedDBLocation(string *Filename, uint64_t LineNumber, string *Received)
|
||||
{
|
||||
ErrorFilenameAndLineNumber(Filename, LineNumber, S_ERROR, ED_CONFIG);
|
||||
fprintf(stderr, "File at db_location %s%.*s%s is in use by another Cinera instance\n", ColourStrings[CS_MAGENTA_BOLD], (int)Received->Length, Received->Base, ColourStrings[CS_END]);
|
||||
}
|
||||
|
||||
void
|
||||
ConfigErrorInt(string *Filename, uint64_t LineNumber, severity Severity, char *Message, uint64_t Number)
|
||||
{
|
||||
|
@ -2740,6 +2789,7 @@ typedef struct
|
|||
{
|
||||
file File;
|
||||
file_signposted Metadata;
|
||||
bool Ready;
|
||||
|
||||
db_header5 Header;
|
||||
db_block_projects5 ProjectsBlock;
|
||||
|
@ -3024,17 +3074,23 @@ ClearTemplateMetadata(template *Template)
|
|||
}
|
||||
|
||||
void
|
||||
FreeFile(file *F)
|
||||
FreeFileBufferAndPath(file *F)
|
||||
{
|
||||
FreeBuffer(&F->Buffer);
|
||||
Free(F->Path);
|
||||
F->Handle = 0;
|
||||
}
|
||||
|
||||
void
|
||||
FreeSignpostedFile(file_signposted *F)
|
||||
FreeFile(file *F, bool CloseLocking)
|
||||
{
|
||||
FreeFile(&F->File);
|
||||
CloseFile(F, CloseLocking);
|
||||
FreeFileBufferAndPath(F);
|
||||
}
|
||||
|
||||
void
|
||||
FreeSignpostedFile(file_signposted *F, bool CloseLocking)
|
||||
{
|
||||
FreeFile(&F->File, CloseLocking);
|
||||
file_signposts Zero = {};
|
||||
F->Signposts = Zero;
|
||||
}
|
||||
|
@ -3042,7 +3098,7 @@ FreeSignpostedFile(file_signposted *F)
|
|||
void
|
||||
FreeTemplate(template *Template)
|
||||
{
|
||||
FreeFile(&Template->File);
|
||||
FreeFile(&Template->File, NA);
|
||||
ClearTemplateMetadata(Template);
|
||||
}
|
||||
|
||||
|
@ -3152,8 +3208,6 @@ typedef enum
|
|||
WT_CONFIG,
|
||||
} watch_type;
|
||||
|
||||
#include "cinera_config.c"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
watch_type Type;
|
||||
|
@ -3161,6 +3215,7 @@ typedef struct
|
|||
extension_id Extension;
|
||||
project *Project;
|
||||
asset *Asset;
|
||||
FILE *Handle;
|
||||
} watch_file;
|
||||
|
||||
typedef struct
|
||||
|
@ -3178,6 +3233,8 @@ typedef struct
|
|||
uint32_t DefaultEventsMask;
|
||||
} watch_handles;
|
||||
|
||||
#include "cinera_config.c"
|
||||
|
||||
#define AFD 1
|
||||
#define AFE 0
|
||||
|
||||
|
@ -4281,17 +4338,17 @@ InitTemplate(template *Template, string Location, template_type Type)
|
|||
Template->Metadata.Type = Type;
|
||||
if(Type == TEMPLATE_GLOBAL_SEARCH)
|
||||
{
|
||||
Template->File = InitFile(&Config->GlobalTemplatesDir, &Config->GlobalSearchTemplatePath, EXT_NULL);
|
||||
Template->File = InitFile(&Config->GlobalTemplatesDir, &Config->GlobalSearchTemplatePath, EXT_NULL, FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Location.Base[0] == '/')
|
||||
{
|
||||
Template->File = InitFile(0, &Location, EXT_NULL);
|
||||
Template->File = InitFile(0, &Location, EXT_NULL, FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
Template->File = InitFile(Type == TEMPLATE_BESPOKE ? &CurrentProject->HMMLDir : &CurrentProject->TemplatesDir, &Location, EXT_NULL);
|
||||
Template->File = InitFile(Type == TEMPLATE_BESPOKE ? &CurrentProject->HMMLDir : &CurrentProject->TemplatesDir, &Location, EXT_NULL, FALSE);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "%sPacking%s template: %s\n", ColourStrings[CS_ONGOING], ColourStrings[CS_END], Template->File.Path);
|
||||
|
@ -4516,7 +4573,7 @@ ConstructAssetPath(file *AssetFile, string Filename, asset_type Type)
|
|||
void
|
||||
CycleFile(file *File)
|
||||
{
|
||||
fclose(File->Handle);
|
||||
CloseFile(File, FALSE);
|
||||
// TODO(matt): Rather than freeing the buffer, why not just realloc it to fit the new file? Couldn't that work easily?
|
||||
// The reason we Free / Reread is to save us having to shuffle the buffer contents around, basically
|
||||
// If we switch the whole database over to use linked lists - the database being the only file we actually cycle
|
||||
|
@ -4541,7 +4598,7 @@ CycleSignpostedFile(file_signposted *File)
|
|||
// NOTE(matt) This file_signposted struct is totally hardcoded to the Metadata file, but may suffice for now
|
||||
//
|
||||
// TODO(matt): This is probably insufficient. We likely need to offset the pointers any time we add stuff to the database
|
||||
fclose(File->File.Handle);
|
||||
CloseFile(&File->File, FALSE);
|
||||
// TODO(matt): Rather than freeing the buffer, why not just realloc it to fit the new file? Couldn't that work easily?
|
||||
// The reason we Free / Reread is to save us having to shuffle the buffer contents around, basically
|
||||
// If we switch the whole database over to use linked lists - the database being the only file we actually cycle
|
||||
|
@ -4834,9 +4891,7 @@ SnipeChecksumAndCloseFile(file *HTMLFile, db_asset *Asset, int LandmarksInFile,
|
|||
|
||||
HTMLFile->Handle = fopen(HTMLFile->Path, "w");
|
||||
fwrite(HTMLFile->Buffer.Location, HTMLFile->Buffer.Size, 1, HTMLFile->Handle);
|
||||
fclose(HTMLFile->Handle);
|
||||
HTMLFile->Handle = 0;
|
||||
FreeFile(HTMLFile);
|
||||
FreeFile(HTMLFile, NA);
|
||||
}
|
||||
|
||||
// TODO(matt): Bounds-check the Metadata.File.Buffer
|
||||
|
@ -5136,13 +5191,25 @@ AccumulateFileEditSize(file_signposted *File, int64_t Bytes)
|
|||
File->Signposts.Edit.Size += Bytes;
|
||||
}
|
||||
|
||||
FILE *
|
||||
OpenFileForWriting(file *F)
|
||||
{
|
||||
if(F->Locking && F->Handle)
|
||||
{
|
||||
fclose(F->Handle);
|
||||
}
|
||||
F->Handle = fopen(F->Path, "w");
|
||||
TryLock(F);
|
||||
return F->Handle;
|
||||
}
|
||||
|
||||
// TODO(matt): Consider enforcing an order of these blocks, basically putting the easy-to-skip ones first...
|
||||
void *
|
||||
InitBlock(block_id ID)
|
||||
{
|
||||
db_header *Header = (db_header *)DB.Metadata.File.Buffer.Location;
|
||||
++Header->BlockCount;
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
fwrite(DB.Metadata.File.Buffer.Location, DB.Metadata.File.Buffer.Size, 1, DB.Metadata.File.Handle);
|
||||
|
||||
SetFileEditPosition(&DB.Metadata);
|
||||
|
@ -5419,9 +5486,10 @@ IsSymlink(char *Filepath)
|
|||
return Result;
|
||||
}
|
||||
|
||||
void
|
||||
watch_file *
|
||||
PushWatchFileUniquely(watch_handle *Handle, string Filepath, extension_id Extension, watch_type Type, project *Project, asset *Asset)
|
||||
{
|
||||
watch_file *Result = 0;
|
||||
bool Required = TRUE;
|
||||
for(int i = 0; i < Handle->Files.ItemCount; ++i)
|
||||
{
|
||||
|
@ -5432,14 +5500,16 @@ PushWatchFileUniquely(watch_handle *Handle, string Filepath, extension_id Extens
|
|||
{
|
||||
if(This->Extension == Extension)
|
||||
{
|
||||
Result = This;
|
||||
Required = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(StringsMatch(This->Path, Filepath))
|
||||
{
|
||||
Required = FALSE;
|
||||
Result = This;
|
||||
EraseCurrentStringFromBook(&WatchHandles.Paths);
|
||||
Required = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -5459,7 +5529,9 @@ PushWatchFileUniquely(watch_handle *Handle, string Filepath, extension_id Extens
|
|||
New->Type = Type;
|
||||
New->Project = Project;
|
||||
New->Asset = Asset;
|
||||
Result = New;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -5535,9 +5607,10 @@ GetNearestExistingPath(string Path)
|
|||
return Result;
|
||||
}
|
||||
|
||||
void
|
||||
watch_file *
|
||||
PushWatchHandle(string Path, extension_id Extension, watch_type Type, project *Project, asset *Asset)
|
||||
{
|
||||
watch_file *Result = 0;
|
||||
// NOTE(matt): This function influences RemoveAndFreeAllButFirstWatchFile(). If we change to write different strings into
|
||||
// the W->Paths memory_book, we must reflect that change over to RemoveAndFreeAllButFirstWatchFile()
|
||||
|
||||
|
@ -5675,7 +5748,8 @@ PushWatchHandle(string Path, extension_id Extension, watch_type Type, project *P
|
|||
//PrintWatchHandles();
|
||||
}
|
||||
|
||||
PushWatchFileUniquely(Watch, Filename, Extension, Type, Project, Asset);
|
||||
Result = PushWatchFileUniquely(Watch, Filename, Extension, Type, Project, Asset);
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -5795,6 +5869,17 @@ UpdateNeighbourhoodPointers(neighbourhood *N, file_signposts *S)
|
|||
N->Next = S->Next.Ptr;
|
||||
}
|
||||
|
||||
void
|
||||
WriteEntireDatabase(neighbourhood *N)
|
||||
{
|
||||
// NOTE(matt): This may suffice when the only changes are to existing fixed-size variables,
|
||||
// e.g. ProjectsBlock->GlobalSearchDir
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
fwrite(DB.Metadata.File.Buffer.Location, DB.Metadata.File.Buffer.Size, 1, DB.Metadata.File.Handle);
|
||||
CycleSignpostedFile(&DB.Metadata);
|
||||
UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts);
|
||||
}
|
||||
|
||||
int
|
||||
UpdateAssetInDB(asset *Asset)
|
||||
{
|
||||
|
@ -5854,7 +5939,7 @@ UpdateAssetInDB(asset *Asset)
|
|||
|
||||
FreeString(&Message);
|
||||
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
fwrite(DB.Metadata.File.Buffer.Location, DB.Metadata.File.Buffer.Size, 1, DB.Metadata.File.Handle);
|
||||
SetFileEditPosition(&DB.Metadata);
|
||||
|
||||
|
@ -5865,7 +5950,7 @@ UpdateAssetInDB(asset *Asset)
|
|||
else
|
||||
{
|
||||
// Append new asset, not bothering to insertion sort because there likely won't be many
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
char *InsertionPoint = LocateEndOfAssetsBlock(AssetsBlock);
|
||||
|
||||
AssetIndexInDB = AssetsBlock->Count;
|
||||
|
@ -5994,7 +6079,7 @@ UpdateAsset(asset *Asset, bool Defer)
|
|||
{
|
||||
AssetIndexInDB = UpdateAssetInDB(Asset);
|
||||
}
|
||||
FreeFile(&File);
|
||||
FreeFile(&File, NA);
|
||||
return AssetIndexInDB;
|
||||
}
|
||||
|
||||
|
@ -6068,7 +6153,7 @@ PlaceAsset(string Filename, asset_type Type, uint64_t Variants, bool Associated,
|
|||
printf("%sNonexistent%s %s asset: %s\n", ColourStrings[CS_WARNING], ColourStrings[CS_END], AssetTypeNames[Type], File.Path);
|
||||
}
|
||||
|
||||
FreeFile(&File);
|
||||
FreeFile(&File, NA);
|
||||
return This;
|
||||
}
|
||||
|
||||
|
@ -7601,7 +7686,7 @@ GenerateTopicColours(neighbourhood *N, string Topic)
|
|||
printf(" Freed Topics (%ld)\n", Topics.Buffer.Size);
|
||||
#endif
|
||||
|
||||
fclose(Topics.Handle);
|
||||
CloseFile(&Topics, NA);
|
||||
|
||||
asset *Asset = GetPlaceInBook(&Assets, ASSET_CSS_TOPICS);
|
||||
if(Asset->Known)
|
||||
|
@ -7623,7 +7708,7 @@ GenerateTopicColours(neighbourhood *N, string Topic)
|
|||
}
|
||||
}
|
||||
}
|
||||
FreeFile(&Topics);
|
||||
FreeFile(&Topics, NA);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
@ -9115,7 +9200,7 @@ void
|
|||
ExamineDB(void)
|
||||
{
|
||||
DB.Metadata.File.Buffer.ID = BID_DATABASE;
|
||||
DB.Metadata.File = InitFile(0, &Config->DatabaseLocation, EXT_NULL);
|
||||
DB.Metadata.File = InitFile(0, &Config->DatabaseLocation, EXT_NULL, TRUE);
|
||||
ReadFileIntoBuffer(&DB.Metadata.File); // NOTE(matt): Could we actually catch errors (permissions?) here and bail?
|
||||
|
||||
if(DB.Metadata.File.Buffer.Location)
|
||||
|
@ -9146,7 +9231,7 @@ ExamineDB(void)
|
|||
{
|
||||
fprintf(stderr, "Unable to open database file %s: %s\n", DB.Metadata.File.Path, strerror(errno));
|
||||
}
|
||||
FreeFile(&DB.Metadata.File);
|
||||
FreeFile(&DB.Metadata.File, TRUE);
|
||||
}
|
||||
|
||||
#define HMMLCleanup() \
|
||||
|
@ -9323,7 +9408,7 @@ DeleteSearchPageFromFilesystem(string BaseDir, string SearchLocation, string Pro
|
|||
|
||||
remove(DB.File.Path);
|
||||
// TODO(matt): Consider the correctness of this.
|
||||
FreeFile(&DB.File);
|
||||
FreeFile(&DB.File, NA);
|
||||
|
||||
char *SearchDirectory = ConstructDirectoryPath(&BaseDir, &SearchLocation, 0);
|
||||
remove(SearchDirectory);
|
||||
|
@ -11866,7 +11951,7 @@ InitIndexFile(project *P)
|
|||
|
||||
DB.File.Handle = fopen(DB.File.Path, "w");
|
||||
fprintf(DB.File.Handle, "---\n");
|
||||
fclose(DB.File.Handle);
|
||||
CloseFile(&DB.File, NA);
|
||||
ReadFileIntoBuffer(&DB.File);
|
||||
}
|
||||
}
|
||||
|
@ -12243,10 +12328,7 @@ RenumberEntries(neighbourhood *N, db_header_project *P, int64_t IndexOfFirstEntr
|
|||
Cursor += fwrite(Cursor, 1, DB.File.Buffer.Size - (Cursor - DB.File.Buffer.Location), DB.File.Handle);
|
||||
CycleFile(&DB.File);
|
||||
|
||||
if(!(DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w"))) { return; }
|
||||
fwrite(DB.Metadata.File.Buffer.Location, 1, DB.Metadata.File.Buffer.Size, DB.Metadata.File.Handle);
|
||||
CycleSignpostedFile(&DB.Metadata);
|
||||
UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts);
|
||||
WriteEntireDatabase(N);
|
||||
}
|
||||
|
||||
db_entry *
|
||||
|
@ -12328,7 +12410,7 @@ InsertIntoDB(neighbourhood *N, buffers *CollationBuffers, template *BespokeTempl
|
|||
fwrite(DB.File.Buffer.Location, EntryInsertionStart, 1, DB.File.Handle);
|
||||
fwrite(CollationBuffers->SearchEntry.Location, N->This->Size, 1, DB.File.Handle);
|
||||
fwrite(DB.File.Buffer.Location + EntryInsertionEnd, DB.File.Buffer.Size - EntryInsertionEnd, 1, DB.File.Handle);
|
||||
fclose(DB.File.Handle);
|
||||
CloseFile(&DB.File, NA);
|
||||
|
||||
if(N->This->Size == EntryInsertionEnd - EntryInsertionStart)
|
||||
{
|
||||
|
@ -12350,7 +12432,7 @@ InsertIntoDB(neighbourhood *N, buffers *CollationBuffers, template *BespokeTempl
|
|||
Ptr += sizeof(*N->Project) + sizeof(db_entry) * N->ThisIndex;
|
||||
uint64_t BytesIntoFile = Ptr - DB.Metadata.File.Buffer.Location;
|
||||
|
||||
if(!(DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w"))) { return 0; }
|
||||
if(!(OpenFileForWriting(&DB.Metadata.File))) { return 0; }
|
||||
fwrite(DB.Metadata.File.Buffer.Location, BytesIntoFile, 1, DB.Metadata.File.Handle);
|
||||
SetFileEditPosition(&DB.Metadata);
|
||||
|
||||
|
@ -12401,7 +12483,7 @@ WritePastAssetsHeader(void)
|
|||
DB.Metadata.File.Buffer.Ptr = DB.Metadata.Signposts.AssetsBlock.Ptr;
|
||||
DB.Metadata.File.Buffer.Ptr += sizeof(db_block_assets);
|
||||
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
fwrite(DB.Metadata.File.Buffer.Location, DB.Metadata.File.Buffer.Ptr - DB.Metadata.File.Buffer.Location, 1, DB.Metadata.File.Handle);
|
||||
}
|
||||
|
||||
|
@ -13197,7 +13279,7 @@ InsertNeighbourLink(db_header_project *P, db_entry *From, db_entry *To, enum8(li
|
|||
|
||||
if(To) { DeclaimBuffer(&ToPlayerURL); }
|
||||
DeclaimBuffer(&Link);
|
||||
fclose(HTML.Handle);
|
||||
CloseFile(&HTML, NA);
|
||||
HTML.Handle = 0;
|
||||
}
|
||||
}
|
||||
|
@ -13205,7 +13287,7 @@ InsertNeighbourLink(db_header_project *P, db_entry *From, db_entry *To, enum8(li
|
|||
{
|
||||
Result = RC_ERROR_FILE;
|
||||
}
|
||||
FreeFile(&HTML);
|
||||
FreeFile(&HTML, NA);
|
||||
MEM_TEST_END("InsertNeighbourLink()");
|
||||
return Result;
|
||||
}
|
||||
|
@ -13236,14 +13318,14 @@ DeleteNeighbourLinks(neighbourhood *N)
|
|||
ReadPlayerPageIntoBuffer(&HTML, Wrap0i(N->Project->BaseDir), Wrap0i(N->Project->PlayerLocation), Wrap0i(Entry->OutputLocation));
|
||||
if(HTML.Buffer.Location)
|
||||
{
|
||||
if(!(HTML.Handle = fopen(HTML.Path, "w"))) { FreeFile(&HTML); return RC_ERROR_FILE; };
|
||||
if(!(HTML.Handle = fopen(HTML.Path, "w"))) { FreeFile(&HTML, NA); return RC_ERROR_FILE; };
|
||||
fwrite(HTML.Buffer.Location, Entry->LinkOffsets.PrevStart, 1, HTML.Handle);
|
||||
fwrite(HTML.Buffer.Location + Entry->LinkOffsets.PrevStart + Entry->LinkOffsets.PrevEnd, Entry->LinkOffsets.NextStart, 1, HTML.Handle);
|
||||
fwrite(HTML.Buffer.Location + Entry->LinkOffsets.PrevStart + Entry->LinkOffsets.PrevEnd + Entry->LinkOffsets.NextStart + Entry->LinkOffsets.NextEnd,
|
||||
HTML.Buffer.Size - (Entry->LinkOffsets.PrevStart + Entry->LinkOffsets.PrevEnd + Entry->LinkOffsets.NextStart + Entry->LinkOffsets.NextEnd),
|
||||
1,
|
||||
HTML.Handle);
|
||||
fclose(HTML.Handle);
|
||||
CloseFile(&HTML, NA);
|
||||
Entry->LinkOffsets.PrevEnd = 0;
|
||||
Entry->LinkOffsets.NextEnd = 0;
|
||||
if(N->Prev && N->PrevIndex >= 0)
|
||||
|
@ -13262,7 +13344,7 @@ DeleteNeighbourLinks(neighbourhood *N)
|
|||
}
|
||||
}
|
||||
|
||||
FreeFile(&HTML);
|
||||
FreeFile(&HTML, NA);
|
||||
return RC_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -13331,7 +13413,7 @@ MarkNextAsFirst(neighbourhood *N)
|
|||
N->Next->LinkOffsets.PrevEnd = Link.Ptr - Link.Location;
|
||||
|
||||
DeclaimBuffer(&Link);
|
||||
fclose(HTML.Handle);
|
||||
CloseFile(&HTML, NA);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -13339,7 +13421,7 @@ MarkNextAsFirst(neighbourhood *N)
|
|||
N->Next->LinkOffsets = Blank;
|
||||
}
|
||||
|
||||
FreeFile(&HTML);
|
||||
FreeFile(&HTML, NA);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -13369,7 +13451,7 @@ MarkPrevAsFinal(neighbourhood *N)
|
|||
N->Prev->LinkOffsets.NextEnd = Link.Ptr - Link.Location;
|
||||
|
||||
DeclaimBuffer(&Link);
|
||||
fclose(HTML.Handle);
|
||||
CloseFile(&HTML, NA);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -13377,7 +13459,7 @@ MarkPrevAsFinal(neighbourhood *N)
|
|||
N->Prev->LinkOffsets = Blank;
|
||||
}
|
||||
|
||||
FreeFile(&HTML);
|
||||
FreeFile(&HTML, NA);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -13534,7 +13616,7 @@ DeleteFromDB(neighbourhood *N, string BaseFilename, db_entry *Deceased)
|
|||
}
|
||||
N->Project->EntryCount = NewEntryCount;
|
||||
|
||||
if(!(DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w"))) { FreeBuffer(&DB.Metadata.File.Buffer); return RC_ERROR_FILE; }
|
||||
if(!(OpenFileForWriting(&DB.Metadata.File))) { FreeBuffer(&DB.Metadata.File.Buffer); return RC_ERROR_FILE; }
|
||||
|
||||
char *Ptr = (char *)N->This;
|
||||
uint64_t BytesIntoFile = Ptr - DB.Metadata.File.Buffer.Location;
|
||||
|
@ -14560,7 +14642,7 @@ SetCurrentProject(project *P, neighbourhood *N)
|
|||
{
|
||||
if(CurrentProject != P)
|
||||
{
|
||||
FreeFile(&DB.File);
|
||||
FreeFile(&DB.File, NA);
|
||||
|
||||
if(CurrentProject)
|
||||
{
|
||||
|
@ -14608,7 +14690,6 @@ RecheckPrivacyRecursively(project *P, neighbourhood *N, buffers *CollationBuffer
|
|||
DeleteStaleAssets();
|
||||
UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for(int i = 0; i < P->Child.ItemCount; ++i)
|
||||
|
@ -14723,7 +14804,7 @@ UpgradeDB(int OriginalDBVersion)
|
|||
ClearCopyStringNoFormat(DB.AssetsBlock.JSDir, sizeof(DB.AssetsBlock.JSDir), Config->JSDir);
|
||||
++DB.Header.BlockCount;
|
||||
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
fwrite(&DB.Header, sizeof(DB.Header), 1, DB.Metadata.File.Handle);
|
||||
|
||||
DB.Metadata.Signposts.ProjectsBlock.Byte = ftell(DB.Metadata.File.Handle);
|
||||
|
@ -14829,7 +14910,7 @@ DeleteDeadDBEntries(neighbourhood *N, bool *Modified)
|
|||
|
||||
Free(NewSearchDirectory);
|
||||
|
||||
FreeFile(&DB.File);
|
||||
FreeFile(&DB.File, NA);
|
||||
DB.File.Path = ConstructIndexFilePath(CurrentProject->BaseDir, CurrentProject->SearchLocation, CurrentProject->ID);
|
||||
ReadFileIntoBuffer(&DB.File); // NOTE(matt): Could we actually catch errors (permissions?) here and bail?
|
||||
|
||||
|
@ -14847,7 +14928,7 @@ DeleteDeadDBEntries(neighbourhood *N, bool *Modified)
|
|||
if(StringsDiffer(CurrentProject->BaseDir, Wrap0i(N->Project->BaseDir)))
|
||||
{
|
||||
ClearCopyStringNoFormat(N->Project->BaseDir, sizeof(N->Project->BaseDir), CurrentProject->BaseDir);
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
WriteFromByteToEnd(&DB.Metadata.File, 0);
|
||||
CycleSignpostedFile(&DB.Metadata);
|
||||
UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts);
|
||||
|
@ -14857,7 +14938,7 @@ DeleteDeadDBEntries(neighbourhood *N, bool *Modified)
|
|||
if(StringsDiffer(CurrentProject->BaseURL, Wrap0i(N->Project->BaseURL)))
|
||||
{
|
||||
ClearCopyStringNoFormat(N->Project->BaseURL, sizeof(N->Project->BaseURL), CurrentProject->BaseURL);
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
WriteFromByteToEnd(&DB.Metadata.File, 0);
|
||||
CycleSignpostedFile(&DB.Metadata);
|
||||
UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts);
|
||||
|
@ -14866,7 +14947,7 @@ DeleteDeadDBEntries(neighbourhood *N, bool *Modified)
|
|||
|
||||
if(HasNewPlayerLocation || HasNewSearchLocation)
|
||||
{
|
||||
if(!(DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w"))) { FreeBuffer(&DB.Metadata.File.Buffer); return RC_ERROR_FILE; }
|
||||
if(!(OpenFileForWriting(&DB.Metadata.File))) { FreeBuffer(&DB.Metadata.File.Buffer); return RC_ERROR_FILE; }
|
||||
ClearCopyStringNoFormat(N->Project->BaseDir, sizeof(N->Project->BaseDir), CurrentProject->BaseDir);
|
||||
ClearCopyStringNoFormat(N->Project->BaseURL, sizeof(N->Project->BaseURL), CurrentProject->BaseURL);
|
||||
fwrite(DB.Metadata.File.Buffer.Location, DB.Metadata.File.Buffer.Size, 1, DB.Metadata.File.Handle);
|
||||
|
@ -14942,7 +15023,7 @@ SyncDBWithInput(neighbourhood *N, buffers *CollationBuffers, template *BespokeTe
|
|||
if(StringsDiffer(CurrentProject->Title, Wrap0i(N->Project->Title)))
|
||||
{
|
||||
ClearCopyStringNoFormat(N->Project->Title, sizeof(N->Project->Title), CurrentProject->Title);
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
WriteFromByteToEnd(&DB.Metadata.File, 0);
|
||||
CycleSignpostedFile(&DB.Metadata);
|
||||
UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts);
|
||||
|
@ -14952,7 +15033,7 @@ SyncDBWithInput(neighbourhood *N, buffers *CollationBuffers, template *BespokeTe
|
|||
if(StringsDiffer(CurrentProject->Theme, Wrap0i(N->Project->Theme)))
|
||||
{
|
||||
ClearCopyStringNoFormat(N->Project->Theme, sizeof(N->Project->Theme), CurrentProject->Theme);
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
WriteFromByteToEnd(&DB.Metadata.File, 0);
|
||||
CycleSignpostedFile(&DB.Metadata);
|
||||
UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts);
|
||||
|
@ -14962,7 +15043,7 @@ SyncDBWithInput(neighbourhood *N, buffers *CollationBuffers, template *BespokeTe
|
|||
if(StringsDiffer(CurrentProject->Numbering.Unit, Wrap0i(N->Project->Unit)))
|
||||
{
|
||||
ClearCopyStringNoFormat(N->Project->Unit, sizeof(N->Project->Unit), CurrentProject->Numbering.Unit);
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
WriteFromByteToEnd(&DB.Metadata.File, 0);
|
||||
CycleSignpostedFile(&DB.Metadata);
|
||||
UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts);
|
||||
|
@ -15082,16 +15163,6 @@ InitAccumulator(project_generations *G)
|
|||
return Result;
|
||||
}
|
||||
|
||||
void
|
||||
WriteEntireDatabase()
|
||||
{
|
||||
// NOTE(matt): This may suffice when the only changes are to existing fixed-size variables,
|
||||
// e.g. ProjectsBlock->GlobalSearchDir
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
fwrite(DB.Metadata.File.Buffer.Location, DB.Metadata.File.Buffer.Size, 1, DB.Metadata.File.Handle);
|
||||
fclose(DB.Metadata.File.Handle);
|
||||
}
|
||||
|
||||
rc
|
||||
InitDB(void)
|
||||
{
|
||||
|
@ -15101,9 +15172,15 @@ InitDB(void)
|
|||
// need a separate InitIndex() function that we can call when looping over the projects after ParseConfig()
|
||||
|
||||
DB.Metadata.File.Buffer.ID = BID_DATABASE;
|
||||
DB.Metadata.File = InitFile(0, &Config->DatabaseLocation, EXT_NULL);
|
||||
ReadFileIntoBuffer(&DB.Metadata.File); // NOTE(matt): Could we actually catch errors (permissions?) here and bail?
|
||||
|
||||
DB.Metadata.File = InitFile(0, &Config->DatabaseLocation, EXT_NULL, TRUE);
|
||||
if(ReadFileIntoBuffer(&DB.Metadata.File) == RC_ERROR_FILE_LOCKED) // NOTE(matt): Could we actually catch errors (permissions?) here and bail?
|
||||
{
|
||||
ConfigErrorLockedDBLocation(0, 0, &Config->DatabaseLocation);
|
||||
CloseFile(&DB.Metadata.File, TRUE);
|
||||
Result = RC_ERROR_FILE_LOCKED;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(DB.Metadata.File.Buffer.Location)
|
||||
{
|
||||
// TODO(matt): Handle this gracefully (it'll be an invalid file)
|
||||
|
@ -15169,12 +15246,12 @@ InitDB(void)
|
|||
}
|
||||
}
|
||||
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
if(DB.Metadata.File.Handle)
|
||||
if(OpenFileForWriting(&DB.Metadata.File))
|
||||
{
|
||||
fwrite(B->Location, B->Size, 1, DB.Metadata.File.Handle);
|
||||
SetFileEditPosition(&DB.Metadata);
|
||||
CycleSignpostedFile(&DB.Metadata);
|
||||
DB.Ready = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -15233,9 +15310,7 @@ InitDB(void)
|
|||
|
||||
if(Result == RC_SUCCESS)
|
||||
{
|
||||
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
if(DB.Metadata.File.Handle)
|
||||
if(OpenFileForWriting(&DB.Metadata.File))
|
||||
{
|
||||
fwrite(&DB.Header, sizeof(DB.Header), 1, DB.Metadata.File.Handle);
|
||||
|
||||
|
@ -15245,10 +15320,11 @@ InitDB(void)
|
|||
DB.Metadata.Signposts.AssetsBlock.Byte = ftell(DB.Metadata.File.Handle);
|
||||
fwrite(&DB.AssetsBlock, sizeof(DB.AssetsBlock), 1, DB.Metadata.File.Handle);
|
||||
|
||||
fclose(DB.Metadata.File.Handle);
|
||||
CloseFile(&DB.Metadata.File, FALSE);
|
||||
ReadFileIntoBuffer(&DB.Metadata.File);
|
||||
DB.Metadata.Signposts.ProjectsBlock.Ptr = DB.Metadata.File.Buffer.Location + DB.Metadata.Signposts.ProjectsBlock.Byte;
|
||||
DB.Metadata.Signposts.AssetsBlock.Ptr = DB.Metadata.File.Buffer.Location + DB.Metadata.Signposts.AssetsBlock.Byte;
|
||||
DB.Ready = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -15262,11 +15338,12 @@ InitDB(void)
|
|||
#if 0
|
||||
DB.File.Handle = fopen(DB.File.Path, "w");
|
||||
fprintf(DB.File.Handle, "---\n");
|
||||
fclose(DB.File.Handle);
|
||||
CloseFile(&DB.File, NA);
|
||||
ReadFileIntoBuffer(&DB.File);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
@ -15407,7 +15484,7 @@ InsertProjectIntoDB_TopLevel(project_generations *G, db_block_projects **Block,
|
|||
{
|
||||
uint64_t Byte = 0;
|
||||
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
WriteFromByteToPointer(&DB.Metadata.File, &Byte, *Block);
|
||||
|
||||
db_block_projects NewBlock = **Block;
|
||||
|
@ -15431,7 +15508,7 @@ InsertProjectIntoDB(project_generations *G, db_header_project **Parent, db_heade
|
|||
{
|
||||
uint64_t Byte = 0;
|
||||
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
WriteFromByteToPointer(&DB.Metadata.File, &Byte, *Parent);
|
||||
|
||||
db_header_project NewParent = **Parent;
|
||||
|
@ -15633,7 +15710,7 @@ DeleteProject_TopLevel(db_block_projects **Parent, db_header_project **Child, pr
|
|||
uint64_t PPos = (char *)*Parent - DB.Metadata.File.Buffer.Location;
|
||||
uint64_t CPos = (char *)*Child - DB.Metadata.File.Buffer.Location;
|
||||
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
uint64_t Byte = 0;
|
||||
|
||||
WriteFromByteToPointer(&DB.Metadata.File, &Byte, *Parent);
|
||||
|
@ -15663,7 +15740,7 @@ DeleteProject(db_header_project **Parent, db_header_project **Child, project_gen
|
|||
uint64_t CPos = (char *)*Child - DB.Metadata.File.Buffer.Location;
|
||||
//db_project_index Index = GetCurrentProjectIndex(G);
|
||||
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
uint64_t Byte = 0;
|
||||
|
||||
WriteFromByteToPointer(&DB.Metadata.File, &Byte, *Parent);
|
||||
|
@ -15723,7 +15800,7 @@ ReorganiseProjectsInterior(project_generations *G, project *CChild, uint64_t Chi
|
|||
project_generations ThisAcc = InitAccumulator(G);
|
||||
AccumulateProjectIndices(&ThisAcc, This);
|
||||
|
||||
DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w");
|
||||
OpenFileForWriting(&DB.Metadata.File);
|
||||
uint64_t Byte = 0;
|
||||
|
||||
WriteFromByteToPointer(&DB.Metadata.File, &Byte, SChild);
|
||||
|
@ -16036,13 +16113,13 @@ SyncGlobalPagesWithInput(neighbourhood *N, buffers *CollationBuffers)
|
|||
if(StringsDiffer(StoredGlobalSearchDir, Config->GlobalSearchDir))
|
||||
{
|
||||
ClearCopyStringNoFormat(ProjectsBlock->GlobalSearchDir, sizeof(ProjectsBlock->GlobalSearchDir), Config->GlobalSearchDir);
|
||||
WriteEntireDatabase();
|
||||
WriteEntireDatabase(N);
|
||||
}
|
||||
|
||||
if(StringsDiffer(StoredGlobalSearchURL, Config->GlobalSearchURL))
|
||||
{
|
||||
ClearCopyStringNoFormat(ProjectsBlock->GlobalSearchURL, sizeof(ProjectsBlock->GlobalSearchURL), Config->GlobalSearchURL);
|
||||
WriteEntireDatabase();
|
||||
WriteEntireDatabase(N);
|
||||
}
|
||||
|
||||
if(!ProjectsBlock->GlobalSearchDir[0])
|
||||
|
@ -16350,6 +16427,15 @@ RemoveAndFreeWatchHandles(watch_handles *W)
|
|||
{
|
||||
watch_handle *This = GetPlaceInBook(&W->Handles, i);
|
||||
inotify_rm_watch(inotifyInstance, This->Descriptor);
|
||||
for(int j = 0; j < This->Files.ItemCount; ++j)
|
||||
{
|
||||
watch_file *File = GetPlaceInBook(&This->Files, j);
|
||||
if(File->Handle)
|
||||
{
|
||||
fclose(File->Handle);
|
||||
File->Handle = 0;
|
||||
}
|
||||
}
|
||||
FreeBook(&This->Files);
|
||||
}
|
||||
FreeAndReinitialiseBook(&W->Handles);
|
||||
|
@ -16359,8 +16445,9 @@ RemoveAndFreeWatchHandles(watch_handles *W)
|
|||
void
|
||||
DiscardAllAndFreeConfig(void)
|
||||
{
|
||||
FreeSignpostedFile(&DB.Metadata); // NOTE(matt): This seems fine
|
||||
FreeFile(&DB.File); // NOTE(matt): This seems fine
|
||||
FreeSignpostedFile(&DB.Metadata, TRUE); // NOTE(matt): This seems fine
|
||||
FreeFile(&DB.File, NA); // NOTE(matt): This seems fine
|
||||
DB.Ready = FALSE;
|
||||
FreeAssets(&Assets); // NOTE(matt): This seems fine
|
||||
RemoveAndFreeWatchHandles(&WatchHandles);
|
||||
CurrentProject = 0; // NOTE(matt): This is fine
|
||||
|
@ -16572,6 +16659,8 @@ MonitorFilesystem(neighbourhood *N, buffers *CollationBuffers, template *Bespoke
|
|||
switch(WatchFile->Type)
|
||||
{
|
||||
case WT_HMML:
|
||||
{
|
||||
if(DB.Ready)
|
||||
{
|
||||
SetCurrentProject(WatchFile->Project, N);
|
||||
string BaseFilename = GetBaseFilename(Wrap0(Event->name), WatchFile->Extension);
|
||||
|
@ -16583,14 +16672,18 @@ MonitorFilesystem(neighbourhood *N, buffers *CollationBuffers, template *Bespoke
|
|||
{
|
||||
Inserted |= (InsertEntry(N, CollationBuffers, BespokeTemplate, BaseFilename, 0) == RC_SUCCESS);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case WT_ASSET:
|
||||
{
|
||||
if(DB.Ready)
|
||||
{
|
||||
UpdateAsset(WatchFile->Asset, FALSE);
|
||||
UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts);
|
||||
#if DEBUG_LANDMARKS
|
||||
UpdatedAsset = TRUE;
|
||||
#endif
|
||||
}
|
||||
} break;
|
||||
case WT_CONFIG:
|
||||
{
|
||||
|
@ -16749,7 +16842,6 @@ main(int ArgC, char **Args)
|
|||
PushWatchHandle(ConfigPathL, EXT_NULL, WT_CONFIG, 0, 0);
|
||||
|
||||
Config = ParseConfig(ConfigPathL, &TokensList);
|
||||
rc Succeeding = RC_SUCCESS;
|
||||
if(Config)
|
||||
{
|
||||
if(Mode & MODE_EXAMINE)
|
||||
|
@ -16766,7 +16858,7 @@ main(int ArgC, char **Args)
|
|||
else
|
||||
{
|
||||
/* */ MEM_TEST_MID("main()");
|
||||
/* +MEM */ Succeeding = InitAll(&Neighbourhood, &CollationBuffers, &BespokeTemplate);
|
||||
/* +MEM */ InitAll(&Neighbourhood, &CollationBuffers, &BespokeTemplate);
|
||||
/* */ MEM_TEST_MID("main()");
|
||||
}
|
||||
}
|
||||
|
@ -16782,8 +16874,6 @@ main(int ArgC, char **Args)
|
|||
}
|
||||
}
|
||||
|
||||
if(Succeeding == RC_SUCCESS)
|
||||
{
|
||||
if(inotifyInstance != -1)
|
||||
{
|
||||
while(GlobalRunning && MonitorFilesystem(&Neighbourhood, &CollationBuffers, &BespokeTemplate, ConfigPathL, &TokensList) != RC_ARENA_FULL)
|
||||
|
@ -16798,7 +16888,7 @@ main(int ArgC, char **Args)
|
|||
// 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)
|
||||
if(!(Mode & MODE_DRYRUN) && DB.Ready && Config && Config->RespectingPrivacy && time(0) - LastPrivacyCheck > Config->PrivacyCheckInterval)
|
||||
{
|
||||
RecheckPrivacy(&Neighbourhood, &CollationBuffers, &BespokeTemplate);
|
||||
}
|
||||
|
@ -16812,9 +16902,7 @@ main(int ArgC, char **Args)
|
|||
}
|
||||
|
||||
DiscardAllAndFreeConfig();
|
||||
RemoveAndFreeWatchHandles(&WatchHandles);
|
||||
MEM_TEST_END("main()");
|
||||
}
|
||||
}
|
||||
Exit();
|
||||
}
|
||||
|
|
|
@ -8,12 +8,6 @@ exit
|
|||
|
||||
// config
|
||||
//
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define ArgCountPointer(...) (sizeof((void *[]){__VA_ARGS__})/sizeof(void *))
|
||||
|
||||
#define DigitsInInt(I) DigitsInInt_(I, sizeof(*I))
|
||||
|
@ -348,6 +342,15 @@ PushTokens(memory_book *TokensList)
|
|||
return This;
|
||||
}
|
||||
|
||||
void
|
||||
PopTokens(memory_book *TokensList)
|
||||
{
|
||||
--TokensList->ItemCount;
|
||||
tokens *This = GetPlaceInBook(TokensList, TokensList->ItemCount);
|
||||
FreeBook(&This->Token);
|
||||
This->CurrentLine = 0;
|
||||
}
|
||||
|
||||
void
|
||||
FreeTokensList(memory_book *TokensList)
|
||||
{
|
||||
|
@ -357,7 +360,8 @@ FreeTokensList(memory_book *TokensList)
|
|||
This->CurrentIndex = 0;
|
||||
This->CurrentLine = 0;
|
||||
FreeBook(&This->Token);
|
||||
FreeFile(&This->File);
|
||||
FreeFileBufferAndPath(&This->File); // NOTE(matt): Not closing file because the next time we touch this handle is in
|
||||
// RemoveAndFreeWatchHandles() where we will close it
|
||||
}
|
||||
TokensList->ItemCount = 0;
|
||||
}
|
||||
|
@ -588,22 +592,36 @@ typedef struct
|
|||
} config;
|
||||
|
||||
char *ExpandPath(string Path, string *RelativeToFile); // NOTE(matt): Forward declared. Consider reorganising the code?
|
||||
void PushWatchHandle(string Path, extension_id Extension, watch_type Type, project *Project, asset *Asset); // NOTE(matt): Forward declared. Consider reorganising the code?
|
||||
watch_file *PushWatchHandle(string Path, extension_id Extension, watch_type Type, project *Project, asset *Asset); // NOTE(matt): Forward declared. Consider reorganising the code?
|
||||
|
||||
tokens *
|
||||
Tokenise(memory_book *TokensList, string Path)
|
||||
Tokenise(memory_book *TokensList, string Path, string *Filename, uint64_t LineNumber)
|
||||
{
|
||||
tokens *Result = 0;
|
||||
char *Path0 = MakeString0("l", &Path);
|
||||
FILE *Handle = 0;
|
||||
PushWatchHandle(Path, EXT_NULL, WT_CONFIG, 0, 0);
|
||||
if((Handle = fopen(Path0, "r")))
|
||||
{
|
||||
fclose(Handle);
|
||||
watch_file *WatchFile = PushWatchHandle(Path, EXT_NULL, WT_CONFIG, 0, 0);
|
||||
|
||||
Result = PushTokens(TokensList);
|
||||
Result->File = InitFile(0, &Path, EXT_NULL);
|
||||
ReadFileIntoBuffer(&Result->File);
|
||||
Result->File = InitFile(0, &Path, EXT_NULL, TRUE);
|
||||
switch(ReadFileIntoBuffer(&Result->File))
|
||||
{
|
||||
case RC_ERROR_FILE_LOCKED:
|
||||
{
|
||||
ConfigErrorLockedConfigLocation(Filename, LineNumber, &Path);
|
||||
FreeFile(&Result->File, TRUE);
|
||||
PopTokens(TokensList);
|
||||
Result = 0;
|
||||
} break;
|
||||
case RC_ERROR_FILE:
|
||||
{
|
||||
ConfigErrorUnopenableConfigLocation(Filename, LineNumber, &Path);
|
||||
FreeFile(&Result->File, TRUE);
|
||||
PopTokens(TokensList);
|
||||
Result = 0;
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
WatchFile->Handle = Result->File.Handle;
|
||||
|
||||
buffer *B = &Result->File.Buffer;
|
||||
SkipWhitespace(Result, B);
|
||||
while(B->Ptr - B->Location < B->Size)
|
||||
|
@ -770,12 +788,8 @@ Tokenise(memory_book *TokensList, string Path)
|
|||
Advance(B, Advancement);
|
||||
SkipWhitespace(Result, B);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConfigError(0, 0, S_WARNING, "Unable to open config file: ", &Path);
|
||||
}
|
||||
Free(Path0);
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
@ -2341,7 +2355,8 @@ ScopeTokens(scope_tree *Tree, memory_book *TokensList, tokens *T, memory_book *T
|
|||
}
|
||||
if(!I)
|
||||
{
|
||||
I = Tokenise(TokensList, IncludePathL);
|
||||
token *This = GetPlaceInBook(&T->Token, IncludePathTokenIndex);
|
||||
I = Tokenise(TokensList, IncludePathL, &Filepath, This->LineNumber);
|
||||
}
|
||||
if(I)
|
||||
{
|
||||
|
@ -2352,11 +2367,6 @@ ScopeTokens(scope_tree *Tree, memory_book *TokensList, tokens *T, memory_book *T
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
token *This = GetPlaceInBook(&T->Token, IncludePathTokenIndex);
|
||||
ConfigFileIncludeError(&Filepath, This->LineNumber, Wrap0(IncludePath));
|
||||
}
|
||||
Free(IncludePath);
|
||||
}
|
||||
else
|
||||
|
@ -4977,7 +4987,7 @@ ParseConfig(string Path, memory_book *TokensList)
|
|||
MEM_LOOP_POST("InitTypeSpecs")
|
||||
#endif
|
||||
config *Result = 0;
|
||||
tokens *T = Tokenise(TokensList, Path);
|
||||
tokens *T = Tokenise(TokensList, Path, NA, NA);
|
||||
|
||||
#if 0
|
||||
MEM_LOOP_PRE_FREE("Tokenise")
|
||||
|
@ -4993,7 +5003,7 @@ ParseConfig(string Path, memory_book *TokensList)
|
|||
scope_tree *ScopeTree = InitRootScopeTree();
|
||||
SetTypeSpec(ScopeTree, &TypeSpecs);
|
||||
SetDefaults(ScopeTree, &TypeSpecs);
|
||||
ScopeTree = ScopeTokens(ScopeTree, TokensList, T, &TypeSpecs, 0);
|
||||
ScopeTree = ScopeTokens(ScopeTree, TokensList, T, &TypeSpecs, NA);
|
||||
|
||||
// TODO(matt): Mem testing
|
||||
//
|
||||
|
|
Loading…
Reference in New Issue