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 = {
|
||||
.Major = 0,
|
||||
.Minor = 7,
|
||||
.Patch = 9
|
||||
.Patch = 10
|
||||
};
|
||||
|
||||
#include <stdarg.h> // NOTE(matt): varargs
|
||||
|
@ -907,6 +907,18 @@ StringsMatch(string A, string B)
|
|||
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
|
||||
StringsMatchCaseInsensitive(string A, string B)
|
||||
{
|
||||
|
@ -2723,6 +2735,15 @@ GetBaseFilename(string Filepath,
|
|||
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[] =
|
||||
{
|
||||
"WT_HMML",
|
||||
|
@ -12351,16 +12372,6 @@ DeleteFromDB(neighbourhood *N, string BaseFilename)
|
|||
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
|
||||
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
|
||||
{
|
||||
string String;
|
||||
string String0;
|
||||
string String1;
|
||||
project **Projects;
|
||||
uint64_t ProjectCount;
|
||||
} config_string_association;
|
||||
|
@ -2966,7 +2967,8 @@ typedef struct
|
|||
typedef struct
|
||||
{
|
||||
config_string_association *Associations;
|
||||
config_identifier_id ID;
|
||||
config_identifier_id ID0;
|
||||
config_identifier_id ID1;
|
||||
uint64_t Count;
|
||||
} config_string_associations;
|
||||
|
||||
|
@ -2974,6 +2976,8 @@ typedef struct
|
|||
{
|
||||
variant_strings VariantStrings;
|
||||
config_string_associations HMMLDirs;
|
||||
config_string_associations BaseDirAndSearchLocation;
|
||||
config_string_associations BaseDirAndPlayerLocation;
|
||||
} config_verifiers;
|
||||
|
||||
void
|
||||
|
@ -3197,14 +3201,60 @@ AddProjectToAssociation(config_string_association *A, project *P)
|
|||
}
|
||||
|
||||
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[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);
|
||||
++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
|
||||
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;
|
||||
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);
|
||||
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)
|
||||
{
|
||||
PushHMMLDirAssociation(HMMLDirs, &Result, P);
|
||||
PushAssociation(HMMLDirs, &Result, 0, P);
|
||||
}
|
||||
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)
|
||||
{
|
||||
ResetPen(&C->ResolvedVariables);
|
||||
|
@ -3511,16 +3583,35 @@ PrintAssociationClashes(config_string_associations *A)
|
|||
config_string_association *Association = A->Associations + i;
|
||||
if(Association->ProjectCount > 1)
|
||||
{
|
||||
string ID = Wrap0(ConfigIdentifiers[A->ID].String);
|
||||
ConfigError(0, 0, S_ERROR, "Multiple projects share the same ", &ID);
|
||||
fprintf(stderr, " ");
|
||||
PrintStringC(CS_MAGENTA_BOLD, Association->String);
|
||||
ConfigError(0, 0, S_ERROR, "Multiple projects share the same:", 0);
|
||||
|
||||
config_pair Assoc0 = { .Key = A->ID0, .Value = Association->String0 };
|
||||
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");
|
||||
for(int j = 0; j < Association->ProjectCount; ++j)
|
||||
{
|
||||
project *P = Association->Projects[j];
|
||||
fprintf(stderr, " ");
|
||||
if(P)
|
||||
{
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
@ -3609,6 +3700,8 @@ FreeVariants(variant_strings *V)
|
|||
void
|
||||
FreeVerifiers(config_verifiers *Verifiers)
|
||||
{
|
||||
FreeAssociations(&Verifiers->BaseDirAndSearchLocation);
|
||||
FreeAssociations(&Verifiers->BaseDirAndPlayerLocation);
|
||||
FreeAssociations(&Verifiers->HMMLDirs);
|
||||
FreeVariants(&Verifiers->VariantStrings);
|
||||
}
|
||||
|
@ -3623,7 +3716,11 @@ ResolveVariables(scope_tree *S)
|
|||
InitBook(&Result->Project, sizeof(project), 8, MBT_PROJECT);
|
||||
resolution_errors Errors = {};
|
||||
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)
|
||||
{
|
||||
|
@ -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
|
||||
if(!Result->GlobalTheme.Length)
|
||||
{
|
||||
|
@ -3762,6 +3866,8 @@ ResolveVariables(scope_tree *S)
|
|||
if(Errors.ErrorCount > 0)
|
||||
{
|
||||
PrintAssociationClashes(&Verifiers.HMMLDirs);
|
||||
PrintAssociationClashes(&Verifiers.BaseDirAndSearchLocation);
|
||||
PrintAssociationClashes(&Verifiers.BaseDirAndPlayerLocation);
|
||||
PrintVariantInconsistencies(&Verifiers.VariantStrings);
|
||||
Free(Result);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue