2016-03-11 14:57:17 +01:00
|
|
|
/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
|
|
|
|
|
2016-03-29 03:03:54 +02:00
|
|
|
#include "Engine/StdH.h"
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
#include <Engine/Base/Shell.h>
|
|
|
|
#include <Engine/Base/Shell_internal.h>
|
|
|
|
#include "ParsingSymbols.h"
|
|
|
|
|
|
|
|
#include <Engine/Templates/DynamicStackArray.h>
|
|
|
|
#include <Engine/Base/Console.h>
|
|
|
|
#include <Engine/Base/Stream.h>
|
|
|
|
|
|
|
|
#include <Engine/Templates/AllocationArray.cpp>
|
|
|
|
#include <Engine/Templates/DynamicArray.cpp>
|
|
|
|
#include <Engine/Templates/DynamicStackArray.cpp>
|
|
|
|
|
2016-03-29 03:03:54 +02:00
|
|
|
template class CDynamicArray<CShellSymbol>;
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
// shell type used for undeclared symbols
|
2016-03-29 03:03:54 +02:00
|
|
|
INDEX _shell_istUndeclared = -1;
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
// pointer to global shell object
|
|
|
|
CShell *_pShell = NULL;
|
|
|
|
void *_pvNextToDeclare=NULL; // != NULL if declaring external symbol defined in exe code
|
|
|
|
|
|
|
|
// define console variable for number of last console lines
|
2016-03-29 03:03:54 +02:00
|
|
|
INDEX con_iLastLines = 5;
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
extern void yy_switch_to_buffer(YY_BUFFER_STATE);
|
|
|
|
|
|
|
|
// declarations for recursive shell script parsing
|
|
|
|
struct BufferStackEntry {
|
|
|
|
YY_BUFFER_STATE bse_bs;
|
|
|
|
const char *bse_strName;
|
|
|
|
const char *bse_strContents;
|
|
|
|
int bse_iLineCt;
|
|
|
|
BOOL bse_bParserEnd;
|
|
|
|
};
|
|
|
|
|
|
|
|
static BufferStackEntry _abseBufferStack[SHELL_MAX_INCLUDE_LEVEL];
|
|
|
|
static int _ibsBufferStackTop = -1;
|
|
|
|
|
|
|
|
BOOL _bExecNextBlock = 1;
|
|
|
|
|
|
|
|
void ShellPushBuffer(const char *strName, const char *strBuffer, BOOL bParserEnd)
|
|
|
|
{
|
|
|
|
_ibsBufferStackTop++;
|
|
|
|
|
|
|
|
_abseBufferStack[_ibsBufferStackTop].bse_strContents = strdup(strBuffer);
|
|
|
|
_abseBufferStack[_ibsBufferStackTop].bse_strName = strdup(strName);
|
|
|
|
_abseBufferStack[_ibsBufferStackTop].bse_iLineCt = 1;
|
|
|
|
_abseBufferStack[_ibsBufferStackTop].bse_bParserEnd = bParserEnd;
|
|
|
|
|
|
|
|
_abseBufferStack[_ibsBufferStackTop].bse_bs = yy_scan_string((char*)(const char*)strBuffer);
|
|
|
|
|
|
|
|
yy_switch_to_buffer(_abseBufferStack[_ibsBufferStackTop].bse_bs);
|
|
|
|
}
|
|
|
|
BOOL ShellPopBuffer(void)
|
|
|
|
{
|
|
|
|
yy_delete_buffer( _abseBufferStack[_ibsBufferStackTop].bse_bs);
|
|
|
|
free((void*)_abseBufferStack[_ibsBufferStackTop].bse_strName);
|
|
|
|
free((void*)_abseBufferStack[_ibsBufferStackTop].bse_strContents);
|
|
|
|
BOOL bParserEnd = _abseBufferStack[_ibsBufferStackTop].bse_bParserEnd;
|
|
|
|
|
|
|
|
_ibsBufferStackTop--;
|
|
|
|
|
|
|
|
if (_ibsBufferStackTop>=0) {
|
|
|
|
yy_switch_to_buffer(_abseBufferStack[_ibsBufferStackTop].bse_bs);
|
|
|
|
}
|
|
|
|
return bParserEnd;
|
|
|
|
}
|
|
|
|
const char *ShellGetBufferName(void)
|
|
|
|
{
|
|
|
|
return _abseBufferStack[_ibsBufferStackTop].bse_strName;
|
|
|
|
}
|
|
|
|
int ShellGetBufferLineNumber(void)
|
|
|
|
{
|
|
|
|
return _abseBufferStack[_ibsBufferStackTop].bse_iLineCt;
|
|
|
|
}
|
|
|
|
int ShellGetBufferStackDepth(void)
|
|
|
|
{
|
|
|
|
return _ibsBufferStackTop;
|
|
|
|
}
|
|
|
|
const char *ShellGetBufferContents(void)
|
|
|
|
{
|
|
|
|
return _abseBufferStack[_ibsBufferStackTop].bse_strContents;
|
|
|
|
}
|
|
|
|
void ShellCountOneLine(void)
|
|
|
|
{
|
|
|
|
_abseBufferStack[_ibsBufferStackTop].bse_iLineCt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// temporary values for parsing
|
|
|
|
CDynamicStackArray<CTString> _shell_astrTempStrings;
|
|
|
|
// values for extern declarations
|
|
|
|
CDynamicStackArray<CTString> _shell_astrExtStrings;
|
|
|
|
CDynamicStackArray<FLOAT> _shell_afExtFloats;
|
|
|
|
|
|
|
|
static const char *strCommandLine = "";
|
|
|
|
|
2016-03-29 03:03:54 +02:00
|
|
|
FLOAT tmp_af[10] = { 0 };
|
|
|
|
INDEX tmp_ai[10] = { 0 };
|
|
|
|
INDEX tmp_fAdd = 0;
|
|
|
|
INDEX tmp_i = 0;
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
void CShellSymbol::Clear(void)
|
|
|
|
{
|
|
|
|
ss_istType = -1;
|
|
|
|
ss_strName.Clear();
|
|
|
|
ss_ulFlags = 0;
|
|
|
|
};
|
|
|
|
BOOL CShellSymbol::IsDeclared(void)
|
|
|
|
{
|
|
|
|
return ss_istType>=0 && ss_istType!=_shell_istUndeclared;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTString CShellSymbol::GetCompletionString(void) const
|
|
|
|
{
|
|
|
|
// get its type
|
|
|
|
ShellType &st = _shell_ast[ss_istType];
|
|
|
|
|
|
|
|
// get its name
|
|
|
|
if (st.st_sttType==STT_FUNCTION) {
|
|
|
|
return ss_strName + "()";
|
|
|
|
} else if (st.st_sttType==STT_ARRAY) {
|
|
|
|
return ss_strName + "[]";
|
|
|
|
} else {
|
|
|
|
return ss_strName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constructor.
|
|
|
|
CShell::CShell(void)
|
|
|
|
{
|
|
|
|
// allocate undefined symbol
|
|
|
|
_shell_istUndeclared = _shell_ast.Allocate();
|
|
|
|
};
|
|
|
|
CShell::~CShell(void)
|
|
|
|
{
|
|
|
|
_shell_astrExtStrings.Clear();
|
|
|
|
_shell_afExtFloats.Clear();
|
|
|
|
};
|
|
|
|
|
|
|
|
static const INDEX _bTRUE = TRUE;
|
|
|
|
static const INDEX _bFALSE = FALSE;
|
|
|
|
|
|
|
|
CTString ScriptEsc(const CTString &str)
|
|
|
|
{
|
|
|
|
CTString strResult = "";
|
|
|
|
|
|
|
|
const char *pchSrc = (const char *)str;
|
|
|
|
char buf[2];
|
|
|
|
buf[1] = 0;
|
|
|
|
|
|
|
|
while (*pchSrc!=0) {
|
|
|
|
switch(*pchSrc) {
|
|
|
|
case 10: strResult+="\\n"; break;
|
|
|
|
case 13: strResult+="\\r"; break;
|
|
|
|
case '\\': strResult+="\\\\"; break;
|
|
|
|
case '"': strResult+="\\\""; break;
|
|
|
|
default: buf[0] = *pchSrc; strResult+=buf; break;
|
|
|
|
}
|
|
|
|
pchSrc++;
|
|
|
|
}
|
|
|
|
return strResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma inline_depth(0)
|
|
|
|
void MakeAccessViolation(void* pArgs)
|
|
|
|
{
|
|
|
|
INDEX bDont = NEXTARGUMENT(INDEX);
|
|
|
|
if( bDont) return;
|
|
|
|
char *p=NULL;
|
|
|
|
*p=1;
|
|
|
|
}
|
|
|
|
|
2016-03-29 03:03:54 +02:00
|
|
|
int _a=123;
|
2016-03-11 14:57:17 +01:00
|
|
|
void MakeStackOverflow(void* pArgs)
|
|
|
|
{
|
|
|
|
INDEX bDont = NEXTARGUMENT(INDEX);
|
|
|
|
if( bDont) return;
|
|
|
|
int a[1000];
|
|
|
|
a[999] = _a;
|
|
|
|
MakeStackOverflow(0);
|
|
|
|
_a=a[999];
|
|
|
|
}
|
|
|
|
|
|
|
|
void MakeFatalError(void* pArgs)
|
|
|
|
{
|
|
|
|
INDEX bDont = NEXTARGUMENT(INDEX);
|
|
|
|
if( bDont) return;
|
|
|
|
FatalError( "MakeFatalError()");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern void ReportGlobalMemoryStatus(void)
|
|
|
|
{
|
2016-03-29 03:03:54 +02:00
|
|
|
#ifdef PLATFORM_WIN32
|
2016-03-29 05:21:44 +02:00
|
|
|
CPrintF(TRANSV("Global memory status...\n"));
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
MEMORYSTATUS ms;
|
|
|
|
GlobalMemoryStatus(&ms);
|
|
|
|
|
|
|
|
#define MB (1024*1024)
|
2016-03-29 05:21:44 +02:00
|
|
|
CPrintF(TRANSV(" Physical memory used: %4d/%4dMB\n"), (ms.dwTotalPhys -ms.dwAvailPhys )/MB, ms.dwTotalPhys /MB);
|
|
|
|
CPrintF(TRANSV(" Page file used: %4d/%4dMB\n"), (ms.dwTotalPageFile-ms.dwAvailPageFile)/MB, ms.dwTotalPageFile/MB);
|
|
|
|
CPrintF(TRANSV(" Virtual memory used: %4d/%4dMB\n"), (ms.dwTotalVirtual -ms.dwAvailVirtual )/MB, ms.dwTotalVirtual /MB);
|
|
|
|
CPrintF(TRANSV(" Memory load: %3d%%\n"), ms.dwMemoryLoad);
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
DWORD dwMin;
|
|
|
|
DWORD dwMax;
|
|
|
|
GetProcessWorkingSetSize(GetCurrentProcess(), &dwMin, &dwMax);
|
2016-03-29 05:21:44 +02:00
|
|
|
CPrintF(TRANSV(" Process working set: %dMB-%dMB\n\n"), dwMin/(1024*1024), dwMax/(1024*1024));
|
2016-03-29 03:03:54 +02:00
|
|
|
#endif
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void MemoryInfo(void)
|
|
|
|
{
|
|
|
|
ReportGlobalMemoryStatus();
|
|
|
|
|
2016-03-29 03:03:54 +02:00
|
|
|
#ifdef PLATFORM_WIN32
|
|
|
|
_HEAPINFO hinfo;
|
2016-03-11 14:57:17 +01:00
|
|
|
int heapstatus;
|
|
|
|
hinfo._pentry = NULL;
|
|
|
|
SLONG slTotalUsed = 0;
|
|
|
|
SLONG slTotalFree = 0;
|
|
|
|
INDEX ctUsed = 0;
|
|
|
|
INDEX ctFree = 0;
|
|
|
|
|
|
|
|
CPrintF( "Walking heap...\n");
|
|
|
|
while( ( heapstatus = _heapwalk( &hinfo ) ) == _HEAPOK )
|
|
|
|
{
|
|
|
|
if (hinfo._useflag == _USEDENTRY ) {
|
|
|
|
slTotalUsed+=hinfo._size;
|
|
|
|
ctUsed++;
|
|
|
|
} else {
|
|
|
|
slTotalFree+=hinfo._size;
|
|
|
|
ctFree++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch( heapstatus ) {
|
|
|
|
case _HEAPEMPTY: CPrintF( "Heap empty?!?\n" ); break;
|
|
|
|
case _HEAPEND: CPrintF( "Heap ok.\n" ); break;
|
|
|
|
case _HEAPBADPTR: CPrintF( "ERROR - bad pointer to heap\n" ); break;
|
|
|
|
case _HEAPBADBEGIN: CPrintF( "ERROR - bad start of heap\n" ); break;
|
|
|
|
case _HEAPBADNODE: CPrintF( "ERROR - bad node in heap\n" ); break;
|
|
|
|
}
|
|
|
|
CPrintF( "Total used: %d bytes (%.2f MB) in %d blocks\n", slTotalUsed, slTotalUsed/1024.0f/1024.0f, ctUsed);
|
|
|
|
CPrintF( "Total free: %d bytes (%.2f MB) in %d blocks\n", slTotalFree, slTotalFree/1024.0f/1024.0f, ctFree);
|
2016-03-29 03:03:54 +02:00
|
|
|
#endif
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// get help for a shell symbol
|
|
|
|
extern CTString GetShellSymbolHelp_t(const CTString &strSymbol)
|
|
|
|
{
|
|
|
|
CTString strPattern = strSymbol+"*";
|
|
|
|
// open the symbol help file
|
|
|
|
CTFileStream strm;
|
|
|
|
strm.Open_t(CTString("Help\\ShellSymbols.txt"));
|
|
|
|
|
|
|
|
// while not at the end of file
|
|
|
|
while (!strm.AtEOF()) {
|
|
|
|
// read the symbol name and its help
|
|
|
|
CTString strSymbolInFile;
|
|
|
|
strm.GetLine_t(strSymbolInFile, ':');
|
|
|
|
strSymbolInFile.TrimSpacesLeft();
|
|
|
|
strSymbolInFile.TrimSpacesRight();
|
|
|
|
CTString strHelpInFile;
|
|
|
|
strm.GetLine_t(strHelpInFile, '$');
|
|
|
|
strHelpInFile.TrimSpacesLeft();
|
|
|
|
strHelpInFile.TrimSpacesRight();
|
|
|
|
// if that is the one
|
|
|
|
if( strSymbolInFile.Matches(strPattern)) {
|
|
|
|
// print the help
|
|
|
|
return strHelpInFile;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if there is help for a shell symbol
|
|
|
|
extern BOOL CheckShellSymbolHelp(const CTString &strSymbol)
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
return GetShellSymbolHelp_t(strSymbol)!="";
|
|
|
|
} catch(char *strError) {
|
|
|
|
(void)strError;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// print help for a shell symbol
|
|
|
|
extern void PrintShellSymbolHelp(const CTString &strSymbol)
|
|
|
|
{
|
|
|
|
// try to
|
|
|
|
try {
|
|
|
|
CTString strHelp = GetShellSymbolHelp_t(strSymbol);
|
|
|
|
if (strHelp!="") {
|
2016-03-29 03:03:54 +02:00
|
|
|
CPrintF("%s\n", (const char *) strHelp);
|
2016-03-11 14:57:17 +01:00
|
|
|
} else {
|
2016-03-29 03:03:54 +02:00
|
|
|
CPrintF( TRANS("No help found for '%s'.\n"), (const char *) strSymbol);
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
// if failed
|
|
|
|
} catch(char *strError) {
|
|
|
|
// just print the error
|
2016-03-29 03:03:54 +02:00
|
|
|
CPrintF( TRANS("Cannot print help for '%s': %s\n"), (const char *) strSymbol, strError);
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern void ListSymbolsByPattern(CTString strPattern)
|
|
|
|
{
|
|
|
|
// synchronize access to global shell
|
|
|
|
CTSingleLock slShell(&_pShell->sh_csShell, TRUE);
|
|
|
|
|
|
|
|
// for each of symbols in the shell
|
|
|
|
FOREACHINDYNAMICARRAY(_pShell->sh_assSymbols, CShellSymbol, itss) {
|
|
|
|
CShellSymbol &ss = *itss;
|
|
|
|
|
|
|
|
// if it is not visible to user, or not matching
|
|
|
|
if (!(ss.ss_ulFlags&SSF_USER) || !ss.ss_strName.Matches(strPattern)) {
|
|
|
|
// skip it
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get its type
|
|
|
|
ShellType &st = _shell_ast[ss.ss_istType];
|
|
|
|
|
|
|
|
if (ss.ss_ulFlags & SSF_CONSTANT) {
|
|
|
|
CPrintF("const ");
|
|
|
|
}
|
|
|
|
if (ss.ss_ulFlags & SSF_PERSISTENT) {
|
|
|
|
CPrintF("persistent ");
|
|
|
|
}
|
|
|
|
|
|
|
|
// print its declaration to the console
|
|
|
|
if (st.st_sttType == STT_FUNCTION) {
|
2016-03-29 03:03:54 +02:00
|
|
|
CPrintF("void %s(void)", (const char *) ss.ss_strName);
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
} else if (st.st_sttType == STT_STRING) {
|
2016-03-29 03:03:54 +02:00
|
|
|
CPrintF("CTString %s = \"%s\"", (const char *) ss.ss_strName, (const char *) (*(CTString*)ss.ss_pvValue));
|
2016-03-11 14:57:17 +01:00
|
|
|
} else if (st.st_sttType == STT_FLOAT) {
|
2016-03-29 03:03:54 +02:00
|
|
|
CPrintF("FLOAT %s = %g", (const char *) ss.ss_strName, *(FLOAT*)ss.ss_pvValue);
|
2016-03-11 14:57:17 +01:00
|
|
|
} else if (st.st_sttType == STT_INDEX) {
|
2016-03-29 03:03:54 +02:00
|
|
|
CPrintF("INDEX %s = %d (0x%08x)", (const char *) ss.ss_strName, *(INDEX*)ss.ss_pvValue, *(INDEX*)ss.ss_pvValue);
|
2016-03-11 14:57:17 +01:00
|
|
|
} else if (st.st_sttType == STT_ARRAY) {
|
|
|
|
// get base type
|
|
|
|
ShellType &stBase = _shell_ast[st.st_istBaseType];
|
|
|
|
if (stBase.st_sttType == STT_FLOAT) {
|
2016-03-29 03:03:54 +02:00
|
|
|
CPrintF("FLOAT %s[%d]", (const char *) ss.ss_strName, st.st_ctArraySize);
|
2016-03-11 14:57:17 +01:00
|
|
|
} else if (stBase.st_sttType == STT_INDEX) {
|
2016-03-29 03:03:54 +02:00
|
|
|
CPrintF("INDEX %s[%d]", (const char *) ss.ss_strName, st.st_ctArraySize);
|
2016-03-11 14:57:17 +01:00
|
|
|
} else if (stBase.st_sttType == STT_STRING) {
|
2016-03-29 03:03:54 +02:00
|
|
|
CPrintF("CTString %s[%d]", (const char *) ss.ss_strName, st.st_ctArraySize);
|
2016-03-11 14:57:17 +01:00
|
|
|
} else {
|
|
|
|
ASSERT(FALSE);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ASSERT(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!CheckShellSymbolHelp(ss.ss_strName)) {
|
|
|
|
CPrintF( TRANS(" help N/A"));
|
|
|
|
}
|
|
|
|
CPrintF("\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print a list of all symbols in global shell to console.
|
|
|
|
static void ListSymbols(void)
|
|
|
|
{
|
|
|
|
// print header
|
|
|
|
CPrintF( TRANS("Useful symbols:\n"));
|
|
|
|
|
|
|
|
// list all symbols
|
|
|
|
ListSymbolsByPattern("*");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// output any string to console
|
|
|
|
void Echo(void* pArgs)
|
|
|
|
{
|
|
|
|
CTString str = *NEXTARGUMENT(CTString*);
|
2016-03-30 07:51:52 +02:00
|
|
|
CPrintF("%s", (const char *) str);
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CTString UndecorateString(void* pArgs)
|
|
|
|
{
|
|
|
|
CTString strString = *NEXTARGUMENT(CTString*);
|
|
|
|
return strString.Undecorated();
|
|
|
|
}
|
|
|
|
BOOL MatchStrings(void* pArgs)
|
|
|
|
{
|
|
|
|
CTString strString = *NEXTARGUMENT(CTString*);
|
|
|
|
CTString strPattern = *NEXTARGUMENT(CTString*);
|
|
|
|
return strString.Matches(strPattern);
|
|
|
|
}
|
|
|
|
CTString MyLoadString(void* pArgs)
|
|
|
|
{
|
|
|
|
CTString strFileName = *NEXTARGUMENT(CTString*);
|
|
|
|
try {
|
|
|
|
CTString strString;
|
|
|
|
strString.Load_t(strFileName);
|
|
|
|
return strString;
|
|
|
|
} catch (char *strError) {
|
|
|
|
(void)strError;
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void MySaveString(void* pArgs)
|
|
|
|
{
|
|
|
|
CTString strFileName = *NEXTARGUMENT(CTString*);
|
|
|
|
CTString strString = *NEXTARGUMENT(CTString*);
|
|
|
|
try {
|
|
|
|
strString.Save_t(strFileName);
|
|
|
|
} catch (char *strError) {
|
|
|
|
(void)strError;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// load command batch files
|
|
|
|
void LoadCommands(void)
|
|
|
|
{
|
|
|
|
// list all command files
|
|
|
|
CDynamicStackArray<CTFileName> afnmCmds;
|
2016-03-29 03:03:54 +02:00
|
|
|
MakeDirList( afnmCmds, CTString("Scripts\\Commands\\"), CTString("*.ini"), DLI_RECURSIVE);
|
2016-03-11 14:57:17 +01:00
|
|
|
// for each file
|
|
|
|
for(INDEX i=0; i<afnmCmds.Count(); i++) {
|
|
|
|
CTFileName &fnm = afnmCmds[i];
|
|
|
|
// load the file
|
|
|
|
CTString strCmd;
|
|
|
|
try {
|
|
|
|
strCmd.Load_t(fnm);
|
|
|
|
} catch (char *strError) {
|
|
|
|
CPrintF("%s\n", strError);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
CTString strName = fnm.FileName();
|
|
|
|
// declare it
|
|
|
|
extern void Declaration(
|
|
|
|
ULONG ulQualifiers, INDEX istType, CShellSymbol &ssNew,
|
|
|
|
INDEX (*pPreFunc)(INDEX), void (*pPostFunc)(INDEX));
|
|
|
|
|
|
|
|
INDEX iType = ShellTypeNewString();
|
|
|
|
CShellSymbol &ssNew = *_pShell->GetSymbol(strName, FALSE);
|
|
|
|
Declaration(SSF_EXTERNAL|SSF_USER, iType, ssNew, NULL, NULL);
|
|
|
|
ShellTypeDelete(iType);
|
|
|
|
|
|
|
|
// get symbol type
|
|
|
|
ShellTypeType stt = _shell_ast[ssNew.ss_istType].st_sttType;
|
|
|
|
|
|
|
|
// if the symbol is ok
|
|
|
|
if (stt == STT_STRING && !(ssNew.ss_ulFlags&SSF_CONSTANT)) {
|
|
|
|
// assign value
|
|
|
|
*(CTString*)ssNew.ss_pvValue = "!command "+strCmd;
|
|
|
|
} else {
|
2016-03-29 03:03:54 +02:00
|
|
|
_pShell->ErrorF("Symbol '%s' is not suitable to be a command", (const char *) ssNew.ss_strName);
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CTString ToUpper(const CTString &strResult)
|
|
|
|
{
|
|
|
|
char *pch = (char*)(const char *)strResult;
|
|
|
|
for(INDEX i=0; i<strlen(pch); i++) {
|
|
|
|
pch[i]=toupper(pch[i]);
|
|
|
|
}
|
|
|
|
return strResult;
|
|
|
|
}
|
|
|
|
CTString ToUpperCfunc(void* pArgs)
|
|
|
|
{
|
|
|
|
CTString strResult = *NEXTARGUMENT(CTString*);
|
|
|
|
return ToUpper(strResult);
|
|
|
|
}
|
|
|
|
CTString ToLower(const CTString &strResult)
|
|
|
|
{
|
|
|
|
char *pch = (char*)(const char *)strResult;
|
|
|
|
for(INDEX i=0; i<strlen(pch); i++) {
|
|
|
|
pch[i]=tolower(pch[i]);
|
|
|
|
}
|
|
|
|
return strResult;
|
|
|
|
}
|
|
|
|
CTString ToLowerCfunc(void* pArgs)
|
|
|
|
{
|
|
|
|
CTString strResult = *NEXTARGUMENT(CTString*);
|
|
|
|
return ToLower(strResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
CTString RemoveSubstring(const CTString &strFull, const CTString &strSub)
|
|
|
|
{
|
|
|
|
CTString strFullL = ToLower(strFull);
|
|
|
|
CTString strSubL = ToLower(strSub);
|
|
|
|
|
|
|
|
const char *pchFullL = strFullL;
|
|
|
|
const char *pchSubL = strSubL;
|
|
|
|
const char *pchFound = strstr(pchFullL, pchSubL);
|
|
|
|
if (pchFound==NULL || strlen(strSub)==0) {
|
|
|
|
return strFull;
|
|
|
|
}
|
|
|
|
INDEX iOffset = pchFound-pchFullL;
|
|
|
|
INDEX iLenFull = strlen(strFull);
|
|
|
|
INDEX iLenSub = strlen(strSub);
|
|
|
|
|
|
|
|
CTString strLeft = strFull;
|
|
|
|
strLeft.TrimRight(iOffset);
|
|
|
|
CTString strRight = strFull;
|
|
|
|
strRight.TrimLeft(iLenFull-iOffset-iLenSub);
|
|
|
|
return strLeft+strRight;
|
|
|
|
}
|
|
|
|
CTString RemoveSubstringCfunc(void* pArgs)
|
|
|
|
{
|
|
|
|
CTString strFull = *NEXTARGUMENT(CTString*);
|
|
|
|
CTString strSub = *NEXTARGUMENT(CTString*);
|
|
|
|
return RemoveSubstring(strFull, strSub);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the shell.
|
|
|
|
void CShell::Initialize(void)
|
|
|
|
{
|
|
|
|
sh_csShell.cs_iIndex = -1;
|
|
|
|
|
|
|
|
// synchronize access to shell
|
|
|
|
CTSingleLock slShell(&sh_csShell, TRUE);
|
|
|
|
// add built in commands and constants
|
|
|
|
DeclareSymbol("const INDEX TRUE;", (void*)&_bTRUE);
|
|
|
|
DeclareSymbol("const INDEX FALSE;", (void*)&_bFALSE);
|
|
|
|
DeclareSymbol("const INDEX ON;", (void*)&_bTRUE);
|
|
|
|
DeclareSymbol("const INDEX OFF;", (void*)&_bFALSE);
|
|
|
|
DeclareSymbol("const INDEX YES;", (void*)&_bTRUE);
|
|
|
|
DeclareSymbol("const INDEX NO;", (void*)&_bFALSE);
|
|
|
|
|
2016-03-29 03:03:54 +02:00
|
|
|
DeclareSymbol("user void LoadCommands(void);", (void *)&LoadCommands);
|
|
|
|
DeclareSymbol("user void ListSymbols(void);", (void *)&ListSymbols);
|
|
|
|
DeclareSymbol("user void MemoryInfo(void);", (void *)&MemoryInfo);
|
|
|
|
DeclareSymbol("user void MakeAccessViolation(INDEX);", (void *)&MakeAccessViolation);
|
|
|
|
DeclareSymbol("user void MakeStackOverflow(INDEX);", (void *)&MakeStackOverflow);
|
|
|
|
DeclareSymbol("user void MakeFatalError(INDEX);", (void *)&MakeFatalError);
|
|
|
|
DeclareSymbol("persistent user INDEX con_iLastLines;", (void *)&con_iLastLines);
|
|
|
|
DeclareSymbol("persistent user FLOAT tmp_af[10];", (void *)&tmp_af);
|
|
|
|
DeclareSymbol("persistent user INDEX tmp_ai[10];", (void *)&tmp_ai);
|
|
|
|
DeclareSymbol("persistent user INDEX tmp_i;", (void *)&tmp_i);
|
|
|
|
DeclareSymbol("persistent user FLOAT tmp_fAdd;", (void *)&tmp_fAdd);
|
|
|
|
|
|
|
|
DeclareSymbol("user void Echo(CTString);", (void *)&Echo);
|
|
|
|
DeclareSymbol("user CTString UndecorateString(CTString);", (void *)&UndecorateString);
|
|
|
|
DeclareSymbol("user INDEX Matches(CTString, CTString);", (void *)&MatchStrings);
|
|
|
|
DeclareSymbol("user CTString LoadString(CTString);", (void *)&MyLoadString);
|
|
|
|
DeclareSymbol("user void SaveString(CTString, CTString);", (void *)&MySaveString);
|
|
|
|
DeclareSymbol("user CTString RemoveSubstring(CTString, CTString);", (void *)&RemoveSubstringCfunc);
|
|
|
|
DeclareSymbol("user CTString ToUpper(CTString);", (void *)&ToUpperCfunc);
|
|
|
|
DeclareSymbol("user CTString ToLower(CTString);", (void *)&ToLowerCfunc);
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL _iParsing = 0;
|
|
|
|
|
|
|
|
// Declare a symbol in the shell.
|
2016-03-29 03:03:54 +02:00
|
|
|
/* rcg10072001 Added second version of DeclareSymbol()... */
|
2016-03-11 14:57:17 +01:00
|
|
|
void CShell::DeclareSymbol(const CTString &strDeclaration, void *pvValue)
|
2016-03-29 03:03:54 +02:00
|
|
|
{
|
|
|
|
DeclareSymbol((const char *) strDeclaration, pvValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CShell::DeclareSymbol(const char *strDeclaration, void *pvValue)
|
2016-03-11 14:57:17 +01:00
|
|
|
{
|
|
|
|
// synchronize access to shell
|
|
|
|
CTSingleLock slShell(&sh_csShell, TRUE);
|
|
|
|
|
|
|
|
_pvNextToDeclare = pvValue;
|
|
|
|
|
|
|
|
_iParsing++;
|
|
|
|
|
|
|
|
// parse the string
|
|
|
|
const BOOL old_bExecNextBlock = _bExecNextBlock;
|
|
|
|
_bExecNextBlock = 1;
|
|
|
|
|
|
|
|
ShellPushBuffer("<declaration>", strDeclaration, TRUE);
|
|
|
|
yyparse();
|
|
|
|
// ShellPopBuffer();
|
|
|
|
|
|
|
|
_bExecNextBlock = old_bExecNextBlock;
|
|
|
|
|
|
|
|
_iParsing--;
|
|
|
|
if (_iParsing<=0) {
|
|
|
|
_shell_astrTempStrings.PopAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't use that value for parsing any more
|
|
|
|
_pvNextToDeclare = NULL;
|
2016-03-29 03:03:54 +02:00
|
|
|
}
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
// Execute command(s).
|
|
|
|
void CShell::Execute(const CTString &strCommands)
|
|
|
|
{
|
|
|
|
// synchronize access to shell
|
|
|
|
CTSingleLock slShell(&sh_csShell, TRUE);
|
|
|
|
|
|
|
|
// ASSERT(_iParsing==0);
|
|
|
|
_iParsing++;
|
|
|
|
|
|
|
|
// parse the string
|
|
|
|
const BOOL old_bExecNextBlock = _bExecNextBlock;
|
|
|
|
_bExecNextBlock = 1;
|
|
|
|
|
|
|
|
ShellPushBuffer("<command>", strCommands, TRUE);
|
|
|
|
yyparse();
|
|
|
|
//ShellPopBuffer();
|
|
|
|
|
|
|
|
_bExecNextBlock = old_bExecNextBlock;
|
|
|
|
|
|
|
|
_iParsing--;
|
|
|
|
if (_iParsing<=0) {
|
|
|
|
_shell_astrTempStrings.PopAll();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Get a shell symbol by its name.
|
|
|
|
CShellSymbol *CShell::GetSymbol(const CTString &strName, BOOL bDeclaredOnly)
|
|
|
|
{
|
|
|
|
// synchronize access to shell
|
|
|
|
CTSingleLock slShell(&sh_csShell, TRUE);
|
|
|
|
|
|
|
|
// for each of symbols in the shell
|
|
|
|
FOREACHINDYNAMICARRAY(sh_assSymbols, CShellSymbol, itss) {
|
|
|
|
// if it is the right one
|
|
|
|
if (itss->ss_strName==strName) {
|
|
|
|
// return it
|
|
|
|
return itss;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// if none is found...
|
|
|
|
|
|
|
|
// if only declared symbols are allowed
|
|
|
|
if (bDeclaredOnly) {
|
|
|
|
// return nothing
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// if undeclared symbols are allowed
|
|
|
|
} else {
|
|
|
|
// create a new one with that name and undefined type
|
|
|
|
CShellSymbol &ssNew = *sh_assSymbols.New(1);
|
|
|
|
ssNew.ss_strName = strName;
|
|
|
|
ssNew.ss_istType = _shell_istUndeclared;
|
|
|
|
ssNew.ss_pvValue = NULL;
|
|
|
|
ssNew.ss_ulFlags = 0;
|
|
|
|
ssNew.ss_pPreFunc = NULL;
|
|
|
|
ssNew.ss_pPostFunc = NULL;
|
|
|
|
return &ssNew;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
FLOAT CShell::GetFLOAT(const CTString &strName)
|
|
|
|
{
|
|
|
|
// get the symbol
|
|
|
|
CShellSymbol *pss = GetSymbol(strName, TRUE);
|
|
|
|
|
|
|
|
// if it doesn't exist or is not of given type
|
|
|
|
if (pss==NULL || _shell_ast[pss->ss_istType].st_sttType!=STT_FLOAT) {
|
|
|
|
// error
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return -666.0f;
|
|
|
|
}
|
|
|
|
// get it
|
|
|
|
return *(FLOAT*)pss->ss_pvValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CShell::SetFLOAT(const CTString &strName, FLOAT fValue)
|
|
|
|
{
|
|
|
|
CShellSymbol *pss = GetSymbol(strName, TRUE);
|
|
|
|
|
|
|
|
// if it doesn't exist or is not of given type
|
|
|
|
if (pss==NULL || _shell_ast[pss->ss_istType].st_sttType!=STT_FLOAT) {
|
|
|
|
// error
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// set it
|
|
|
|
*(FLOAT*)pss->ss_pvValue = fValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
INDEX CShell::GetINDEX(const CTString &strName)
|
|
|
|
{
|
|
|
|
// get the symbol
|
|
|
|
CShellSymbol *pss = GetSymbol(strName, TRUE);
|
|
|
|
|
|
|
|
// if it doesn't exist or is not of given type
|
|
|
|
if (pss==NULL || _shell_ast[pss->ss_istType].st_sttType!=STT_INDEX) {
|
|
|
|
// error
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return -666;
|
|
|
|
}
|
|
|
|
// get it
|
|
|
|
return *(INDEX*)pss->ss_pvValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CShell::SetINDEX(const CTString &strName, INDEX iValue)
|
|
|
|
{
|
|
|
|
CShellSymbol *pss = GetSymbol(strName, TRUE);
|
|
|
|
|
|
|
|
// if it doesn't exist or is not of given type
|
|
|
|
if (pss==NULL || _shell_ast[pss->ss_istType].st_sttType!=STT_INDEX) {
|
|
|
|
// error
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// set it
|
|
|
|
*(INDEX*)pss->ss_pvValue = iValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTString CShell::GetString(const CTString &strName)
|
|
|
|
{
|
|
|
|
// get the symbol
|
|
|
|
CShellSymbol *pss = GetSymbol(strName, TRUE);
|
|
|
|
|
|
|
|
// if it doesn't exist or is not of given type
|
|
|
|
if (pss==NULL || _shell_ast[pss->ss_istType].st_sttType!=STT_STRING) {
|
|
|
|
// error
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return "<invalid>";
|
|
|
|
}
|
|
|
|
// get it
|
|
|
|
return *(CTString*)pss->ss_pvValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CShell::SetString(const CTString &strName, const CTString &strValue)
|
|
|
|
{
|
|
|
|
CShellSymbol *pss = GetSymbol(strName, TRUE);
|
|
|
|
|
|
|
|
// if it doesn't exist or is not of given type
|
|
|
|
if (pss==NULL || _shell_ast[pss->ss_istType].st_sttType!=STT_STRING) {
|
|
|
|
// error
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// set it
|
|
|
|
*(CTString*)pss->ss_pvValue = strValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CTString CShell::GetValue(const CTString &strName)
|
|
|
|
{
|
|
|
|
// get the symbol
|
|
|
|
CShellSymbol *pss = GetSymbol(strName, TRUE);
|
|
|
|
|
|
|
|
// if it doesn't exist
|
|
|
|
if (pss==NULL) {
|
|
|
|
// error
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return "<invalid>";
|
|
|
|
}
|
|
|
|
|
|
|
|
// get it
|
|
|
|
ShellTypeType stt = _shell_ast[pss->ss_istType].st_sttType;
|
|
|
|
CTString strValue;
|
|
|
|
switch(stt) {
|
|
|
|
case STT_STRING:
|
|
|
|
strValue = *(CTString*)pss->ss_pvValue;
|
|
|
|
break;
|
|
|
|
case STT_INDEX:
|
|
|
|
strValue.PrintF("%d", *(INDEX*)pss->ss_pvValue);
|
|
|
|
break;
|
|
|
|
case STT_FLOAT:
|
|
|
|
strValue.PrintF("%g", *(FLOAT*)pss->ss_pvValue);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return strValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CShell::SetValue(const CTString &strName, const CTString &strValue)
|
|
|
|
{
|
|
|
|
// get the symbol
|
|
|
|
CShellSymbol *pss = GetSymbol(strName, TRUE);
|
|
|
|
|
|
|
|
// if it doesn't exist
|
|
|
|
if (pss==NULL) {
|
|
|
|
// error
|
|
|
|
ASSERT(FALSE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// get it
|
|
|
|
ShellTypeType stt = _shell_ast[pss->ss_istType].st_sttType;
|
|
|
|
switch(stt) {
|
|
|
|
case STT_STRING:
|
|
|
|
*(CTString*)pss->ss_pvValue = strValue;
|
|
|
|
break;
|
|
|
|
case STT_INDEX:
|
|
|
|
((CTString&)strValue).ScanF("%d", (INDEX*)pss->ss_pvValue);
|
|
|
|
break;
|
|
|
|
case STT_FLOAT:
|
|
|
|
((CTString&)strValue).ScanF("%g", (FLOAT*)pss->ss_pvValue);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Report error in shell script processing.
|
|
|
|
*/
|
|
|
|
void CShell::ErrorF(const char *strFormat, ...)
|
|
|
|
{
|
|
|
|
// synchronize access to shell
|
|
|
|
CTSingleLock slShell(&sh_csShell, TRUE);
|
|
|
|
|
|
|
|
// print the error file and line
|
|
|
|
const char *strName = ShellGetBufferName();
|
|
|
|
int iLine = ShellGetBufferLineNumber();
|
|
|
|
if (strName[0] == '<') {
|
|
|
|
CPrintF("%s\n%s(%d): ", ShellGetBufferContents(), strName, iLine);
|
|
|
|
} else {
|
|
|
|
CPrintF("%s(%d): ", strName, iLine);
|
|
|
|
}
|
|
|
|
|
|
|
|
// format the message in buffer
|
|
|
|
va_list arg;
|
|
|
|
va_start(arg, strFormat);
|
|
|
|
CTString strBuffer;
|
|
|
|
strBuffer.VPrintF(strFormat, arg);
|
2016-03-29 03:03:54 +02:00
|
|
|
va_end(arg);
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
// print it to the main console
|
|
|
|
CPrintF(strBuffer);
|
|
|
|
// go to new line
|
|
|
|
CPrintF("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save shell commands to restore persistent symbols to a script file
|
|
|
|
void CShell::StorePersistentSymbols(const CTFileName &fnScript)
|
|
|
|
{
|
|
|
|
// synchronize access to global shell
|
|
|
|
CTSingleLock slShell(&sh_csShell, TRUE);
|
|
|
|
|
|
|
|
try {
|
|
|
|
// open the file
|
|
|
|
CTFileStream fScript;
|
|
|
|
fScript.Create_t(fnScript);
|
|
|
|
|
|
|
|
// print header
|
|
|
|
fScript.FPrintF_t("// automatically saved persistent symbols:\n");
|
|
|
|
// for each of symbols in the shell
|
|
|
|
FOREACHINDYNAMICARRAY(sh_assSymbols, CShellSymbol, itss) {
|
|
|
|
CShellSymbol &ss = *itss;
|
|
|
|
|
|
|
|
// if it is not persistent
|
|
|
|
if (! (ss.ss_ulFlags & SSF_PERSISTENT)) {
|
|
|
|
// skip it
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-03-29 03:03:54 +02:00
|
|
|
const char *strUser = (ss.ss_ulFlags & SSF_USER)?"user ":"";
|
2016-03-11 14:57:17 +01:00
|
|
|
|
|
|
|
// get its type
|
|
|
|
ShellType &st = _shell_ast[ss.ss_istType];
|
|
|
|
// if array
|
|
|
|
if (st.st_sttType==STT_ARRAY) {
|
|
|
|
// get base type
|
|
|
|
ShellType &stBase = _shell_ast[st.st_istBaseType];
|
|
|
|
CTString strType;
|
|
|
|
// if float
|
|
|
|
if (stBase.st_sttType==STT_FLOAT) {
|
|
|
|
// dump all members as floats
|
|
|
|
for(INDEX i=0; i<st.st_ctArraySize; i++) {
|
2016-03-29 03:03:54 +02:00
|
|
|
fScript.FPrintF_t("%s[%d]=(FLOAT)%g;\n", (const char *) ss.ss_strName, i, ((FLOAT*)ss.ss_pvValue)[i]);
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
// if index
|
|
|
|
} else if (stBase.st_sttType==STT_INDEX) {
|
|
|
|
// dump all members as indices
|
|
|
|
for(INDEX i=0; i<st.st_ctArraySize; i++) {
|
2016-03-29 03:03:54 +02:00
|
|
|
fScript.FPrintF_t("%s[%d]=(INDEX)%d;\n", (const char *) ss.ss_strName, i, ((INDEX*)ss.ss_pvValue)[i]);
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
// if string
|
|
|
|
} else if (stBase.st_sttType==STT_STRING) {
|
|
|
|
// dump all members
|
|
|
|
for(INDEX i=0; i<st.st_ctArraySize; i++) {
|
2016-03-30 07:51:52 +02:00
|
|
|
fScript.FPrintF_t("%s[%d]=\"%c\";\n", (const char *) ss.ss_strName, i, (ScriptEsc(*(CTString*)ss.ss_pvValue)[i]) );
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
// otherwise
|
|
|
|
} else {
|
2016-03-29 03:03:54 +02:00
|
|
|
ThrowF_t("%s is an array of wrong type", (const char *) ss.ss_strName);
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
// if float
|
|
|
|
} else if (st.st_sttType==STT_FLOAT) {
|
|
|
|
// dump as float
|
2016-03-29 03:03:54 +02:00
|
|
|
fScript.FPrintF_t("persistent extern %sFLOAT %s=(FLOAT)%g;\n", strUser, (const char *) ss.ss_strName, *(FLOAT*)ss.ss_pvValue);
|
2016-03-11 14:57:17 +01:00
|
|
|
// if index
|
|
|
|
} else if (st.st_sttType==STT_INDEX) {
|
|
|
|
// dump as index
|
2016-03-29 03:03:54 +02:00
|
|
|
fScript.FPrintF_t("persistent extern %sINDEX %s=(INDEX)%d;\n", strUser, (const char *) ss.ss_strName, *(INDEX*)ss.ss_pvValue);
|
2016-03-11 14:57:17 +01:00
|
|
|
// if string
|
|
|
|
} else if (st.st_sttType==STT_STRING) {
|
|
|
|
// dump as index
|
2016-03-29 03:03:54 +02:00
|
|
|
fScript.FPrintF_t("persistent extern %sCTString %s=\"%s\";\n", strUser, (const char *) ss.ss_strName, (const char*)ScriptEsc(*(CTString*)ss.ss_pvValue) );
|
2016-03-11 14:57:17 +01:00
|
|
|
// otherwise
|
|
|
|
} else {
|
2016-03-29 03:03:54 +02:00
|
|
|
ThrowF_t("%s of wrong type", (const char *) ss.ss_strName);
|
2016-03-11 14:57:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (char *strError) {
|
|
|
|
WarningMessage(TRANS("Cannot save persistent symbols:\n%s"), strError);
|
|
|
|
}
|
|
|
|
}
|