cinera.c: Gracefully handle unset env variables
This commit is contained in:
parent
2748687839
commit
b3470e0f48
231
cinera/cinera.c
231
cinera/cinera.c
|
@ -23,7 +23,7 @@ typedef struct
|
||||||
version CINERA_APP_VERSION = {
|
version CINERA_APP_VERSION = {
|
||||||
.Major = 0,
|
.Major = 0,
|
||||||
.Minor = 7,
|
.Minor = 7,
|
||||||
.Patch = 16
|
.Patch = 17
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <stdarg.h> // NOTE(matt): varargs
|
#include <stdarg.h> // NOTE(matt): varargs
|
||||||
|
@ -1825,12 +1825,14 @@ char *ErrorDomainStrings[] =
|
||||||
{
|
{
|
||||||
"Config",
|
"Config",
|
||||||
"Indexing",
|
"Indexing",
|
||||||
|
"System",
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
ED_CONFIG,
|
ED_CONFIG,
|
||||||
ED_INDEXING,
|
ED_INDEXING,
|
||||||
|
ED_SYSTEM,
|
||||||
} error_domain;
|
} error_domain;
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -2014,6 +2016,20 @@ IndexingErrorCustomSizing(string *Filename, uint64_t LineNumber, int CustomIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SystemError(string *Filename, uint64_t LineNumber, severity Severity, char *Message, string *Received)
|
||||||
|
{
|
||||||
|
ErrorFilenameAndLineNumber(Filename, LineNumber, Severity, ED_SYSTEM);
|
||||||
|
// TODO(matt): Typeset the Message?
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s", Message);
|
||||||
|
if(Received)
|
||||||
|
{
|
||||||
|
PrintStringC(CS_MAGENTA_BOLD, *Received);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
// NOTE(matt): https://tools.ietf.org/html/rfc5424#section-6.2.1
|
// NOTE(matt): https://tools.ietf.org/html/rfc5424#section-6.2.1
|
||||||
|
@ -3877,7 +3893,35 @@ ExpandPath(string Path, string *RelativeToFile)
|
||||||
wordexp_t Expansions = {};
|
wordexp_t Expansions = {};
|
||||||
|
|
||||||
char *WorkingPath = MakeString0("l", &Path);
|
char *WorkingPath = MakeString0("l", &Path);
|
||||||
wordexp(WorkingPath, &Expansions, Flags);
|
int wordexpResult = wordexp(WorkingPath, &Expansions, Flags);
|
||||||
|
if(wordexpResult)
|
||||||
|
{
|
||||||
|
switch(wordexpResult)
|
||||||
|
{
|
||||||
|
case WRDE_BADCHAR:
|
||||||
|
{
|
||||||
|
SystemError(0, 0, S_ERROR, "wordexp: Illegal occurrence of newline or one of |, &, ;, <, >, (, ), {, } in: ", &Path);
|
||||||
|
} break;
|
||||||
|
case WRDE_BADVAL:
|
||||||
|
{
|
||||||
|
SystemError(0, 0, S_ERROR, "wordexp: An undefined shell variable was referenced, and the WRDE_UNDEF flag told us to consider this an error, in: ", &Path);
|
||||||
|
} break;
|
||||||
|
case WRDE_CMDSUB:
|
||||||
|
{
|
||||||
|
SystemError(0, 0, S_ERROR, "wordexp: Command substitution requested, but the WRDE_NOCMD flag told us to consider this an error, in: ", &Path);
|
||||||
|
} break;
|
||||||
|
case WRDE_NOSPACE:
|
||||||
|
{
|
||||||
|
SystemError(0, 0, S_ERROR, "wordexp: Out of memory", 0);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case WRDE_SYNTAX:
|
||||||
|
{
|
||||||
|
SystemError(0, 0, S_ERROR, "wordexp: Shell syntax error, such as unbalanced parentheses or unmatched quotes, in: ", &Path);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Free(WorkingPath);
|
Free(WorkingPath);
|
||||||
|
|
||||||
char *Result = 0;
|
char *Result = 0;
|
||||||
|
@ -3903,7 +3947,10 @@ ExpandPath(string Path, string *RelativeToFile)
|
||||||
|
|
||||||
wordfree(&Expansions);
|
wordfree(&Expansions);
|
||||||
|
|
||||||
ResolvePath(&Result);
|
if(Result)
|
||||||
|
{
|
||||||
|
ResolvePath(&Result);
|
||||||
|
}
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15719,112 +15766,114 @@ main(int ArgC, char **Args)
|
||||||
MEM_TEST_MID("main()");
|
MEM_TEST_MID("main()");
|
||||||
|
|
||||||
ConfigPath = ExpandPath(Wrap0(ConfigPath), 0);
|
ConfigPath = ExpandPath(Wrap0(ConfigPath), 0);
|
||||||
|
if(ConfigPath)
|
||||||
PrintVersions();
|
{
|
||||||
|
PrintVersions();
|
||||||
|
|
||||||
#if DEBUG_MEM
|
#if DEBUG_MEM
|
||||||
FILE *MemLog = fopen("/home/matt/cinera_mem", "a+");
|
FILE *MemLog = fopen("/home/matt/cinera_mem", "a+");
|
||||||
fprintf(MemLog, " Allocated MemoryArena (%d)\n", MemoryArena.Size);
|
fprintf(MemLog, " Allocated MemoryArena (%d)\n", MemoryArena.Size);
|
||||||
fclose(MemLog);
|
fclose(MemLog);
|
||||||
printf(" Allocated MemoryArena (%d)\n", MemoryArena.Size);
|
printf(" Allocated MemoryArena (%d)\n", MemoryArena.Size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
printf("Allocated MemoryArena: %d\n\n", MemoryArena.Size);
|
printf("Allocated MemoryArena: %d\n\n", MemoryArena.Size);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// NOTE(matt): Tree structure of buffer dependencies
|
// NOTE(matt): Tree structure of buffer dependencies
|
||||||
// IncludesPlayer
|
// IncludesPlayer
|
||||||
// Menus
|
// Menus
|
||||||
// Player
|
// Player
|
||||||
// ScriptPlayer
|
// ScriptPlayer
|
||||||
//
|
//
|
||||||
// IncludesSearch
|
// IncludesSearch
|
||||||
// Search
|
// Search
|
||||||
|
|
||||||
buffers CollationBuffers = {};
|
buffers CollationBuffers = {};
|
||||||
if(ClaimBuffer(&CollationBuffers.IncludesPlayer, BID_COLLATION_BUFFERS_INCLUDES_PLAYER, Kilobytes(2)) == RC_ARENA_FULL) { Exit(); };
|
if(ClaimBuffer(&CollationBuffers.IncludesPlayer, BID_COLLATION_BUFFERS_INCLUDES_PLAYER, Kilobytes(2)) == RC_ARENA_FULL) { Exit(); };
|
||||||
if(ClaimBuffer(&CollationBuffers.Player, BID_COLLATION_BUFFERS_PLAYER, Kilobytes(552)) == RC_ARENA_FULL) { Exit(); };
|
if(ClaimBuffer(&CollationBuffers.Player, BID_COLLATION_BUFFERS_PLAYER, Kilobytes(552)) == RC_ARENA_FULL) { Exit(); };
|
||||||
|
|
||||||
if(ClaimBuffer(&CollationBuffers.IncludesSearch, BID_COLLATION_BUFFERS_INCLUDES_SEARCH, Kilobytes(2)) == RC_ARENA_FULL) { Exit(); };
|
if(ClaimBuffer(&CollationBuffers.IncludesSearch, BID_COLLATION_BUFFERS_INCLUDES_SEARCH, Kilobytes(2)) == RC_ARENA_FULL) { Exit(); };
|
||||||
if(ClaimBuffer(&CollationBuffers.SearchEntry, BID_COLLATION_BUFFERS_SEARCH_ENTRY, Kilobytes(32)) == RC_ARENA_FULL) { Exit(); };
|
if(ClaimBuffer(&CollationBuffers.SearchEntry, BID_COLLATION_BUFFERS_SEARCH_ENTRY, Kilobytes(32)) == RC_ARENA_FULL) { Exit(); };
|
||||||
|
|
||||||
CollationBuffers.Search.ID = BID_COLLATION_BUFFERS_SEARCH; // NOTE(matt): Allocated by SearchToBuffer()
|
CollationBuffers.Search.ID = BID_COLLATION_BUFFERS_SEARCH; // NOTE(matt): Allocated by SearchToBuffer()
|
||||||
|
|
||||||
memory_book TokensList = InitBook(MBT_TOKENS, 8);
|
memory_book TokensList = InitBook(MBT_TOKENS, 8);
|
||||||
template BespokeTemplate = {};
|
template BespokeTemplate = {};
|
||||||
neighbourhood Neighbourhood = {};
|
neighbourhood Neighbourhood = {};
|
||||||
|
|
||||||
InitWatchHandles((IN_CREATE | IN_MOVED_FROM | IN_MOVED_TO | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF), Kilobytes(4));
|
InitWatchHandles((IN_CREATE | IN_MOVED_FROM | IN_MOVED_TO | IN_CLOSE_WRITE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF), Kilobytes(4));
|
||||||
|
|
||||||
inotifyInstance = inotify_init1(IN_NONBLOCK);
|
inotifyInstance = inotify_init1(IN_NONBLOCK);
|
||||||
int inotifyError = errno;
|
int inotifyError = errno;
|
||||||
|
|
||||||
string ConfigPathL = Wrap0(ConfigPath);
|
string ConfigPathL = Wrap0(ConfigPath);
|
||||||
PushWatchHandle(ConfigPathL, EXT_NULL, WT_CONFIG, 0, 0);
|
PushWatchHandle(ConfigPathL, EXT_NULL, WT_CONFIG, 0, 0);
|
||||||
|
|
||||||
Config = ParseConfig(ConfigPathL, &TokensList);
|
Config = ParseConfig(ConfigPathL, &TokensList);
|
||||||
if(Config)
|
if(Config)
|
||||||
{
|
|
||||||
if(Mode & MODE_EXAMINE)
|
|
||||||
{
|
{
|
||||||
// TODO(matt): Allow optionally passing a .db file as an argument?
|
if(Mode & MODE_EXAMINE)
|
||||||
ExamineDB();
|
|
||||||
_exit(RC_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(Mode & MODE_DRYRUN)
|
|
||||||
{
|
|
||||||
PrintConfig(Config, FALSE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* */ MEM_TEST_MID("main()");
|
|
||||||
/* +MEM */ InitAll(&Neighbourhood, &CollationBuffers, &BespokeTemplate);
|
|
||||||
/* */ MEM_TEST_MID("main()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(SeekConfirmation("Print config help?", TRUE))
|
|
||||||
{
|
|
||||||
if(ArgC < 2)
|
|
||||||
{
|
|
||||||
PrintHelp(Args[0], ConfigPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PrintHelpConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(inotifyInstance)
|
|
||||||
{
|
|
||||||
while(MonitorFilesystem(&Neighbourhood, &CollationBuffers, &BespokeTemplate, ConfigPathL, &TokensList) != RC_ARENA_FULL)
|
|
||||||
{
|
|
||||||
// TODO(matt): Refetch the quotes and rebuild player pages if needed
|
|
||||||
//
|
|
||||||
// Every sixty mins, redownload the quotes and, I suppose, SyncDBWithInput(). But here we still don't even know
|
|
||||||
// who the speaker is. To know, we'll probably have to store all quoted speakers in the project's .metadata. Maybe
|
|
||||||
// postpone this for now, but we will certainly need this to happen
|
|
||||||
//
|
|
||||||
// The most ideal solution is possibly that we store quote numbers in the Metadata->Entry, listen for and handle a
|
|
||||||
// REST PUT request from insobot when a quote changes (unless we're supposed to poll insobot for them?), and rebuild
|
|
||||||
// the player page(s) accordingly.
|
|
||||||
//
|
|
||||||
if(!(Mode & MODE_DRYRUN) && Config && Config->RespectingPrivacy && time(0) - LastPrivacyCheck > Config->PrivacyCheckInterval)
|
|
||||||
{
|
{
|
||||||
RecheckPrivacy(&Neighbourhood, &CollationBuffers, &BespokeTemplate);
|
// TODO(matt): Allow optionally passing a .db file as an argument?
|
||||||
|
ExamineDB();
|
||||||
|
_exit(RC_SUCCESS);
|
||||||
}
|
}
|
||||||
sleep(GLOBAL_UPDATE_INTERVAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Error: Quitting because inotify initialisation failed with message\n"
|
|
||||||
" %s\n", strerror(inotifyError));
|
|
||||||
}
|
|
||||||
|
|
||||||
DiscardAllAndFreeConfig();
|
if(Mode & MODE_DRYRUN)
|
||||||
RemoveAndFreeWatchHandles(&WatchHandles);
|
{
|
||||||
MEM_TEST_END("main()");
|
PrintConfig(Config, FALSE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* */ MEM_TEST_MID("main()");
|
||||||
|
/* +MEM */ InitAll(&Neighbourhood, &CollationBuffers, &BespokeTemplate);
|
||||||
|
/* */ MEM_TEST_MID("main()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(SeekConfirmation("Print config help?", TRUE))
|
||||||
|
{
|
||||||
|
if(ArgC < 2)
|
||||||
|
{
|
||||||
|
PrintHelp(Args[0], ConfigPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PrintHelpConfig();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(inotifyInstance)
|
||||||
|
{
|
||||||
|
while(MonitorFilesystem(&Neighbourhood, &CollationBuffers, &BespokeTemplate, ConfigPathL, &TokensList) != RC_ARENA_FULL)
|
||||||
|
{
|
||||||
|
// TODO(matt): Refetch the quotes and rebuild player pages if needed
|
||||||
|
//
|
||||||
|
// Every sixty mins, redownload the quotes and, I suppose, SyncDBWithInput(). But here we still don't even know
|
||||||
|
// who the speaker is. To know, we'll probably have to store all quoted speakers in the project's .metadata. Maybe
|
||||||
|
// postpone this for now, but we will certainly need this to happen
|
||||||
|
//
|
||||||
|
// The most ideal solution is possibly that we store quote numbers in the Metadata->Entry, listen for and handle a
|
||||||
|
// REST PUT request from insobot when a quote changes (unless we're supposed to poll insobot for them?), and rebuild
|
||||||
|
// the player page(s) accordingly.
|
||||||
|
//
|
||||||
|
if(!(Mode & MODE_DRYRUN) && Config && Config->RespectingPrivacy && time(0) - LastPrivacyCheck > Config->PrivacyCheckInterval)
|
||||||
|
{
|
||||||
|
RecheckPrivacy(&Neighbourhood, &CollationBuffers, &BespokeTemplate);
|
||||||
|
}
|
||||||
|
sleep(GLOBAL_UPDATE_INTERVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error: Quitting because inotify initialisation failed with message\n"
|
||||||
|
" %s\n", strerror(inotifyError));
|
||||||
|
}
|
||||||
|
|
||||||
|
DiscardAllAndFreeConfig();
|
||||||
|
RemoveAndFreeWatchHandles(&WatchHandles);
|
||||||
|
MEM_TEST_END("main()");
|
||||||
|
}
|
||||||
Exit();
|
Exit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2280,38 +2280,45 @@ ScopeTokens(scope_tree *Tree, memory_book *TokensList, tokens *T, memory_book *T
|
||||||
{
|
{
|
||||||
string IncluderFilePath = Wrap0(T->File.Path);
|
string IncluderFilePath = Wrap0(T->File.Path);
|
||||||
char *IncludePath = ExpandPath(Pair.Value, &IncluderFilePath);
|
char *IncludePath = ExpandPath(Pair.Value, &IncluderFilePath);
|
||||||
string IncludePathL = Wrap0(IncludePath);
|
if(IncludePath)
|
||||||
|
{
|
||||||
|
string IncludePathL = Wrap0(IncludePath);
|
||||||
|
|
||||||
tokens *I = 0;
|
tokens *I = 0;
|
||||||
for(int i = 0; i < TokensList->ItemCount; ++i)
|
for(int i = 0; i < TokensList->ItemCount; ++i)
|
||||||
{
|
|
||||||
tokens *This = GetPlaceInBook(TokensList, i);
|
|
||||||
if(!StringsDifferLv0(Value.Content, This->File.Path))
|
|
||||||
{
|
{
|
||||||
I = This;
|
tokens *This = GetPlaceInBook(TokensList, i);
|
||||||
I->CurrentIndex = 0;
|
if(!StringsDifferLv0(Value.Content, This->File.Path))
|
||||||
I->CurrentLine = 0;
|
{
|
||||||
|
I = This;
|
||||||
|
I->CurrentIndex = 0;
|
||||||
|
I->CurrentLine = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if(!I)
|
||||||
if(!I)
|
|
||||||
{
|
|
||||||
I = Tokenise(TokensList, IncludePathL);
|
|
||||||
}
|
|
||||||
if(I)
|
|
||||||
{
|
|
||||||
Parent = ScopeTokens(Parent, TokensList, I, TypeSpecs, &Rules);
|
|
||||||
FreeBook(&Rules.ID);
|
|
||||||
if(!Parent)
|
|
||||||
{
|
{
|
||||||
return 0;
|
I = Tokenise(TokensList, IncludePathL);
|
||||||
}
|
}
|
||||||
|
if(I)
|
||||||
|
{
|
||||||
|
Parent = ScopeTokens(Parent, TokensList, I, TypeSpecs, &Rules);
|
||||||
|
FreeBook(&Rules.ID);
|
||||||
|
if(!Parent)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token *This = GetPlaceInBook(&T->Token, IncludePathTokenIndex);
|
||||||
|
ConfigFileIncludeError(&Filepath, This->LineNumber, Wrap0(IncludePath));
|
||||||
|
}
|
||||||
|
Free(IncludePath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
token *This = GetPlaceInBook(&T->Token, IncludePathTokenIndex);
|
ConfigError(&IncluderFilePath, Pair.Position.LineNumber, S_WARNING, "Unable to include file: ", &Pair.Value);
|
||||||
ConfigFileIncludeError(&Filepath, This->LineNumber, Wrap0(IncludePath));
|
|
||||||
}
|
}
|
||||||
Free(IncludePath);
|
|
||||||
} break;
|
} break;
|
||||||
case RC_SCHEME_MIXTURE:
|
case RC_SCHEME_MIXTURE:
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue