Prevent file clashes
Config parsing now errors-out when it detects clashes between: base_dir and search_location, and global_search_dir base_dir and player_location This prevents us from generating search / project pages of multiple projects (incl. global_search_dir) at the same location, which would screw up the asset landmarks.
This commit is contained in:
parent
877dbab5bd
commit
ce9a0e7635
|
@ -23,7 +23,7 @@ typedef struct
|
||||||
version CINERA_APP_VERSION = {
|
version CINERA_APP_VERSION = {
|
||||||
.Major = 0,
|
.Major = 0,
|
||||||
.Minor = 7,
|
.Minor = 7,
|
||||||
.Patch = 9
|
.Patch = 10
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <stdarg.h> // NOTE(matt): varargs
|
#include <stdarg.h> // NOTE(matt): varargs
|
||||||
|
@ -907,6 +907,18 @@ StringsMatch(string A, string B)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
StringsMatchSized(string A, int Size, string B)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
while(i < A.Length && i < B.Length && A.Base[i] == B.Base[i]) { ++i; }
|
||||||
|
if(i == Size)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
StringsMatchCaseInsensitive(string A, string B)
|
StringsMatchCaseInsensitive(string A, string B)
|
||||||
{
|
{
|
||||||
|
@ -2723,6 +2735,15 @@ GetBaseFilename(string Filepath,
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
TrimString(string S, uint32_t CharsFromStart, uint32_t CharsFromEnd)
|
||||||
|
{
|
||||||
|
string Result = S;
|
||||||
|
Result.Length -= (CharsFromStart + CharsFromEnd);
|
||||||
|
Result.Base += CharsFromStart;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
char *WatchTypeStrings[] =
|
char *WatchTypeStrings[] =
|
||||||
{
|
{
|
||||||
"WT_HMML",
|
"WT_HMML",
|
||||||
|
@ -12351,16 +12372,6 @@ DeleteFromDB(neighbourhood *N, string BaseFilename)
|
||||||
return Entry ? RC_SUCCESS : RC_NOOP;
|
return Entry ? RC_SUCCESS : RC_NOOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
string
|
|
||||||
TrimString(string S, uint32_t CharsFromStart, uint32_t CharsFromEnd)
|
|
||||||
{
|
|
||||||
string Result = S;
|
|
||||||
Result.Length -= (CharsFromStart + CharsFromEnd);
|
|
||||||
Result.Base += CharsFromStart;
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
GenerateFilterOfProjectAndChildren(buffer *Filter, db_header_project *StoredP, project *P, bool *SearchRequired, bool TopLevel, bool *RequiresCineraJS)
|
GenerateFilterOfProjectAndChildren(buffer *Filter, db_header_project *StoredP, project *P, bool *SearchRequired, bool TopLevel, bool *RequiresCineraJS)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2958,7 +2958,8 @@ PushVariantUniquely(resolution_errors *E, variant_strings *VS, string Path, uint
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
string String;
|
string String0;
|
||||||
|
string String1;
|
||||||
project **Projects;
|
project **Projects;
|
||||||
uint64_t ProjectCount;
|
uint64_t ProjectCount;
|
||||||
} config_string_association;
|
} config_string_association;
|
||||||
|
@ -2966,7 +2967,8 @@ typedef struct
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
config_string_association *Associations;
|
config_string_association *Associations;
|
||||||
config_identifier_id ID;
|
config_identifier_id ID0;
|
||||||
|
config_identifier_id ID1;
|
||||||
uint64_t Count;
|
uint64_t Count;
|
||||||
} config_string_associations;
|
} config_string_associations;
|
||||||
|
|
||||||
|
@ -2974,6 +2976,8 @@ typedef struct
|
||||||
{
|
{
|
||||||
variant_strings VariantStrings;
|
variant_strings VariantStrings;
|
||||||
config_string_associations HMMLDirs;
|
config_string_associations HMMLDirs;
|
||||||
|
config_string_associations BaseDirAndSearchLocation;
|
||||||
|
config_string_associations BaseDirAndPlayerLocation;
|
||||||
} config_verifiers;
|
} config_verifiers;
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -3197,14 +3201,60 @@ AddProjectToAssociation(config_string_association *A, project *P)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PushHMMLDirAssociation(config_string_associations *HMMLDirs, string *S, project *P)
|
PushAssociation(config_string_associations *HMMLDirs, string *S0, string *S1, project *P)
|
||||||
{
|
{
|
||||||
HMMLDirs->Associations = Fit(HMMLDirs->Associations, sizeof(*HMMLDirs->Associations), HMMLDirs->Count, 8, TRUE);
|
HMMLDirs->Associations = Fit(HMMLDirs->Associations, sizeof(*HMMLDirs->Associations), HMMLDirs->Count, 8, TRUE);
|
||||||
HMMLDirs->Associations[HMMLDirs->Count].String = *S;
|
if(S0) { HMMLDirs->Associations[HMMLDirs->Count].String0 = *S0; }
|
||||||
|
if(S1) { HMMLDirs->Associations[HMMLDirs->Count].String1 = *S1; }
|
||||||
AddProjectToAssociation(&HMMLDirs->Associations[HMMLDirs->Count], P);
|
AddProjectToAssociation(&HMMLDirs->Associations[HMMLDirs->Count], P);
|
||||||
++HMMLDirs->Count;
|
++HMMLDirs->Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config_string_association *
|
||||||
|
GetAssociation(config_string_associations *Set, string String0, string String1)
|
||||||
|
{
|
||||||
|
config_string_association *Result = 0;
|
||||||
|
for(int i = 0; i < Set->Count; ++i)
|
||||||
|
{
|
||||||
|
config_string_association *This = Set->Associations + i;
|
||||||
|
if(StringsMatch(String0, This->String0) && StringsMatch(String1, This->String1))
|
||||||
|
{
|
||||||
|
Result = This;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
config_string_association *
|
||||||
|
GetGlobalPathAssociation(config_string_associations *Set, string Path)
|
||||||
|
{
|
||||||
|
config_string_association *Result = 0;
|
||||||
|
for(int i = 0; i < Set->Count; ++i)
|
||||||
|
{
|
||||||
|
config_string_association *This = Set->Associations + i;
|
||||||
|
if(StringsMatchSized(Path, This->String0.Length, This->String0))
|
||||||
|
{
|
||||||
|
Path = TrimString(Path, This->String0.Length, 0);
|
||||||
|
if(Path.Length == 0 && This->String1.Length == 0)
|
||||||
|
{
|
||||||
|
Result = This;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if(StringsMatchSized(Path, 1, Wrap0("/")))
|
||||||
|
{
|
||||||
|
Path = TrimString(Path, 1, 0);
|
||||||
|
if(Path.Length > 0 && StringsMatchSized(Path, This->String1.Length, This->String1))
|
||||||
|
{
|
||||||
|
Result = This;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SetUniqueHMMLDir(config *C, resolution_errors *E, config_string_associations *HMMLDirs, project *P, scope_tree *ProjectTree, config_pair *HMMLDir)
|
SetUniqueHMMLDir(config *C, resolution_errors *E, config_string_associations *HMMLDirs, project *P, scope_tree *ProjectTree, config_pair *HMMLDir)
|
||||||
{
|
{
|
||||||
|
@ -3212,7 +3262,7 @@ SetUniqueHMMLDir(config *C, resolution_errors *E, config_string_associations *HM
|
||||||
bool Clashed = FALSE;
|
bool Clashed = FALSE;
|
||||||
for(int i = 0; i < HMMLDirs->Count; ++i)
|
for(int i = 0; i < HMMLDirs->Count; ++i)
|
||||||
{
|
{
|
||||||
if(StringsMatch(HMMLDirs->Associations[i].String, Result))
|
if(StringsMatch(HMMLDirs->Associations[i].String0, Result))
|
||||||
{
|
{
|
||||||
AddProjectToAssociation(&HMMLDirs->Associations[i], P);
|
AddProjectToAssociation(&HMMLDirs->Associations[i], P);
|
||||||
PushError(E, S_ERROR, &HMMLDir->Position, IDENT_HMML_DIR);
|
PushError(E, S_ERROR, &HMMLDir->Position, IDENT_HMML_DIR);
|
||||||
|
@ -3223,7 +3273,7 @@ SetUniqueHMMLDir(config *C, resolution_errors *E, config_string_associations *HM
|
||||||
|
|
||||||
if(!Clashed)
|
if(!Clashed)
|
||||||
{
|
{
|
||||||
PushHMMLDirAssociation(HMMLDirs, &Result, P);
|
PushAssociation(HMMLDirs, &Result, 0, P);
|
||||||
}
|
}
|
||||||
P->HMMLDir = Result;
|
P->HMMLDir = Result;
|
||||||
}
|
}
|
||||||
|
@ -3399,6 +3449,28 @@ PushProject(config *C, resolution_errors *E, config_verifiers *V, project *P, sc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config_string_association *BaseDirAndSearchLocation = GetAssociation(&V->BaseDirAndSearchLocation, P->BaseDir, P->SearchLocation);
|
||||||
|
if(BaseDirAndSearchLocation)
|
||||||
|
{
|
||||||
|
PushError(E, S_ERROR, 0, IDENT_SEARCH_LOCATION);
|
||||||
|
AddProjectToAssociation(BaseDirAndSearchLocation, P);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PushAssociation(&V->BaseDirAndSearchLocation, &P->BaseDir, &P->SearchLocation, P);
|
||||||
|
}
|
||||||
|
|
||||||
|
config_string_association *BaseDirAndPlayerLocation = GetAssociation(&V->BaseDirAndPlayerLocation, P->BaseDir, P->PlayerLocation);
|
||||||
|
if(BaseDirAndPlayerLocation)
|
||||||
|
{
|
||||||
|
PushError(E, S_ERROR, 0, IDENT_PLAYER_LOCATION);
|
||||||
|
AddProjectToAssociation(BaseDirAndPlayerLocation, P);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PushAssociation(&V->BaseDirAndPlayerLocation, &P->BaseDir, &P->PlayerLocation, P);
|
||||||
|
}
|
||||||
|
|
||||||
if(!P->DefaultMedium)
|
if(!P->DefaultMedium)
|
||||||
{
|
{
|
||||||
ResetPen(&C->ResolvedVariables);
|
ResetPen(&C->ResolvedVariables);
|
||||||
|
@ -3511,16 +3583,35 @@ PrintAssociationClashes(config_string_associations *A)
|
||||||
config_string_association *Association = A->Associations + i;
|
config_string_association *Association = A->Associations + i;
|
||||||
if(Association->ProjectCount > 1)
|
if(Association->ProjectCount > 1)
|
||||||
{
|
{
|
||||||
string ID = Wrap0(ConfigIdentifiers[A->ID].String);
|
ConfigError(0, 0, S_ERROR, "Multiple projects share the same:", 0);
|
||||||
ConfigError(0, 0, S_ERROR, "Multiple projects share the same ", &ID);
|
|
||||||
fprintf(stderr, " ");
|
config_pair Assoc0 = { .Key = A->ID0, .Value = Association->String0 };
|
||||||
PrintStringC(CS_MAGENTA_BOLD, Association->String);
|
int IndentLevel = 2;
|
||||||
|
PrintPair(&Assoc0, ": ", FALSE, IndentLevel, FALSE, FALSE);
|
||||||
|
if(A->ID1 != IDENT_NULL)
|
||||||
|
{
|
||||||
|
config_pair Assoc1 = { .Key = A->ID1, .Value = Association->String1 };
|
||||||
|
PrintPair(&Assoc1, ": ", FALSE, IndentLevel, TRUE, FALSE);
|
||||||
|
}
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
for(int j = 0; j < Association->ProjectCount; ++j)
|
for(int j = 0; j < Association->ProjectCount; ++j)
|
||||||
{
|
{
|
||||||
project *P = Association->Projects[j];
|
project *P = Association->Projects[j];
|
||||||
fprintf(stderr, " ");
|
fprintf(stderr, " ");
|
||||||
|
if(P)
|
||||||
|
{
|
||||||
PrintStringC(CS_CYAN, P->Lineage);
|
PrintStringC(CS_CYAN, P->Lineage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PrintStringC(CS_RED, Wrap0("[global scope: "));
|
||||||
|
// TODO(matt): Maybe generalise this, next time we're in this situation
|
||||||
|
if(A->ID0 == IDENT_BASE_DIR && A->ID1 == IDENT_SEARCH_LOCATION)
|
||||||
|
{
|
||||||
|
PrintC(CS_YELLOW_BOLD, ConfigIdentifiers[IDENT_GLOBAL_SEARCH_DIR].String);
|
||||||
|
}
|
||||||
|
PrintStringC(CS_RED, Wrap0("]"));
|
||||||
|
}
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3609,6 +3700,8 @@ FreeVariants(variant_strings *V)
|
||||||
void
|
void
|
||||||
FreeVerifiers(config_verifiers *Verifiers)
|
FreeVerifiers(config_verifiers *Verifiers)
|
||||||
{
|
{
|
||||||
|
FreeAssociations(&Verifiers->BaseDirAndSearchLocation);
|
||||||
|
FreeAssociations(&Verifiers->BaseDirAndPlayerLocation);
|
||||||
FreeAssociations(&Verifiers->HMMLDirs);
|
FreeAssociations(&Verifiers->HMMLDirs);
|
||||||
FreeVariants(&Verifiers->VariantStrings);
|
FreeVariants(&Verifiers->VariantStrings);
|
||||||
}
|
}
|
||||||
|
@ -3623,7 +3716,11 @@ ResolveVariables(scope_tree *S)
|
||||||
InitBook(&Result->Project, sizeof(project), 8, MBT_PROJECT);
|
InitBook(&Result->Project, sizeof(project), 8, MBT_PROJECT);
|
||||||
resolution_errors Errors = {};
|
resolution_errors Errors = {};
|
||||||
config_verifiers Verifiers = {};
|
config_verifiers Verifiers = {};
|
||||||
Verifiers.HMMLDirs.ID = IDENT_HMML_DIR;
|
Verifiers.HMMLDirs.ID0 = IDENT_HMML_DIR;
|
||||||
|
Verifiers.BaseDirAndSearchLocation.ID0 = IDENT_BASE_DIR;
|
||||||
|
Verifiers.BaseDirAndSearchLocation.ID1 = IDENT_SEARCH_LOCATION;
|
||||||
|
Verifiers.BaseDirAndPlayerLocation.ID0 = IDENT_BASE_DIR;
|
||||||
|
Verifiers.BaseDirAndPlayerLocation.ID1 = IDENT_PLAYER_LOCATION;
|
||||||
|
|
||||||
for(int i = 0; i < S->PairCount; ++i)
|
for(int i = 0; i < S->PairCount; ++i)
|
||||||
{
|
{
|
||||||
|
@ -3752,6 +3849,13 @@ ResolveVariables(scope_tree *S)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config_string_association *BaseDirAndSearchLocation = GetGlobalPathAssociation(&Verifiers.BaseDirAndSearchLocation, Result->GlobalSearchDir);
|
||||||
|
if(BaseDirAndSearchLocation)
|
||||||
|
{
|
||||||
|
PushError(&Errors, S_ERROR, 0, IDENT_GLOBAL_SEARCH_DIR);
|
||||||
|
AddProjectToAssociation(BaseDirAndSearchLocation, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(matt): Mandate that, if a GlobalSearchDir or GlobalSearchURL is set, then both must be set
|
// TODO(matt): Mandate that, if a GlobalSearchDir or GlobalSearchURL is set, then both must be set
|
||||||
if(!Result->GlobalTheme.Length)
|
if(!Result->GlobalTheme.Length)
|
||||||
{
|
{
|
||||||
|
@ -3762,6 +3866,8 @@ ResolveVariables(scope_tree *S)
|
||||||
if(Errors.ErrorCount > 0)
|
if(Errors.ErrorCount > 0)
|
||||||
{
|
{
|
||||||
PrintAssociationClashes(&Verifiers.HMMLDirs);
|
PrintAssociationClashes(&Verifiers.HMMLDirs);
|
||||||
|
PrintAssociationClashes(&Verifiers.BaseDirAndSearchLocation);
|
||||||
|
PrintAssociationClashes(&Verifiers.BaseDirAndPlayerLocation);
|
||||||
PrintVariantInconsistencies(&Verifiers.VariantStrings);
|
PrintVariantInconsistencies(&Verifiers.VariantStrings);
|
||||||
Free(Result);
|
Free(Result);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue