
Compiler output is identical before & after this step git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/branches/wizou@6036 212acab6-be3b-0410-9dea-997c60f758d6
1563 lines
57 KiB
C
1563 lines
57 KiB
C
// Unicode support by Jim Park -- 08/22/2007
|
|
|
|
#include <windows.h>
|
|
#include <nsis/pluginapi.h> // nsis plugin
|
|
#include "MyMath.h"
|
|
#include "Math.h"
|
|
|
|
extern "C" int _fltused;
|
|
|
|
#ifdef __MINGW32__
|
|
int _fltused = 1;
|
|
#endif
|
|
|
|
ExpressionItem *stack;
|
|
|
|
int UserVarsCount, UserFuncsCount;
|
|
UserVar UserVars[MAX_USER_VARS];
|
|
UserFunc UserFuncs[MAX_USER_FUNCS];
|
|
|
|
void PrintTree(ExpressionItem *root, TCHAR *str);
|
|
void ParseString(TCHAR *&sp, ExpressionItem* &itemplace);
|
|
void CleanupItems(ExpressionItem* &itemplace);
|
|
void PlaceVariable(TCHAR *&vb, ParseInfo *pi);
|
|
|
|
void PlaceNewItem(TCHAR *&vb, ParseInfo *pi, int precedence)
|
|
{
|
|
ExpressionItem *newroot;
|
|
PlaceVariable(vb, pi);
|
|
if (pi->item == NULL) return;
|
|
|
|
while ((pi->OpsStack) && ((((int) pi->OpsStack->param2) < precedence)
|
|
|| ((((int)pi->OpsStack->param2) == precedence)
|
|
&& (precedence != OPERATOR_SET_PRECEDENCE))))
|
|
{
|
|
// second operand for our operator
|
|
newroot = pi->OpsStack;
|
|
*((ExpressionItem **)&(newroot->param2)) = pi->item;
|
|
pi->OpsStack = newroot->next;
|
|
newroot->next = NULL;
|
|
pi->item = newroot;
|
|
}
|
|
// finally we have got new root
|
|
newroot = pi->item;
|
|
|
|
if (pi->SetupNewRoot)
|
|
{
|
|
(*pi->root)->next = newroot;
|
|
pi->root = &((*pi->root)->next);
|
|
pi->SetupNewRoot = 0;
|
|
}
|
|
if (*pi->place == *pi->root) *pi->place = *pi->root = newroot;
|
|
else *pi->root = newroot;
|
|
// no item at our pockets
|
|
pi->item = NULL;
|
|
}
|
|
|
|
#define NSIS_VARS_COUNT 27
|
|
#define NSIS_VARS_STACK 25
|
|
#define NSIS_VARS_NSTACK 26
|
|
|
|
typedef TCHAR smallstr[2];
|
|
const smallstr NSISVariablesNames[NSIS_VARS_COUNT] = {{_T('r'),_T('0')}, {_T('r'),_T('1')}, {_T('r'),_T('2')}, {_T('r'),_T('3')}, {_T('r'),_T('4')}, {_T('r'),_T('5')}, {_T('r'),_T('6')}, {_T('r'),_T('7')}, {_T('r'),_T('8')}, {_T('r'),_T('9')},
|
|
{_T('R'),_T('0')}, {_T('R'),_T('1')}, {_T('R'),_T('2')}, {_T('R'),_T('3')}, {_T('R'),_T('4')}, {_T('R'),_T('5')}, {_T('R'),_T('6')}, {_T('R'),_T('7')}, {_T('R'),_T('8')}, {_T('R'),_T('9')},
|
|
{_T('C'),_T('L')}, {_T('I'),_T('D')}, {_T('O'),_T('D')}, {_T('E'),_T('D')}, {_T('L'),_T('G')}, {_T('S'),0}, {_T('N'),_T('S')}};
|
|
|
|
ExpressionItem *FindVariable(TCHAR *varname)
|
|
{
|
|
int i;
|
|
|
|
ExpressionItem *item = AllocItem();
|
|
// check NSIS variables
|
|
for (i = 0; i < NSIS_VARS_COUNT; i++)
|
|
{
|
|
if (lstrcmpn(varname, NSISVariablesNames[i],2) == 0)
|
|
{
|
|
if (i == NSIS_VARS_STACK) item->type = IT_VARIABLE | ITV_STACK;
|
|
else if (i == NSIS_VARS_NSTACK) item->type = IT_VARIABLE | ITV_NSTACK;
|
|
else
|
|
item->type = (IT_VARIABLE | ITV_NSIS) + i;
|
|
return item;
|
|
}
|
|
}
|
|
// no.. that's user variable
|
|
for (i = 0; i < UserVarsCount; i++)
|
|
{
|
|
if (lstrcmp(varname, UserVars[i].name) == 0)
|
|
{
|
|
// ok. we found user var expression needed
|
|
break;
|
|
}
|
|
}
|
|
if (i == UserVarsCount)
|
|
{
|
|
// new variable
|
|
UserVarsCount++;
|
|
lstrcpy(UserVars[i].name, varname);
|
|
UserVars[i].item = NULL;
|
|
}
|
|
item->type = (IT_VARIABLE | ITV_USER) + i;
|
|
return item;
|
|
}
|
|
|
|
void PlaceVariable(TCHAR *&vb, ParseInfo *pi)
|
|
{
|
|
if (vb <= pi->valbuf) return;
|
|
*vb = 0;
|
|
pi->item = FindVariable(pi->valbuf);
|
|
vb = pi->valbuf;
|
|
}
|
|
|
|
typedef double (*math_d2)(double, double);
|
|
typedef double (*math_ddp)(double, double*);
|
|
typedef double (*math_di)(double, int*);
|
|
|
|
#define MATHFUNCNUM 29
|
|
const MathFunction MathFunctions[MATHFUNCNUM] = {
|
|
{{_T('s'),_T('i'),_T('n')}, ITF_MATH1 >> 8, sin},
|
|
{{_T('s'),_T('n'),_T('h')}, ITF_MATH1 >> 8, sinh},
|
|
{{_T('a'),_T('s'),_T('n')}, ITF_MATH1 >> 8, asin},
|
|
{{_T('c'),_T('o'),_T('s')}, ITF_MATH1 >> 8, cos},
|
|
{{_T('c'),_T('s'),_T('h')}, ITF_MATH1 >> 8, cosh},
|
|
{{_T('a'),_T('c'),_T('s')}, ITF_MATH1 >> 8, acos},
|
|
{{_T('t'),_T('a'),_T('n')}, ITF_MATH1 >> 8, tan},
|
|
{{_T('t'),_T('n'),_T('h')}, ITF_MATH1 >> 8, tanh},
|
|
{{_T('a'),_T('t'),_T('n')}, ITF_MATH1 >> 8, atan},
|
|
{{_T('a'),_T('b'),_T('s')}, ITF_MATH1 >> 8, fabs},
|
|
{{_T('l'),_T('n'),0}, ITF_MATH1 >> 8, log},
|
|
{{_T('l'),_T('o'),_T('g')}, ITF_MATH1 >> 8, log10},
|
|
{{_T('e'),_T('x'),_T('p')}, ITF_MATH1 >> 8, exp},
|
|
{{_T('s'),_T('q'),_T('t')}, ITF_MATH1 >> 8, sqrt},
|
|
{{_T('c'),_T('e'),_T('l')}, ITF_MATH1 >> 8, ceil},
|
|
{{_T('f'),_T('l'),_T('r')}, ITF_MATH1 >> 8, floor},
|
|
|
|
{{_T('a'),_T('t'),_T('2')}, ITF_MATH2 >> 8, (Math1FuncPtr)(math_d2)atan2},
|
|
{{_T('p'),_T('o'),_T('w')}, ITF_MATH2 >> 8, (Math1FuncPtr)(math_d2)pow},
|
|
{{_T('f'),_T('m'),_T('d')}, ITF_MATH2 >> 8, (Math1FuncPtr)(math_d2)fmod},
|
|
|
|
// type conversions
|
|
{{_T('i'),0,0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_INT},
|
|
{{_T('s'),0,0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_STRING},
|
|
{{_T('f'),0,0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_FLOAT},
|
|
{{_T('a'),0,0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_ARRAY},
|
|
#define ITFT_CARRAY_ID 23
|
|
{{_T('c'),_T('a'),0}, ITF_TYPE >> 8, (Math1FuncPtr)ITC_ARRAY},
|
|
{{_T('f'),_T('f'),0}, ITF_TYPE >> 8, (Math1FuncPtr)FTT_FLOATF},
|
|
{{_T('l'),0,0}, ITF_TYPE >> 8, (Math1FuncPtr)FTT_LEN},
|
|
{{_T('c'),0,0}, ITF_TYPE >> 8, (Math1FuncPtr)FTT_CHAR},
|
|
|
|
{{_T('f'),_T('e'),_T('x')}, ITF_MATH2 >> 8, (Math1FuncPtr)(math_di)frexp},
|
|
{{_T('m'),_T('d'),_T('f')}, ITF_MATH2 >> 8, (Math1FuncPtr)(math_ddp)modf},
|
|
};
|
|
|
|
void PlaceFunction(TCHAR *&vb, TCHAR *&sp, ParseInfo *pi, int redefine)
|
|
{
|
|
int i;
|
|
ExpressionItem *item = pi->item = AllocItem();
|
|
*vb = 0;
|
|
|
|
// check BUILTIN functions
|
|
for (i = 0; i < MATHFUNCNUM; i++)
|
|
{
|
|
if (lstrcmpn(pi->valbuf, MathFunctions[i].name, 3) == 0)
|
|
{
|
|
item->type = IT_FUNCTION | (MathFunctions[i].type << 8) | i;
|
|
// get first argument
|
|
sp++;
|
|
ParseString(sp, *((ExpressionItem **)(&item->param1)));
|
|
if (*sp == _T(','))
|
|
{
|
|
// get second argument
|
|
sp++;
|
|
ParseString(sp, *((ExpressionItem **)(&item->param2)));
|
|
}
|
|
sp++; vb = pi->valbuf;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// heh, may be it user function
|
|
for (i = 0; i < UserFuncsCount; i++)
|
|
{
|
|
if (lstrcmp(pi->valbuf, UserFuncs[i].name) == 0)
|
|
{
|
|
// Function found? Redefine option specified?
|
|
if (redefine) break;
|
|
|
|
item->type = IT_FUNCTION | ITF_USER | i;
|
|
// get arguments list
|
|
ExpressionItem **newplace = ((ExpressionItem **)(&pi->item->param1));
|
|
while (*sp != _T(')'))
|
|
{
|
|
*newplace = AllocItem();
|
|
(*newplace)->type = IT_EXPRESSION;
|
|
sp++;
|
|
ParseString(sp, *((ExpressionItem **)(&(*newplace)->param1)));
|
|
newplace = &((*newplace)->next);
|
|
}
|
|
sp++; vb = pi->valbuf;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// oops, we need no item for function defenition
|
|
CleanupItems(item); pi->item = NULL;
|
|
|
|
// it's user function define
|
|
int flags = 0;
|
|
TCHAR buffer[128], *buf = buffer;
|
|
|
|
// workaround for redefine flag - if the function already present,
|
|
// it will be cleared and redefined
|
|
UserFunc *f = &UserFuncs[i];
|
|
if (i == UserFuncsCount) UserFuncsCount++;
|
|
else CleanupItems(f->root);
|
|
|
|
lstrcpy(f->name, pi->valbuf);
|
|
f->varflags = 0;
|
|
f->varsnum = 0;
|
|
do
|
|
{
|
|
sp++;
|
|
switch (*sp)
|
|
{
|
|
case _T(' '):
|
|
break;
|
|
case _T(','):
|
|
case _T(')'):
|
|
if (buf > buffer)
|
|
{
|
|
*buf = 0;
|
|
// it should be user variable
|
|
ExpressionItem *it = FindVariable(buffer);
|
|
f->vars[f->varsnum++] = (it->type) & ITEMOPTIONS;
|
|
CleanupItems(it);
|
|
buf = buffer;
|
|
flags <<= 1;
|
|
}
|
|
break;
|
|
case _T('&'):
|
|
flags |= 1;
|
|
break;
|
|
default:
|
|
*(buf++) = *sp;
|
|
break;
|
|
}
|
|
}
|
|
while (*sp != _T(')'));
|
|
|
|
// prepare flag for fast analisys
|
|
for (i = 0; i < f->varsnum; i++)
|
|
{
|
|
f->varflags <<= 1;
|
|
flags >>= 1;
|
|
f->varflags |= flags&1;
|
|
}
|
|
|
|
// find nearest round bracket - function body
|
|
while (*sp != _T('(') && *sp) sp++;
|
|
if (!*sp)
|
|
return;
|
|
sp++;
|
|
|
|
// now we are ready to parse function body
|
|
ParseString(sp, f->root);
|
|
sp++; // closing bracket
|
|
vb = pi->valbuf;
|
|
|
|
#ifdef _DEBUG
|
|
// dump function (in debug mode)
|
|
TCHAR place[1024];
|
|
wsprintf(place, _T("function %s("), f->name);
|
|
flags = f->varflags;
|
|
for (i = 0; i < f->varsnum; i++)
|
|
{
|
|
if (flags&1) lstrcat(place, _T("&"));
|
|
lstrcat(place, UserVars[f->vars[i]].name);
|
|
if (i < f->varsnum-1) lstrcat(place, _T(", "));
|
|
flags >>= 1;
|
|
}
|
|
lstrcat(place, _T(")"));
|
|
PrintTree(f->root, place);
|
|
#endif
|
|
}
|
|
|
|
// operator options
|
|
#define PO_UNARYPRE 0x1 // this operator can be uniary pre (--a) for ex
|
|
#define PO_UNARYPOST 0x2 // this op can be uniary post (a++) (couldn't be binary)
|
|
#define PO_PRENONCONST 0x4 // pre argument (a = b) -> a is non const
|
|
#define PO_POSTNONCONST 0x8 // post argument (b--) is non const
|
|
#define PO_LASTOP 0x10 // op should be the last item at expression (=, -=, etc)
|
|
#define PO_SET 0x20 // op will set new value to one of args
|
|
#define PO_USESPRE 0x40 // operator will use pre operand
|
|
#define PO_USESPOST 0x80 // operator will use post operan
|
|
|
|
void PlaceOp(TCHAR *&vb, int type, int precedence, ParseInfo *pi)
|
|
{
|
|
PlaceVariable(vb, pi);
|
|
if ((type & PO_UNARYPRE) && (!pi->item))
|
|
{
|
|
// uniary pre op
|
|
ExpressionItem *item = AllocItem();
|
|
item->type = type;
|
|
item->param2 = (EIPARAM) precedence;
|
|
item->next = pi->OpsStack;
|
|
pi->OpsStack = item;
|
|
}
|
|
else
|
|
{
|
|
// get previous tree as items and operators of lower precedence
|
|
PlaceNewItem(vb, pi, precedence);
|
|
// post operators
|
|
ExpressionItem *item = AllocItem();
|
|
item->type = type;
|
|
item->param1 = (EIPARAM) (*pi->root);
|
|
|
|
if (*pi->place == *pi->root) *pi->place = *pi->root = NULL;
|
|
else *pi->root = NULL;
|
|
|
|
if (type & PO_UNARYPOST)
|
|
{
|
|
// uniary post op
|
|
pi->item = item;
|
|
} else
|
|
{
|
|
// binary operator
|
|
item->param2 = (EIPARAM) precedence;
|
|
item->next = pi->OpsStack;
|
|
pi->OpsStack = item;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define OPSNUM 35
|
|
const OpStruct Operators[OPSNUM] =
|
|
{
|
|
// three byte ops
|
|
{{_T('>'),_T('>'),_T('=')}, 14, ITO_SHR | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
|
|
{{_T('<'),_T('<'),_T('=')}, 14, ITO_SHL | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
|
|
|
|
// two byte ops
|
|
// !!! don't forget to change Set Operator Precedence !!!
|
|
{_T("-="), 14, ITO_MINUS | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
|
|
{_T("+="), 14, ITO_PLUS | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
|
|
{_T("/="), 14, ITO_DIV | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
|
|
{_T("*="), 14, ITO_MUL | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
|
|
{_T("|="), 14, ITO_OR | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
|
|
{_T("&="), 14, ITO_AND | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
|
|
{_T("^="), 14, ITO_XOR | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
|
|
{_T("%="), 14, ITO_MOD | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPRE | PO_USESPOST},
|
|
{_T("--"), 2, ITO_DEC | PO_POSTNONCONST | PO_PRENONCONST | PO_UNARYPRE | PO_UNARYPOST | PO_SET | PO_USESPRE | PO_USESPOST},
|
|
{_T("++"), 2, ITO_INC | PO_POSTNONCONST | PO_PRENONCONST | PO_UNARYPRE | PO_UNARYPOST | PO_SET | PO_USESPRE | PO_USESPOST},
|
|
{_T(">>"), 6, ITO_SHR | PO_USESPRE | PO_USESPOST},
|
|
{_T("<<"), 6, ITO_SHL | PO_USESPRE | PO_USESPOST},
|
|
|
|
// logical
|
|
{_T("&&"), 12, ITO_LAND | PO_USESPRE | PO_USESPOST},
|
|
{_T("||"), 13, ITO_LOR | PO_USESPRE | PO_USESPOST},
|
|
|
|
// comparisons
|
|
{_T("<="), 7, ITO_LE | PO_USESPRE | PO_USESPOST},
|
|
{_T("=<"), 7, ITO_LE | PO_USESPRE | PO_USESPOST},
|
|
{_T(">="), 7, ITO_GE | PO_USESPRE | PO_USESPOST},
|
|
{_T("=>"), 7, ITO_GE | PO_USESPRE | PO_USESPOST},
|
|
{_T("!="), 8, ITO_NE | PO_USESPRE | PO_USESPOST},
|
|
{_T("=="), 8, ITO_EQ | PO_USESPRE | PO_USESPOST},
|
|
|
|
// single byte ops
|
|
// !!! don't forget to change Set Operator Precedence !!!
|
|
{_T("="), 14, ITO_SET | PO_PRENONCONST | PO_LASTOP | PO_SET | PO_USESPOST},
|
|
{_T("+"), 5, ITO_PLUS | PO_USESPRE | PO_USESPOST},
|
|
{_T("-"), 5, ITO_MINUS | PO_USESPRE | PO_USESPOST | PO_UNARYPRE},
|
|
{_T("*"), 4, ITO_MUL | PO_USESPRE | PO_USESPOST | PO_UNARYPRE},
|
|
{_T("/"), 4, ITO_DIV | PO_USESPRE | PO_USESPOST},
|
|
{_T("%"), 4, ITO_MOD | PO_USESPRE | PO_USESPOST},
|
|
{_T("<"), 7, ITO_LS | PO_USESPRE | PO_USESPOST},
|
|
{_T(">"), 7, ITO_GR | PO_USESPRE | PO_USESPOST},
|
|
{_T("&"), 9, ITO_AND | PO_USESPRE | PO_USESPOST | PO_UNARYPRE},
|
|
{_T("|"), 11, ITO_OR | PO_USESPRE | PO_USESPOST},
|
|
{_T("^"), 10, ITO_XOR | PO_USESPRE | PO_USESPOST},
|
|
{_T("~"), 3, ITO_NOT | PO_USESPOST | PO_UNARYPRE},
|
|
{_T("!"), 3, ITO_LNOT |PO_USESPOST | PO_UNARYPRE}
|
|
};
|
|
|
|
void CheckForOperator(TCHAR *&sp, TCHAR *&vb, ParseInfo *pi)
|
|
{
|
|
for (int op = 0; op < OPSNUM; op++)
|
|
{
|
|
int c = lstrlen(Operators[op].name);
|
|
if (c > 3) c = 3; // real operator length
|
|
if (lstrcmpn(sp, Operators[op].name, c))
|
|
{
|
|
// wrong - different op
|
|
continue;
|
|
}
|
|
// that is our op
|
|
sp += c;
|
|
PlaceOp(vb, ((int) Operators[op].type) | IT_OPERATOR, Operators[op].precedence, pi);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ParseString(TCHAR *&sp, ExpressionItem* &itemplace)
|
|
{
|
|
ParseInfo pi = {0, NULL, NULL, &itemplace, &itemplace};
|
|
|
|
int redefine = 0;
|
|
TCHAR *vb = pi.valbuf;
|
|
// cycle until current expression end
|
|
while ((*sp != 0) && (*sp != _T(')')) && (*sp != _T('}')) &&
|
|
(*sp != _T(']')) && (*sp != _T(',')))
|
|
{
|
|
int processed = 1;
|
|
switch (*sp)
|
|
{
|
|
case _T(' '):
|
|
case _T('\t'):
|
|
sp++;
|
|
break;
|
|
case _T(';'):
|
|
// expression delimeter
|
|
PlaceNewItem(vb, &pi, 255);
|
|
if (*pi.root) pi.SetupNewRoot = 1;
|
|
sp++;
|
|
break;
|
|
case _T('0'): case _T('1'): case _T('2'): case _T('3'): case _T('4'):
|
|
case _T('5'): case _T('6'): case _T('7'): case _T('8'): case _T('9'):
|
|
// variable & function names could contain numbers as non first chars
|
|
if (vb > pi.valbuf)
|
|
{
|
|
processed = FALSE;
|
|
break;
|
|
}
|
|
case _T('\''): case _T('\"'): case _T('`'):
|
|
// constant meet
|
|
pi.item = AllocItem();
|
|
StringToItem(sp, pi.item, STI_STRING | STI_FLOAT | STI_INT);
|
|
break;
|
|
|
|
case _T('('): // start of function or expression
|
|
if (vb > pi.valbuf)
|
|
{
|
|
// thats function
|
|
PlaceFunction(vb, sp, &pi, redefine);
|
|
} else
|
|
{
|
|
// expression
|
|
sp++;
|
|
ParseString(sp, pi.item);
|
|
if (*sp == _T(')')) sp++;
|
|
}
|
|
redefine = 0;
|
|
break;
|
|
|
|
case _T('#'): // start of one of logical expresions
|
|
sp++;
|
|
if ((*sp != _T('[')) && (*sp != _T('{')))
|
|
{
|
|
// function redefine flag
|
|
redefine = 1;
|
|
break;
|
|
}
|
|
{
|
|
pi.item = AllocItem();
|
|
// IF or WHILE
|
|
pi.item->type = ((*sp == _T('['))?(IT_LOGIC | ITL_IF):(IT_LOGIC | ITL_WHILE));
|
|
// first expr - logic statement
|
|
sp++;
|
|
|
|
ParseString(sp, *((ExpressionItem **)(&pi.item->param1)));
|
|
// ok, second expr - then, third - else statement.. others???
|
|
ExpressionItem **newplace = ((ExpressionItem **)(&pi.item->param2));
|
|
while (*sp == _T(','))
|
|
{
|
|
*newplace = AllocItem();
|
|
(*newplace)->type = IT_EXPRESSION;
|
|
sp++;
|
|
ParseString(sp, *((ExpressionItem **)(&(*newplace)->param1)));
|
|
newplace = &((*newplace)->next);
|
|
}
|
|
}
|
|
sp++;
|
|
break;
|
|
|
|
case _T('['):
|
|
{
|
|
// thats array access
|
|
PlaceOp(vb, IT_ARRAY | ITA_ACCESS | PO_UNARYPOST, 1, &pi);
|
|
sp++;
|
|
// item index
|
|
ParseString(sp, *(ExpressionItem **)&(pi.item->param2));
|
|
if (*sp == _T(','))
|
|
{
|
|
// two indexes - string access
|
|
ExpressionItem *it = AllocItem();
|
|
it->type = IT_EXPRESSION;
|
|
it->param1 = (EIPARAM) *(ExpressionItem **)&(pi.item->param2);
|
|
*(ExpressionItem **)&(pi.item->param2) = it;
|
|
it = it->next = AllocItem();
|
|
it->type = IT_EXPRESSION;
|
|
sp++;
|
|
ParseString(sp, *((ExpressionItem **)(&it->param1)));
|
|
}
|
|
sp++;
|
|
}
|
|
break;
|
|
|
|
case _T('{'): // start of array define
|
|
{
|
|
// array define - constists of array copy operator and array define itself
|
|
|
|
// type conversion item (to create a copy of array)
|
|
pi.item = AllocItem();
|
|
pi.item->type = IT_FUNCTION | ITF_TYPE | ITFT_CARRAY_ID | ITFA_COPY;
|
|
|
|
// during first create our array descriptor and array pointers
|
|
ExpressionItem *ai = AllocArray(DEFAULT_ARRAY_SIZE);
|
|
pi.item->param1 = (EIPARAM) ai;
|
|
ArrayDesc *ad = *((ArrayDesc**)&(ai->param1));
|
|
|
|
// parse array initializers
|
|
while (*sp != _T('}'))
|
|
{
|
|
sp++;
|
|
ParseString(sp, ad->array[ad->count]);
|
|
if (ad->array[ad->count])
|
|
ad->count++;
|
|
}
|
|
|
|
sp++;
|
|
}
|
|
break;
|
|
case _T('-'): case _T('+'): case _T('<'): case _T('='): case _T('>'):
|
|
case _T('/'): case _T('*'): case _T('~'): case _T('^'): case _T('!'):
|
|
case _T('&'): case _T('|'): case _T('%'):
|
|
CheckForOperator(sp, vb, &pi);
|
|
break;
|
|
|
|
// non expression? ok, then it should be common char, like function or var name
|
|
default:
|
|
processed = FALSE;
|
|
break;
|
|
}
|
|
if (!processed) *(vb++) = *(sp++);
|
|
}
|
|
PlaceNewItem(vb, &pi, 255);
|
|
}
|
|
|
|
void CleanupArray(ArrayDesc *ad)
|
|
{
|
|
if (!(--(ad->references)))
|
|
{
|
|
// last array reference, we could kill it
|
|
// cleanup array items
|
|
for (int i = 0; i < ad->count; i++)
|
|
{
|
|
ExpressionItem *aritem = ad->array[i];
|
|
if (aritem)
|
|
CleanupItems(aritem);
|
|
}
|
|
// cleanup ptrs and descriptor
|
|
dbgGlobalFree(ad->array);
|
|
dbgGlobalFree(ad);
|
|
}
|
|
}
|
|
|
|
void CleanupItems(ExpressionItem* &itemplace)
|
|
{
|
|
if (itemplace == NULL) return;
|
|
ExpressionItem *item = itemplace, *itemnext;
|
|
do
|
|
{
|
|
if (((item->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_VARIABLE|ITV_ARRITEM))
|
|
||
|
|
((item->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_ARRAY)))
|
|
{
|
|
CleanupArray((ArrayDesc *)item->param1);
|
|
}
|
|
else
|
|
if ((item->type & ITEMTYPE) == IT_CONST)
|
|
{
|
|
if ((item->type & ITEMSUBTYPE) == ITC_STRING)
|
|
dbgGlobalFree((HGLOBAL) item->param1);
|
|
} else
|
|
{
|
|
CleanupItems(*((ExpressionItem**) &item->param1));
|
|
CleanupItems(*((ExpressionItem**) &item->param2));
|
|
}
|
|
// free the item itself
|
|
itemnext = item->next;
|
|
dbgGlobalFree((HGLOBAL) item);
|
|
item = itemnext;
|
|
} while (item != NULL);
|
|
itemplace = NULL;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
HANDLE myfile;
|
|
|
|
TCHAR *opsnames[] = {_T(""), _T("-"), _T("+"), _T("<<"), _T(">>"), _T("*"), _T("/"), _T("="), _T("&&"), _T("||"), _T("++"), _T("--"),
|
|
_T("=<"), _T(">="), _T("!="), _T("=="), _T("<"), _T(">"), _T("&"), _T("%"), _T("|"), _T("^"), _T("~"), _T("!")};
|
|
|
|
void PrintNode(int index, int spaces, ExpressionItem* itemplace)
|
|
{
|
|
if (itemplace == NULL) return;
|
|
|
|
ExpressionItem *item = itemplace;
|
|
do
|
|
{
|
|
DWORD wrote;
|
|
TCHAR buffer[1024], *place = buffer;
|
|
for (int k = 0; k < spaces; k++)
|
|
*(place++) = 32;
|
|
*place = 0;
|
|
|
|
switch (item->type & ITEMTYPE)
|
|
{
|
|
case IT_EXPRESSION:
|
|
wsprintf(place, _T("Expression Place-Holder "));
|
|
break;
|
|
case IT_CONST:
|
|
switch (item->type & ITEMSUBTYPE)
|
|
{
|
|
case ITC_STRING:
|
|
wsprintf(place, _T("String: \"%s\""), (TCHAR *) item->param1);
|
|
break;
|
|
case ITC_INT:
|
|
wsprintf(place, _T("Int: "));
|
|
itoa64(*((__int64*)&(item->param1)), place+5);
|
|
break;
|
|
case ITC_FLOAT:
|
|
wsprintf(place, _T("Float: "));
|
|
FloatFormat(place+7, *((double*)&(item->param1)), 6);
|
|
break;
|
|
case ITC_ARRAY:
|
|
ArrayDesc *ad = (ArrayDesc*) item->param1;
|
|
wsprintf(place, _T("Array, ptr %08X, size %d, count %d, references %d"),
|
|
ad->array, ad->size, ad->count, ad->references);
|
|
break;
|
|
}
|
|
_tcscat(place, _T(" "));
|
|
break;
|
|
case IT_OPERATOR:
|
|
wsprintf(place, _T("Op: %s%s "), opsnames[(item->type & ITEMSUBTYPE) >> 8], (item->type & PO_SET)?(_T("(=)")):(_T("")));
|
|
break;
|
|
case IT_VARIABLE:
|
|
switch (item->type & ITEMSUBTYPE)
|
|
{
|
|
case ITV_NSIS:
|
|
{
|
|
TCHAR buffer[128];
|
|
buffer[0] = NSISVariablesNames[item->type & ITEMOPTIONS][0];
|
|
buffer[1] = NSISVariablesNames[item->type & ITEMOPTIONS][1];
|
|
buffer[2] = 0;
|
|
wsprintf(place, _T("Var: %s (%d) "),
|
|
buffer,
|
|
item->type & ITEMOPTIONS);
|
|
}
|
|
break;
|
|
case ITV_USER:
|
|
wsprintf(place, _T("Var: %s (%d) "), UserVars[item->type & ITEMOPTIONS].name, item->type & ITEMOPTIONS);
|
|
break;
|
|
case ITV_STACK:
|
|
wsprintf(place, _T("Plugin Stack "));
|
|
break;
|
|
case ITV_NSTACK:
|
|
wsprintf(place, _T("NSIS Stack "));
|
|
break;
|
|
}
|
|
break;
|
|
case IT_LOGIC:
|
|
if ((item->type & ITEMSUBTYPE) == ITL_IF)
|
|
wsprintf(place, _T("IF "));
|
|
else
|
|
wsprintf(place, _T("WHILE "));
|
|
break;
|
|
case IT_FUNCTION:
|
|
if (((item->type & ITEMSUBTYPE) == ITF_MATH1) ||
|
|
((item->type & ITEMSUBTYPE) == ITF_MATH2) ||
|
|
((item->type & ITEMSUBTYPE) == ITF_TYPE))
|
|
{
|
|
TCHAR buffer[128];
|
|
buffer[0] = (MathFunctions[item->type &ITEMOPTIONS].name)[0];
|
|
buffer[1] = (MathFunctions[item->type &ITEMOPTIONS].name)[1];
|
|
buffer[2] = (MathFunctions[item->type &ITEMOPTIONS].name)[2];
|
|
buffer[3] = 0;
|
|
wsprintf(place, _T("Built-In Function %s() [%d] "),
|
|
buffer,
|
|
item->type &ITEMOPTIONS);
|
|
}
|
|
else
|
|
{
|
|
UserFunc *f = &(UserFuncs[item->type & ITEMOPTIONS]);
|
|
wsprintf(place, _T("User Function: %s("), f->name);
|
|
int flags = f->varflags;
|
|
for (int i = 0; i < f->varsnum; i++)
|
|
{
|
|
if (flags&1) lstrcat(place, _T("&"));
|
|
lstrcat(place, UserVars[f->vars[i]].name);
|
|
if (i < f->varsnum-1) lstrcat(place, _T(", "));
|
|
flags >>= 1;
|
|
}
|
|
lstrcat(place, _T(") "));
|
|
}
|
|
break;
|
|
case IT_ARRAY:
|
|
wsprintf(place, _T("Array access [] "));
|
|
break;
|
|
}
|
|
place += lstrlen(place);
|
|
wsprintf(place, _T("Addr: %08X Type: %08X Next: %08X Param1: %08X Param2: %08X"), item, item->type, item->next, item->param1, item->param2);
|
|
lstrcat(place, _T("\n"));
|
|
WriteFile(myfile, buffer, lstrlen(buffer), &wrote, NULL);
|
|
if (((item->type & ITEMTYPE) != IT_CONST) && ((item->type & (ITEMTYPE|ITEMSUBTYPE)) != (IT_VARIABLE|ITV_ARRITEM)))
|
|
{
|
|
place = buffer;
|
|
for (int k = 0; k < spaces+2; k++)
|
|
*(place++) = 32;
|
|
int show = 0;
|
|
if (((item->param1 != NULL) && ((*((ExpressionItem**) &item->param1))->next != NULL)) ||
|
|
((item->param2 != NULL) && ((*((ExpressionItem**) &item->param2))->next != NULL)))
|
|
show = 1;
|
|
if (show)
|
|
{
|
|
wsprintf(place, _T("Sub1:\n"));
|
|
WriteFile(myfile, buffer, lstrlen(buffer), &wrote, NULL);
|
|
}
|
|
PrintNode(1, spaces + 4, *((ExpressionItem**) &item->param1));
|
|
if (show)
|
|
{
|
|
wsprintf(place, _T("Sub2:\n"));
|
|
WriteFile(myfile, buffer, lstrlen(buffer), &wrote, NULL);
|
|
}
|
|
PrintNode(2, spaces + 4, *((ExpressionItem**) &item->param2));
|
|
} else if ((item->type & (ITEMSUBTYPE|ITEMTYPE)) == (ITC_ARRAY|IT_CONST))
|
|
{
|
|
ArrayDesc *ad = (ArrayDesc *) item->param1;
|
|
for (int i = 0; i < ad->count; i++)
|
|
{
|
|
ExpressionItem *aritem = ad->array[i];
|
|
if (aritem)
|
|
PrintNode(2, spaces + 4, aritem);
|
|
}
|
|
}
|
|
item = item->next;
|
|
} while (item != NULL);
|
|
}
|
|
|
|
void PrintTree(ExpressionItem *root, TCHAR *str)
|
|
{
|
|
myfile = CreateFile(_T("d:\\math.debug"), GENERIC_ALL, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, 0);
|
|
SetFilePointer(myfile, 0, NULL, FILE_END);
|
|
TCHAR buffer[1024]; DWORD wrote;
|
|
wsprintf(buffer, _T("New tree for \'%s\'\n"), str);
|
|
WriteFile(myfile, buffer, lstrlen(buffer), &wrote, NULL);
|
|
|
|
PrintNode(0, 4, root);
|
|
CloseHandle(myfile);
|
|
myfile = NULL;
|
|
}
|
|
#endif
|
|
|
|
void CopyArray(ExpressionItem *&item)
|
|
{
|
|
if (item == NULL) return;
|
|
// especial case - array to array conversion is requested array copy
|
|
ExpressionItem *olditem = item;
|
|
ArrayDesc *oad = (ArrayDesc *) (olditem->param1);
|
|
// Initialize the array of the same size
|
|
item = AllocArray(oad->size);
|
|
ArrayDesc *nad = (ArrayDesc *) (item->param1);
|
|
nad->count = oad->count;
|
|
// copy items
|
|
for (int i = 0; i < oad->count; i++)
|
|
nad->array[i] = CopyItem(oad->array[i], TRUE);
|
|
// cleanup old array pointer (may be array itself)
|
|
CleanupItems(olditem);
|
|
}
|
|
|
|
void ItemToType(ExpressionItem* &item, int type)
|
|
{
|
|
TCHAR *buffer, *bp;
|
|
if (item == NULL) return;
|
|
int itemt = item->type & ITEMTYPE, oldtype = item->type & ITEMSUBTYPE;
|
|
|
|
if (((itemt == IT_CONST) && (oldtype == type))
|
|
|| (itemt != IT_CONST)) return;
|
|
|
|
switch (type)
|
|
{
|
|
case ITC_STRING:
|
|
buffer = AllocString();
|
|
ItemToString(buffer, item);
|
|
item->param1 = (EIPARAM) buffer;
|
|
item->param2 = 0;
|
|
break;
|
|
case ITC_FLOAT:
|
|
if (oldtype == ITC_INT)
|
|
*((double *)&(item->param1)) = (double) *((__int64 *)&(item->param1));
|
|
else
|
|
{
|
|
bp = buffer = (TCHAR*) item->param1;
|
|
StringToItem(buffer, item, STI_FLOAT);
|
|
dbgGlobalFree(bp);
|
|
}
|
|
break;
|
|
case ITC_INT:
|
|
if (oldtype == ITC_FLOAT)
|
|
*((__int64 *)&(item->param1)) = (__int64) *((double *)&(item->param1));
|
|
else
|
|
{
|
|
bp = buffer = (TCHAR*) item->param1;
|
|
StringToItem(buffer, item, STI_INT);
|
|
dbgGlobalFree(bp);
|
|
}
|
|
break;
|
|
case ITC_ARRAY:
|
|
if (oldtype == ITC_STRING)
|
|
{
|
|
TCHAR *buf = (TCHAR*) item->param1;
|
|
int len = lstrlen(buf)+1;
|
|
ExpressionItem *ni = AllocArray(lstrlen(buf)+1);
|
|
ArrayDesc *ad = (ArrayDesc*) ni->param1;
|
|
for (int i = 0; i < len; i++)
|
|
{
|
|
ad->array[i] = AllocItem();
|
|
*((__int64 *) &(ad->array[i]->param1)) = (__int64) buf[i];
|
|
}
|
|
ad->count = len;
|
|
CleanupItems(item);
|
|
item = ni;
|
|
}
|
|
break;
|
|
}
|
|
item->type = IT_CONST | type;
|
|
}
|
|
|
|
void SaveResult(ExpressionItem *var, ExpressionItem *result)
|
|
{
|
|
if ((var->type & ITEMTYPE) != IT_VARIABLE) return;
|
|
|
|
// result should be stored at variable to
|
|
int varindex = var->type & ITEMOPTIONS;
|
|
switch (var->type & ITEMSUBTYPE)
|
|
{
|
|
case ITV_NSIS:
|
|
{
|
|
// store string result direct to NSIS variable
|
|
TCHAR *ptr = g_variables + varindex*g_stringsize;
|
|
ItemToString(ptr, result);
|
|
}
|
|
break;
|
|
case ITV_USER:
|
|
{
|
|
CleanupItems(UserVars[varindex].item);
|
|
UserVars[varindex].item = CopyItem(result);
|
|
}
|
|
break;
|
|
case ITV_ARRITEM:
|
|
{
|
|
ExpressionItem *&ei = ((ArrayDesc*)(var->param1))->array[(int)var->param2];
|
|
CleanupItems(ei);
|
|
ei = CopyItem(result);
|
|
}
|
|
break;
|
|
case ITV_STACK:
|
|
{
|
|
ExpressionItem *newitem = CopyItem(result);
|
|
newitem->next = stack;
|
|
stack = newitem;
|
|
}
|
|
break;
|
|
case ITV_NSTACK:
|
|
{
|
|
TCHAR *buf = AllocString();
|
|
ItemToString(buf, result);
|
|
pushstring(buf);
|
|
dbgGlobalFree(buf);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void RunAndGetConst(int from, ExpressionItem* &result, int type)
|
|
{
|
|
RunTree(*((ExpressionItem**)&(from)), result, type | RTO_NEEDCONST);
|
|
ItemToType(result, type);
|
|
}
|
|
|
|
void RunTree(ExpressionItem *from, ExpressionItem* &result, int options)
|
|
{
|
|
ExpressionItem *item = from;
|
|
result = NULL;
|
|
while (item != NULL)
|
|
{
|
|
CleanupItems(result);
|
|
int type = item->type & ITEMTYPE,
|
|
subtype = item->type & ITEMSUBTYPE,
|
|
ioptions = item->type & ITEMOPTIONS;
|
|
switch (type)
|
|
{
|
|
case IT_EXPRESSION:
|
|
RunTree(*((ExpressionItem**)&(item->param1)), result, options);
|
|
break;
|
|
case IT_CONST:
|
|
result = CopyItem(item);
|
|
break;
|
|
case IT_VARIABLE:
|
|
if (options & RTO_NEEDCONST)
|
|
{
|
|
// we need const result - :) is it nsis or common variable
|
|
switch (subtype)
|
|
{
|
|
case ITV_NSIS:
|
|
{
|
|
// nsis
|
|
result = AllocItem();
|
|
TCHAR *variable = getuservariable(ioptions);
|
|
StringToItem(variable, result, options);
|
|
}
|
|
break;
|
|
case ITV_USER:
|
|
{
|
|
// usual variable
|
|
if (UserVars[ioptions].item)
|
|
result = CopyItem(UserVars[ioptions].item);
|
|
else
|
|
result = AllocItem();
|
|
}
|
|
break;
|
|
case ITV_ARRITEM:
|
|
{
|
|
// array item
|
|
ExpressionItem *ei = ((ArrayDesc*)(item->param1))->array[(int)item->param2];
|
|
if (ei)
|
|
result = CopyItem(ei);
|
|
else
|
|
result = AllocItem();
|
|
}
|
|
break;
|
|
case ITV_STACK:
|
|
{
|
|
// pop from plugin stack
|
|
result = stack;
|
|
if (result == NULL) result = AllocItem();
|
|
stack = result->next;
|
|
result->next = NULL;
|
|
}
|
|
break;
|
|
case ITV_NSTACK:
|
|
{
|
|
// NSIS stack
|
|
TCHAR buffer[1024], *buf = buffer;
|
|
result = AllocItem();
|
|
popstring(buffer);
|
|
StringToItem(buf, result, options);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
// if we don't need const - we will just pass variable record
|
|
result = CopyItem(item);
|
|
break;
|
|
case IT_OPERATOR:
|
|
{
|
|
ExpressionItem *var = NULL, *item1 = NULL, *item2 = NULL;
|
|
// prepare arguments in case of SET operator
|
|
if (ioptions & PO_SET)
|
|
{
|
|
if ((item->param1) && (ioptions & PO_PRENONCONST))
|
|
{
|
|
RunTree(*((ExpressionItem**)&(item->param1)), var, 0);
|
|
if (ioptions & PO_USESPRE)
|
|
RunTree(var, item1, RTO_NEEDCONST | STI_INT | STI_FLOAT | STI_STRING);
|
|
} else
|
|
if ((item->param2) && (ioptions & PO_POSTNONCONST))
|
|
{
|
|
RunTree(*((ExpressionItem**)&(item->param2)), var, 0);
|
|
if (ioptions & PO_USESPOST)
|
|
RunTree(var, item2, RTO_NEEDCONST | STI_INT | STI_FLOAT | STI_STRING);
|
|
}
|
|
}
|
|
|
|
// prepare arguments in case of any operator
|
|
int needmore = 1;
|
|
if ((!item1) && (item->param1) && (ioptions & PO_USESPRE))
|
|
{
|
|
RunTree(*((ExpressionItem**)&(item->param1)), item1, RTO_NEEDCONST | STI_INT | STI_FLOAT | STI_STRING);
|
|
// logical expressions && and || can make decision on first arg basis
|
|
if ((subtype == ITO_LAND) || (subtype == ITO_LOR) )
|
|
{
|
|
ItemToType(item1, ITC_INT);
|
|
int res = (int) *((__int64*) &(item1->param1));
|
|
if (((res)&&(subtype==ITO_LOR)) || ((!res)&&(subtype==ITO_LAND)))
|
|
needmore = 0;
|
|
}
|
|
}
|
|
|
|
// get-reference operator
|
|
if ((!item1) && (subtype == ITO_AND) && (!item2) && (item->param2))
|
|
{
|
|
RunTree(*((ExpressionItem**)&(item->param2)), result, 0);
|
|
break;
|
|
}
|
|
|
|
if ((needmore) && (!item2) && (item->param2) && (ioptions & PO_USESPOST))
|
|
RunTree(*((ExpressionItem**)&(item->param2)), item2, RTO_NEEDCONST | STI_INT | STI_FLOAT | STI_STRING);
|
|
|
|
// reference operator
|
|
if ((!item1) && (subtype == ITO_MUL) && ((item2->type & ITEMTYPE) == (IT_VARIABLE)))
|
|
{
|
|
// ok, that's the result we need
|
|
if (options & RTO_NEEDCONST)
|
|
{
|
|
RunTree(item2, result, options);
|
|
CleanupItems(item2);
|
|
} else
|
|
result = item2;
|
|
break;
|
|
}
|
|
|
|
__int64 i1=0, i2=0, i3=0, i4=0;
|
|
if (((!item1)||((item1->type & ITEMTYPE)==IT_CONST)) &&
|
|
((!item2)||((item2->type & ITEMTYPE)==IT_CONST)))
|
|
{
|
|
|
|
// find the best type match for operation
|
|
int it1 = (item1 && (ioptions & PO_USESPRE))?(item1->type & ITEMSUBTYPE):(ITC_UNKNOWN),
|
|
it2 = (item2 && (ioptions & PO_USESPOST))?(item2->type & ITEMSUBTYPE):(ITC_UNKNOWN),
|
|
type = (it1 < it2)?(it1):(it2);
|
|
|
|
// convert operands to desired type
|
|
ItemToType(item1, type);
|
|
ItemToType(item2, type);
|
|
|
|
switch (type)
|
|
{
|
|
case ITC_INT:
|
|
{
|
|
i1 = (item1)?(*((__int64*)&item1->param1)):(0);
|
|
i2 = (item2)?(*((__int64*)&item2->param1)):(0);
|
|
|
|
switch (subtype)
|
|
{
|
|
case ITO_MINUS: i1 -= i2; break; // unary minus auto handled with NULL
|
|
case ITO_PLUS: i1 += i2; break;
|
|
case ITO_SHL: i1 <<= i2; break;
|
|
case ITO_SHR: i1 >>= i2; break;
|
|
case ITO_MUL: i1 *= i2; break;
|
|
case ITO_MOD:
|
|
case ITO_DIV:
|
|
if (i2 == 0) { i3 = 0; i4 = i1; }
|
|
else { i3 = i1 / i2; i4 = i1 % i2; }
|
|
if (subtype == ITO_DIV) i1 = i3; else i1 = i4;
|
|
break;
|
|
case ITO_SET: i1 = i2; break;
|
|
case ITO_LE: i1 = (i1 <= i2); break;
|
|
case ITO_GE: i1 = (i1 >= i2); break;
|
|
case ITO_NE: i1 = (i1 != i2); break;
|
|
case ITO_EQ: i1 = (i1 == i2); break;
|
|
case ITO_LS: i1 = (i1 < i2); break;
|
|
case ITO_GR: i1 = (i1 > i2); break;
|
|
case ITO_AND: i1 = (i1 & i2); break;
|
|
case ITO_OR: i1 = (i1 | i2); break;
|
|
case ITO_XOR: i1 = (i1 ^ i2); break;
|
|
case ITO_NOT: i1 = ~i2; break;
|
|
case ITO_LNOT: i1 = !i2; break;
|
|
case ITO_LAND: i1 = i1 && i2; break;
|
|
case ITO_LOR: i1 = i1 || i2; break;
|
|
case ITO_INC:
|
|
if (item1) i2 = i1++;
|
|
else i1 = ++i2;
|
|
break;
|
|
case ITO_DEC:
|
|
if (item1) i2 = i1--;
|
|
else i1 = --i2;
|
|
break;
|
|
}
|
|
result = AllocItem();
|
|
*((__int64*)&result->param1) = i1;
|
|
}
|
|
break;
|
|
case ITC_FLOAT:
|
|
{
|
|
int ir = -666;
|
|
double i1 = (item1)?(*((double*)&item1->param1)):(0.0);
|
|
double i2 = (item2)?(*((double*)&item2->param1)):(0.0);
|
|
|
|
switch (subtype)
|
|
{
|
|
case ITO_MINUS: i1 -= i2; break; // unary minus auto handled with NULL
|
|
case ITO_PLUS: i1 += i2; break;
|
|
case ITO_MUL: i1 *= i2; break;
|
|
case ITO_DIV: i1 /= i2; break;
|
|
case ITO_SET: i1 = i2; break;
|
|
case ITO_LE: ir = (i1 <= i2); break;
|
|
case ITO_GE: ir = (i1 >= i2); break;
|
|
case ITO_NE: ir = (i1 != i2); break;
|
|
case ITO_EQ: ir = (i1 == i2); break;
|
|
case ITO_LS: ir = (i1 < i2); break;
|
|
case ITO_GR: ir = (i1 > i2); break;
|
|
}
|
|
result = AllocItem();
|
|
if (ir == -666)
|
|
{
|
|
// if ir value left intact - result is double
|
|
result->type = IT_CONST | ITC_FLOAT;
|
|
*((double*)&result->param1) = i1;
|
|
} else
|
|
*((__int64*)&result->param1) = (__int64) ir;
|
|
}
|
|
break;
|
|
case ITC_STRING:
|
|
{
|
|
int ir = -666;
|
|
TCHAR *i1 = (item1)?((TCHAR*)item1->param1):(NULL);
|
|
TCHAR *i2 = (item2)?((TCHAR*)item2->param1):(NULL);
|
|
int sc = (i1 && i2)?(lstrcmp(i1, i2)):((i1)?(1):((i2)?(-1):(0)));
|
|
|
|
switch (subtype)
|
|
{
|
|
case ITO_PLUS: lstrcat(i1, i2); break;
|
|
case ITO_SET: i1 = i2; break;
|
|
case ITO_LE: ir = (sc <= 0); break;
|
|
case ITO_GE: ir = (sc >= 0); break;
|
|
case ITO_NE: ir = (sc != 0); break;
|
|
case ITO_EQ: ir = (sc == 0); break;
|
|
case ITO_LS: ir = (sc < 0); break;
|
|
case ITO_GR: ir = (sc > 0); break;
|
|
}
|
|
if (ir == -666)
|
|
{
|
|
result = CopyItem((item1)?(item1):(item2));
|
|
} else
|
|
{
|
|
result = AllocItem();
|
|
*((__int64*)&result->param1) = (__int64) ir;
|
|
}
|
|
}
|
|
break;
|
|
case ITC_ARRAY:
|
|
result = CopyItem(item2);
|
|
break;
|
|
}
|
|
|
|
} // check for both items constant
|
|
// the other case - usually UniaryPre operators working with non constants
|
|
else result = CopyItem(item2);
|
|
|
|
if (ioptions & PO_SET)
|
|
{
|
|
// Save our result in output variable
|
|
SaveResult(var, result);
|
|
}
|
|
|
|
// Actual value to be returned as result is at i2 for ++ and -- ops
|
|
if ((subtype == ITO_DEC) || (subtype == ITO_INC))
|
|
*((__int64*)&result->param1) = i2;
|
|
|
|
CleanupItems(item1); CleanupItems(item2); CleanupItems(var);
|
|
}
|
|
break;
|
|
case IT_LOGIC:
|
|
{
|
|
int ifmode = (subtype == ITL_IF);
|
|
ExpressionItem *ifbr = *((ExpressionItem**)&(item->param1)),
|
|
*dobr = *((ExpressionItem**)&(item->param2)),
|
|
*thbr = NULL, *elbr = NULL;
|
|
// check do branche for existance
|
|
if (dobr && ifmode)
|
|
{
|
|
// then...
|
|
thbr = *((ExpressionItem**)&(dobr->param1));
|
|
// ... and else branches
|
|
if (dobr->next) elbr = *((ExpressionItem**)&(dobr->next->param1));
|
|
}
|
|
while (true)
|
|
{
|
|
RunAndGetConst((int) ifbr, result, ITC_INT);
|
|
if (ifmode)
|
|
{
|
|
// we need then or else branch?
|
|
if ((*((__int64*)&result->param1))) dobr = thbr;
|
|
else dobr = elbr;
|
|
} else
|
|
{
|
|
// while mode
|
|
if (!(*((__int64*)&result->param1))) break;
|
|
}
|
|
// ok, run the approtiate branch of if statement (if available)
|
|
if (dobr)
|
|
{
|
|
CleanupItems(result);
|
|
RunTree(dobr, result, options);
|
|
}
|
|
if (ifmode) break;
|
|
CleanupItems(result);
|
|
}
|
|
}
|
|
break;
|
|
case IT_FUNCTION:
|
|
if (subtype == ITF_USER)
|
|
{
|
|
int i;
|
|
UserFunc *f = &(UserFuncs[ioptions]);
|
|
int flags = f->varflags;
|
|
ExpressionItem *ip = *((ExpressionItem**)&(item->param1));
|
|
ExpressionItem *si = AllocItem(), *var = AllocItem();
|
|
ExpressionItem *vals[32];
|
|
si->type = IT_VARIABLE | ITV_STACK;
|
|
for (i = 0; i < f->varsnum; i++)
|
|
{
|
|
// push every variable
|
|
ExpressionItem *val;
|
|
var->type = (IT_VARIABLE | ITV_USER) + f->vars[i];
|
|
RunTree(var, val, RTO_NEEDCONST | ITC_STRING | ITC_INT | ITC_FLOAT | ITC_ARRAY);
|
|
SaveResult(si, val);
|
|
CleanupItems(val);
|
|
// calculate argument value and for future
|
|
if (ip)
|
|
{
|
|
if (flags&1)
|
|
{
|
|
// var ptr required
|
|
RunTree(*((ExpressionItem**)&(ip->param1)), vals[i], 0);
|
|
} else
|
|
{
|
|
RunTree(*((ExpressionItem**)&(ip->param1)), vals[i], RTO_NEEDCONST | ITC_STRING | ITC_INT | ITC_FLOAT | ITC_ARRAY);
|
|
}
|
|
ip = ip->next;
|
|
} else vals[i] = AllocItem();
|
|
|
|
flags >>= 1;
|
|
}
|
|
|
|
// now when all values got we could save them to variables
|
|
for (i = 0; i < f->varsnum; i++)
|
|
{
|
|
var->type = (IT_VARIABLE | ITV_USER) + f->vars[i];
|
|
SaveResult(var, vals[i]);
|
|
CleanupItems(vals[i]);
|
|
}
|
|
|
|
|
|
// ok, call the func
|
|
RunTree(f->root, result, RTO_NEEDCONST | ITC_STRING | ITC_INT | ITC_FLOAT | ITC_ARRAY);
|
|
|
|
// pop original params
|
|
for (i = f->varsnum-1; i >= 0; i--)
|
|
{
|
|
// pop every variable
|
|
ExpressionItem *val;
|
|
var->type = (IT_VARIABLE | ITV_USER) + f->vars[i];
|
|
RunTree(si, val, RTO_NEEDCONST | ITC_STRING | ITC_INT | ITC_FLOAT | ITC_ARRAY);
|
|
SaveResult(var, val);
|
|
CleanupItems(val);
|
|
}
|
|
|
|
// free used items
|
|
CleanupItems(si); CleanupItems(var);
|
|
} else if (subtype == ITF_TYPE)
|
|
{
|
|
int newtype = (int) MathFunctions[ioptions].fptr;
|
|
if (newtype < ITC_UNKNOWN)
|
|
{
|
|
// get as possibly close to ready expression
|
|
RunAndGetConst((int)item->param1, result, newtype);
|
|
if (ioptions == ITFT_CARRAY_ID)
|
|
CopyArray(result);
|
|
} else if (newtype == FTT_FLOATF)
|
|
{
|
|
// float format function
|
|
ExpressionItem *arg1, *arg2;
|
|
RunAndGetConst((int)item->param1, arg1, ITC_FLOAT);
|
|
double value = *((double*)&(arg1->param1));
|
|
RunAndGetConst((int)item->param2, arg2, ITC_INT);
|
|
int format = (int) *((__int64*)&(arg2->param1));
|
|
|
|
result = AllocItem();
|
|
result->type = IT_CONST | ITC_STRING;
|
|
result->param1 = (EIPARAM) AllocString();
|
|
FloatFormat((TCHAR*) result->param1, value, format);
|
|
CleanupItems(arg1); CleanupItems(arg2);
|
|
} else if (newtype == FTT_LEN)
|
|
{
|
|
// length function
|
|
RunTree(*((ExpressionItem **) &(item->param1)), result, RTO_NEEDCONST | ITC_STRING | ITC_ARRAY);
|
|
if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_ARRAY))
|
|
{
|
|
int len = ((ArrayDesc*)(result->param1))->count;
|
|
CleanupItems(result);
|
|
result = AllocItem();
|
|
*((__int64*)&(result->param1)) = (__int64) len;
|
|
break;
|
|
} else
|
|
if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) != (IT_CONST|ITC_STRING))
|
|
ItemToType(result, ITC_STRING);
|
|
|
|
if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_STRING))
|
|
{
|
|
// ok, that's string
|
|
int len = lstrlen((TCHAR*)result->param1);
|
|
dbgGlobalFree((HGLOBAL) result->param1);
|
|
*((__int64*)&(result->param1)) = (__int64) len;
|
|
result->type = IT_CONST | ITC_INT;
|
|
} else CleanupItems(result);
|
|
} else
|
|
{
|
|
// only one left - c() - char/int/char conversion
|
|
RunTree(*((ExpressionItem **) &(item->param1)), result, RTO_NEEDCONST | ITC_STRING | ITC_INT);
|
|
if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_STRING))
|
|
{
|
|
// ok, that's string - convert first char to int
|
|
int chr = (*((TCHAR*)result->param1)) & 0xFF;
|
|
dbgGlobalFree((HGLOBAL) result->param1);
|
|
*((__int64*)&(result->param1)) = (__int64) chr;
|
|
result->type = IT_CONST | ITC_INT;
|
|
break;
|
|
}
|
|
if ((result->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST|ITC_INT))
|
|
{
|
|
// ok, that's int - convert to new string (char+0)
|
|
int chr = (int) (*((__int64*)&(result->param1))) & 0xFF;
|
|
result->param1 = (EIPARAM) AllocString();
|
|
*((TCHAR*)result->param1) = (TCHAR) chr;
|
|
*((TCHAR*)(result->param1+1)) = (TCHAR) 0;
|
|
result->type = IT_CONST | ITC_STRING;
|
|
break;
|
|
} else CleanupItems(result);
|
|
}
|
|
} else
|
|
{
|
|
// oops :-o function call :)
|
|
RunAndGetConst((int)item->param1, result, ITC_FLOAT);
|
|
double &value = *((double*)&(result->param1));
|
|
if (subtype == ITF_MATH1)
|
|
{
|
|
// Built-In math function with 1 arg
|
|
value = MathFunctions[ioptions].fptr(value);
|
|
} else
|
|
if (subtype == ITF_MATH2)
|
|
{
|
|
// Built-In math function with 2 args
|
|
if (ioptions >= MATHFUNCNUM-2)
|
|
{
|
|
// specific function - we need second arg as out
|
|
ExpressionItem *arg2, *res2 = AllocItem();
|
|
RunTree(*((ExpressionItem**)&(item->param2)), arg2, 0);
|
|
if (ioptions == MATHFUNCNUM-1)
|
|
{
|
|
// fmodf function - second arg is ptr to double
|
|
res2->type = IT_CONST | ITC_FLOAT;
|
|
double &v = *((double*)&(res2->param1));
|
|
value = ((Math2dFuncPtr)(MathFunctions[ioptions].fptr))(value, &v);
|
|
} else
|
|
{
|
|
// frexp function - second arg is ptr to int
|
|
int v = 0;
|
|
value = ((Math2iFuncPtr)(MathFunctions[ioptions].fptr))(value, &v);
|
|
*((__int64 *)&(res2->param1)) = (__int64) v;
|
|
}
|
|
SaveResult(arg2, res2);
|
|
CleanupItems(arg2); CleanupItems(res2);
|
|
} else
|
|
{
|
|
// normal 2-arg math function
|
|
ExpressionItem *arg2;
|
|
RunAndGetConst((int)item->param2, arg2, ITC_FLOAT);
|
|
double value2 = *((double*)&(arg2->param1));
|
|
value = ((Math2FuncPtr)(MathFunctions[ioptions].fptr))(value, value2);
|
|
CleanupItems(arg2);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case IT_ARRAY:
|
|
{
|
|
// currently only array access is used
|
|
ExpressionItem *index, *aritem;
|
|
RunTree(*((ExpressionItem **) &(item->param1)), aritem, RTO_NEEDCONST | ITC_STRING | ITC_ARRAY);
|
|
|
|
if ((aritem->type & (ITEMTYPE|ITEMSUBTYPE)) == (IT_CONST | ITC_STRING))
|
|
{
|
|
// argument is string
|
|
TCHAR *str = (TCHAR*)(aritem->param1);
|
|
int len = lstrlen(str);
|
|
// have we two indexes or one?
|
|
if ((*((ExpressionItem **) &(item->param2)))->type != IT_EXPRESSION)
|
|
{
|
|
// one index - user need a char
|
|
RunAndGetConst((int)item->param2, index, ITC_INT);
|
|
|
|
int pos = (int) *((__int64*)&(index->param1));
|
|
if (pos < 0) pos += len; // -index - means from end
|
|
if ((pos > len) || (pos < 0))
|
|
*str = 0; // index is accross string boundaries
|
|
else
|
|
{
|
|
// new string - just a single char
|
|
*str = *(str+pos);
|
|
*(str+1) = 0;
|
|
}
|
|
} else
|
|
{
|
|
// two indexes
|
|
ExpressionItem *index2;
|
|
// if first index is skipped -> 0 (from first char)
|
|
if ((*((ExpressionItem **) &(item->param2)))->param1 == 0)
|
|
index = AllocItem();
|
|
else
|
|
RunAndGetConst((int)(*((ExpressionItem **) &(item->param2)))->param1, index, ITC_INT);
|
|
if ((*((ExpressionItem **) &(item->param2)))->next->param1 == 0)
|
|
{
|
|
// if second index is skipped -> -1 (till last char)
|
|
index2 = AllocItem();
|
|
*((__int64*)&(index2->param1)) = -1;
|
|
}
|
|
else
|
|
RunAndGetConst((int)(*((ExpressionItem **) &(item->param2)))->next->param1, index2, ITC_INT);
|
|
|
|
// ok, we've got two indexes
|
|
int pos1 = (int) *((__int64*)&(index->param1));
|
|
int pos2 = (int) *((__int64*)&(index2->param1));
|
|
if (pos1 < 0) pos1 += len; // -index - means from end
|
|
if (pos2 < 0) pos2 += len; // -index - means from end
|
|
// limit start/stop positions
|
|
if (pos1 < 0) pos1 = 0;
|
|
if (pos2 < 0) pos2 = 0;
|
|
if (pos1 > len) pos1 = len;
|
|
if (pos2 >= len) pos2 = len-1;
|
|
|
|
// copy string part
|
|
TCHAR* lpos = str + (pos2-pos1);
|
|
while (str <= lpos)
|
|
{
|
|
*str = *(str + pos1);
|
|
str++;
|
|
}
|
|
// null-termiante
|
|
*str = 0;
|
|
|
|
CleanupItems(index2);
|
|
}
|
|
|
|
} else
|
|
{
|
|
// argument is array
|
|
RunAndGetConst((int)item->param2, index, ITC_INT);
|
|
|
|
// convert array pointer to array item pointer
|
|
aritem->type = IT_VARIABLE | ITV_ARRITEM;
|
|
aritem->param2 = (EIPARAM) *((__int64*)&(index->param1));
|
|
|
|
ArrayDesc *ad = (ArrayDesc*)aritem->param1;
|
|
if (((int)aritem->param2) >= ad->count)
|
|
{
|
|
ad->count = ((int)aritem->param2)+1;
|
|
while (ad->count > ad->size)
|
|
{
|
|
// resize array
|
|
ExpressionItem **oldei = ad->array;
|
|
ad->array = (ExpressionItem**) dbgGlobalAlloc(GPTR, 2*ad->size*sizeof(ExpressionItem*));
|
|
for (int i = 0; i < ad->size; i++)
|
|
ad->array[i] = oldei[i];
|
|
ad->size*=2;
|
|
dbgGlobalFree(oldei);
|
|
}
|
|
}
|
|
}
|
|
|
|
CleanupItems(index);
|
|
|
|
// we need constant result?
|
|
if (options & RTO_NEEDCONST)
|
|
{
|
|
RunTree(aritem, result, options);
|
|
CleanupItems(aritem);
|
|
} else result = aritem;
|
|
}
|
|
break;
|
|
}
|
|
item = item->next;
|
|
}
|
|
}
|
|
|
|
static UINT_PTR PluginCallback(enum NSPIM msg)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
HINSTANCE g_hInstance;
|
|
|
|
extern "C"
|
|
void __declspec(dllexport) Script(HWND hwndParent, int string_size,
|
|
TCHAR *variables, stack_t **stacktop,
|
|
extra_parameters *extra)
|
|
{
|
|
EXDLL_INIT();
|
|
TCHAR *buffer = AllocString(), *buf = buffer;
|
|
ExpressionItem *root = NULL; // root of current tree
|
|
|
|
// keep loaded to save user defined variables
|
|
extra->RegisterPluginCallback(g_hInstance, PluginCallback);
|
|
|
|
// pop script string
|
|
popstring(buffer);
|
|
|
|
// parse it
|
|
ParseString(buf, root);
|
|
|
|
#ifdef _DEBUG
|
|
// dump
|
|
PrintTree(root, buffer);
|
|
#endif
|
|
|
|
ExpressionItem *result;
|
|
RunTree(root, result, 0);
|
|
CleanupItems(result);
|
|
|
|
CleanupItems(root);
|
|
dbgGlobalFree((HGLOBAL) buffer);
|
|
}
|
|
|
|
double _infinity;
|
|
extern "C" void _fpreset();
|
|
|
|
void CleanAll(int init)
|
|
{
|
|
if (init)
|
|
{
|
|
unsigned char _infinity_base[8] = {0, 0, 0, 0, 0, 0, 0xf0, 0x7f};
|
|
_fltused = 0;
|
|
_infinity = *((double*)(_infinity_base));
|
|
_fpreset();
|
|
|
|
stack = NULL;
|
|
UserVarsCount = 0;
|
|
UserFuncsCount = 0;
|
|
} else
|
|
{
|
|
int i;
|
|
// cleanup stack
|
|
CleanupItems(stack); stack = NULL;
|
|
// cleanup user vars
|
|
for (i = 0; i < UserVarsCount; i++)
|
|
CleanupItems(UserVars[i].item);
|
|
// cleanup user funcs
|
|
for (i = 0; i < UserFuncsCount; i++)
|
|
CleanupItems(UserFuncs[i].root);
|
|
UserVarsCount = 0;
|
|
UserFuncsCount = 0;
|
|
|
|
dbgGlobalCheck();
|
|
}
|
|
}
|
|
|
|
BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
|
|
{
|
|
g_hInstance = hInst;
|
|
CleanAll(ul_reason_for_call == DLL_PROCESS_ATTACH);
|
|
return TRUE;
|
|
}
|
|
|