cinera: Upgrade to hmmlib2
Features: User-configurable roles and credits Handle SIGINT to quit cleanly and avoid database corruption Fixes: Filesystem event monitoring handles directory creation / deletion Fixed buffer overflow when trying to curl in a non-existent quote
This commit is contained in:
parent
65fe93fb57
commit
0d88c24db6
1313
cinera/cinera.c
1313
cinera/cinera.c
File diff suppressed because it is too large
Load Diff
|
@ -480,10 +480,26 @@ typedef struct
|
|||
{
|
||||
string ID;
|
||||
string Name;
|
||||
// TODO(matt): string SortName;
|
||||
string QuoteUsername;
|
||||
string Homepage;
|
||||
_memory_book(support) Support;
|
||||
} person;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
string ID;
|
||||
string Name;
|
||||
string Plural;
|
||||
bool NonSpeaking;
|
||||
} role;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
person *Person;
|
||||
role *Role;
|
||||
} credit;
|
||||
|
||||
typedef struct project
|
||||
{
|
||||
string ID;
|
||||
|
@ -515,7 +531,10 @@ typedef struct project
|
|||
uint64_t IconVariants;
|
||||
asset *IconAsset;
|
||||
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
#else
|
||||
string StreamUsername;
|
||||
#endif
|
||||
string VODPlatform; // TODO(matt): Make this an enum
|
||||
|
||||
bool DenyBespokeTemplates;
|
||||
|
@ -523,9 +542,13 @@ typedef struct project
|
|||
bool IgnorePrivacy;
|
||||
|
||||
person *Owner;
|
||||
_memory_book(credit *) Credit;
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
#else
|
||||
_memory_book(person *) Indexer;
|
||||
_memory_book(person *) Guest;
|
||||
_memory_book(person *) CoHost;
|
||||
#endif
|
||||
|
||||
_memory_book(medium) Medium;
|
||||
medium *DefaultMedium;
|
||||
|
@ -567,6 +590,7 @@ typedef struct
|
|||
uint8_t LogLevel;
|
||||
|
||||
_memory_book(person) Person;
|
||||
_memory_book(role) Role;
|
||||
_memory_book(project) Project;
|
||||
memory_book ResolvedVariables;
|
||||
} config;
|
||||
|
@ -669,6 +693,13 @@ Tokenise(memory_book *TokensList, string Path)
|
|||
}
|
||||
Advancement = 1;
|
||||
}
|
||||
else if(!StringsDifferS(TokenStrings[TOKEN_MINUS], B))
|
||||
{
|
||||
T.Type = TOKEN_MINUS;
|
||||
T.Content.Base = B->Ptr;
|
||||
++T.Content.Length;
|
||||
Advancement = 1;
|
||||
}
|
||||
else if(!StringsDifferS(TokenStrings[TOKEN_ASSIGN], B))
|
||||
{
|
||||
T.Type = TOKEN_ASSIGN;
|
||||
|
@ -747,7 +778,6 @@ Tokenise(memory_book *TokensList, string Path)
|
|||
Advance(B, Advancement);
|
||||
SkipWhitespace(Result, B);
|
||||
}
|
||||
// TODO(matt): PushConfigWatchHandle()
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -893,7 +923,10 @@ InitTypeSpecs(void)
|
|||
PushTypeSpecField(Root, FT_STRING, IDENT_SEARCH_LOCATION, TRUE);
|
||||
PushTypeSpecField(Root, FT_STRING, IDENT_SEARCH_TEMPLATE, TRUE);
|
||||
PushTypeSpecField(Root, FT_STRING, IDENT_TEMPLATES_DIR, TRUE);
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
#else
|
||||
PushTypeSpecField(Root, FT_STRING, IDENT_STREAM_USERNAME, TRUE);
|
||||
#endif
|
||||
PushTypeSpecField(Root, FT_STRING, IDENT_VOD_PLATFORM, TRUE);
|
||||
PushTypeSpecField(Root, FT_STRING, IDENT_THEME, TRUE);
|
||||
PushTypeSpecField(Root, FT_STRING, IDENT_TITLE, TRUE);
|
||||
|
@ -916,6 +949,7 @@ InitTypeSpecs(void)
|
|||
PushTypeSpecField(Root, FT_SCOPE, IDENT_INCLUDE, FALSE);
|
||||
PushTypeSpecField(Root, FT_SCOPE, IDENT_MEDIUM, FALSE);
|
||||
PushTypeSpecField(Root, FT_SCOPE, IDENT_PERSON, FALSE);
|
||||
PushTypeSpecField(Root, FT_SCOPE, IDENT_ROLE, FALSE);
|
||||
PushTypeSpecField(Root, FT_SCOPE, IDENT_PROJECT, FALSE);
|
||||
PushTypeSpecField(Root, FT_SCOPE, IDENT_SUPPORT, FALSE);
|
||||
|
||||
|
@ -934,8 +968,22 @@ InitTypeSpecs(void)
|
|||
config_type_spec *Person = PushTypeSpec(&Result, IDENT_PERSON, FALSE);
|
||||
PushTypeSpecField(Person, FT_STRING, IDENT_NAME, TRUE);
|
||||
PushTypeSpecField(Person, FT_STRING, IDENT_HOMEPAGE, TRUE);
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
PushTypeSpecField(Person, FT_STRING, IDENT_QUOTE_USERNAME, TRUE);
|
||||
#endif
|
||||
PushTypeSpecField(Person, FT_SCOPE, IDENT_SUPPORT, FALSE);
|
||||
|
||||
config_type_spec *Role = PushTypeSpec(&Result, IDENT_ROLE, FALSE);
|
||||
PushTypeSpecField(Role, FT_STRING, IDENT_NAME, TRUE);
|
||||
PushTypeSpecField(Role, FT_STRING, IDENT_PLURAL, TRUE);
|
||||
PushTypeSpecField(Role, FT_NUMBER, IDENT_POSITION, TRUE);
|
||||
PushTypeSpecField(Role, FT_BOOLEAN, IDENT_NON_SPEAKING, TRUE);
|
||||
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
config_type_spec *Credit = PushTypeSpec(&Result, IDENT_CREDIT, FALSE);
|
||||
PushTypeSpecField(Credit, FT_STRING, IDENT_ROLE, FALSE);
|
||||
#endif
|
||||
|
||||
config_type_spec *Medium = PushTypeSpec(&Result, IDENT_MEDIUM, FALSE);
|
||||
PushTypeSpecField(Medium, FT_STRING, IDENT_ICON, TRUE);
|
||||
PushTypeSpecField(Medium, FT_STRING, IDENT_ICON_NORMAL, TRUE);
|
||||
|
@ -949,21 +997,33 @@ InitTypeSpecs(void)
|
|||
config_type_spec *Project = PushTypeSpec(&Result, IDENT_PROJECT, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_BASE_DIR, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_BASE_URL, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_COHOST, FALSE);
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
#else
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_COHOST, FALSE); // TODO(matt): Remove
|
||||
#endif
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_DEFAULT_MEDIUM, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_GENRE, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_GUEST, FALSE);
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
#else
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_GUEST, FALSE); // TODO(matt): Remove
|
||||
#endif
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_HMML_DIR, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_INDEXER, FALSE);
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
#else
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_INDEXER, FALSE); // TODO(matt): Remove
|
||||
#endif
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_NUMBERING_SCHEME, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_OWNER, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_OWNER, TRUE); // NOTE(matt): Do not remove, because ResolveLocalVariable() recognises it
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_PLAYER_LOCATION, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_PLAYER_TEMPLATE, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_QUERY_STRING, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_SEARCH_LOCATION, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_SEARCH_TEMPLATE, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_TEMPLATES_DIR, TRUE);
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
#else
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_STREAM_USERNAME, TRUE);
|
||||
#endif
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_VOD_PLATFORM, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_THEME, TRUE);
|
||||
PushTypeSpecField(Project, FT_STRING, IDENT_TITLE, TRUE);
|
||||
|
@ -995,6 +1055,9 @@ InitTypeSpecs(void)
|
|||
PushTypeSpecField(Project, FT_BOOLEAN, IDENT_SINGLE_BROWSER_TAB, TRUE);
|
||||
//
|
||||
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
PushTypeSpecField(Project, FT_SCOPE, IDENT_CREDIT, FALSE);
|
||||
#endif
|
||||
PushTypeSpecField(Project, FT_SCOPE, IDENT_INCLUDE, FALSE);
|
||||
PushTypeSpecField(Project, FT_SCOPE, IDENT_MEDIUM, FALSE);
|
||||
PushTypeSpecField(Project, FT_SCOPE, IDENT_PROJECT, FALSE);
|
||||
|
@ -1045,6 +1108,30 @@ PrintTypeField(config_type_field *F, config_identifier_id ParentScopeID, int Ind
|
|||
--IndentationLevel;
|
||||
}
|
||||
}
|
||||
else if((F->ID == IDENT_NAME) && ParentScopeID == IDENT_ROLE)
|
||||
{
|
||||
if(!ConfigIdentifiers[F->ID].IdentifierDescription_RoleDisplayed)
|
||||
{
|
||||
++IndentationLevel;
|
||||
IndentedCarriageReturn(IndentationLevel);
|
||||
TypesetString(INDENT_WIDTH * IndentationLevel, Wrap0(ConfigIdentifiers[F->ID].IdentifierDescription_Role));
|
||||
ConfigIdentifiers[F->ID].IdentifierDescription_RoleDisplayed = TRUE;
|
||||
--IndentationLevel;
|
||||
}
|
||||
}
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
else if((F->ID == IDENT_ROLE) && ParentScopeID == IDENT_CREDIT)
|
||||
{
|
||||
if(!ConfigIdentifiers[F->ID].IdentifierDescription_CreditDisplayed)
|
||||
{
|
||||
++IndentationLevel;
|
||||
IndentedCarriageReturn(IndentationLevel);
|
||||
TypesetString(INDENT_WIDTH * IndentationLevel, Wrap0(ConfigIdentifiers[F->ID].IdentifierDescription_Credit));
|
||||
ConfigIdentifiers[F->ID].IdentifierDescription_CreditDisplayed = TRUE;
|
||||
--IndentationLevel;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
if(!ConfigIdentifiers[F->ID].IdentifierDescriptionDisplayed)
|
||||
|
@ -1127,7 +1214,7 @@ typedef struct
|
|||
typedef struct
|
||||
{
|
||||
config_identifier_id Key;
|
||||
uint64_t Value;
|
||||
int64_t Value;
|
||||
token_position Position;
|
||||
} config_int_pair;
|
||||
|
||||
|
@ -1438,7 +1525,7 @@ PrintIntPair(config_int_pair *P, char *Delimiter, bool FillSyntax, uint64_t Inde
|
|||
default:
|
||||
{
|
||||
Colourise(CS_BLUE_BOLD);
|
||||
fprintf(stderr, "%lu", P->Value);
|
||||
fprintf(stderr, "%li", P->Value);
|
||||
} break;
|
||||
}
|
||||
Colourise(CS_END);
|
||||
|
@ -1523,6 +1610,22 @@ PushPair(scope_tree *Parent, config_pair *P)
|
|||
*New = *P;
|
||||
}
|
||||
|
||||
config_pair *
|
||||
GetPair(scope_tree *Parent, config_identifier_id FieldID)
|
||||
{
|
||||
config_pair *Result = 0;
|
||||
for(int i = 0; i < Parent->Pairs.ItemCount; ++i)
|
||||
{
|
||||
config_pair *This = GetPlaceInBook(&Parent->Pairs, i);
|
||||
if(This->Key == FieldID)
|
||||
{
|
||||
Result = This;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void
|
||||
PushIntPair(scope_tree *Parent, config_int_pair *I)
|
||||
{
|
||||
|
@ -1530,6 +1633,22 @@ PushIntPair(scope_tree *Parent, config_int_pair *I)
|
|||
*New = *I;
|
||||
}
|
||||
|
||||
config_int_pair *
|
||||
GetIntPair(scope_tree *Parent, config_identifier_id FieldID)
|
||||
{
|
||||
config_int_pair *Result = 0;
|
||||
for(int i = 0; i < Parent->IntPairs.ItemCount; ++i)
|
||||
{
|
||||
config_int_pair *This = GetPlaceInBook(&Parent->IntPairs, i);
|
||||
if(This->Key == FieldID)
|
||||
{
|
||||
Result = This;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void
|
||||
PushBoolPair(scope_tree *Parent, config_bool_pair *B)
|
||||
{
|
||||
|
@ -2020,6 +2139,13 @@ PushDefaultIntPair(scope_tree *Parent, config_identifier_id Key, uint64_t Value)
|
|||
return PushIntPair(Parent, &IntPair);
|
||||
}
|
||||
|
||||
void
|
||||
PushDefaultBoolPair(scope_tree *Parent, config_identifier_id Key, bool Value)
|
||||
{
|
||||
config_bool_pair BoolPair = { .Key = Key, .Value = Value };
|
||||
return PushBoolPair(Parent, &BoolPair);
|
||||
}
|
||||
|
||||
#define DEFAULT_PRIVACY_CHECK_INTERVAL 60 * 4
|
||||
void
|
||||
SetDefaults(scope_tree *Root, memory_book *TypeSpecs)
|
||||
|
@ -2037,6 +2163,11 @@ SetDefaults(scope_tree *Root, memory_book *TypeSpecs)
|
|||
PushDefaultPair(MediumAuthored, IDENT_ICON, Wrap0("🗪"));
|
||||
PushDefaultPair(MediumAuthored, IDENT_NAME, Wrap0("Chat Comment"));
|
||||
|
||||
scope_tree *RoleIndexer = PushDefaultScope(TypeSpecs, Root, IDENT_ROLE, Wrap0("indexer"));
|
||||
PushDefaultPair(RoleIndexer, IDENT_NAME, Wrap0("Indexer"));
|
||||
PushDefaultBoolPair(RoleIndexer, IDENT_NON_SPEAKING, TRUE);
|
||||
PushDefaultIntPair(RoleIndexer, IDENT_POSITION, -1);
|
||||
|
||||
PushDefaultPair(Root, IDENT_QUERY_STRING, Wrap0("r"));
|
||||
PushDefaultPair(Root, IDENT_THEME, Wrap0("$origin"));
|
||||
PushDefaultPair(Root, IDENT_CACHE_DIR, Wrap0("$XDG_CACHE_HOME/cinera"));
|
||||
|
@ -2244,8 +2375,14 @@ ScopeTokens(scope_tree *Tree, memory_book *TokensList, tokens *T, memory_book *T
|
|||
if(!ExpectToken(T, TOKEN_ASSIGN, 0)) { FreeScopeTree(Tree); return 0; }
|
||||
|
||||
token Value = {};
|
||||
bool IsNegative = FALSE;
|
||||
if(TokenIs(T, TOKEN_MINUS))
|
||||
{
|
||||
IsNegative = TRUE;
|
||||
++T->CurrentIndex;
|
||||
}
|
||||
if(!ExpectToken(T, TOKEN_NUMBER, &Value)) { FreeScopeTree(Tree); return 0; }
|
||||
IntPair.Value = Value.int64_t;
|
||||
IntPair.Value = IsNegative ? 0 - Value.int64_t : Value.int64_t;
|
||||
|
||||
if(!ExpectToken(T, TOKEN_SEMICOLON, 0)) { FreeScopeTree(Tree); return 0; }
|
||||
|
||||
|
@ -2320,16 +2457,12 @@ ScopeTokens(scope_tree *Tree, memory_book *TokensList, tokens *T, memory_book *T
|
|||
ConfigError(&IncluderFilePath, Pair.Position.LineNumber, S_WARNING, "Unable to include file: ", &Pair.Value);
|
||||
}
|
||||
} break;
|
||||
case RC_SCHEME_MIXTURE:
|
||||
{
|
||||
} break;
|
||||
case RC_SYNTAX_ERROR:
|
||||
{
|
||||
FreeScopeTree(Tree); return 0;
|
||||
} break;
|
||||
case RC_SCHEME_MIXTURE:
|
||||
case RC_INVALID_IDENTIFIER:
|
||||
{
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
@ -2550,25 +2683,66 @@ ResolveEnvironmentVariable(memory_book *M, string Variable)
|
|||
return Result;
|
||||
}
|
||||
|
||||
role *
|
||||
GetRole(config *C, resolution_errors *E, config_pair *RoleID)
|
||||
{
|
||||
role *Result = 0;
|
||||
for(int i = 0; i < C->Role.ItemCount; ++i)
|
||||
{
|
||||
role *Role = GetPlaceInBook(&C->Role, i);
|
||||
if(!StringsDifferCaseInsensitive(Role->ID, RoleID->Value))
|
||||
{
|
||||
Result = Role;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!Result && !GetError(E, S_WARNING, &RoleID->Position, IDENT_ROLE))
|
||||
{
|
||||
string Filepath = Wrap0(RoleID->Position.Filename);
|
||||
ConfigError(&Filepath, RoleID->Position.LineNumber, S_WARNING, "Could not find role: ", &RoleID->Value);
|
||||
PushError(E, S_WARNING, &RoleID->Position, IDENT_ROLE);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
role *
|
||||
GetRoleByID(config *C, string ID)
|
||||
{
|
||||
role *Result = 0;
|
||||
for(int i = 0; i < C->Role.ItemCount; ++i)
|
||||
{
|
||||
role *Role = GetPlaceInBook(&C->Role, i);
|
||||
if(!StringsDifferCaseInsensitive(Role->ID, ID))
|
||||
{
|
||||
Result = Role;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
person *
|
||||
GetPerson(config *C, resolution_errors *E, config_pair *PersonID)
|
||||
{
|
||||
person *Result = 0;
|
||||
for(int i = 0; i < C->Person.ItemCount; ++i)
|
||||
{
|
||||
person *Person = GetPlaceInBook(&C->Person, i);
|
||||
if(!StringsDifferCaseInsensitive(Person->ID, PersonID->Value))
|
||||
{
|
||||
return Person;
|
||||
Result = Person;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!GetError(E, S_WARNING, &PersonID->Position, IDENT_PERSON))
|
||||
if(!Result && !GetError(E, S_WARNING, &PersonID->Position, IDENT_PERSON))
|
||||
{
|
||||
string Filepath = Wrap0(PersonID->Position.Filename);
|
||||
ConfigError(&Filepath, PersonID->Position.LineNumber, S_WARNING, "Could not find person: ", &PersonID->Value);
|
||||
PushError(E, S_WARNING, &PersonID->Position, IDENT_PERSON);
|
||||
}
|
||||
return 0;
|
||||
return Result;
|
||||
}
|
||||
|
||||
string
|
||||
|
@ -2712,27 +2886,31 @@ ResolveLocalVariable(config *C, resolution_errors *E, scope_tree *Scope, config_
|
|||
case IDENT_OWNER:
|
||||
{
|
||||
bool Processed = FALSE;
|
||||
for(int i = 0; i < Scope->Pairs.ItemCount; ++i)
|
||||
while(!Processed && Scope)
|
||||
{
|
||||
config_pair *Pair = GetPlaceInBook(&Scope->Pairs, i);
|
||||
if(Variable == Pair->Key)
|
||||
for(int i = 0; i < Scope->Pairs.ItemCount; ++i)
|
||||
{
|
||||
if(GetPerson(C, E, Pair))
|
||||
config_pair *Pair = GetPlaceInBook(&Scope->Pairs, i);
|
||||
if(Variable == Pair->Key)
|
||||
{
|
||||
Result = ExtendStringInBook(&C->ResolvedVariables, Pair->Value);
|
||||
Processed = TRUE;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!GetError(E, S_WARNING, Position, Variable))
|
||||
if(GetPerson(C, E, Pair))
|
||||
{
|
||||
ConfigError(&Filepath, Position->LineNumber, S_WARNING, "Owner set, but the person does not exist: ", &Pair->Value);
|
||||
PushError(E, S_WARNING, Position, Variable);
|
||||
Result = ExtendStringInBook(&C->ResolvedVariables, Pair->Value);
|
||||
Processed = TRUE;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!GetError(E, S_WARNING, Position, Variable))
|
||||
{
|
||||
ConfigError(&Filepath, Position->LineNumber, S_WARNING, "Owner set, but the person does not exist: ", &Pair->Value);
|
||||
PushError(E, S_WARNING, Position, Variable);
|
||||
}
|
||||
Processed = TRUE;
|
||||
}
|
||||
Processed = TRUE;
|
||||
}
|
||||
}
|
||||
Scope = Scope->Parent;
|
||||
}
|
||||
if(!Processed)
|
||||
{
|
||||
|
@ -2745,7 +2923,8 @@ ResolveLocalVariable(config *C, resolution_errors *E, scope_tree *Scope, config_
|
|||
} break;
|
||||
case IDENT_PERSON:
|
||||
{
|
||||
if(Scope->ID.Key != Variable)
|
||||
// NOTE(matt): Allow scopes within a person scope, e.g. support, to resolve $person
|
||||
while(Scope && Scope->ID.Key != Variable)
|
||||
{
|
||||
Scope = Scope->Parent;
|
||||
}
|
||||
|
@ -3123,8 +3302,21 @@ PushPersonOntoConfig(config *C, resolution_errors *E, config_verifiers *V, scope
|
|||
{
|
||||
This->Homepage = ResolveString(C, E, PersonTree, Pair, FALSE);
|
||||
}
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
else if(IDENT_QUOTE_USERNAME == Pair->Key)
|
||||
{
|
||||
This->QuoteUsername = ResolveString(C, E, PersonTree, Pair, FALSE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
if(This->QuoteUsername.Length == 0)
|
||||
{
|
||||
This->QuoteUsername = This->ID;
|
||||
}
|
||||
#endif
|
||||
|
||||
This->Support = InitBook(MBT_SUPPORT, 2);
|
||||
for(int i = 0; i < PersonTree->Trees.ItemCount; ++i)
|
||||
{
|
||||
|
@ -3133,6 +3325,35 @@ PushPersonOntoConfig(config *C, resolution_errors *E, config_verifiers *V, scope
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
PushRoleOntoConfig(config *C, resolution_errors *E, config_verifiers *V, scope_tree *RoleTree)
|
||||
{
|
||||
role *This = MakeSpaceInBook(&C->Role);
|
||||
This->ID = ResolveString(C, E, RoleTree, &RoleTree->ID, FALSE);
|
||||
for(int i = 0; i < RoleTree->Pairs.ItemCount; ++i)
|
||||
{
|
||||
config_pair *Pair = GetPlaceInBook(&RoleTree->Pairs, i);
|
||||
if(IDENT_NAME == Pair->Key)
|
||||
{
|
||||
This->Name = ResolveString(C, E, RoleTree, Pair, FALSE);
|
||||
}
|
||||
else if(IDENT_PLURAL == Pair->Key)
|
||||
{
|
||||
This->Plural = ResolveString(C, E, RoleTree, Pair, FALSE);
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < RoleTree->BoolPairs.ItemCount; ++i)
|
||||
{
|
||||
config_bool_pair *BoolPair = GetPlaceInBook(&RoleTree->BoolPairs, i);
|
||||
if(IDENT_NON_SPEAKING == BoolPair->Key)
|
||||
{
|
||||
This->NonSpeaking = BoolPair->Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
#else
|
||||
void
|
||||
PushPersonOntoProject(config *C, resolution_errors *E, project *P, config_pair *Actor)
|
||||
{
|
||||
|
@ -3160,6 +3381,32 @@ PushPersonOntoProject(config *C, resolution_errors *E, project *P, config_pair *
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
PushCredit(config *C, resolution_errors *E, project *P, scope_tree *CreditTree)
|
||||
{
|
||||
CreditTree->ID.Value = ResolveString(C, E, CreditTree, &CreditTree->ID, FALSE);
|
||||
if(CreditTree->ID.Value.Base)
|
||||
{
|
||||
person *Person = GetPerson(C, E, &CreditTree->ID);
|
||||
if(Person)
|
||||
{
|
||||
for(int i = 0; i < CreditTree->Pairs.ItemCount; ++i)
|
||||
{
|
||||
config_pair *This = GetPlaceInBook(&CreditTree->Pairs, i);
|
||||
role *Role = GetRole(C, E, This);
|
||||
if(Role)
|
||||
{
|
||||
credit *Credit = MakeSpaceInBook(&P->Credit);
|
||||
// TODO(matt): Sort the P->Credit book by Person->SortName
|
||||
Credit->Role = Role;
|
||||
Credit->Person = Person;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PushProjectOntoProject(config *C, resolution_errors *E, config_verifiers *Verifiers, project *Parent, scope_tree *ProjectTree);
|
||||
|
||||
|
@ -3255,9 +3502,13 @@ PushProject(config *C, resolution_errors *E, config_verifiers *V, project *P, sc
|
|||
{
|
||||
P->Medium = InitBook(MBT_MEDIUM, 8);
|
||||
P->Child = InitBook(MBT_PROJECT, 8);
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
P->Credit = InitBook(MBT_CREDIT, 4);
|
||||
#else
|
||||
P->Indexer = InitBookOfPointers(MBT_PERSON_PTR, 4);
|
||||
P->CoHost = InitBookOfPointers(MBT_PERSON_PTR, 4);
|
||||
P->Guest = InitBookOfPointers(MBT_PERSON_PTR, 4);
|
||||
#endif
|
||||
|
||||
config_string_associations *HMMLDirs = &V->HMMLDirs;
|
||||
P->ID = ResolveString(C, E, ProjectTree, &ProjectTree->ID, FALSE);
|
||||
|
@ -3271,12 +3522,40 @@ PushProject(config *C, resolution_errors *E, config_verifiers *V, project *P, sc
|
|||
ResetPen(&C->ResolvedVariables);
|
||||
P->Lineage = DeriveLineageOfProject(C, ProjectTree);
|
||||
|
||||
// NOTE(matt): Initial pass over as-yet unresolvable local variable(s)
|
||||
//
|
||||
for(int i = 0; i < ProjectTree->Pairs.ItemCount; ++i)
|
||||
{
|
||||
config_pair *This = GetPlaceInBook(&ProjectTree->Pairs, i);
|
||||
switch(This->Key)
|
||||
{
|
||||
case IDENT_OWNER:
|
||||
{
|
||||
// TODO(matt): Do we need to fail completely if owner cannot be found?
|
||||
P->Owner = GetPerson(C, E, This);
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
//
|
||||
////
|
||||
|
||||
for(int i = 0; i < ProjectTree->Trees.ItemCount; ++i)
|
||||
{
|
||||
scope_tree *This = GetPlaceInBook(&ProjectTree->Trees, i);
|
||||
if(This->ID.Key == IDENT_MEDIUM)
|
||||
switch(This->ID.Key)
|
||||
{
|
||||
PushMedium(C, E, V, P, This);
|
||||
case IDENT_MEDIUM:
|
||||
{
|
||||
PushMedium(C, E, V, P, This);
|
||||
} break;
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
case IDENT_CREDIT:
|
||||
{
|
||||
PushCredit(C, E, P, This);
|
||||
} break;
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3290,15 +3569,13 @@ PushProject(config *C, resolution_errors *E, config_verifiers *V, project *P, sc
|
|||
{ P->DefaultMedium = GetMediumFromProject(P, This->Value); } break;
|
||||
case IDENT_HMML_DIR:
|
||||
{ SetUniqueHMMLDir(C, E, HMMLDirs, P, ProjectTree, This); } break;
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
#else
|
||||
case IDENT_INDEXER:
|
||||
case IDENT_COHOST:
|
||||
case IDENT_GUEST:
|
||||
{ PushPersonOntoProject(C, E, P, This); } break;
|
||||
case IDENT_OWNER:
|
||||
{
|
||||
// TODO(matt): Do we need to fail completely if owner cannot be found?
|
||||
P->Owner = GetPerson(C, E, This);
|
||||
} break;
|
||||
#endif
|
||||
case IDENT_PLAYER_TEMPLATE:
|
||||
{ P->PlayerTemplatePath = StripSlashes(ResolveString(C, E, ProjectTree, This, FALSE), P_REL); } break;
|
||||
case IDENT_THEME:
|
||||
|
@ -3371,8 +3648,11 @@ PushProject(config *C, resolution_errors *E, config_verifiers *V, project *P, sc
|
|||
{ P->SearchTemplatePath = StripSlashes(ResolveString(C, E, ProjectTree, This, FALSE), P_REL); } break;
|
||||
case IDENT_TEMPLATES_DIR:
|
||||
{ P->TemplatesDir = StripSlashes(ResolveString(C, E, ProjectTree, This, TRUE), P_ABS); } break;
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
#else
|
||||
case IDENT_STREAM_USERNAME:
|
||||
{ P->StreamUsername = ResolveString(C, E, ProjectTree, This, FALSE); } break;
|
||||
#endif
|
||||
case IDENT_VOD_PLATFORM:
|
||||
{ P->VODPlatform = ResolveString(C, E, ProjectTree, This, FALSE); } break;
|
||||
|
||||
|
@ -3841,7 +4121,6 @@ TypesetVariants(typography *T, uint8_t Generation, config_identifier_id Key, uin
|
|||
void
|
||||
PrintMedium(typography *T, medium *M, char *Delimiter, uint64_t Indentation, bool AppendNewline)
|
||||
{
|
||||
// TODO(matt): Print Me!
|
||||
PrintStringC(CS_BLUE_BOLD, M->ID);
|
||||
if(M->Hidden)
|
||||
{
|
||||
|
@ -3970,7 +4249,7 @@ GetRowsRequiredForPersonInfo(typography *T, person *P)
|
|||
}
|
||||
|
||||
void
|
||||
PrintPerson(person *P, config_identifier_id Role, typography *Typography)
|
||||
PrintPerson(person *P, typography *Typography)
|
||||
{
|
||||
bool HaveInfo = P->Name.Length > 0 || P->Homepage.Length > 0 || P->Support.ItemCount > 0;
|
||||
fprintf(stderr, "\n"
|
||||
|
@ -4003,6 +4282,49 @@ PrintPerson(person *P, config_identifier_id Role, typography *Typography)
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t
|
||||
GetRowsRequiredForRoleInfo(role *R)
|
||||
{
|
||||
uint8_t RowsRequired = 0;
|
||||
if(R->Name.Length > 0) { ++RowsRequired; }
|
||||
if(R->Plural.Length > 0) { ++RowsRequired; }
|
||||
int NonSpeakingLines = 1;
|
||||
RowsRequired += NonSpeakingLines;
|
||||
return RowsRequired;
|
||||
}
|
||||
|
||||
void
|
||||
PrintRole(role *R, typography *Typography)
|
||||
{
|
||||
bool HaveInfo = R->Name.Length > 0 || R->Plural.Length > 0;
|
||||
fprintf(stderr, "\n"
|
||||
"%s%s%s%s%s ", HaveInfo ? Typography->UpperLeftCorner : Typography->UpperLeft,
|
||||
Typography->Horizontal, Typography->Horizontal, Typography->Horizontal, Typography->UpperRight);
|
||||
PrintStringC(CS_GREEN_BOLD, R->ID);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
uint8_t RowsRequired = GetRowsRequiredForRoleInfo(R);
|
||||
|
||||
int IndentationLevel = 0;
|
||||
bool ShouldFillSyntax = FALSE;
|
||||
if(R->Name.Length > 0)
|
||||
{
|
||||
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
|
||||
config_pair Name = { .Key = IDENT_NAME, .Value = R->Name }; PrintPair(&Name, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
|
||||
--RowsRequired;
|
||||
}
|
||||
|
||||
if(R->Plural.Length > 0)
|
||||
{
|
||||
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
|
||||
config_pair Plural = { .Key = IDENT_PLURAL, .Value = R->Plural }; PrintPair(&Plural, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
|
||||
--RowsRequired;
|
||||
}
|
||||
fprintf(stderr, "%s ", RowsRequired == 1 ? Typography->LowerLeft : Typography->Vertical);
|
||||
config_bool_pair NonSpeaking = { .Key = IDENT_NON_SPEAKING, .Value = R->NonSpeaking }; PrintBoolPair(&NonSpeaking, Typography->Delimiter, ShouldFillSyntax, IndentationLevel, FALSE, TRUE);
|
||||
--RowsRequired;
|
||||
}
|
||||
|
||||
void
|
||||
PrintLineage(string Lineage, bool AppendNewline)
|
||||
{
|
||||
|
@ -4026,7 +4348,8 @@ GetColumnsRequiredForMedium(medium *M, typography *T)
|
|||
}
|
||||
|
||||
void
|
||||
PrintTitle(char *Text, int AvailableColumns)
|
||||
PrintTitle(char *Text, typography *T, uint8_t Generation, int AvailableColumns,
|
||||
int64_t SectionItemCount /* NOTE(matt): Use any non-0 when the section is known to have content */)
|
||||
{
|
||||
char *LeftmostChar = "╾";
|
||||
char *InnerChar = "─";
|
||||
|
@ -4049,6 +4372,11 @@ PrintTitle(char *Text, int AvailableColumns)
|
|||
fprintf(stderr, "%s", InnerChar);
|
||||
}
|
||||
fprintf(stderr, "%s", RightmostChar);
|
||||
if(SectionItemCount == 0)
|
||||
{
|
||||
//fprintf(stderr, "\n");
|
||||
CarriageReturn(T, Generation);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -4057,7 +4385,7 @@ PrintMedia(project *P, typography *T, uint8_t Generation, int AvailableColumns)
|
|||
int IndentationLevel = 0;
|
||||
CarriageReturn(T, Generation);
|
||||
fprintf(stderr, "%s", T->Margin);
|
||||
PrintTitle("Media", AvailableColumns);
|
||||
PrintTitle("Media", T, Generation, AvailableColumns, P->Medium.ItemCount);
|
||||
CarriageReturn(T, Generation);
|
||||
fprintf(stderr, "%s", T->Margin);
|
||||
//AvailableColumns -= Generation + 1;
|
||||
|
@ -4106,6 +4434,63 @@ PrintMedia(project *P, typography *T, uint8_t Generation, int AvailableColumns)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
PrintCredits(config *C, project *P, typography *T, uint8_t Generation, int IndentationLevel, int AvailableColumns)
|
||||
{
|
||||
for(int i = 0; i < C->Role.ItemCount; ++i)
|
||||
{
|
||||
role *Role = GetPlaceInBook(&C->Role, i);
|
||||
int CreditCount = 0;
|
||||
for(int j = 0; j < P->Credit.ItemCount; ++j)
|
||||
{
|
||||
credit *Credit = GetPlaceInBook(&P->Credit, j);
|
||||
if(Role == Credit->Role)
|
||||
{
|
||||
++CreditCount;
|
||||
}
|
||||
}
|
||||
|
||||
if(CreditCount > 0)
|
||||
{
|
||||
CarriageReturn(T, Generation);
|
||||
int Alignment = 0;
|
||||
Alignment += fprintf(stderr, "%s", T->Margin);
|
||||
if(CreditCount == 1)
|
||||
{
|
||||
Alignment += PrintStringC(CS_YELLOW_BOLD, Role->Name.Length ? Role->Name : Role->ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Role->Plural.Length > 0)
|
||||
{
|
||||
Alignment += PrintStringC(CS_YELLOW_BOLD, Role->Plural);
|
||||
}
|
||||
else
|
||||
{
|
||||
Alignment += PrintStringC(CS_YELLOW_BOLD, Role->Name.Length ? Role->Name : Role->ID);
|
||||
Alignment += PrintStringC(CS_YELLOW_BOLD, Wrap0("s"));
|
||||
}
|
||||
}
|
||||
Alignment += fprintf(stderr, "%s", T->Delimiter);
|
||||
bool Printed = FALSE;
|
||||
for(int j = 0; j < P->Credit.ItemCount; ++j)
|
||||
{
|
||||
credit *Credit = GetPlaceInBook(&P->Credit, j);
|
||||
if(Role == Credit->Role)
|
||||
{
|
||||
if(Printed)
|
||||
{
|
||||
CarriageReturn(T, Generation);
|
||||
AlignText(Alignment);
|
||||
}
|
||||
PrintStringC(CS_GREEN_BOLD, Credit->Person->Name.Length ? Credit->Person->Name : Credit->Person->ID);
|
||||
Printed = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TypesetPair(typography *T, uint8_t Generation, config_identifier_id Key, string Value, int AvailableColumns)
|
||||
{
|
||||
|
@ -4177,7 +4562,7 @@ TypesetNumberingScheme(typography *T, uint8_t Generation, numbering_scheme N)
|
|||
}
|
||||
|
||||
void
|
||||
PrintProject(project *P, typography *T, int Ancestors, int IndentationLevel, int TerminalColumns)
|
||||
PrintProject(config *C, project *P, typography *T, int Ancestors, int IndentationLevel, int TerminalColumns)
|
||||
{
|
||||
int Generation = Ancestors + 1;
|
||||
CarriageReturn(T, Ancestors);
|
||||
|
@ -4196,7 +4581,7 @@ PrintProject(project *P, typography *T, int Ancestors, int IndentationLevel, int
|
|||
CarriageReturn(T, Generation);
|
||||
CarriageReturn(T, Generation);
|
||||
fprintf(stderr, "%s", T->Margin);
|
||||
PrintTitle("Settings", AvailableColumns);
|
||||
PrintTitle("Settings", T, Generation, AvailableColumns, -1);
|
||||
|
||||
TypesetPair(T, Generation, IDENT_TITLE, P->Title, AvailableColumns);
|
||||
TypesetPair(T, Generation, IDENT_HTML_TITLE, P->HTMLTitle, AvailableColumns);
|
||||
|
@ -4213,7 +4598,10 @@ PrintProject(project *P, typography *T, int Ancestors, int IndentationLevel, int
|
|||
TypesetPair(T, Generation, IDENT_BASE_URL, P->BaseURL, AvailableColumns);
|
||||
TypesetPair(T, Generation, IDENT_SEARCH_LOCATION, P->SearchLocation, AvailableColumns);
|
||||
TypesetPair(T, Generation, IDENT_PLAYER_LOCATION, P->PlayerLocation, AvailableColumns);
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
#else
|
||||
TypesetPair(T, Generation, IDENT_STREAM_USERNAME, P->StreamUsername, AvailableColumns);
|
||||
#endif
|
||||
TypesetPair(T, Generation, IDENT_VOD_PLATFORM, P->VODPlatform, AvailableColumns);
|
||||
TypesetPair(T, Generation, IDENT_THEME, P->Theme, AvailableColumns);
|
||||
|
||||
|
@ -4233,11 +4621,10 @@ PrintProject(project *P, typography *T, int Ancestors, int IndentationLevel, int
|
|||
TypesetBool(T, Generation, IDENT_IGNORE_PRIVACY, P->IgnorePrivacy);
|
||||
TypesetBool(T, Generation, IDENT_SINGLE_BROWSER_TAB, P->SingleBrowserTab);
|
||||
|
||||
if(P->Owner)
|
||||
{
|
||||
TypesetPair(T, Generation, IDENT_OWNER, P->Owner->ID, AvailableColumns);
|
||||
}
|
||||
TypesetPair(T, Generation, IDENT_OWNER, P->Owner ? P->Owner->ID : Wrap0(""), AvailableColumns);
|
||||
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
#else
|
||||
for(int i = 0; i < P->Indexer.ItemCount; ++i)
|
||||
{
|
||||
person **Indexer = GetPlaceInBook(&P->Indexer, i);
|
||||
|
@ -4253,17 +4640,32 @@ PrintProject(project *P, typography *T, int Ancestors, int IndentationLevel, int
|
|||
person **Guest = GetPlaceInBook(&P->Guest, i);
|
||||
TypesetPair(T, Generation, IDENT_GUEST, (*Guest)->ID, AvailableColumns);
|
||||
}
|
||||
#endif
|
||||
|
||||
CarriageReturn(T, Generation);
|
||||
CarriageReturn(T, Generation);
|
||||
fprintf(stderr, "%s", T->Margin);
|
||||
PrintTitle("Credits", T, Generation, AvailableColumns, P->Credit.ItemCount);
|
||||
if(P->Credit.ItemCount > 0)
|
||||
{
|
||||
PrintCredits(C, P, T, Generation, IndentationLevel, AvailableColumns);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s", T->Margin);
|
||||
PrintC(CS_YELLOW, "[none]");
|
||||
}
|
||||
|
||||
if(P->Child.ItemCount)
|
||||
{
|
||||
CarriageReturn(T, Generation);
|
||||
CarriageReturn(T, Generation);
|
||||
fprintf(stderr, "%s", T->Margin);
|
||||
PrintTitle("Children", AvailableColumns);
|
||||
PrintTitle("Children", T, Generation, AvailableColumns, -1);
|
||||
++Ancestors;
|
||||
for(int i = 0; i < P->Child.ItemCount; ++i)
|
||||
{
|
||||
PrintProject(GetPlaceInBook(&P->Child, i), T, Ancestors, IndentationLevel, TerminalColumns);
|
||||
PrintProject(C, GetPlaceInBook(&P->Child, i), T, Ancestors, IndentationLevel, TerminalColumns);
|
||||
}
|
||||
--Ancestors;
|
||||
}
|
||||
|
@ -4296,7 +4698,7 @@ PrintConfig(config *C, bool ShouldClearTerminal)
|
|||
|
||||
// separate
|
||||
fprintf(stderr, "\n");
|
||||
PrintTitle("Global Settings", TermCols);
|
||||
PrintTitle("Global Settings", &Typography, 0, TermCols, -1);
|
||||
int IndentationLevel = 0;
|
||||
|
||||
int AvailableColumns = TermCols - StringLength(Typography.Margin);
|
||||
|
@ -4330,17 +4732,24 @@ PrintConfig(config *C, bool ShouldClearTerminal)
|
|||
PrintLogLevel(ConfigIdentifiers[IDENT_LOG_LEVEL].String, C->LogLevel, Typography.Delimiter, IndentationLevel, TRUE);
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
PrintTitle("People", TermCols);
|
||||
PrintTitle("People", &Typography, 0, TermCols, C->Person.ItemCount);
|
||||
for(int i = 0; i < C->Person.ItemCount; ++i)
|
||||
{
|
||||
PrintPerson(GetPlaceInBook(&C->Person, i), IDENT_NULL, &Typography);
|
||||
PrintPerson(GetPlaceInBook(&C->Person, i), &Typography);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
PrintTitle("Projects", TermCols);
|
||||
PrintTitle("Roles", &Typography, 0, TermCols, C->Role.ItemCount);
|
||||
for(int i = 0; i < C->Role.ItemCount; ++i)
|
||||
{
|
||||
PrintRole(GetPlaceInBook(&C->Role, i), &Typography);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
PrintTitle("Projects", &Typography, 0, TermCols, -1);
|
||||
for(int i = 0; i < C->Project.ItemCount; ++i)
|
||||
{
|
||||
PrintProject(GetPlaceInBook(&C->Project, i), &Typography, 0, IndentationLevel, TermCols);
|
||||
PrintProject(C, GetPlaceInBook(&C->Project, i), &Typography, 0, IndentationLevel, TermCols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4349,10 +4758,13 @@ void
|
|||
FreeProject(project *P)
|
||||
{
|
||||
FreeBook(&P->Medium);
|
||||
|
||||
#if(HMMLIB_MAJOR_VERSION == 2)
|
||||
FreeBook(&P->Credit);
|
||||
#else
|
||||
FreeBook(&P->Indexer);
|
||||
FreeBook(&P->Guest);
|
||||
FreeBook(&P->CoHost);
|
||||
#endif
|
||||
for(int i = 0; i < P->Child.ItemCount; ++i)
|
||||
{
|
||||
FreeProject(GetPlaceInBook(&P->Child, i));
|
||||
|
@ -4373,6 +4785,7 @@ FreeProject(project *P)
|
|||
FreeBook(&Person->Support);\
|
||||
}\
|
||||
FreeBook(&C->Person);\
|
||||
FreeBook(&C->Role);\
|
||||
for(int i = 0; i < C->Project.ItemCount; ++i)\
|
||||
{\
|
||||
FreeProject(GetPlaceInBook(&C->Project, i));\
|
||||
|
@ -4404,6 +4817,93 @@ InitVerifiers(void)
|
|||
return Result;
|
||||
}
|
||||
|
||||
bool
|
||||
IsPositioned(config_int_pair *Position)
|
||||
{
|
||||
return Position && Position->Value != 0;
|
||||
}
|
||||
|
||||
void
|
||||
PositionRolesInConfig(config *C, resolution_errors *E, config_verifiers *V, scope_tree *S)
|
||||
{
|
||||
memory_book RolesStaging = InitBookOfPointers(MBT_SCOPE_TREE_PTR, 4);
|
||||
for(int i = 0; i < S->Trees.ItemCount; ++i)
|
||||
{
|
||||
scope_tree *Tree = GetPlaceInBook(&S->Trees, i);
|
||||
if(Tree->ID.Key == IDENT_ROLE)
|
||||
{
|
||||
scope_tree **This = MakeSpaceInBook(&RolesStaging);
|
||||
*This = Tree;
|
||||
}
|
||||
}
|
||||
|
||||
int SlotCount = RolesStaging.ItemCount;
|
||||
scope_tree *RoleSlot[SlotCount];
|
||||
Clear(RoleSlot, sizeof(scope_tree *) * SlotCount);
|
||||
for(int i = 0; i < SlotCount; ++i)
|
||||
{
|
||||
scope_tree **Role = GetPlaceInBook(&RolesStaging, i);
|
||||
config_int_pair *Position = GetIntPair(*Role, IDENT_POSITION);
|
||||
if(IsPositioned(Position))
|
||||
{
|
||||
int TargetPos;
|
||||
if(Position->Value < 0)
|
||||
{
|
||||
TargetPos = SlotCount + Position->Value;
|
||||
Clamp(0, TargetPos, SlotCount - 1);
|
||||
while(RoleSlot[TargetPos] && TargetPos > 0)
|
||||
{
|
||||
--TargetPos;
|
||||
}
|
||||
|
||||
while(RoleSlot[TargetPos] && TargetPos < SlotCount - 1)
|
||||
{
|
||||
++TargetPos;
|
||||
}
|
||||
}
|
||||
else if(Position->Value > 0)
|
||||
{
|
||||
TargetPos = Position->Value - 1;
|
||||
Clamp(0, TargetPos, SlotCount - 1);
|
||||
while(RoleSlot[TargetPos] && TargetPos < SlotCount - 1)
|
||||
{
|
||||
++TargetPos;
|
||||
}
|
||||
|
||||
while(RoleSlot[TargetPos] && TargetPos > 0)
|
||||
{
|
||||
--TargetPos;
|
||||
}
|
||||
}
|
||||
RoleSlot[TargetPos] = *Role;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < SlotCount; ++i)
|
||||
{
|
||||
scope_tree **Role = GetPlaceInBook(&RolesStaging, i);
|
||||
config_int_pair *Position = GetIntPair(*Role, IDENT_POSITION);
|
||||
if(!IsPositioned(Position))
|
||||
{
|
||||
for(int j = 0; j < SlotCount; ++j)
|
||||
{
|
||||
if(!RoleSlot[j])
|
||||
{
|
||||
RoleSlot[j] = *Role;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < SlotCount; ++i)
|
||||
{
|
||||
PushRoleOntoConfig(C, E, V, RoleSlot[i]);
|
||||
}
|
||||
|
||||
FreeBook(&RolesStaging);
|
||||
}
|
||||
|
||||
config *
|
||||
ResolveVariables(scope_tree *S)
|
||||
{
|
||||
|
@ -4411,6 +4911,7 @@ ResolveVariables(scope_tree *S)
|
|||
config *Result = calloc(1, sizeof(config));
|
||||
Result->ResolvedVariables = InitBookOfStrings(Kilobytes(1));
|
||||
Result->Person = InitBook(MBT_PERSON, 8);
|
||||
Result->Role = InitBook(MBT_ROLE, 4);
|
||||
Result->Project = InitBook(MBT_PROJECT, 8);
|
||||
|
||||
resolution_errors Errors = {};
|
||||
|
@ -4553,6 +5054,8 @@ ResolveVariables(scope_tree *S)
|
|||
}
|
||||
}
|
||||
|
||||
PositionRolesInConfig(Result, &Errors, &Verifiers, S);
|
||||
|
||||
for(int i = 0; i < S->Trees.ItemCount; ++i)
|
||||
{
|
||||
scope_tree *Tree = GetPlaceInBook(&S->Trees, i);
|
||||
|
|
Loading…
Reference in New Issue