Bug Summary

File:libsynthesis/src/sysync/scriptcontext.cpp
Warning:line 4563, column 15
Value stored to 'tk' is never read

Annotated Source Code

1/*
2 * File: scriptcontext.cpp
3 *
4 * Author: Lukas Zeller (luz@plan44.ch)
5 *
6 * TScriptContext
7 * Environment to tokenize, prepare and run scripts
8 *
9 * Copyright (c) 2002-2011 by Synthesis AG + plan44.ch
10 *
11 * 2002-09-11 : luz : created
12 *
13 */
14
15#include "prefix_file.h"
16
17#ifdef SCRIPT_SUPPORT1
18
19// includes
20#include "scriptcontext.h"
21
22#include "platform_exec.h" // for SHELLEXECUTE
23#include "rrules.h" // for RECURRENCE_COUNT/DATE
24#include "vtimezone.h" // for SETTIMEZONE
25#include "mimediritemtype.h" // for AlldayCount/MakeAllday
26#ifdef REGEX_SUPPORT1
27 #include "pcre.h" // for RegEx functions
28#endif
29
30#include <stdio.h>
31#include <errno(*__errno_location ()).h>
32
33// script debug messages
34#ifdef SYDEBUG2
35 #define SCRIPTDBGMSGX(lvl,x){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((lvl) & (fSessionP)->getDbgMask()) == (lvl))) (fSessionP
)->getDbgLogger()->setNextMask(lvl).DebugPrintfLastMask
x; }; } }
{ if (debugon && fSessionP) { POBJDEBUGPRINTFX(fSessionP,lvl,x){ if ((fSessionP) && (((lvl) & (fSessionP)->getDbgMask
()) == (lvl))) (fSessionP)->getDbgLogger()->setNextMask
(lvl).DebugPrintfLastMask x; }
; } }
36 #define SCRIPTDBGMSG(x){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask x; }; } }
SCRIPTDBGMSGX(DBG_SCRIPTS,x){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask x; }; } }
37 #define SCRIPTDBGSTART(nam){ if (debugon && fSessionP) { if (fSessionP->getDbgMask
() & 0x00001000) { fSessionP->getDbgLogger()->DebugOpenBlockCollapsed
("ScriptExecute","Start executing Script","name=%s",nam); } else
{ { if ((fSessionP) && (((0x00000080) & (fSessionP
)->getDbgMask()) == (0x00000080))) (fSessionP)->getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ("Executing Script '%s'"
,nam); }; } } }
{ if (debugon && fSessionP) { if (fSessionP->getDbgMask() & DBG_SCRIPTS0x00001000) { fSessionP->PDEBUGBLOCKFMTCOLL(("ScriptExecute","Start executing Script","name=%s",nam))getDbgLogger()->DebugOpenBlockCollapsed ("ScriptExecute","Start executing Script"
,"name=%s",nam)
; } else { POBJDEBUGPRINTFX(fSessionP,DBG_DATA,("Executing Script '%s'",nam)){ if ((fSessionP) && (((0x00000080) & (fSessionP)
->getDbgMask()) == (0x00000080))) (fSessionP)->getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ("Executing Script '%s'"
,nam); }
; } } }
38 #define SCRIPTDBGEND(){ if (debugon && fSessionP && (fSessionP->
getDbgMask() & 0x00001000)) { fSessionP->getDbgLogger(
)->DebugCloseBlock( "ScriptExecute"); } }
{ if (debugon && fSessionP && (fSessionP->getDbgMask() & DBG_SCRIPTS0x00001000)) { fSessionP->PDEBUGENDBLOCK("ScriptExecute")getDbgLogger()->DebugCloseBlock( "ScriptExecute"); } }
39 #define SCRIPTDBGTEST(debugon && fSessionP && (fSessionP->getDbgMask
() & 0x00001000))
(debugon && fSessionP && (fSessionP->getDbgMask() & DBG_SCRIPTS0x00001000))
40 #define EXPRDBGTEST(debugon && fSessionP && ((fSessionP->getDbgMask
() & (0x00001000|0x00000800)) == (0x00001000|0x00000800))
)
(debugon && fSessionP && ((fSessionP->getDbgMask() & (DBG_SCRIPTS0x00001000|DBG_SCRIPTEXPR0x00000800)) == (DBG_SCRIPTS0x00001000|DBG_SCRIPTEXPR0x00000800)))
41 #define DBGSTRINGDEF(s)string s string s
42 #define DBGVALUESHOW(s,v)dbgValueShow(s,v) dbgValueShow(s,v)
43 #if SYDEBUG2>1
44 #define SHOWVARDEFS(t)showVarDefs(t) showVarDefs(t)
45 #else
46 #define SHOWVARDEFS(t)showVarDefs(t)
47 #endif
48#else
49 #define SCRIPTDBGMSGX(lvl,x){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((lvl) & (fSessionP)->getDbgMask()) == (lvl))) (fSessionP
)->getDbgLogger()->setNextMask(lvl).DebugPrintfLastMask
x; }; } }
50 #define SCRIPTDBGMSG(x){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask x; }; } }
51 #define SCRIPTDBGSTART(nam){ if (debugon && fSessionP) { if (fSessionP->getDbgMask
() & 0x00001000) { fSessionP->getDbgLogger()->DebugOpenBlockCollapsed
("ScriptExecute","Start executing Script","name=%s",nam); } else
{ { if ((fSessionP) && (((0x00000080) & (fSessionP
)->getDbgMask()) == (0x00000080))) (fSessionP)->getDbgLogger
()->setNextMask(0x00000080).DebugPrintfLastMask ("Executing Script '%s'"
,nam); }; } } }
52 #define SCRIPTDBGEND(){ if (debugon && fSessionP && (fSessionP->
getDbgMask() & 0x00001000)) { fSessionP->getDbgLogger(
)->DebugCloseBlock( "ScriptExecute"); } }
53 #define SCRIPTDBGTEST(debugon && fSessionP && (fSessionP->getDbgMask
() & 0x00001000))
false
54 #define EXPRDBGTEST(debugon && fSessionP && ((fSessionP->getDbgMask
() & (0x00001000|0x00000800)) == (0x00001000|0x00000800))
)
false
55 #define DBGSTRINGDEF(s)string s
56 #define DBGVALUESHOW(s,v)dbgValueShow(s,v)
57 #define SHOWVARDEFS(t)showVarDefs(t)
58#endif
59
60
61namespace sysync {
62
63#ifdef SYDEBUG2
64
65// show value of a field
66static void dbgValueShow(string &aString, TItemField *aFieldP)
67{
68 TItemFieldTypes ty;
69 if (aFieldP) {
70 // value
71 aString = "&html;<span class=\"value\">&html;";
72 aFieldP->StringObjFieldAppend(aString,200);
73 aString += "&html;</span>&html;";
74 // add type info
75 ty=aFieldP->getType();
76 aString += " (";
77 aString += ItemFieldTypeNames[ty];
78 aString += ")";
79 }
80 else {
81 aString="<NO FIELD>";
82 }
83} // dbgValueShow
84
85#endif
86
87
88TTokenizeException::TTokenizeException(cAppCharP aScriptName, cAppCharP aMsg1,cAppCharP aScript, uInt16 aIndex, uInt16 aLine)
89 : TConfigParseException("")
90{
91 cAppCharP p2=aScript+aIndex;
92 cAppCharP p1=p2-(aIndex>5 ? 5 : aIndex);
93
94 StringObjPrintf(fMessage,
95 "%s: %s at line %hd: '...%-.5s_%-.10s...'",
96 aScriptName ? aScriptName : "<unnamed script>",
97 aMsg1,
98 aLine,
99 p1,
100 p2
101 );
102} // TTokenizeException::TTokenizeException
103
104
105TScriptErrorException::TScriptErrorException(cAppCharP aMsg1, uInt16 aLine, cAppCharP aIdent)
106 : TConfigParseException("")
107{
108 if (aIdent) {
109 StringObjPrintf(fMessage,aMsg1,aIdent);
110 }
111 else {
112 StringObjPrintf(fMessage,"%s",aMsg1);
113 }
114 StringObjAppendPrintf(
115 fMessage,
116 " in script at line %hd",
117 aLine
118 );
119} // TScriptErrorException::TScriptErrorException
120
121
122
123/*
124 * Implementation of TScriptConfig
125 */
126
127
128// config constructor
129TScriptConfig::TScriptConfig(TConfigElement *aParentElementP) :
130 TConfigElement("scripting",aParentElementP)
131{
132 clear();
133} // TScriptConfig::TScriptConfig
134
135
136// config destructor
137TScriptConfig::~TScriptConfig()
138{
139 clear();
140} // TScriptConfig::~TScriptConfig
141
142
143// init defaults
144void TScriptConfig::clear(void)
145{
146 // delete options
147 fMaxLoopProcessingTime =
148 #if SYDEBUG2>1
149 60; // 1 min for debugging
150 #else
151 5; // 5 seconds for real execution
152 #endif
153 // delete functions
154 TUserScriptList::iterator pos;
155 for (pos=fFunctionScripts.begin();pos!=fFunctionScripts.end();++pos) {
156 delete (*pos);
157 }
158 fFunctionScripts.clear();
159 fScriptMacros.clear();
160 // clear inherited
161 inherited::clear();
162} // TScriptConfig::clear
163
164
165// server config element parsing
166bool TScriptConfig::localStartElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
167{
168 // checking the elements
169 if (strucmp(aElementName,"looptimeout")==0)
170 expectUInt32(fMaxLoopProcessingTime);
171 else if (strucmp(aElementName,"function")==0) {
172 // create new function definition
173 TUserScriptFunction *funcdefP = new TUserScriptFunction;
174 fFunctionScripts.push_back(funcdefP);
175 expectFunction(*funcdefP,aLine);
176 }
177 else if (strucmp(aElementName,"macro")==0) {
178 const char *macroname = getAttr(aAttributes,"name");
179 if (!macroname)
180 fail("<macro> must have a 'name' attribute");
181 // create new macro
182 expectRawString(fScriptMacros[macroname]);
183 }
184 // - none known here
185 else
186 return inherited::localStartElement(aElementName,aAttributes,aLine);
187 // ok
188 return true;
189} // TScriptConfig::localStartElement
190
191
192// resolve
193void TScriptConfig::localResolve(bool aLastPass)
194{
195 // resolve inherited
196 inherited::localResolve(aLastPass);
197} // TScriptConfig::localResolve
198
199
200// get script text
201string *TScriptConfig::getFunctionScript(sInt16 aFuncIndex)
202{
203 if (aFuncIndex<0 || aFuncIndex>(sInt16)fFunctionScripts.size())
204 return NULL__null;
205 return &(fFunctionScripts[aFuncIndex]->fFuncDef);
206} // TScriptConfig::getFunctionScript
207
208
209// get index of specific function
210sInt16 TScriptConfig::getFunctionIndex(cAppCharP aName, size_t aLen)
211{
212 TUserScriptList::iterator pos;
213 sInt16 i=0;
214 for (pos=fFunctionScripts.begin();pos!=fFunctionScripts.end();++pos) {
215 if (strucmp((*pos)->fFuncName.c_str(),aName)==0)
216 return i;
217 i++;
218 }
219 // unknown
220 return VARIDX_UNDEFINED-128;
221} // TScriptConfig::getFunctionIndex
222
223
224// Script variable definition
225
226// create new scrip variable definition
227TScriptVarDef::TScriptVarDef(cAppCharP aName,uInt16 aIdx, TItemFieldTypes aType, bool aIsArray, bool aIsRef, bool aIsOpt)
228{
229 fVarName=aName;
230 fIdx=aIdx;
231 fVarType=aType;
232 fIsArray=aIsArray;
233 fIsRef=aIsRef;
234 fIsOpt=aIsOpt;
235} // TScriptVarDef::TScriptVarDef
236
237
238TScriptVarDef::~TScriptVarDef()
239{
240} // TScriptVarDef::~TScriptVarDef
241
242
243
244/*
245 * builtin function definitions
246 */
247
248
249class TBuiltinStdFuncs {
250public:
251
252 // timestamp NOW()
253 // returns current date/time stamp in UTC with timezone set
254 static void func_Now(TItemField *&aTermP, TScriptContext *aFuncContextP)
255 {
256 TTimestampField *tsP = static_cast<TTimestampField *>(aTermP);
257
258 tsP->setTimestampAndContext(
259 getSystemNowAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)), tsP->getGZones()),
260 TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ))
261 );
262 }; // func_Now
263
264
265 // timestamp SYSTEMNOW()
266 // returns current date/time stamp as system time with timezone set
267 static void func_SystemNow(TItemField *&aTermP, TScriptContext *aFuncContextP)
268 {
269 TTimestampField *tsP = static_cast<TTimestampField *>(aTermP);
270
271 tsP->setTimestampAndContext(
272 getSystemNowAs(TCTX_SYSTEM((timecontext_t) ((tctx_tz_system) | TCTX_SYMBOLIC_TZ)), tsP->getGZones()),
273 TCTX_SYSTEM((timecontext_t) ((tctx_tz_system) | TCTX_SYMBOLIC_TZ))
274 );
275 }; // func_SystemNow
276
277
278 // timestamp DBNOW()
279 // returns database's idea of "now" in UTC with local timezone set
280 static void func_DbNow(TItemField *&aTermP, TScriptContext *aFuncContextP)
281 {
282 TTimestampField *tsP = static_cast<TTimestampField *>(aTermP);
283
284 tsP->setTimestampAndContext(
285 aFuncContextP->getSession()->getDatabaseNowAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ))),
286 TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ))
287 );
288 }; // func_DbNow
289
290
291 // integer ZONEOFFSET(timestamp atime)
292 // returns zone offset for given timestamp.
293 // New in 3.1: Floating timestamps return unassigned
294 static void func_ZoneOffset(TItemField *&aTermP, TScriptContext *aFuncContextP)
295 {
296 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
297 sInt16 moffs=0;
298
299 if (tsP->isFloating()) {
300 // floating timestamps do not have an offset
301 aTermP->unAssign();
302 }
303 else {
304 moffs = tsP->getMinuteOffset();
305 aTermP->setAsInteger((sInt32)moffs*SecsPerMin);
306 }
307 }; // func_ZoneOffset
308
309
310 // helper to get a time context from integer seconds offset or string zone name
311 static timecontext_t contextFromSpec(TItemField *aVariantP, TScriptContext *aFuncContextP)
312 {
313 timecontext_t tctx = TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ));
314
315 if (aVariantP->isBasedOn(fty_timestamp)) {
316 // just use context of another timestamp
317 tctx = static_cast<TTimestampField *>(aVariantP)->getTimeContext();
318 }
319 else if (aVariantP->getCalcType()==fty_integer) {
320 // integer specifies a seconds offset
321 tctx = TCTX_MINOFFSET(aVariantP->getAsInteger() / SecsPerMin);
322 }
323 else if (aVariantP->isEmpty()) {
324 // empty non-timestamp and non-integer mean unknown/floating timezone
325 tctx = TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ));
326 }
327 else {
328 // treat as string specifying time zone by name or vTimezone
329 string str;
330 aVariantP->getAsString(str);
331 if (strucmp(str.c_str(),"USERTIMEZONE")==0) {
332 // special case - session's user time zone
333 tctx = aFuncContextP->getSession()->fUserTimeContext;
334 }
335 else if (strucmp(str.c_str(),"SYSTEM")==0) {
336 tctx = TCTX_SYSTEM((timecontext_t) ((tctx_tz_system) | TCTX_SYMBOLIC_TZ));
337 }
338 else if (strucmp(str.c_str(),"BEGIN:VTIMEZONE",15)==0) {
339 // is a vTimezone, get it
340 if (!VTIMEZONEtoInternal(str.c_str(), tctx, aFuncContextP->getSession()->getSessionZones(),aFuncContextP->getSession()->getDbgLogger()))
341 tctx = TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ));
342 }
343 else {
344 // search for timezone by name (internal or olson)
345 if (!TimeZoneNameToContext(str.c_str(), tctx, aFuncContextP->getSession()->getSessionZones(), true)) {
346 // last attempt is parsing it as a ISO8601 offset spec
347 ISO8601StrToContext(str.c_str(), tctx);
348 }
349 }
350 }
351 return tctx;
352 } // contextFromSpec
353
354
355 // helper to represent a time context as string
356 static void zoneStrFromContext(timecontext_t aContext, TItemField *aZoneStrFieldP, TScriptContext *aFuncContextP)
357 {
358 string str;
359
360 if (TCTX_IS_UNKNOWN(aContext)) {
361 // no time zone
362 aZoneStrFieldP->unAssign();
363 }
364 else if (TCTX_IS_TZ(aContext)) {
365 // symbolic time zone, show name
366 TimeZoneContextToName(aContext,str,aFuncContextP->getSession()->getSessionZones());
367 aZoneStrFieldP->setAsString(str);
368 }
369 else {
370 // is non-symbolic minute offset, show it in ISO8601 extended form
371 str.erase();
372 ContextToISO8601StrAppend(str, aContext, true);
373 }
374 } // zoneStrFromContext
375
376
377 // string TIMEZONE(timestamp atime)
378 // returns time zone name
379 static void func_Timezone(TItemField *&aTermP, TScriptContext *aFuncContextP)
380 {
381 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
382
383 zoneStrFromContext(tsP->getTimeContext(), aTermP, aFuncContextP);
384 }; // func_Timezone
385
386
387 // string VTIMEZONE(timestamp atime)
388 // returns time zone in VTIMEZONE format
389 static void func_VTimezone(TItemField *&aTermP, TScriptContext *aFuncContextP)
390 {
391 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
392
393 string z;
394 internalToVTIMEZONE(tsP->getTimeContext(),z,aFuncContextP->getSession()->getSessionZones());
395 aTermP->setAsString(z);
396 }; // func_VTimezone
397
398
399 // SETTIMEZONE(timestamp &atime,variant zonespec)
400 // sets time zone for given timestamp field
401 static void func_SetTimezone(TItemField *&aTermP, TScriptContext *aFuncContextP)
402 {
403 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
404
405 // get context from variant spec
406 timecontext_t tctx = contextFromSpec(aFuncContextP->getLocalVar(1), aFuncContextP);
407 // set it
408 tsP->setTimeContext(tctx);
409 }; // func_SetTimezone
410
411
412 // SETFLOATING(timestamp &atime)
413 // sets given timestamp to floating (no timezone)
414 // this is an efficient shortform for SETTIMEZONE(atime,"FLOATING") or SETTIMEZONE(atime,"")
415 static void func_SetFloating(TItemField *&aTermP, TScriptContext *aFuncContextP)
416 {
417 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
418 // set it
419 tsP->setTimeContext(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)));
420 }; // func_SetFloating
421
422
423 // timestamp CONVERTTOZONE(timestamp atime, variant zonespec [,boolean doUnfloat])
424 // returns timestamp converted to specified zone.
425 // - If doUnfloat, floating timestamps will be fixed in the new zone w/o conversion of the timestamp itself.
426 // - timestamps that already have a zone will be converted
427 static void func_ConvertToZone(TItemField *&aTermP, TScriptContext *aFuncContextP)
428 {
429 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
430
431 // get context from variant spec
432 timecontext_t actual,tctx = contextFromSpec(aFuncContextP->getLocalVar(1), aFuncContextP);
433 // convert and get actually resulting context back (can also be floating)
434 lineartime_t ts = tsP->getTimestampAs(tctx,&actual);
435 // unfloat floats if selected
436 if (aFuncContextP->getLocalVar(2)->getAsBoolean() && TCTX_IS_UNKNOWN(actual)) actual=tctx; // unfloat
437 // assign it to result
438 static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(ts,actual);
439 }; // func_ConvertToZone
440
441
442 // timestamp CONVERTTOUSERZONE(timestamp atime [,boolean doUnfloat])
443 // returns timestamp converted to user time zone.(or floating timestamp as-is)
444 // - this is an efficient shortform for CONVERTTOZONE(atime,"USERTIMEZONE")
445 // - If doUnfloat, floating timestamps will be fixed in the new zone w/o conversion of the timestamp itself.
446 // - timestamps that already have a zone will be converted
447 static void func_ConvertToUserZone(TItemField *&aTermP, TScriptContext *aFuncContextP)
448 {
449 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
450
451 timecontext_t actual,tctx = aFuncContextP->getSession()->fUserTimeContext;
452 // convert and get actually resulting context back (can also be floating)
453 lineartime_t ts = tsP->getTimestampAs(tctx,&actual);
454 // unfloat floats if selected
455 if (aFuncContextP->getLocalVar(1)->getAsBoolean() && TCTX_IS_UNKNOWN(actual)) actual=tctx; // unfloat
456 // assign it to result
457 static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(ts,actual);
458 }; // func_ConvertToUserZone
459
460
461 // string USERTIMEZONE()
462 // returns session user time zone name
463 static void func_UserTimezone(TItemField *&aTermP, TScriptContext *aFuncContextP)
464 {
465 zoneStrFromContext(aFuncContextP->getSession()->fUserTimeContext,aTermP, aFuncContextP);
466 }; // func_UserTimezone
467
468
469 // SETUSERTIMEZONE(variant zonespec)
470 // sets session user time zone
471 static void func_SetUserTimezone(TItemField *&aTermP, TScriptContext *aFuncContextP)
472 {
473 // get context from variant spec
474 timecontext_t tctx = contextFromSpec(aFuncContextP->getLocalVar(1), aFuncContextP);
475
476 aFuncContextP->getSession()->fUserTimeContext = tctx;
477 }; // func_SetUserTimezone
478
479
480 // integer ISDATEONLY(timestamp atime)
481 // returns true if given timestamp is a date-only
482 static void func_IsDateOnly(TItemField *&aTermP, TScriptContext *aFuncContextP)
483 {
484 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
485
486 aTermP->setAsInteger(TCTX_IS_DATEONLY(tsP->getTimeContext()) ? 1 : 0);
487 }; // func_IsDateOnly
488
489
490 // timestamp DATEONLY(timestamp atime)
491 // returns a floating(!) date-only of the given timestamp
492 static void func_DateOnly(TItemField *&aTermP, TScriptContext *aFuncContextP)
493 {
494 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
495
496 // get timestamp as dateonly
497 timecontext_t tctx;
498 lineartime_t ts;
499 ts = tsP->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)) | TCTX_DATEONLY, &tctx);
500 // assign it to result (but do NOT pass in tctx, as it might contain a zone
501 // but we need it as floating to make dates comparable
502 static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(ts,TCTX_DATEONLY|TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)));
503 }; // func_DateOnly
504
505
506 // timestamp TIMEONLY(timestamp atime)
507 // returns a floating(!) time-only of the given timestamp
508 static void func_TimeOnly(TItemField *&aTermP, TScriptContext *aFuncContextP)
509 {
510 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
511
512 // get timestamp as dateonly
513 timecontext_t tctx;
514 lineartime_t ts;
515 ts = tsP->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)) | TCTX_TIMEONLY, &tctx);
516 // assign it to result (but do NOT pass in tctx, as it might contain a zone
517 // but we need it as floating to make dates comparable
518 static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(ts,TCTX_TIMEONLY|TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)));
519 }; // func_TimeOnly
520
521
522
523 // integer ISDURATION(timestamp atime)
524 // returns true if given timestamp is a duration value
525 static void func_IsDuration(TItemField *&aTermP, TScriptContext *aFuncContextP)
526 {
527 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
528 aTermP->setAsInteger(tsP->isDuration() ? 1 : 0);
529 }; // func_IsDuration
530
531
532 // timestamp DURATION(timestamp atime)
533 // returns the timestamp as a duration (floating, duration flag set)
534 static void func_Duration(TItemField *&aTermP, TScriptContext *aFuncContextP)
535 {
536 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
537
538 // get timestamp as-is
539 timecontext_t tctx;
540 lineartime_t ts;
541 ts = tsP->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)), &tctx);
542 // result is floating timestamp with duration flag set (and dateonly/timeonly flags retained)
543 static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(ts,TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ))|(tctx&TCTX_RFLAGMASK)|TCTX_DURATION);
544 }; // func_Duration
545
546
547 // timestamp POINTINTIME(timestamp atime)
548 // returns the timestamp as a point in time (i.e. not duration and not dateonly/timeonly)
549 static void func_PointInTime(TItemField *&aTermP, TScriptContext *aFuncContextP)
550 {
551 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
552
553 // get timestamp as-is
554 timecontext_t tctx;
555 lineartime_t ts;
556 ts = tsP->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)), &tctx);
557 // assign it to result with duration and dateonly flag cleared
558 static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(ts,tctx & (~(TCTX_DURATION+TCTX_DATEONLY+TCTX_TIMEONLY)));
559 }; // func_PointInTime
560
561
562
563 // integer ISFLOATING(timestamp atime)
564 // returns true if given timestamp is floating (i.e. not bound to a time zone)
565 static void func_IsFloating(TItemField *&aTermP, TScriptContext *aFuncContextP)
566 {
567 TTimestampField *tsP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
568 aTermP->setAsInteger(tsP->isFloating() ? 1 : 0);
569 }; // func_IsFloating
570
571
572
573
574 // integer ALLDAYCOUNT(timestamp start, timestamp end [, boolean checkinusercontext [, onlyutcinusercontext]])
575 // returns number of days for an all-day event
576 // Note: Timestamps must be in the context in which they are to be checked for midnight, 23:59:xx etc.
577 // except if checkinusercontext ist set (then non-floating timestamps are moved into user context before
578 // checking. onlyutcinusercontext limits moving to user context to UTC timestamps only.
579 static void func_AlldayCount(TItemField *&aTermP, TScriptContext *aFuncContextP)
580 {
581 sInt16 c = AlldayCount(
582 aFuncContextP->getLocalVar(0),
583 aFuncContextP->getLocalVar(1),
584 aFuncContextP->getLocalVar(2)->getAsBoolean() ? aFuncContextP->getSession()->fUserTimeContext : TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)),
585 aFuncContextP->getLocalVar(3)->getAsBoolean()
586 );
587 aTermP->setAsInteger(c);
588 }; // func_AlldayCount
589
590
591 // MAKEALLDAY(timestamp &start, timestamp &end [,integer days])
592 // adjusts timestamps for allday representation, makes them floating
593 // Note: Timestamps must already represent local day times
594 static void func_MakeAllday(TItemField *&aTermP, TScriptContext *aFuncContextP)
595 {
596 sInt16 days=aFuncContextP->getLocalVar(2)->getAsInteger(); // returns 0 if unassigned
597 MakeAllday(
598 aFuncContextP->getLocalVar(0),
599 aFuncContextP->getLocalVar(1),
600 TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)),
601 days
602 );
603 }; // func_MakeAllday
604
605
606 // integer WEEKDAY(timestamp timestamp)
607 // returns weekday (0=sunday, 1=monday ... 6=saturday) from a timestamp
608 static void func_Weekday(TItemField *&aTermP, TScriptContext *aFuncContextP)
609 {
610 lineartime_t lt = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0))->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)));
611 aTermP->setAsInteger(lineartime2weekday(lt));
612 }; // func_Weekday
613
614
615 // integer SECONDS(integer timeunits)
616 // returns number of seconds from a time unit spec
617 static void func_Seconds(TItemField *&aTermP, TScriptContext *aFuncContextP)
618 {
619 fieldinteger_t v=aFuncContextP->getLocalVar(0)->getAsInteger();
620 aTermP->setAsInteger(v/secondToLinearTimeFactor);
621 }; // func_Seconds
622
623
624 // integer MILLISECONDS(integer timeunits)
625 // returns number of milliseconds from a time unit spec
626 static void func_Milliseconds(TItemField *&aTermP, TScriptContext *aFuncContextP)
627 {
628 fieldinteger_t v=aFuncContextP->getLocalVar(0)->getAsInteger();
629 if (secondToLinearTimeFactor==1) {
630 v=v*1000; // internal unit is seconds
631 }
632 else if (secondToLinearTimeFactor!=1000) {
633 v=v/secondToLinearTimeFactor; // seconds
634 v=v*1000; // artifical milliseconds
635 }
636 aTermP->setAsInteger(v);
637 }; // func_Milliseconds
638
639
640 // void SLEEPMS(integer milliseconds)
641 // sleeps process (or thread) for specified time
642 static void func_SleepMS(TItemField *&aTermP, TScriptContext *aFuncContextP)
643 {
644 fieldinteger_t sl=aFuncContextP->getLocalVar(0)->getAsInteger();
645 // make nanoseconds, then timeunits
646 sl *= 1000000LL;
647 sl /= nanosecondsPerLinearTime;
648 sleepLineartime(sl);
649 }; // func_SleepMS
650
651
652 // integer TIMEUNITS(integer seconds)
653 // returns number of time units from a seconds spec
654 static void func_Timeunits(TItemField *&aTermP, TScriptContext *aFuncContextP)
655 {
656 fieldinteger_t v=aFuncContextP->getLocalVar(0)->getAsInteger();
657 aTermP->setAsInteger(v*secondToLinearTimeFactor);
658 }; // func_Timeunits
659
660
661 // integer DAYUNITS(integer days)
662 // returns number of time units from a seconds spec
663 static void func_Dayunits(TItemField *&aTermP, TScriptContext *aFuncContextP)
664 {
665 fieldinteger_t v=aFuncContextP->getLocalVar(0)->getAsInteger();
666 aTermP->setAsInteger(v*linearDateToTimeFactor);
667 }; // func_Dayunits
668
669
670 // integer MONTHDAYS(timestamp date)
671 // returns number of days of the month date is in
672 static void func_MonthDays(TItemField *&aTermP, TScriptContext *aFuncContextP)
673 {
674 lineartime_t ts = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0))->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)));
675 aTermP->setAsInteger(getMonthDays(lineartime2dateonly(ts)));
676 }; // func_MonthDays
677
678
679
680 // DEBUGMESSAGE(string msg)
681 // writes debug message to debug log file if debugging is not completely disabled
682 static void func_Debugmessage(TItemField *&aTermP, TScriptContext *aFuncContextP)
683 {
684 #ifdef SYDEBUG2
685 if (aFuncContextP->getDbgMask()) {
686 // get message
687 string s;
688 aFuncContextP->getLocalVar(0)->getAsString(s);
689 // write it
690 if (aFuncContextP->getSession()) {
691 POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_HOT,("Script DEBUGMESSAGE() at Line %hd: %s",aFuncContextP->getScriptLine(),s.c_str())){ if ((aFuncContextP->getSession()) && (((0x00000001
) & (aFuncContextP->getSession())->getDbgMask()) ==
(0x00000001))) (aFuncContextP->getSession())->getDbgLogger
()->setNextMask(0x00000001).DebugPrintfLastMask ("Script DEBUGMESSAGE() at Line %hd: %s"
,aFuncContextP->getScriptLine(),s.c_str()); }
;
692 } else {
693 POBJDEBUGPRINTFX(aFuncContextP->getSyncAppBase(),DBG_HOT,("Script DEBUGMESSAGE() at Line %hd: %s",aFuncContextP->getScriptLine(),s.c_str())){ if ((aFuncContextP->getSyncAppBase()) && (((0x00000001
) & (aFuncContextP->getSyncAppBase())->getDbgMask()
) == (0x00000001))) (aFuncContextP->getSyncAppBase())->
getDbgLogger()->setNextMask(0x00000001).DebugPrintfLastMask
("Script DEBUGMESSAGE() at Line %hd: %s",aFuncContextP->getScriptLine
(),s.c_str()); }
;
694 }
695 }
696 #endif
697 }; // func_Debugmessage
698
699
700 // DEBUGSHOWVARS()
701 // shows values of all local script variables
702 static void func_DebugShowVars(TItemField *&aTermP, TScriptContext *aFuncContextP)
703 {
704 #ifdef SYDEBUG2
705 if (aFuncContextP->getDbgMask() && aFuncContextP->fParentContextP) {
706 POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_HOT,("Script DEBUGSHOWVARS() at Line %hd:",aFuncContextP->getScriptLine())){ if ((aFuncContextP->getSession()) && (((0x00000001
) & (aFuncContextP->getSession())->getDbgMask()) ==
(0x00000001))) (aFuncContextP->getSession())->getDbgLogger
()->setNextMask(0x00000001).DebugPrintfLastMask ("Script DEBUGSHOWVARS() at Line %hd:"
,aFuncContextP->getScriptLine()); }
;
707 // show all local vars (of PARENT!)
708 TScriptContext *showContextP = aFuncContextP->fParentContextP;
709 string fshow;
710 uInt16 i;
711 for (i=0; i<showContextP->getNumLocals(); i++) {
712 StringObjAppendPrintf(fshow,"- %-20s : ",showContextP->getVarDef(i)->fVarName.c_str());
713 showContextP->getLocalVar(i)->StringObjFieldAppend(fshow,80);
714 fshow += '\n';
715 }
716 // output preformatted
717 POBJDEBUGPUTSXX(aFuncContextP->getSession(),DBG_HOT,fshow.c_str(),0,true){ if ((aFuncContextP->getSession()) && (((0x00000001
) & (aFuncContextP->getSession())->getDbgMask()) ==
(0x00000001))) (aFuncContextP->getSession())->getDbgLogger
()->DebugPuts( 0x00000001,fshow.c_str(),0,true); }
;
718 }
719 #endif
720 }; // func_DebugShowVars
721
722
723 // DEBUGSHOWITEM([bool aShowRefItem])
724 // shows all fields and values of current item
725 static void func_DebugShowItem(TItemField *&aTermP, TScriptContext *aFuncContextP)
726 {
727 #ifdef SYDEBUG2
728 if (aFuncContextP->getDbgMask() && aFuncContextP->fParentContextP) {
729 POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_HOT,("Script DEBUGSHOWITEM() at Line %hd:",aFuncContextP->getScriptLine())){ if ((aFuncContextP->getSession()) && (((0x00000001
) & (aFuncContextP->getSession())->getDbgMask()) ==
(0x00000001))) (aFuncContextP->getSession())->getDbgLogger
()->setNextMask(0x00000001).DebugPrintfLastMask ("Script DEBUGSHOWITEM() at Line %hd:"
,aFuncContextP->getScriptLine()); }
;
730 // select item
731 TMultiFieldItem *showItemP =
732 aFuncContextP->getLocalVar(0)->getAsBoolean() ? // optional param to select ref item instead of target
733 aFuncContextP->fParentContextP->fReferenceItemP :
734 aFuncContextP->fParentContextP->fTargetItemP;
735 if (showItemP) {
736 showItemP->debugShowItem(DBG_HOT0x00000001);
737 }
738 else {
739 POBJDEBUGPRINTFX(aFuncContextP->getSession(),DBG_HOT,("- no item to show")){ if ((aFuncContextP->getSession()) && (((0x00000001
) & (aFuncContextP->getSession())->getDbgMask()) ==
(0x00000001))) (aFuncContextP->getSession())->getDbgLogger
()->setNextMask(0x00000001).DebugPrintfLastMask ("- no item to show"
); }
;
740 }
741 }
742 #endif
743 }; // func_DebugShowItem
744
745
746
747
748 // SETXMLTRANSLATE(bool yesorno)
749 // enables or disables XML translated SyncML message dumping on a per session basis
750 static void func_SetXMLTranslate(TItemField *&aTermP, TScriptContext *aFuncContextP)
751 {
752 #ifdef SYDEBUG2
753 if (aFuncContextP->getSession())
754 aFuncContextP->getSession()->fXMLtranslate=aFuncContextP->getLocalVar(0)->getAsBoolean();
755 #endif
756 }; // func_SetXMLTranslate
757
758
759 // SETMSGDUMP(bool yesorno)
760 // enables or disables raw SyncML message dumping on a per session basis
761 static void func_SetMsgDump(TItemField *&aTermP, TScriptContext *aFuncContextP)
762 {
763 #ifdef SYDEBUG2
764 if (aFuncContextP->getSession())
765 aFuncContextP->getSession()->fMsgDump=aFuncContextP->getLocalVar(0)->getAsBoolean();
766 #endif
767 }; // func_SetMsgDump
768
769
770 // SETDEBUGOPTIONS(string optionname, boolean set)
771 // sets or clears debug option flags (for the currently running session)
772 static void func_SetDebugOptions(TItemField *&aTermP, TScriptContext *aFuncContextP)
773 {
774 #ifdef SYDEBUG2
775 TDebugLogger *loggerP=aFuncContextP->getDbgLogger();
776 if (loggerP) {
777 // get option string
778 string s;
779 aFuncContextP->getLocalVar(0)->getAsString(s);
780 // convert to bitmask
781 sInt16 k;
782 if (StrToEnum(debugOptionNames,numDebugOptions,k,s.c_str())) {
783 // found mask, modify
784 uInt32 currentmask=loggerP->getRealMask();
785 if (aFuncContextP->getLocalVar(1)->getAsBoolean())
786 currentmask |= debugOptionMasks[k];
787 else
788 currentmask &= ~debugOptionMasks[k];
789 // .. and apply
790 loggerP->setMask(currentmask);
791 }
792 }
793 #endif
794 }; // func_SetDebugOptions
795
796
797 // SETDEBUGMASK(integer mask)
798 // sets the debug mask
799 static void func_SetDebugMask(TItemField *&aTermP, TScriptContext *aFuncContextP)
800 {
801 #ifdef SYDEBUG2
802 TDebugLogger *loggerP=aFuncContextP->getDbgLogger();
803 if (loggerP) {
804 // get mask value
805 loggerP->setMask(aFuncContextP->getLocalVar(0)->getAsInteger());
806 }
807 #endif
808 }; // func_SetDebugMask
809
810
811 // integer GETDEBUGMASK()
812 // gets the current debug mask
813 static void func_GetDebugMask(TItemField *&aTermP, TScriptContext *aFuncContextP)
814 {
815 uInt32 m=0; // without debug, mask is always 0
816 #ifdef SYDEBUG2
817 TDebugLogger *loggerP=aFuncContextP->getDbgLogger();
818 if (loggerP) {
819 // get mask value
820 loggerP->getRealMask();
821 }
822 #endif
823 aTermP->setAsInteger(m);
824 }; // func_GetDebugMask
825
826
827 // void REQUESTMAXTIME(integer maxtime_seconds)
828 static void func_RequestMaxTime(TItemField *&aTermP, TScriptContext *aFuncContextP)
829 {
830 TSyncSession *sessionP = aFuncContextP->getSession();
831 if (sessionP) {
832 sessionP->fRequestMaxTime=aFuncContextP->getLocalVar(0)->getAsInteger();
833 }
834 } // func_RequestMaxTime
835
836
837 // void REQUESTMINTIME(integer maxtime_seconds)
838 // artificial delay for testing
839 static void func_RequestMinTime(TItemField *&aTermP, TScriptContext *aFuncContextP)
840 {
841 TSyncSession *sessionP = aFuncContextP->getSession();
842 if (sessionP) {
843 sessionP->fRequestMinTime=aFuncContextP->getLocalVar(0)->getAsInteger();
844 }
845 } // func_RequestMinTime
846
847
848 // string SYNCMLVERS()
849 // gets the SyncML version of the current session as string like "1.2" or "1.0" etc.
850 static void func_SyncMLVers(TItemField *&aTermP, TScriptContext *aFuncContextP)
851 {
852 aTermP->setAsString(SyncMLVersionNames[aFuncContextP->getSession()->getSyncMLVersion()]);
853 }; // func_SyncMLVers
854
855
856 // integer SHELLEXECUTE(string command, string arguments [,boolean inbackground])
857 // executes the command line in a shell, returns the exit code of the command
858 static void func_Shellexecute(TItemField *&aTermP, TScriptContext *aFuncContextP)
859 {
860 // get params
861 string cmd,params;
862 bool inbackground;
863 sInt32 exitcode;
864 aFuncContextP->getLocalVar(0)->getAsString(cmd);
865 aFuncContextP->getLocalVar(1)->getAsString(params);
866 // optional param
867 inbackground=aFuncContextP->getLocalVar(2)->getAsBoolean(); // returns false if not assigned -> not in background
868 // execute now
869 exitcode=shellExecCommand(cmd.c_str(),params.c_str(),inbackground);
870 // return result code
871 aTermP->setAsInteger(exitcode);
872 }; // func_Shellexecute
873
874 // string READ(string file)
875 // reads the file and returns its content or UNASSIGNED in case of failure;
876 // errors are logged
877 static void func_Read(TItemField *&aTermP, TScriptContext *aFuncContextP)
878 {
879 // get params
880 string file;
881 aFuncContextP->getLocalVar(0)->getAsString(file);
882
883 // execute now
884 string content;
885 FILE *in;
886 in = fopen(file.c_str(), "rb");
887 if (in) {
888 long size = fseek(in, 0, SEEK_END2);
889 if (size >= 0) {
890 // managed to obtain size, use it to pre-allocate result
891 content.reserve(size);
892 fseek(in, 0, SEEK_SET0);
893 } else {
894 // ignore seek error, might not be a plain file
895 clearerr(in);
896 }
897
898 if (!ferror(in)) {
899 char buf[8192];
900 size_t read;
901 while ((read = fread(buf, 1, sizeof(buf), in)) > 0) {
902 content.append(buf, read);
903 }
904 }
905 }
906
907 if (in && !ferror(in)) {
908 // return content as string
909 aTermP->setAsString(content);
910 } else {
911 PLOGDEBUGPRINTFX(aFuncContextP->getDbgLogger(),DBG_ERROR,({ if ((aFuncContextP->getDbgLogger()) && ((0x00000002
) & (aFuncContextP->getDbgLogger())->getMask()) == (
0x00000002)) (aFuncContextP->getDbgLogger())->setNextMask
(0x00000002).DebugPrintfLastMask ( "IO error in READ(\"%s\"): %s "
, file.c_str(), strerror((*__errno_location ())) ); }
912 "IO error in READ(\"%s\"): %s ",{ if ((aFuncContextP->getDbgLogger()) && ((0x00000002
) & (aFuncContextP->getDbgLogger())->getMask()) == (
0x00000002)) (aFuncContextP->getDbgLogger())->setNextMask
(0x00000002).DebugPrintfLastMask ( "IO error in READ(\"%s\"): %s "
, file.c_str(), strerror((*__errno_location ())) ); }
913 file.c_str(),{ if ((aFuncContextP->getDbgLogger()) && ((0x00000002
) & (aFuncContextP->getDbgLogger())->getMask()) == (
0x00000002)) (aFuncContextP->getDbgLogger())->setNextMask
(0x00000002).DebugPrintfLastMask ( "IO error in READ(\"%s\"): %s "
, file.c_str(), strerror((*__errno_location ())) ); }
914 strerror(errno){ if ((aFuncContextP->getDbgLogger()) && ((0x00000002
) & (aFuncContextP->getDbgLogger())->getMask()) == (
0x00000002)) (aFuncContextP->getDbgLogger())->setNextMask
(0x00000002).DebugPrintfLastMask ( "IO error in READ(\"%s\"): %s "
, file.c_str(), strerror((*__errno_location ())) ); }
915 )){ if ((aFuncContextP->getDbgLogger()) && ((0x00000002
) & (aFuncContextP->getDbgLogger())->getMask()) == (
0x00000002)) (aFuncContextP->getDbgLogger())->setNextMask
(0x00000002).DebugPrintfLastMask ( "IO error in READ(\"%s\"): %s "
, file.c_str(), strerror((*__errno_location ())) ); }
;
916 }
917
918 if (in) {
919 fclose(in);
920 }
921 } // func_Read
922
923 // string URIToPath(string uri)
924 // extracts the file path in a file:// uri; handles uri decoding
925 // Returns UNASSIGNED if not a file:// uri
926 static void func_URIToPath(TItemField *&aTermP, TScriptContext *aFuncContextP)
927 {
928 // get params
929 string uri;
930 aFuncContextP->getLocalVar(0)->getAsString(uri);
931
932 string protocol, doc;
933 splitURL(uri.c_str(), &protocol, NULL__null, &doc, NULL__null, NULL__null, NULL__null, NULL__null);
934 if (protocol == "file") {
935 string path;
936 path.reserve(doc.size() + 1);
937 path += "/"; // leading slash is never included by splitURL()
938 path += doc;
939 urlDecode(&path);
940 aTermP->setAsString(path);
941 }
942 } // func_URIToPath
943
944 // string URLEncode(string text)
945 static void func_URLDecode(TItemField *&aTermP, TScriptContext *aFuncContextP)
946 {
947 string text;
948 aFuncContextP->getLocalVar(0)->getAsString(text);
949 urlDecode(&text);
950 aTermP->setAsString(text);
951 } // func_URLDecode
952
953 // string URLDecode(string url)
954 static void func_URLEncode(TItemField *&aTermP, TScriptContext *aFuncContextP)
955 {
956 string text;
957 aFuncContextP->getLocalVar(0)->getAsString(text);
958 urlEncode(&text);
959 aTermP->setAsString(text);
960 } // func_URLEncode
961
962 // string REMOTERULENAME()
963 // returns name of the LAST matched remote rule (or subrule), empty if none
964 // Note: this is legacy from 3.4.0.4 onwards, as we now have a list of multiple active rules
965 static void func_Remoterulename(TItemField *&aTermP, TScriptContext *aFuncContextP)
966 {
967 #ifndef NO_REMOTE_RULES
968 if (aFuncContextP->getSession()) {
969 // there is a session
970 if (!aFuncContextP->getSession()->fActiveRemoteRules.empty()) {
971 // there is at least one rule applied, get the last one in the list
972 aTermP->setAsString(aFuncContextP->getSession()->fActiveRemoteRules.back()->getName());
973 return;
974 }
975 }
976 #endif
977 // no remote rule applied
978 aTermP->assignEmpty();
979 }; // func_Remoterulename
980
981
982 // boolean ISACTIVERULE(string rulename)
983 // checks if given rule is currently activated
984 static void func_isActiveRule(TItemField *&aTermP, TScriptContext *aFuncContextP)
985 {
986 #ifndef NO_REMOTE_RULES
987 string r;
988 aFuncContextP->getLocalVar(0)->getAsString(r);
989 if (aFuncContextP->getSession()) {
990 // there is a session, return status
991 aTermP->setAsBoolean(aFuncContextP->getSession()->isActiveRule(r.c_str()));
992 return;
993 }
994 #endif
995 // remote rules not supported or no session active
996 aTermP->assignEmpty();
997 }; // func_Remoterulename
998
999
1000
1001
1002 // TREATASLOCALTIME(integer flag)
1003 static void func_SetTreatAsLocaltime(TItemField *&aTermP, TScriptContext *aFuncContextP)
1004 {
1005 TSyncSession *sessionP = aFuncContextP->getSession();
1006 if (sessionP) sessionP->fTreatRemoteTimeAsLocal = aFuncContextP->getLocalVar(0)->getAsBoolean();
1007 }; // func_SetTreatAsLocaltime
1008
1009
1010 // TREATASUTC(integer flag)
1011 static void func_SetTreatAsUTC(TItemField *&aTermP, TScriptContext *aFuncContextP)
1012 {
1013 TSyncSession *sessionP = aFuncContextP->getSession();
1014 if (sessionP) sessionP->fTreatRemoteTimeAsUTC = aFuncContextP->getLocalVar(0)->getAsBoolean();
1015 }; // func_SetTreatAsUTC
1016
1017
1018 // UPDATECLIENTINSLOWSYNC(integer flag)
1019 static void func_SetUpdateClientInSlowSync(TItemField *&aTermP, TScriptContext *aFuncContextP)
1020 {
1021 TSyncSession *sessionP = aFuncContextP->getSession();
1022 if (sessionP) sessionP->fUpdateClientDuringSlowsync = aFuncContextP->getLocalVar(0)->getAsBoolean();
1023 }; // func_SetUpdateClientInSlowSync
1024
1025
1026 // UPDATESERVERINSLOWSYNC(integer flag)
1027 static void func_SetUpdateServerInSlowSync(TItemField *&aTermP, TScriptContext *aFuncContextP)
1028 {
1029 TSyncSession *sessionP = aFuncContextP->getSession();
1030 if (sessionP) sessionP->fUpdateServerDuringSlowsync = aFuncContextP->getLocalVar(0)->getAsBoolean();
1031 }; // func_SetUpdateServerInSlowSync
1032
1033
1034 // SHOWCTCAPPROPERTIES(bool yesorno)
1035 static void func_ShowCTCapProps(TItemField *&aTermP, TScriptContext *aFuncContextP)
1036 {
1037 TSyncSession *sessionP = aFuncContextP->getSession();
1038 if (sessionP) sessionP->fShowCTCapProps = aFuncContextP->getLocalVar(0)->getAsBoolean();
1039 } // func_ShowCTCapProps
1040
1041
1042 // SHOWTYPESIZEINCTCAP10(bool yesorno)
1043 static void func_ShowTypeSizeInCTCap10(TItemField *&aTermP, TScriptContext *aFuncContextP)
1044 {
1045 TSyncSession *sessionP = aFuncContextP->getSession();
1046 if (sessionP) sessionP->fShowTypeSzInCTCap10 = aFuncContextP->getLocalVar(0)->getAsBoolean();
1047 } // func_ShowTypeSizeInCTCap10
1048
1049
1050 // ENUMDEFAULTPROPPARAMS(bool yesorno)
1051 static void func_EnumDefaultPropParams(TItemField *&aTermP, TScriptContext *aFuncContextP)
1052 {
1053 TSyncSession *sessionP = aFuncContextP->getSession();
1054 // Note that this is a tristate!
1055 if (sessionP) sessionP->fEnumDefaultPropParams =
1056 aFuncContextP->getLocalVar(0)->getAsBoolean() ? 1 : 0;
1057 } // func_EnumDefaultPropParams
1058
1059
1060 // string LOCALURI()
1061 // returns local URI as used by client for starting session
1062 static void func_LocalURI(TItemField *&aTermP, TScriptContext *aFuncContextP)
1063 {
1064 string r;
1065 if (aFuncContextP->getSession()) {
1066 // there is a session
1067 aTermP->setAsString(aFuncContextP->getSession()->getInitialLocalURI());
1068 return;
1069 }
1070 // no session??
1071 aTermP->assignEmpty();
1072 }; // func_LocalURI
1073
1074
1075 // string SUBSTR(string, from [, count])
1076 static void func_Substr(TItemField *&aTermP, TScriptContext *aFuncContextP)
1077 {
1078 // get params
1079 string s;
1080 aFuncContextP->getLocalVar(0)->getAsString(s);
1081 string::size_type i=aFuncContextP->getLocalVar(1)->getAsInteger();
1082 // optional param
1083 string::size_type l=s.size(); // default to entire string
1084 if (aFuncContextP->getLocalVar(2)->isAssigned())
1085 l=aFuncContextP->getLocalVar(2)->getAsInteger(); // use specified count
1086 string r;
1087 // adjust params
1088 if (i>=s.size()) l=0;
1089 else if (i+l>s.size()) l=s.size()-i;
1090 // evaluate
1091 if (l>0) r.assign(s,i,l);
1092 // save result
1093 aTermP->setAsString(r);
1094 }; // func_Substr
1095
1096
1097 // string EXPLODE(string glue, variant &parts[])
1098 static void func_Explode(TItemField *&aTermP, TScriptContext *aFuncContextP)
1099 {
1100 // get params
1101 string glue,s;
1102 aFuncContextP->getLocalVar(0)->getAsString(glue);
1103 TItemField *fldP = aFuncContextP->getLocalVar(1);
1104 // concatenate array elements with glue
1105 aTermP->assignEmpty();
1106 for (uInt16 i=0; i<fldP->arraySize(); i++) {
1107 // get array element as string
1108 fldP->getArrayField(i)->getAsString(s);
1109 // add to output
1110 aTermP->appendString(s);
1111 if (i+1<fldP->arraySize()) {
1112 // we have more elements, add glue
1113 aTermP->appendString(glue);
1114 }
1115 }
1116 }; // func_Explode
1117
1118
1119 #ifdef _MSC_VER
1120 static long long V_llabs( long long j )
1121 {
1122 return (j < 0 ? -j : j);
1123 }
1124 #endif
1125
1126 // integer ABS(integer val)
1127 static void func_Abs(TItemField *&aTermP, TScriptContext *aFuncContextP)
1128 {
1129 #ifdef _MSC_VER
1130 aTermP->setAsInteger(V_llabs(aFuncContextP->getLocalVar(0)->getAsInteger()));
1131 #else
1132 aTermP->setAsInteger(::llabs(aFuncContextP->getLocalVar(0)->getAsInteger()));
1133 #endif
1134 }; // func_Abs
1135
1136
1137 // integer SIGN(integer val)
1138 // i == 0 : 0
1139 // i > 0 : 1
1140 // i < 0 : -1
1141 static void func_Sign(TItemField *&aTermP, TScriptContext *aFuncContextP)
1142 {
1143 fieldinteger_t i = aFuncContextP->getLocalVar(0)->getAsInteger();
1144
1145 aTermP->setAsInteger(i==0 ? 0 : (i>0 ? 1 : -1));
1146 }; // func_Sign
1147
1148
1149 // integer RANDOM(integer range [, integer seed])
1150 // generates random number between 0 and range-1. Seed is optional to init random generator
1151 static void func_Random(TItemField *&aTermP, TScriptContext *aFuncContextP)
1152 {
1153 // seed if second param there
1154 if (aFuncContextP->getLocalVar(1)->isAssigned()) {
1155 // seed random gen first
1156 srand((unsigned int)aFuncContextP->getLocalVar(1)->getAsInteger());
1157 }
1158 // now get random value
1159 fieldinteger_t r = rand();
1160 fieldinteger_t max = RAND_MAX2147483647;
1161 // scale to specified range
1162 aTermP->setAsInteger(r * aFuncContextP->getLocalVar(0)->getAsInteger() / (max+1));
1163 }; // func_Random
1164
1165
1166
1167 // string NUMFORMAT(integer num, integer digits [,string filler=" " [,boolean opts=""]])
1168 static void func_NumFormat(TItemField *&aTermP, TScriptContext *aFuncContextP)
1169 {
1170 // mandatory params
1171 fieldinteger_t i = aFuncContextP->getLocalVar(0)->getAsInteger();
1172 sInt16 numdigits = aFuncContextP->getLocalVar(1)->getAsInteger(); // negative = left justified
1173 // optional params
1174 string filler = " "; // default to filling with spaces
1175 if (aFuncContextP->getLocalVar(2)->isAssigned())
1176 aFuncContextP->getLocalVar(2)->getAsString(filler); // empty: no filling, only truncation
1177 string opts;
1178 aFuncContextP->getLocalVar(3)->getAsString(opts); // +: show plus sign. space: show space as plus sign
1179
1180 // generate raw string
1181 string s;
1182 // - determine hex mode
1183 bool hex = opts.find("x")!=string::npos;
1184 // - create sign
1185 char sign = 0;
1186 if (!hex) {
1187 if (i<0)
1188 sign = '-';
1189 else {
1190 if (opts.find("+")!=string::npos) sign='+';
1191 else if (opts.find(" ")!=string::npos) sign=' ';
1192 }
1193 }
1194 // create raw numeric string
1195 if (hex) {
1196 StringObjPrintf(s,"%llX",(long long)i);
1197 }
1198 else {
1199 #ifdef _MSC_VER
1200 StringObjPrintf(s,"%lld",V_llabs(i));
1201 #else
1202 StringObjPrintf(s,"%lld",::llabs(i));
1203 #endif
1204 }
1205 // adjust
1206 char c = *(filler.c_str()); // NUL or filler char
1207 if (c!='0' && sign) {
1208 s.insert((size_t)0,(size_t)1,sign); // no zero-padding: insert sign before padding
1209 sign=0; // done now
1210 }
1211 sInt32 n,sz = s.size() + (sign ? 1 : 0); // leave room for sign after zero padding
1212 if (numdigits>0) {
1213 // right aligned field
1214 n = numdigits-sz; // empty space in field
1215 if (n<0)
1216 s.erase(0,-n); // delete at beginning
1217 else if (n>0 && c)
1218 s.insert((size_t)0,(size_t)n,c); // insert at beginning
1219 }
1220 else {
1221 // left aligned field
1222 n = -numdigits-sz; // empty space in field
1223 if (n<0)
1224 s.erase(sz-n,-n); // delete at end
1225 else if (n>0 && c)
1226 s.insert((size_t)sz,(size_t)n,c); // insert at end
1227 }
1228 // insert plus now if filled with zeroes
1229 if (sign)
1230 s.insert((size_t)0,(size_t)1,sign); // insert sign after zero padding
1231 // return string
1232 aTermP->setAsString(s);
1233 } // func_NumFormat
1234
1235
1236 // integer LENGTH(string)
1237 static void func_Length(TItemField *&aTermP, TScriptContext *aFuncContextP)
1238 {
1239 TItemField *fldP = aFuncContextP->getLocalVar(0);
1240 fieldinteger_t siz;
1241 if (fldP->isBasedOn(fty_string)) {
1242 // don't get value to avoid pulling large strings just for size
1243 siz=static_cast<TStringField *>(fldP)->getStringSize();
1244 }
1245 else {
1246 // brute force
1247 string s;
1248 fldP->getAsString(s);
1249 siz=s.size();
1250 }
1251 // save result
1252 aTermP->setAsInteger(siz);
1253 }; // func_Length
1254
1255
1256 // integer SIZE(&var)
1257 static void func_Size(TItemField *&aTermP, TScriptContext *aFuncContextP)
1258 {
1259 TItemField *fldP = aFuncContextP->getLocalVar(0);
1260 fieldinteger_t siz;
1261 if (fldP->isArray()) {
1262 siz=fldP->arraySize();
1263 }
1264 else if (fldP->isBasedOn(fty_string)) {
1265 // don't get value to avoid pulling large strings just for size
1266 siz=static_cast<TStringField *>(fldP)->getStringSize();
1267 }
1268 else {
1269 // brute force
1270 string s;
1271 fldP->getAsString(s);
1272 siz=s.size();
1273 }
1274 // save result
1275 aTermP->setAsInteger(siz);
1276 }; // func_Size
1277
1278
1279
1280 // integer FIND(string, pattern [, startat])
1281 static void func_Find(TItemField *&aTermP, TScriptContext *aFuncContextP)
1282 {
1283 // get params
1284 string s,pat;
1285 string::size_type p;
1286 aFuncContextP->getLocalVar(0)->getAsString(s);
1287 aFuncContextP->getLocalVar(1)->getAsString(pat);
1288 // optional param
1289 uInt32 i=aFuncContextP->getLocalVar(2)->getAsInteger(); // returns 0 if unassigned
1290 // find in string
1291 if (i<s.size())
1292 p=s.find(pat,i);
1293 else
1294 p=string::npos;
1295 // return UNASSIGNED for "not found" and position otherwise
1296 if (p==string::npos) aTermP->unAssign();
1297 else aTermP->setAsInteger(p);
1298 }; // func_Find
1299
1300
1301 // integer RFIND(string, pattern [, startat])
1302 static void func_RFind(TItemField *&aTermP, TScriptContext *aFuncContextP)
1303 {
1304 // get params
1305 string s,pat;
1306 string::size_type p;
1307 aFuncContextP->getLocalVar(0)->getAsString(s);
1308 aFuncContextP->getLocalVar(1)->getAsString(pat);
1309 // optional param
1310 uInt32 i=aFuncContextP->getLocalVar(2)->getAsInteger(); // returns 0 if unassigned
1311 if (i>s.size()) i=s.size();
1312 // find in string
1313 p=s.rfind(pat,i);
1314 // return UNASSIGNED for "not found" and position otherwise
1315 if (p==string::npos) aTermP->unAssign();
1316 else aTermP->setAsInteger(p);
1317 }; // func_RFind
1318
1319
1320 #ifdef REGEX_SUPPORT1
1321
1322 // run PCRE regexp
1323 // Returns: > 0 => success; value is the number of elements filled in
1324 // = 0 => success, but offsets is not big enough
1325 // -1 => failed to match
1326 // -2 => PCRE_ERROR_NULL => did not compile, error reported to aDbgLogger
1327 // < -2 => some kind of unexpected problem
1328 static int run_pcre(cAppCharP aRegEx, cAppCharP aSubject, stringSize aSubjLen, stringSize aSubjStart, int *aOutVec, int aOVSize, TDebugLogger *aDbgLogger)
1329 {
1330 string regexpat;
1331 // set default options
1332 int options=0;
1333 // scan input pattern. If it starts with /, we assume /xxx/opt form
1334 cAppCharP p = aRegEx;
1335 char c=*p;
1336 if (c=='/') {
1337 // delimiter found
1338 p++;
1339 // - now search end
1340 while (*p) {
1341 if (*p=='\\') {
1342 // escaped char
1343 p++;
1344 if (*p) p++;
1345 }
1346 else {
1347 if (*p==c) {
1348 // found end of regex
1349 size_t n=p-aRegEx-1; // size of plain regExp
1350 // - scan options
1351 cAppCharP o = p++;
1352 while (*o) {
1353 switch (*o) {
1354 case 'i' : options |= PCRE_CASELESS0x00000001; break;
1355 case 'm' : options |= PCRE_MULTILINE0x00000002; break;
1356 case 's' : options |= PCRE_DOTALL0x00000004; break;
1357 case 'x' : options |= PCRE_EXTENDED0x00000008; break;
1358 case 'U' : options |= PCRE_UNGREEDY0x00000200; break;
1359 }
1360 o++;
1361 }
1362 // - extract regex itself
1363 regexpat.assign(aRegEx+1,n);
1364 aRegEx = regexpat.c_str();
1365 break; // done
1366 }
1367 p++;
1368 }
1369 } // while chars in regex
1370 } // if regex with delimiter
1371 // - compile regex
1372 pcre *regex;
1373 cAppCharP errMsg=NULL__null;
1374 int errOffs=0;
1375 regex = pcre_compile(aRegEx, options | PCRE_UTF80x00000800, &errMsg, &errOffs, NULL__null);
1376 if (regex==NULL__null) {
1377 // error, display it in log if script logging is on
1378 PLOGDEBUGPRINTFX(aDbgLogger,DBG_SCRIPTS+DBG_ERROR,({ if ((aDbgLogger) && ((0x00001000 +0x00000002) &
(aDbgLogger)->getMask()) == (0x00001000 +0x00000002)) (aDbgLogger
)->setNextMask(0x00001000 +0x00000002).DebugPrintfLastMask
( "RegEx error at pattern pos %d: %s ", errOffs, errMsg ? errMsg
: "<unknown>" ); }
1379 "RegEx error at pattern pos %d: %s ",{ if ((aDbgLogger) && ((0x00001000 +0x00000002) &
(aDbgLogger)->getMask()) == (0x00001000 +0x00000002)) (aDbgLogger
)->setNextMask(0x00001000 +0x00000002).DebugPrintfLastMask
( "RegEx error at pattern pos %d: %s ", errOffs, errMsg ? errMsg
: "<unknown>" ); }
1380 errOffs,{ if ((aDbgLogger) && ((0x00001000 +0x00000002) &
(aDbgLogger)->getMask()) == (0x00001000 +0x00000002)) (aDbgLogger
)->setNextMask(0x00001000 +0x00000002).DebugPrintfLastMask
( "RegEx error at pattern pos %d: %s ", errOffs, errMsg ? errMsg
: "<unknown>" ); }
1381 errMsg ? errMsg : "<unknown>"{ if ((aDbgLogger) && ((0x00001000 +0x00000002) &
(aDbgLogger)->getMask()) == (0x00001000 +0x00000002)) (aDbgLogger
)->setNextMask(0x00001000 +0x00000002).DebugPrintfLastMask
( "RegEx error at pattern pos %d: %s ", errOffs, errMsg ? errMsg
: "<unknown>" ); }
1382 )){ if ((aDbgLogger) && ((0x00001000 +0x00000002) &
(aDbgLogger)->getMask()) == (0x00001000 +0x00000002)) (aDbgLogger
)->setNextMask(0x00001000 +0x00000002).DebugPrintfLastMask
( "RegEx error at pattern pos %d: %s ", errOffs, errMsg ? errMsg
: "<unknown>" ); }
;
1383 return PCRE_ERROR_NULL(-2); // -2, regexp did not compile
1384 }
1385 else {
1386 // regExp is ok and can be executed against subject
1387 int r = pcre_exec(regex, NULL__null, aSubject, aSubjLen, aSubjStart, 0, aOutVec, aOVSize);
1388 pcre_free(regex);
1389 return r;
1390 }
1391 } // run_pcre
1392
1393
1394 // integer REGEX_FIND(string subject, string pattern [, integer startat])
1395 static void func_Regex_Find(TItemField *&aTermP, TScriptContext *aFuncContextP)
1396 {
1397 // get params
1398 string s,pat;
1399 aFuncContextP->getLocalVar(0)->getAsString(s);
1400 aFuncContextP->getLocalVar(1)->getAsString(pat);
1401 // optional param
1402 sInt16 i=aFuncContextP->getLocalVar(2)->getAsInteger(); // returns 0 if unassigned
1403 // use PCRE to find
1404 const int ovsize=3; // we need no matches
1405 int ov[ovsize];
1406 int rc = run_pcre(pat.c_str(),s.c_str(),s.size(),i,ov,ovsize,aFuncContextP->getDbgLogger());
1407 if (rc>=0) {
1408 // return start position
1409 aTermP->setAsInteger(ov[0]);
1410 }
1411 else {
1412 // return UNASSIGNED for "not found" and error
1413 aTermP->unAssign();
1414 }
1415 }; // func_Regex_Find
1416
1417
1418 // integer REGEX_MATCH(string subject, string regexp, integer startat, array &matches)
1419 static void func_Regex_Match(TItemField *&aTermP, TScriptContext *aFuncContextP)
1420 {
1421 // get params
1422 string s,pat;
1423 aFuncContextP->getLocalVar(0)->getAsString(s);
1424 aFuncContextP->getLocalVar(1)->getAsString(pat);
1425 sInt32 i=aFuncContextP->getLocalVar(2)->getAsInteger();
1426 TItemField *matchesP = aFuncContextP->getLocalVar(3);
1427 string m;
1428 // use PCRE to find
1429 const int ovsize=54; // max matches (they say this must be a multiple of 3, no idea why; I'd say 2...)
1430 int ov[ovsize];
1431 int rc = run_pcre(pat.c_str(),s.c_str(),s.size(),i,ov,ovsize,aFuncContextP->getDbgLogger());
1432 if (rc>0) {
1433 // return start position
1434 aTermP->setAsInteger(ov[0]);
1435 // return matches
1436 int mIdx;
1437 TItemField *fldP;
1438 for (mIdx=0; mIdx<rc; mIdx++) {
1439 // get field to assign match to
1440 if (matchesP->isArray())
1441 fldP = matchesP->getArrayField(mIdx);
1442 else {
1443 // non-array specified
1444 fldP = matchesP;
1445 // - if there are no subpatterns, assign the first match (entire pattern)
1446 // - if there are subpatterns, assign the first subpattern match
1447 if (rc>1) {
1448 // there is at least one subpattern
1449 mIdx++; // skip the entire pattern match such that 1st subpattern gets assigned
1450 }
1451 }
1452 // assign match (first is entire pattern)
1453 fldP->setAsString(s.c_str()+ov[mIdx*2],ov[mIdx*2+1]-ov[mIdx*2]); // assign substring
1454 // end if matches is not an array
1455 if (!matchesP->isArray())
1456 break;
1457 }
1458 }
1459 else {
1460 // return UNASSIGNED for "not found" and all errors
1461 aTermP->unAssign();
1462 }
1463 }; // func_Regex_Match
1464
1465
1466 // integer REGEX_SPLIT(string subject, string separatorregexp, array elements [, boolean emptyElements])
1467 // returns number of elements created in elements array
1468 static void func_Regex_Split(TItemField *&aTermP, TScriptContext *aFuncContextP)
1469 {
1470 // get params
1471 string s,pat;
1472 aFuncContextP->getLocalVar(0)->getAsString(s);
1473 aFuncContextP->getLocalVar(1)->getAsString(pat);
1474 TItemField *elementsP = aFuncContextP->getLocalVar(2);
1475 // optional params
1476 bool emptyElements = aFuncContextP->getLocalVar(3)->getAsBoolean(); // skip empty elements by default
1477 // find all recurrences of separator
1478 uInt32 i = 0; // start at beginning
1479 sInt32 rIdx = 0; // no results so far
1480 while (i<s.size()) {
1481 // use PCRE to find
1482 const int ovsize=3; // we need no matches
1483 int ov[ovsize];
1484 int rc = run_pcre(pat.c_str(),s.c_str(),s.size(),i,ov,ovsize,aFuncContextP->getDbgLogger());
1485 if (rc<=0) {
1486 // no further match found
1487 // - simulate match at end of string
1488 ov[0]=s.size();
1489 ov[1]=ov[0];
1490 }
1491 // copy element
1492 if (uInt32(ov[0])>i || emptyElements) {
1493 TItemField *fldP = elementsP->getArrayField(rIdx);
1494 if (ov[0]-i>0)
1495 fldP->setAsString(s.c_str()+i,ov[0]-i);
1496 else
1497 fldP->assignEmpty(); // empty element
1498 // next element
1499 rIdx++;
1500 }
1501 // skip separator
1502 i = ov[1];
1503 } // while not at end of string
1504 // return number of elements found
1505 aTermP->setAsInteger(rIdx);
1506 }; // func_Regex_Split
1507
1508
1509 // string REGEX_REPLACE(string,regexp,replacement [,startat [,repeat]])
1510 static void func_Regex_Replace(TItemField *&aTermP, TScriptContext *aFuncContextP)
1511 {
1512 // get params
1513 string s,pat,reppat,res;
1514 aFuncContextP->getLocalVar(0)->getAsString(s);
1515 aFuncContextP->getLocalVar(1)->getAsString(pat);
1516 aFuncContextP->getLocalVar(2)->getAsString(reppat);
1517 // optional params
1518 sInt16 i=aFuncContextP->getLocalVar(3)->getAsInteger(); // returns 0 if unassigned -> start at beginning
1519 sInt32 c=aFuncContextP->getLocalVar(4)->getAsInteger(); // returns 0 if unassigned -> replace all
1520 // use PCRE to find
1521 const int ovsize=54; // max matches (they say this must be a multiple of 3, no idea why; I'd say 2...)
1522 int ov[ovsize];
1523 res.assign(s.c_str(),i); // part of string not searched at all
1524 do {
1525 int rc = run_pcre(pat.c_str(),s.c_str(),s.size(),i,ov,ovsize,aFuncContextP->getDbgLogger());
1526 if (rc<0)
1527 break; // error or no more matches found
1528 // found an occurrence
1529 // - subsititute matches in replacement string
1530 cAppCharP p=reppat.c_str();
1531 cAppCharP q=p;
1532 string rep;
1533 rep.erase();
1534 while (*p) {
1535 if (*p=='\\') {
1536 p++;
1537 if (*p==0) break;
1538 // check for escaped backslash
1539 if (*p=='\\') { p++; continue; }
1540 // get replacement number
1541 if (isdigit(*p)) {
1542 // is replacement escape sequence, get index
1543 uInt16 mIdx = *(p++)-'0';
1544 // append chars before \x
1545 rep.append(q,p-q-2);
1546 // append match (if there is one)
1547 if (mIdx<rc)
1548 rep.append(s.c_str(),ov[mIdx*2],ov[mIdx*2+1]-ov[mIdx*2]);
1549 // update rest pointer
1550 q=p;
1551 continue;
1552 }
1553 }
1554 p++; // next
1555 }
1556 rep.append(q); // copy rest of pattern
1557 // - insert replacement string into result
1558 res.append(s.c_str(),i,ov[0]-i); // from previous match or beginning of string to current match
1559 res.append(rep); // replacement
1560 i=ov[1]; // search continues here
1561 } while (c<=0 || (--c)>0); // if c==0, replace all, otherwise as many times as c says
1562 res.append(s.c_str()+i); // rest
1563 // return result
1564 aTermP->setAsString(res);
1565 }; // func_Regex_Replace
1566
1567 #endif // REGEX_SUPPORT
1568
1569
1570 // integer COMPARE(value, value)
1571 // - returns 0 if equal, 1 if first > second, -1 if first < second,
1572 // SYSYNC_NOT_COMPARABLE if not equal and no ordering known or if field
1573 // types do not match.
1574 static void func_Compare(TItemField *&aTermP, TScriptContext *aFuncContextP)
1575 {
1576 // compare first field with second one
1577 aTermP->setAsInteger(
1578 aFuncContextP->getLocalVar(0)->compareWith(*(aFuncContextP->getLocalVar(1)))
1579 );
1580 }; // func_Compare
1581
1582
1583 // integer CONTAINS(&ref, value [,bool caseinsensitive])
1584 // - returns 1 if value contained in ref, 0 if not
1585 static void func_Contains(TItemField *&aTermP, TScriptContext *aFuncContextP)
1586 {
1587 // check if second is contained in first
1588 bool caseinsensitive = aFuncContextP->getLocalVar(2)->getAsBoolean(); // returns false if not specified
1589 aTermP->setAsBoolean(
1590 aFuncContextP->getLocalVar(0)->contains(*(aFuncContextP->getLocalVar(1)),caseinsensitive)
1591 );
1592 }; // func_Contains
1593
1594
1595 // APPEND(&ref, value)
1596 // - appends value to ref (ref can be array)
1597 static void func_Append(TItemField *&aTermP, TScriptContext *aFuncContextP)
1598 {
1599 // append second to first
1600 aFuncContextP->getLocalVar(0)->append(*(aFuncContextP->getLocalVar(1)));
1601 }; // func_Append
1602
1603
1604
1605 // string UPPERCASE(string)
1606 static void func_UpperCase(TItemField *&aTermP, TScriptContext *aFuncContextP)
1607 {
1608 string s;
1609 TItemField *fldP = aFuncContextP->getLocalVar(0);
1610 if (fldP->isAssigned()) {
1611 fldP->getAsString(s);
1612 StringUpper(s);
1613 // save result
1614 aTermP->setAsString(s);
1615 }
1616 else {
1617 aTermP->unAssign();
1618 }
1619 }; // func_UpperCase
1620
1621
1622 // string LOWERCASE(string)
1623 static void func_LowerCase(TItemField *&aTermP, TScriptContext *aFuncContextP)
1624 {
1625 string s;
1626 TItemField *fldP = aFuncContextP->getLocalVar(0);
1627 if (fldP->isAssigned()) {
1628 fldP->getAsString(s);
1629 StringLower(s);
1630 // save result
1631 aTermP->setAsString(s);
1632 }
1633 else {
1634 aTermP->unAssign();
1635 }
1636 }; // func_LowerCase
1637
1638
1639 // string NORMALIZED(variant value)
1640 // get as normalized string (trimmed CR/LF/space at both ends, no special chars for telephone numbers, http:// added for URLs w/o protocol spec)
1641 static void func_Normalized(TItemField *&aTermP, TScriptContext *aFuncContextP)
1642 {
1643 // get field reference
1644 TItemField *fldP = aFuncContextP->getLocalVar(0);
1645 if (fldP->isAssigned()) {
1646 // get normalized version
1647 string s;
1648 fldP->getAsNormalizedString(s);
1649 // save it
1650 aTermP->setAsString(s);
1651 }
1652 else {
1653 aTermP->unAssign();
1654 }
1655 }; // func_Normalized
1656
1657
1658 // bool ISAVAILABLE(variant &fieldvar)
1659 // check if field is available (supported by both ends)
1660 // - returns EMPTY if availability is not known
1661 // - fieldvar must be a field contained in the primary item of the caller, else function returns UNASSIGNED
1662 static void func_IsAvailable(TItemField *&aTermP, TScriptContext *aFuncContextP)
1663 {
1664 if (aFuncContextP->fParentContextP) {
1665 // get item to find field in
1666 TMultiFieldItem *checkItemP = aFuncContextP->fParentContextP->fTargetItemP;
1667 // check if this item's type has actually received availability info
1668 if (!checkItemP->knowsRemoteFieldOptions()) {
1669 aTermP->assignEmpty(); // nothing known about field availability
1670 return;
1671 }
1672 else {
1673 // we have availability info
1674 // - get index of field by field pointer (passed by reference)
1675 sInt16 fid = checkItemP->getIndexOfField(aFuncContextP->getLocalVar(0));
1676 if (fid!=FID_NOT_SUPPORTED-128) {
1677 // field exists, return availability
1678 aTermP->setAsBoolean(checkItemP->isAvailable(fid));
1679 return;
1680 }
1681 }
1682 }
1683 // no parent context or field not found
1684 aTermP->unAssign();
1685 }; // func_IsAvailable
1686
1687
1688 // SETFIELDOPTIONS(variant &fieldvar, bool available [[[, int maxsize=0 ], int maxoccur=0 ], int notruncate=FALSE])
1689 // set field options (override what might have been set by reading devInf)
1690 // - fieldvar must be a field contained in the primary item of the caller, else function is NOP
1691 static void func_SetFieldOptions(TItemField *&aTermP, TScriptContext *aFuncContextP)
1692 {
1693 if (aFuncContextP->fParentContextP) {
1694 // get item to find field in
1695 TMultiFieldItem *checkItemP = aFuncContextP->fParentContextP->fTargetItemP;
1696 // modify options on the "remote" type
1697 TMultiFieldItemType *mfitP = checkItemP->getRemoteItemType();
1698 // - get index of field by field pointer (passed by reference)
1699 sInt16 fid = checkItemP->getIndexOfField(aFuncContextP->getLocalVar(0));
1700 if (mfitP && fid!=FID_NOT_SUPPORTED-128) {
1701 // field exists, we can set availability
1702 // - get params
1703 bool available = aFuncContextP->getLocalVar(1)->getAsBoolean();
1704 sInt32 maxsize = FIELD_OPT_MAXSIZE_NONE0;
1705 if (aFuncContextP->getLocalVar(2)->isAssigned())
1706 maxsize = aFuncContextP->getLocalVar(2)->getAsInteger();
1707 sInt32 maxoccur = aFuncContextP->getLocalVar(3)->getAsInteger(); // returns 0 if not specified
1708 bool notruncate = aFuncContextP->getLocalVar(4)->getAsBoolean();
1709 // - now set options
1710 TFieldOptions *fo = mfitP->getFieldOptions(fid);
1711 if (fo) {
1712 fo->available=available;
1713 fo->maxsize=maxsize;
1714 fo->maxoccur=maxoccur;
1715 fo->notruncate=notruncate;
1716 }
1717 }
1718 }
1719 }; // func_SetFieldOptions
1720
1721
1722 // string ITEMDATATYPE()
1723 // returns the type's internal name (like "vcard21")
1724 static void func_ItemDataType(TItemField *&aTermP, TScriptContext *aFuncContextP)
1725 {
1726 if (aFuncContextP->fParentContextP) {
1727 // get item of which we want to know the type
1728 TMultiFieldItem *checkItemP = aFuncContextP->fParentContextP->fTargetItemP;
1729 if (checkItemP) {
1730 TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(checkItemP->getItemType());
1731 if (mfitP) {
1732 aTermP->setAsString(mfitP->getTypeConfig()->getName());
1733 return;
1734 }
1735 }
1736 }
1737 // no type associated or no item in current context
1738 aTermP->unAssign();
1739 }; // func_ItemDataType
1740
1741
1742 // string ITEMTYPENAME()
1743 // returns the type's name (like "text/x-vcard")
1744 static void func_ItemTypeName(TItemField *&aTermP, TScriptContext *aFuncContextP)
1745 {
1746 if (aFuncContextP->fParentContextP) {
1747 // get item of which we want to know the type
1748 TMultiFieldItem *checkItemP = aFuncContextP->fParentContextP->fTargetItemP;
1749 if (checkItemP) {
1750 TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(checkItemP->getItemType());
1751 if (mfitP) {
1752 aTermP->setAsString(mfitP->getTypeName());
1753 return;
1754 }
1755 }
1756 }
1757 // no type associated or no item in current context
1758 aTermP->unAssign();
1759 }; // func_ItemTypeName
1760
1761
1762 // string ITEMTYPEVERS()
1763 // returns the type's version string (like "2.1")
1764 static void func_ItemTypeVers(TItemField *&aTermP, TScriptContext *aFuncContextP)
1765 {
1766 if (aFuncContextP->fParentContextP) {
1767 // get item of which we want to know the type
1768 TMultiFieldItem *checkItemP = aFuncContextP->fParentContextP->fTargetItemP;
1769 if (checkItemP) {
1770 TMultiFieldItemType *mfitP = static_cast<TMultiFieldItemType *>(checkItemP->getItemType());
1771 if (mfitP) {
1772 aTermP->setAsString(mfitP->getTypeVers());
1773 return;
1774 }
1775 }
1776 }
1777 // no type associated or no item in current context
1778 aTermP->unAssign();
1779 }; // func_ItemTypeVers
1780
1781
1782
1783 // void SWAP(untyped1,untyped2)
1784 static void func_Swap(TItemField *&aTermP, TScriptContext *aFuncContextP)
1785 {
1786 // get params
1787 TItemField *p1 = aFuncContextP->getLocalVar(0);
1788 TItemField *p2 = aFuncContextP->getLocalVar(1);
1789 TItemField *tempP = newItemField(p1->getType(), aFuncContextP->getSessionZones());
1790
1791 // swap
1792 (*tempP)=(*p1);
1793 (*p1)=(*p2);
1794 (*p2)=(*tempP);
1795
1796 delete tempP;
1797 }; // func_Swap
1798
1799
1800 // string TYPENAME(untyped1)
1801 static void func_TypeName(TItemField *&aTermP, TScriptContext *aFuncContextP)
1802 {
1803 // get params
1804 TItemFieldTypes ty = aFuncContextP->getLocalVar(0)->getType();
1805 // return type name
1806 aTermP->setAsString(ItemFieldTypeNames[ty]);
1807 }; // func_TypeName
1808
1809
1810 // variant SESSIONVAR(string varname)
1811 static void func_SessionVar(TItemField *&aTermP, TScriptContext *aFuncContextP)
1812 {
1813 TItemField *sessionVarP;
1814 TScriptVarDef *sessionVarDefP;
1815 string varname;
1816 TScriptContext *sessionContextP=NULL__null;
1817
1818 // get name
1819 aFuncContextP->getLocalVar(0)->getAsString(varname);
1820 // get variable from session
1821 if (aFuncContextP->getSession())
1822 sessionContextP=aFuncContextP->getSession()->getSessionScriptContext();
1823 if (sessionContextP) {
1824 // get definition
1825 sessionVarDefP = sessionContextP->getVarDef(
1826 varname.c_str(),varname.size()
1827 );
1828 if (sessionVarDefP) {
1829 // get variable
1830 sessionVarP = sessionContextP->getLocalVar(sessionVarDefP->fIdx);
1831 if (sessionVarP) {
1832 // create result field of appropriate type
1833 aTermP = newItemField(sessionVarP->getType(), aFuncContextP->getSessionZones());
1834 // copy value
1835 (*aTermP) = (*sessionVarP);
1836 }
1837 }
1838 }
1839 if (!aTermP) {
1840 // if no such variable found, return unassigned (but not no-value, which would abort script)
1841 aTermP=newItemField(fty_none, aFuncContextP->getSessionZones());
1842 aTermP->unAssign(); // make it (already is...) unassigned
1843 }
1844 }; // func_SessionVar
1845
1846
1847 // SETSESSIONVAR(string varname, value)
1848 static void func_SetSessionVar(TItemField *&aTermP, TScriptContext *aFuncContextP)
1849 {
1850 TItemField *sessionVarP;
1851 TScriptVarDef *sessionVarDefP;
1852 string varname;
1853 TScriptContext *sessionContextP=NULL__null;
1854
1855 // get name
1856 aFuncContextP->getLocalVar(0)->getAsString(varname);
1857 // get variable from session
1858 if (aFuncContextP->getSession()) sessionContextP=aFuncContextP->getSession()->getSessionScriptContext();
1859 if (sessionContextP) {
1860 // get definition
1861 sessionVarDefP = sessionContextP->getVarDef(
1862 varname.c_str(),varname.size()
1863 );
1864 if (sessionVarDefP) {
1865 // get variable
1866 sessionVarP = sessionContextP->getLocalVar(sessionVarDefP->fIdx);
1867 if (sessionVarP) {
1868 // store new value
1869 (*sessionVarP) = (*(aFuncContextP->getLocalVar(1)));
1870 }
1871 }
1872 }
1873 }; // func_SetSessionVar
1874
1875
1876 // void ABORTSESSION(integer statuscode)
1877 static void func_AbortSession(TItemField *&aTermP, TScriptContext *aFuncContextP)
1878 {
1879 TSyncSession *sessionP = aFuncContextP->getSession();
1880 if (sessionP) {
1881 sessionP->AbortSession(aFuncContextP->getLocalVar(0)->getAsInteger(),true); // locally caused
1882 }
1883 }; // func_AbortSession
1884
1885
1886 // SETDEBUGLOG(integer enabled)
1887 // set debug log output for this sync session
1888 static void func_SetDebugLog(TItemField *&aTermP, TScriptContext *aFuncContextP)
1889 {
1890 #ifdef SYDEBUG2
1891 TSyncSession *sessionP = aFuncContextP->getSession();
1892 if (sessionP) {
1893 sessionP->getDbgLogger()->setEnabled(
1894 aFuncContextP->getLocalVar(0)->getAsBoolean()
1895 );
1896 /// @todo: remove this
1897 // %%% for now, we also need to set this separate flag
1898 sessionP->fSessionDebugLogs=
1899 aFuncContextP->getLocalVar(0)->getAsBoolean();
1900 }
1901 #endif
1902 }; // func_SetDebugLog
1903
1904
1905 // SETLOG(integer enabled)
1906 // set debug log output for this sync session
1907 static void func_SetLog(TItemField *&aTermP, TScriptContext *aFuncContextP)
1908 {
1909 #ifndef MINIMAL_CODE
1910 TSyncSession *sessionP = aFuncContextP->getSession();
1911 if (sessionP) {
1912 sessionP->fLogEnabled=
1913 aFuncContextP->getLocalVar(0)->getAsBoolean();
1914 }
1915 #endif
1916 }; // func_SetLog
1917
1918
1919 // SETREADONLY(integer readonly)
1920 // set readonly option of this sync session
1921 static void func_SetReadOnly(TItemField *&aTermP, TScriptContext *aFuncContextP)
1922 {
1923 TSyncSession *sessionP = aFuncContextP->getSession();
1924 if (sessionP) {
1925 sessionP->setReadOnly(aFuncContextP->getLocalVar(0)->getAsBoolean());
1926 }
1927 }; // func_SetReadOnly
1928
1929
1930 // string CONFIGVAR(string varname)
1931 static void func_ConfigVar(TItemField *&aTermP, TScriptContext *aFuncContextP)
1932 {
1933 string varname,value;
1934
1935 // get name
1936 aFuncContextP->getLocalVar(0)->getAsString(varname);
1937 // get value from syncappbase
1938 if (!aFuncContextP->getSyncAppBase()->getConfigVar(varname.c_str(),value))
1939 aTermP->setAsString(value);
1940 else
1941 aTermP->unAssign(); // not found
1942 }; // func_ConfigVar
1943
1944
1945 // timestamp RECURRENCE_DATE(
1946 // timestamp start,
1947 // string rr_freq, integer interval,
1948 // integer fmask, integer lmask,
1949 // boolean occurrencecount,
1950 // integer count
1951 // )
1952 static void func_Recurrence_Date(TItemField *&aTermP, TScriptContext *aFuncContextP)
1953 {
1954 // get params
1955 string rr_freq;
1956 TTimestampField *startFldP = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0));
1957 timecontext_t tctx;
1958 // - get start timestamp as is along with current context
1959 lineartime_t start = startFldP->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)),&tctx);
1960 aFuncContextP->getLocalVar(1)->getAsString(rr_freq);
1961 char freq = rr_freq.size()>0 ? rr_freq[0] : ' ';
1962 char freqmod = rr_freq.size()>1 ? rr_freq[1] : ' ';
1963 sInt16 interval = aFuncContextP->getLocalVar(2)->getAsInteger();
1964 fieldinteger_t fmask = aFuncContextP->getLocalVar(3)->getAsInteger();
1965 fieldinteger_t lmask = aFuncContextP->getLocalVar(4)->getAsInteger();
1966 bool occurrencecount = aFuncContextP->getLocalVar(5)->getAsBoolean();
1967 uInt16 count = aFuncContextP->getLocalVar(6)->getAsInteger();
1968 // now calculate
1969 lineartime_t occurrence;
1970 if(endDateFromCount(
1971 occurrence,
1972 start,
1973 freq,freqmod,
1974 interval,
1975 fmask,lmask,
1976 count,
1977 occurrencecount,
1978 aFuncContextP->getDbgLogger()
1979 )) {
1980 // successful, set timestamp in same context as start timestamp had
1981 static_cast<TTimestampField *>(aTermP)->setTimestampAndContext(occurrence,tctx);
1982 }
1983 else {
1984 // unsuccessful
1985 aTermP->unAssign();
1986 }
1987 } // func_Recurrence_Date
1988
1989
1990 // integer RECURRENCE_COUNT(
1991 // timestamp start,
1992 // string rr_freq, integer interval,
1993 // integer fmask, integer lmask,
1994 // boolean occurrencecount,
1995 // timestamp occurrence
1996 // )
1997 static void func_Recurrence_Count(TItemField *&aTermP, TScriptContext *aFuncContextP)
1998 {
1999 // get params
2000 string rr_freq;
2001 timecontext_t tctx;
2002 // - start time context is used for rule evaluation
2003 lineartime_t start = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(0))->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)),&tctx);
2004 aFuncContextP->getLocalVar(1)->getAsString(rr_freq);
2005 char freq = rr_freq.size()>0 ? rr_freq[0] : ' ';
2006 char freqmod = rr_freq.size()>1 ? rr_freq[1] : ' ';
2007 sInt16 interval = aFuncContextP->getLocalVar(2)->getAsInteger();
2008 fieldinteger_t fmask = aFuncContextP->getLocalVar(3)->getAsInteger();
2009 fieldinteger_t lmask = aFuncContextP->getLocalVar(4)->getAsInteger();
2010 bool occurrencecount = aFuncContextP->getLocalVar(5)->getAsBoolean();
2011 // - get end date / recurrence to get count for
2012 // Note: this is obtained in the same context as start, even if it might be in another context
2013 lineartime_t occurrence = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(6))->getTimestampAs(tctx);
2014 // now calculate
2015 sInt16 count;
2016 if(countFromEndDate(
2017 count, occurrencecount,
2018 start,
2019 freq,freqmod,
2020 interval,
2021 fmask,lmask,
2022 occurrence,
2023 aFuncContextP->getDbgLogger()
2024 )) {
2025 // successful
2026 aTermP->setAsInteger(count);
2027 }
2028 else {
2029 // unsuccessful
2030 aTermP->unAssign();
2031 }
2032 } // func_Recurrence_Count
2033
2034
2035
2036 // string MAKE_RRULE(
2037 // boolean rrule2,
2038 // string rr_freq, integer interval,
2039 // integer fmask, integer lmask,
2040 // timestamp until
2041 // )
2042 static void func_Make_RRULE(TItemField *&aTermP, TScriptContext *aFuncContextP)
2043 {
2044 // get params
2045 string rr_freq;
2046 timecontext_t untilcontext;
2047 // - start time context is used for rule evaluation
2048 bool rruleV2 = aFuncContextP->getLocalVar(0)->getAsBoolean();
2049 aFuncContextP->getLocalVar(1)->getAsString(rr_freq);
2050 char freq = rr_freq.size()>0 ? rr_freq[0] : ' ';
2051 char freqmod = rr_freq.size()>1 ? rr_freq[1] : ' ';
2052 sInt16 interval = aFuncContextP->getLocalVar(2)->getAsInteger();
2053 fieldinteger_t fmask = aFuncContextP->getLocalVar(3)->getAsInteger();
2054 fieldinteger_t lmask = aFuncContextP->getLocalVar(4)->getAsInteger();
2055 lineartime_t until = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(5))->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)),&untilcontext);
2056 // convert to RRULE string
2057 string rrule;
2058 bool ok;
2059 if (rruleV2)
2060 ok = internalToRRULE2(rrule,freq,freqmod,interval,fmask,lmask,until,untilcontext,aFuncContextP->getDbgLogger());
2061 else
2062 ok = internalToRRULE1(rrule,freq,freqmod,interval,fmask,lmask,until,untilcontext,aFuncContextP->getDbgLogger());
2063 if (ok) {
2064 // successful
2065 aTermP->setAsString(rrule);
2066 }
2067 else {
2068 // unsuccessful
2069 aTermP->unAssign();
2070 }
2071 } // func_Make_RRULE
2072
2073
2074 // boolean PARSE_RRULE(
2075 // boolean rruleV2,
2076 // string rrule,
2077 // timestamp start,
2078 // string &rr_freq, integer &interval,
2079 // integer &fmask, integer &lmask,
2080 // timestamp &until
2081 // )
2082 static void func_Parse_RRULE(TItemField *&aTermP, TScriptContext *aFuncContextP)
2083 {
2084 timecontext_t startcontext;
2085 char freq[3] = {' ', ' ', 0};
2086 sInt16 interval;
2087 fieldinteger_t fmask,lmask;
2088 lineartime_t until;
2089 timecontext_t untilcontext;
2090
2091 // get params
2092 // - start time context is used for rule evaluation
2093 bool rruleV2 = aFuncContextP->getLocalVar(0)->getAsBoolean();
2094 string rrule;
2095 aFuncContextP->getLocalVar(1)->getAsString(rrule);
2096 lineartime_t start = static_cast<TTimestampField *>(aFuncContextP->getLocalVar(2))->getTimestampAs(TCTX_UNKNOWN((timecontext_t) ((tctx_tz_unknown) | TCTX_SYMBOLIC_TZ)),&startcontext);
2097 bool ok;
2098 if (rruleV2)
2099 ok = RRULE2toInternal(rrule.c_str(),start,startcontext,freq[0],freq[1],interval,fmask,lmask,until,untilcontext,aFuncContextP->getDbgLogger());
2100 else
2101 ok = RRULE1toInternal(rrule.c_str(),start,startcontext,freq[0],freq[1],interval,fmask,lmask,until,untilcontext,aFuncContextP->getDbgLogger());
2102 if (ok) {
2103 // successful
2104 // - save return values
2105 aFuncContextP->getLocalVar(3)->setAsString(freq);
2106 aFuncContextP->getLocalVar(4)->setAsInteger(interval);
2107 aFuncContextP->getLocalVar(5)->setAsInteger(fmask);
2108 aFuncContextP->getLocalVar(6)->setAsInteger(lmask);
2109 static_cast<TTimestampField *>(aFuncContextP->getLocalVar(7))->setTimestampAndContext(until, untilcontext);
2110 }
2111 aTermP->setAsBoolean(ok);
2112 } // func_Parse_RRULE
2113
2114
2115
2116 // integer PARSEEMAILSPEC(string emailspec, string &name, string &email)
2117 static void func_ParseEmailSpec(TItemField *&aTermP, TScriptContext *aFuncContextP)
2118 {
2119 string spec,name,addr;
2120 aFuncContextP->getLocalVar(0)->getAsString(spec);
2121 cAppCharP e = parseRFC2822AddrSpec(spec.c_str(),name,addr);
2122 aTermP->setAsInteger(e-spec.c_str()); // return number of chars parsed
2123 aFuncContextP->getLocalVar(1)->setAsString(name);
2124 aFuncContextP->getLocalVar(2)->setAsString(addr);
2125 } // func_ParseEmailSpec
2126
2127
2128 // string MAKEEMAILSPEC(string name, string email)
2129 static void func_MakeEmailSpec(TItemField *&aTermP, TScriptContext *aFuncContextP)
2130 {
2131 string spec,name,addr;
2132 aFuncContextP->getLocalVar(0)->getAsString(name);
2133 aFuncContextP->getLocalVar(1)->getAsString(addr);
2134 makeRFC2822AddrSpec(name.c_str(),addr.c_str(),spec);
2135 aTermP->setAsString(spec); // return RFC2822 email address specification
2136 } // func_MakeEmailSpec
2137
2138
2139 // helper to create profile handler by name
2140 // - returns NULL if no such profile name or profile's field list does not match the item's fieldlist
2141 static TProfileHandler *newProfileHandlerByName(cAppCharP aProfileName, TMultiFieldItem *aItemP)
2142 {
2143 // get type registry to find profile config in
2144 TMultiFieldDatatypesConfig *mufcP;
2145 GET_CASTED_PTR(mufcP,TMultiFieldDatatypesConfig,aItemP->getItemType()->getTypeConfig()->getParentElement(),"PARSETEXTWITHPROFILE/MAKETEXTWITHPROFILE used with non-multifield item"){ mufcP = dynamic_cast<TMultiFieldDatatypesConfig *>(aItemP
->getItemType()->getTypeConfig()->getParentElement()
); if (!mufcP) { throw TSyncException("PARSETEXTWITHPROFILE/MAKETEXTWITHPROFILE used with non-multifield item"
); } }
;
2146 // get profile config from type registry
2147 TProfileConfig *profileConfig = mufcP->getProfile(aProfileName);
2148 if (profileConfig) {
2149 // create a profile handler for the item type
2150 return profileConfig->newProfileHandler(aItemP->getItemType());
2151 }
2152 return NULL__null; // no such profile
2153 }
2154
2155
2156 // integer PARSETEXTWITHPROFILE(string textformat, string profileName [, int mode = 0 = default [, string remoteRuleName = "" = other]])
2157 static void func_ParseTextWithProfile(TItemField *&aTermP, TScriptContext *aFuncContextP)
2158 {
2159 bool ok = false;
2160 if (aFuncContextP->fParentContextP) {
2161 // get the item to work with
2162 TMultiFieldItem *itemP = aFuncContextP->fParentContextP->fTargetItemP;
2163 // get a handler by name
2164 string s;
2165 aFuncContextP->getLocalVar(1)->getAsString(s);
2166 TProfileHandler *profileHandlerP = newProfileHandlerByName(s.c_str(), itemP);
2167 if (profileHandlerP) {
2168 // now we can convert
2169 // - set the mode code (none = 0 = default)
2170 profileHandlerP->setProfileMode(aFuncContextP->getLocalVar(2)->getAsInteger());
2171 profileHandlerP->setRelatedDatastore(NULL__null); // no datastore in particular is related
2172 #ifndef NO_REMOTE_RULES
2173 // - try to find remote rule
2174 TItemField *field = aFuncContextP->getLocalVar(3);
2175 if (field) {
2176 field->getAsString(s);
2177 if (!s.empty())
2178 profileHandlerP->setRemoteRule(s);
2179 }
2180 #endif
2181 // - convert
2182 aFuncContextP->getLocalVar(0)->getAsString(s);
2183 ok = profileHandlerP->parseText(s.c_str(), s.size(), *itemP);
2184 // - forget
2185 delete profileHandlerP;
2186 }
2187 }
2188 aTermP->setAsBoolean(ok);
2189 } // func_ParseTextWithProfile
2190
2191
2192 // string MAKETEXTWITHPROFILE(string profileName [, int mode [, string remoteRuleName = "" = other] ])
2193 static void func_MakeTextWithProfile(TItemField *&aTermP, TScriptContext *aFuncContextP)
2194 {
2195 if (aFuncContextP->fParentContextP) {
2196 // get the item to work with
2197 TMultiFieldItem *itemP = aFuncContextP->fParentContextP->fTargetItemP;
2198 // get a handler by name
2199 string s;
2200 aFuncContextP->getLocalVar(0)->getAsString(s);
2201 TProfileHandler *profileHandlerP = newProfileHandlerByName(s.c_str(), itemP);
2202 if (profileHandlerP) {
2203 // now we can convert
2204 // - set the mode code (none = 0 = default)
2205 profileHandlerP->setProfileMode(aFuncContextP->getLocalVar(1)->getAsInteger());
2206 profileHandlerP->setRelatedDatastore(NULL__null); // no datastore in particular is related
2207 #ifndef NO_REMOTE_RULES
2208 // - try to find remote rule
2209 TItemField *field = aFuncContextP->getLocalVar(2);
2210 if (field) {
2211 field->getAsString(s);
2212 if (!s.empty())
2213 profileHandlerP->setRemoteRule(s);
2214 }
2215 #endif
2216 // - convert, after clearing the string (some generateText() implementations
2217 // append instead of overwriting)
2218 s = "";
2219 profileHandlerP->generateText(*itemP,s);
2220 aTermP->setAsString(s); // return text generated according to profile
2221
2222 // - forget
2223 delete profileHandlerP;
2224 }
2225 }
2226 } // func_MakeTextWithProfile
2227
2228
2229
2230}; // TBuiltinStdFuncs
2231
2232
2233const uInt8 param_oneTimestamp[] = { VAL(fty_timestamp)( (uInt8)fty_timestamp) };
2234const uInt8 param_oneInteger[] = { VAL(fty_integer)( (uInt8)fty_integer) };
2235const uInt8 param_oneString[] = { VAL(fty_string)( (uInt8)fty_string) };
2236const uInt8 param_oneVariant[] = { VAL(fty_none)( (uInt8)fty_none) };
2237const uInt8 param_oneOptInteger[] = { OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2238
2239const uInt8 param_Random[] = { VAL(fty_integer)( (uInt8)fty_integer), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2240const uInt8 param_SetTimezone[] = { REF(fty_timestamp)(((uInt8)fty_timestamp)+0x40), VAL(fty_none)( (uInt8)fty_none) };
2241const uInt8 param_SetFloating[] = { REF(fty_timestamp)(((uInt8)fty_timestamp)+0x40) };
2242const uInt8 param_ConvertToZone[] = { VAL(fty_timestamp)( (uInt8)fty_timestamp), VAL(fty_none)( (uInt8)fty_none), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2243const uInt8 param_ConvertToUserZone[] = { VAL(fty_timestamp)( (uInt8)fty_timestamp), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2244const uInt8 param_Shellexecute[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2245const uInt8 param_substr[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_integer)( (uInt8)fty_integer), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2246const uInt8 param_size[] = { REF(fty_none)(((uInt8)fty_none)+0x40) };
2247const uInt8 param_Normalized[] = { REF(fty_none)(((uInt8)fty_none)+0x40) };
2248const uInt8 param_find[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2249const uInt8 param_compare[] = { VAL(fty_none)( (uInt8)fty_none), VAL(fty_none)( (uInt8)fty_none) };
2250const uInt8 param_contains[] = { REF(fty_none)(((uInt8)fty_none)+0x40), VAL(fty_none)( (uInt8)fty_none), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2251const uInt8 param_append[] = { REF(fty_none)(((uInt8)fty_none)+0x40), VAL(fty_none)( (uInt8)fty_none) };
2252const uInt8 param_swap[] = { REF(fty_none)(((uInt8)fty_none)+0x40), REF(fty_none)(((uInt8)fty_none)+0x40) };
2253const uInt8 param_isAvailable[] = { REF(fty_none)(((uInt8)fty_none)+0x40) };
2254const uInt8 param_setFieldOptions[] = { REF(fty_none)(((uInt8)fty_none)+0x40), VAL(fty_integer)( (uInt8)fty_integer), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2255// const uInt8 param_typename[] = { VAL(fty_none) };
2256const uInt8 param_SetSessionVar[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_none)( (uInt8)fty_none) };
2257const uInt8 param_SetDebugOptions[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_integer)( (uInt8)fty_integer) };
2258const uInt8 param_Recurrence_Date[] = { VAL(fty_timestamp)( (uInt8)fty_timestamp), VAL(fty_string)( (uInt8)fty_string), VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_integer)( (uInt8)fty_integer) };
2259const uInt8 param_Recurrence_Count[] = { VAL(fty_timestamp)( (uInt8)fty_timestamp), VAL(fty_string)( (uInt8)fty_string), VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_timestamp)( (uInt8)fty_timestamp) };
2260const uInt8 param_Make_RRULE[] = { VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_string)( (uInt8)fty_string), VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_timestamp)( (uInt8)fty_timestamp) };
2261const uInt8 param_Parse_RRULE[] = { VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_string)( (uInt8)fty_string), VAL(fty_timestamp)( (uInt8)fty_timestamp), REF(fty_string)(((uInt8)fty_string)+0x40), REF(fty_integer)(((uInt8)fty_integer)+0x40), REF(fty_integer)(((uInt8)fty_integer)+0x40), REF(fty_integer)(((uInt8)fty_integer)+0x40), REF(fty_timestamp)(((uInt8)fty_timestamp)+0x40) };
2262const uInt8 param_AlldayCount[] = { VAL(fty_timestamp)( (uInt8)fty_timestamp), VAL(fty_timestamp)( (uInt8)fty_timestamp), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2263const uInt8 param_MakeAllday[] = { REF(fty_timestamp)(((uInt8)fty_timestamp)+0x40), REF(fty_timestamp)(((uInt8)fty_timestamp)+0x40), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2264const uInt8 param_NumFormat[] = { VAL(fty_integer)( (uInt8)fty_integer), VAL(fty_integer)( (uInt8)fty_integer), OPTVAL(fty_string)(((uInt8)fty_string)+0x20), OPTVAL(fty_string)(((uInt8)fty_string)+0x20) };
2265const uInt8 param_Explode[] = { VAL(fty_string)( (uInt8)fty_string), REFARR(fty_none)(((uInt8)fty_none)+0x40 +0x80) };
2266const uInt8 param_parseEmailSpec[] = { VAL(fty_string)( (uInt8)fty_string), REF(fty_string)(((uInt8)fty_string)+0x40), REF(fty_string)(((uInt8)fty_string)+0x40) };
2267const uInt8 param_makeEmailSpec[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string) };
2268const uInt8 param_parseTextWithProfile[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20), OPTVAL(fty_string)(((uInt8)fty_string)+0x20) };
2269const uInt8 param_makeTextWithProfile[] = { VAL(fty_string)( (uInt8)fty_string), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20), OPTVAL(fty_string)(((uInt8)fty_string)+0x20) };
2270
2271
2272#ifdef REGEX_SUPPORT1
2273const uInt8 param_regexfind[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2274const uInt8 param_regexmatch[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string), VAL(fty_integer)( (uInt8)fty_integer), REF(fty_none)(((uInt8)fty_none)+0x40) };
2275const uInt8 param_regexreplace[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2276const uInt8 param_regexsplit[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string), REFARR(fty_none)(((uInt8)fty_none)+0x40 +0x80), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) };
2277#endif
2278
2279// builtin function table
2280const TBuiltInFuncDef BuiltInFuncDefs[] = {
2281 { "ABS", TBuiltinStdFuncs::func_Abs, fty_integer, 1, param_oneInteger },
2282 { "SIGN", TBuiltinStdFuncs::func_Sign, fty_integer, 1, param_oneInteger },
2283 { "RANDOM", TBuiltinStdFuncs::func_Random, fty_integer, 2, param_Random },
2284 { "NUMFORMAT", TBuiltinStdFuncs::func_NumFormat, fty_string, 4, param_NumFormat },
2285 { "NORMALIZED", TBuiltinStdFuncs::func_Normalized, fty_string, 1, param_Normalized },
2286 { "ISAVAILABLE", TBuiltinStdFuncs::func_IsAvailable, fty_integer, 1, param_isAvailable },
2287 { "SETFIELDOPTIONS", TBuiltinStdFuncs::func_SetFieldOptions, fty_none, 5, param_setFieldOptions },
2288 { "ITEMDATATYPE", TBuiltinStdFuncs::func_ItemDataType, fty_string, 0, NULL__null },
2289 { "ITEMTYPENAME", TBuiltinStdFuncs::func_ItemTypeName, fty_string, 0, NULL__null },
2290 { "ITEMTYPEVERS", TBuiltinStdFuncs::func_ItemTypeVers, fty_string, 0, NULL__null },
2291 { "EXPLODE", TBuiltinStdFuncs::func_Explode, fty_string, 2, param_Explode },
2292 { "SUBSTR", TBuiltinStdFuncs::func_Substr, fty_string, 3, param_substr },
2293 { "LENGTH", TBuiltinStdFuncs::func_Length, fty_integer, 1, param_oneString },
2294 { "SIZE", TBuiltinStdFuncs::func_Size, fty_integer, 1, param_size },
2295 { "FIND", TBuiltinStdFuncs::func_Find, fty_integer, 3, param_find },
2296 { "RFIND", TBuiltinStdFuncs::func_RFind, fty_integer, 3, param_find },
2297 #ifdef REGEX_SUPPORT1
2298 { "REGEX_FIND", TBuiltinStdFuncs::func_Regex_Find, fty_integer, 3, param_regexfind },
2299 { "REGEX_MATCH", TBuiltinStdFuncs::func_Regex_Match, fty_integer, 4, param_regexmatch },
2300 { "REGEX_SPLIT", TBuiltinStdFuncs::func_Regex_Split, fty_integer, 4, param_regexsplit },
2301 { "REGEX_REPLACE", TBuiltinStdFuncs::func_Regex_Replace, fty_string, 5, param_regexreplace },
2302 #endif
2303 { "COMPARE", TBuiltinStdFuncs::func_Compare, fty_integer, 2, param_compare },
2304 { "CONTAINS", TBuiltinStdFuncs::func_Contains, fty_integer, 3, param_contains },
2305 { "APPEND", TBuiltinStdFuncs::func_Append, fty_none, 2, param_append },
2306 { "UPPERCASE", TBuiltinStdFuncs::func_UpperCase, fty_string, 1, param_oneString },
2307 { "LOWERCASE", TBuiltinStdFuncs::func_LowerCase, fty_string, 1, param_oneString },
2308 { "SWAP", TBuiltinStdFuncs::func_Swap, fty_none, 2, param_swap },
2309 { "TYPENAME", TBuiltinStdFuncs::func_TypeName, fty_string, 1, param_oneVariant },
2310 { "REMOTERULENAME", TBuiltinStdFuncs::func_Remoterulename, fty_string, 0, NULL__null },
2311 { "ISACTIVERULE", TBuiltinStdFuncs::func_isActiveRule, fty_integer, 1, param_oneString },
2312 { "LOCALURI", TBuiltinStdFuncs::func_LocalURI, fty_string, 0, NULL__null },
2313 { "NOW", TBuiltinStdFuncs::func_Now, fty_timestamp, 0, NULL__null },
2314 { "SYSTEMNOW", TBuiltinStdFuncs::func_SystemNow, fty_timestamp, 0, NULL__null },
2315 { "DBNOW", TBuiltinStdFuncs::func_DbNow, fty_timestamp, 0, NULL__null },
2316 { "ZONEOFFSET", TBuiltinStdFuncs::func_ZoneOffset, fty_integer, 1, param_oneTimestamp },
2317 { "TIMEZONE", TBuiltinStdFuncs::func_Timezone, fty_string, 1, param_oneTimestamp },
2318 { "VTIMEZONE", TBuiltinStdFuncs::func_VTimezone, fty_string, 1, param_oneTimestamp },
2319 { "SETTIMEZONE", TBuiltinStdFuncs::func_SetTimezone, fty_none, 2, param_SetTimezone },
2320 { "SETFLOATING", TBuiltinStdFuncs::func_SetFloating, fty_none, 1, param_SetFloating },
2321 { "CONVERTTOZONE", TBuiltinStdFuncs::func_ConvertToZone, fty_timestamp, 3, param_ConvertToZone },
2322 { "CONVERTTOUSERZONE", TBuiltinStdFuncs::func_ConvertToUserZone, fty_timestamp, 2, param_ConvertToUserZone },
2323 { "USERTIMEZONE", TBuiltinStdFuncs::func_UserTimezone, fty_string, 0, NULL__null },
2324 { "SETUSERTIMEZONE", TBuiltinStdFuncs::func_SetUserTimezone, fty_none, 1, param_oneVariant },
2325 { "ISDATEONLY", TBuiltinStdFuncs::func_IsDateOnly, fty_integer, 1, param_oneTimestamp },
2326 { "DATEONLY", TBuiltinStdFuncs::func_DateOnly, fty_timestamp, 1, param_oneTimestamp },
2327 { "TIMEONLY", TBuiltinStdFuncs::func_TimeOnly, fty_timestamp, 1, param_oneTimestamp },
2328 { "ISDURATION", TBuiltinStdFuncs::func_IsDuration, fty_integer, 1, param_oneTimestamp },
2329 { "DURATION", TBuiltinStdFuncs::func_Duration, fty_timestamp, 1, param_oneTimestamp },
2330 { "POINTINTIME", TBuiltinStdFuncs::func_PointInTime, fty_timestamp, 1, param_oneTimestamp },
2331 { "ISFLOATING", TBuiltinStdFuncs::func_IsFloating, fty_integer, 1, param_oneTimestamp },
2332 { "WEEKDAY", TBuiltinStdFuncs::func_Weekday, fty_integer, 1, param_oneTimestamp },
2333 { "SECONDS", TBuiltinStdFuncs::func_Seconds, fty_integer, 1, param_oneInteger },
2334 { "MILLISECONDS", TBuiltinStdFuncs::func_Milliseconds, fty_integer, 1, param_oneInteger },
2335 { "SLEEPMS", TBuiltinStdFuncs::func_SleepMS, fty_none, 1, param_oneInteger },
2336 { "TIMEUNITS", TBuiltinStdFuncs::func_Timeunits, fty_integer, 1, param_oneInteger },
2337 { "DAYUNITS", TBuiltinStdFuncs::func_Dayunits, fty_integer, 1, param_oneInteger },
2338 { "MONTHDAYS", TBuiltinStdFuncs::func_MonthDays, fty_integer, 1, param_oneTimestamp },
2339 { "DEBUGMESSAGE", TBuiltinStdFuncs::func_Debugmessage, fty_none, 1, param_oneString },
2340 { "DEBUGSHOWVARS", TBuiltinStdFuncs::func_DebugShowVars, fty_none, 0, NULL__null },
2341 { "DEBUGSHOWITEM", TBuiltinStdFuncs::func_DebugShowItem, fty_none, 1, param_oneOptInteger },
2342 { "SETDEBUGOPTIONS", TBuiltinStdFuncs::func_SetDebugOptions, fty_none, 2, param_SetDebugOptions },
2343 { "SETDEBUGMASK", TBuiltinStdFuncs::func_SetDebugMask, fty_none, 1, param_oneInteger },
2344 { "SETXMLTRANSLATE", TBuiltinStdFuncs::func_SetXMLTranslate, fty_none, 1, param_oneInteger },
2345 { "SETMSGDUMP", TBuiltinStdFuncs::func_SetMsgDump, fty_none, 1, param_oneInteger },
2346 { "GETDEBUGMASK", TBuiltinStdFuncs::func_GetDebugMask, fty_integer, 0, NULL__null },
2347 { "REQUESTMAXTIME", TBuiltinStdFuncs::func_RequestMaxTime, fty_none, 1, param_oneInteger },
2348 { "REQUESTMINTIME", TBuiltinStdFuncs::func_RequestMinTime, fty_none, 1, param_oneInteger },
2349 { "SHELLEXECUTE", TBuiltinStdFuncs::func_Shellexecute, fty_integer, 3, param_Shellexecute },
2350 { "READ", TBuiltinStdFuncs::func_Read, fty_string, 1, param_oneString },
2351 { "URITOPATH", TBuiltinStdFuncs::func_URIToPath, fty_string, 1, param_oneString },
2352 { "URLENCODE", TBuiltinStdFuncs::func_URLEncode, fty_string, 1, param_oneString },
2353 { "URLDECODE", TBuiltinStdFuncs::func_URLDecode, fty_string, 1, param_oneString },
2354 { "SESSIONVAR", TBuiltinStdFuncs::func_SessionVar, fty_none, 1, param_oneString },
2355 { "SETSESSIONVAR", TBuiltinStdFuncs::func_SetSessionVar, fty_none, 2, param_SetSessionVar },
2356 { "ABORTSESSION", TBuiltinStdFuncs::func_AbortSession, fty_none, 1, param_oneInteger },
2357 { "SETDEBUGLOG", TBuiltinStdFuncs::func_SetDebugLog, fty_none, 1, param_oneInteger },
2358 { "SETLOG", TBuiltinStdFuncs::func_SetLog, fty_none, 1, param_oneInteger },
2359 { "SETREADONLY", TBuiltinStdFuncs::func_SetReadOnly, fty_none, 1, param_oneInteger },
2360 { "CONFIGVAR", TBuiltinStdFuncs::func_ConfigVar, fty_string, 1, param_oneString },
2361 { "TREATASLOCALTIME", TBuiltinStdFuncs::func_SetTreatAsLocaltime, fty_none, 1, param_oneInteger },
2362 { "TREATASUTC", TBuiltinStdFuncs::func_SetTreatAsUTC, fty_none, 1, param_oneInteger },
2363 { "UPDATECLIENTINSLOWSYNC", TBuiltinStdFuncs::func_SetUpdateClientInSlowSync, fty_none, 1, param_oneInteger },
2364 { "UPDATESERVERINSLOWSYNC", TBuiltinStdFuncs::func_SetUpdateServerInSlowSync, fty_none, 1, param_oneInteger },
2365 { "SHOWCTCAPPROPERTIES", TBuiltinStdFuncs::func_ShowCTCapProps, fty_none, 1, param_oneInteger },
2366 { "SHOWTYPESIZEINCTCAP10", TBuiltinStdFuncs::func_ShowTypeSizeInCTCap10, fty_none, 1, param_oneInteger },
2367 { "ENUMDEFAULTPROPPARAMS", TBuiltinStdFuncs::func_EnumDefaultPropParams, fty_none, 1, param_oneInteger },
2368 { "RECURRENCE_DATE", TBuiltinStdFuncs::func_Recurrence_Date, fty_timestamp, 7, param_Recurrence_Date },
2369 { "RECURRENCE_COUNT", TBuiltinStdFuncs::func_Recurrence_Count, fty_integer, 7, param_Recurrence_Count },
2370 { "MAKE_RRULE", TBuiltinStdFuncs::func_Make_RRULE, fty_string, 6, param_Make_RRULE },
2371 { "PARSE_RRULE", TBuiltinStdFuncs::func_Parse_RRULE, fty_integer, 8, param_Parse_RRULE },
2372 { "PARSEEMAILSPEC", TBuiltinStdFuncs::func_ParseEmailSpec, fty_integer, 3, param_parseEmailSpec },
2373 { "MAKEEMAILSPEC", TBuiltinStdFuncs::func_MakeEmailSpec, fty_string, 2, param_makeEmailSpec },
2374 { "PARSETEXTWITHPROFILE", TBuiltinStdFuncs::func_ParseTextWithProfile, fty_integer, 4, param_parseTextWithProfile },
2375 { "MAKETEXTWITHPROFILE", TBuiltinStdFuncs::func_MakeTextWithProfile, fty_string, 3, param_makeTextWithProfile },
2376 { "SYNCMLVERS", TBuiltinStdFuncs::func_SyncMLVers, fty_string, 0, NULL__null },
2377 { "ALLDAYCOUNT", TBuiltinStdFuncs::func_AlldayCount, fty_integer, 4, param_AlldayCount },
2378 { "MAKEALLDAY", TBuiltinStdFuncs::func_MakeAllday, fty_integer, 3, param_MakeAllday },
2379};
2380
2381const TFuncTable BuiltInFuncTable = {
2382 sizeof(BuiltInFuncDefs) / sizeof(TBuiltInFuncDef), // size of table
2383 BuiltInFuncDefs, // table pointer
2384 NULL__null // no chain func
2385};
2386
2387
2388/*
2389 * Implementation of TScriptContext
2390 */
2391
2392/* public TScriptContext members */
2393
2394
2395TScriptContext::TScriptContext(TSyncAppBase *aAppBaseP, TSyncSession *aSessionP) :
2396 fAppBaseP(aAppBaseP), // save syncappbase link, must always exist
2397 fSessionP(aSessionP), // save session, can be NULL
2398 fNumVars(0), // number of instantiated vars
2399 fNumParams(0),
2400 fFieldsP(NULL__null), // no field contents yet
2401 scriptname(NULL__null), // no script name known yet
2402 linesource(NULL__null),
2403 executing(false),
2404 debugon(false),
2405 fTargetItemP(NULL__null),
2406 fReferenceItemP(NULL__null),
2407 fParentContextP(NULL__null)
2408{
2409 fVarDefs.clear();
2410} // TScriptContext::TScriptContext
2411
2412
2413TScriptContext::~TScriptContext()
2414{
2415 clear();
2416} // TScriptContext::~TScriptContext
2417
2418
2419// Reset context (clear all variables and definitions)
2420void TScriptContext::clear(void)
2421{
2422 // clear actual fields
2423 clearFields();
2424 // clear definitions
2425 TVarDefs::iterator pos;
2426 for (pos=fVarDefs.begin(); pos!=fVarDefs.end(); pos++) {
2427 if (*pos) delete (*pos);
2428 }
2429 fVarDefs.clear();
2430} // TScriptContext::clear
2431
2432
2433GZones *TScriptContext::getSessionZones(void)
2434{
2435 return
2436 fSessionP ? fSessionP->getSessionZones() : NULL__null;
2437} // TScriptContext::getSessionZones
2438
2439
2440#ifdef SYDEBUG2
2441// get debug logger
2442TDebugLogger *TScriptContext::getDbgLogger(void)
2443{
2444 // use session logger if linked to a session
2445 if (fSessionP)
2446 return fSessionP->getDbgLogger();
2447 // otherwise, use global logger
2448 return fAppBaseP ? fAppBaseP->getDbgLogger() : NULL__null;
2449} // TScriptContext::getDbgLogger
2450
2451
2452uInt32 TScriptContext::getDbgMask(void)
2453{
2454 // use session logger if linked to a session
2455 if (fSessionP)
2456 return fSessionP->getDbgMask();
2457 // otherwise, use global logger
2458 return fAppBaseP ? fAppBaseP->getDbgMask() : 0;
2459} // TScriptContext::getDbgMask
2460#endif
2461
2462
2463
2464// Reset context (clear all variables and definitions)
2465void TScriptContext::clearFields(void)
2466{
2467 if (fFieldsP) {
2468 // clear local vars (fields), but not references
2469 TVarDefs::iterator pos;
2470 for (pos=fVarDefs.begin(); pos!=fVarDefs.end(); pos++) {
2471 sInt16 i=(*pos)->fIdx;
2472 if (i>=fNumVars) break; // all instantiated vars done, stop even if more might be defined
2473 if (fFieldsP[i]) {
2474 if (!(*pos)->fIsRef)
2475 delete fFieldsP[i]; // delete field object (but only if not reference)
2476 fFieldsP[i]=NULL__null;
2477 }
2478 }
2479 // clear array of field pointers
2480 delete [] fFieldsP;
2481 fFieldsP=NULL__null;
2482 fNumVars=0;
2483 }
2484} // TScriptContext::clearFields
2485
2486
2487
2488// check for identifier
2489static bool isidentchar(appChar c) {
2490 return isalnum(c) || c=='_';
2491} // isidentchar
2492
2493
2494// adds source line (including source text, if selected) to token stream
2495static void addSourceLine(uInt16 aLine, const char *aText, string &aScript, bool aIncludeSource, uInt16 &aLastIncludedLine)
2496{
2497 aScript+=TK_SOURCELINE0x13; // token
2498 #ifdef SYDEBUG2
2499 sInt16 linelen=0;
2500 // line #0 is script/function name and must be included anyway
2501 if (aIncludeSource && (aLine>aLastIncludedLine || aLine==0)) {
2502 const char *tt=aText;
2503 while (*tt && *tt!=0x0D && *tt!=0x0A) ++tt;
2504 linelen=tt-aText+1; // room for the terminator
2505 if (linelen>250) linelen=250; // limit size (2 chars needed for number, 3 reserved)
2506 // Note: even empty line will have at least one char (the terminator)
2507 aScript+=(appChar)(2+linelen); // length of additional data
2508 }
2509 else {
2510 // Note: not-included line will have no extra data (not even the terminator), so
2511 // it can be distinguished from empty line
2512 aIncludeSource=false;
2513 aScript+=(appChar)(2); // length of additional data
2514 }
2515 #else
2516 aScript+=(appChar)(2); // length of additional data
2517 #endif
2518 // add source line number
2519 aScript+=(appChar)(aLine>>8); // source line
2520 aScript+=(appChar)(aLine & 0xFF);
2521 // add source itself
2522 #ifdef SYDEBUG2
2523 if (aIncludeSource) {
2524 aScript.append(aText,linelen-1);
2525 aScript+=(appChar)(0); // add terminator
2526 aLastIncludedLine = aLine;
2527 }
2528 #endif
2529} // addSourceLine
2530
2531
2532// Tokenize input string
2533void TScriptContext::Tokenize(TSyncAppBase *aAppBaseP, cAppCharP aScriptName, sInt32 aLine, cAppCharP aScriptText, string &aTScript, const TFuncTable *aContextFuncs, bool aFuncHeader, bool aNoDeclarations, TMacroArgsArray *aMacroArgsP)
2534{
2535 string itm;
2536 string macro;
2537 appChar c,c2;
2538 appChar token,lasttoken=0;
2539 cAppCharP text = aScriptText;
2540 cAppCharP p;
2541 uInt16 line=aLine; // script starts here
2542 sInt16 enu;
2543
2544 // clear output
2545 aTScript.erase();
2546
2547 // debug info if script debugging is enabled in configuration
2548 bool includesource =
2549 #ifdef SYDEBUG2
2550 aAppBaseP->getRootConfig()->fDebugConfig.fDebug & DBG_SCRIPTS0x00001000;
2551 #else
2552 false;
2553 #endif
2554 uInt16 lastincludedline = 0;
2555
2556 if (*text) {
2557 #ifdef SYDEBUG2
2558 // insert script name as line #0 in all but completely empty scripts (or functions)
2559 if (aScriptName) addSourceLine(0,aScriptName,aTScript,true,lastincludedline);
2560 #endif
2561 // insert source line identification token for start of script for all but completely empty scripts
2562 addSourceLine(line,text,aTScript,includesource,lastincludedline);
2563 }
2564 // marco argument expansion
2565 // Note: $n (with n=1,2,3...9) is expanded before any other processing. To insert e.g. $2 literally, use $$2.
2566 // $n macros that can't be expanded will be left in the text AS IS
2567 string itext;
2568 if (aMacroArgsP) {
2569 itext = text; // we need a string to substitute macro args in
2570 size_t i = 0;
2571 while (i<itext.size()) {
2572 c=itext[i++];
2573 if (c=='$') {
2574 // could be macro argument
2575 c=itext[i];
2576 if (c=='$') {
2577 // $$ expands to $
2578 itext.erase(i, 1); // erase second occurrence of $
2579 continue;
2580 }
2581 else if (isdigit(c)) {
2582 ssize_t argidx = c-'1';
2583 if (argidx>=0 && (size_t)argidx<aMacroArgsP->size()) {
2584 // found macro argument, replace in input string
2585 itext.replace(i-1, 2, (*aMacroArgsP)[argidx]);
2586 // no nested macro argument eval, just advance pointer behind replacement text
2587 i += (*aMacroArgsP)[argidx].size()-1;
2588 // check next char
2589 continue;
2590 }
2591 }
2592 }
2593 }
2594 // now use expanded version of text for tokenizing
2595 text = itext.c_str();
2596 }
2597 // actual tokenisation
2598 cAppCharP textstart = text;
2599 SYSYNC_TRYtry {
2600 // process text
2601 while (*text) {
2602 // get next token
2603 token=0; // none yet
2604 // - skip spaces (but not line ends)
2605 while (*text==' ' || *text=='\t') text++;
2606 // - dispatch different types of tokens
2607 c=*text++;
2608 if (c==0) break; // done with script
2609 // - check input now
2610 if (isdigit(c)) {
2611 // numeric literal
2612 p=text-1; // beginning of literal
2613 while (isalnum(*text)) text++;
2614 // - p=start, text=past end of numeric literal
2615 itm.assign(p,text-p);
2616 // code literal into token string
2617 aTScript+=TK_NUMERIC_LITERAL0x10; // token
2618 aTScript+=(appChar)(itm.size()); // length of additional data
2619 aTScript.append(itm);
2620 }
2621 else if (c=='"') {
2622 // string literal, parse
2623 itm.erase();
2624 while ((c=*text)) {
2625 text++;
2626 if (c=='"') { break; } // done
2627 if (c=='\\') {
2628 // escape char
2629 c2=*text++;
2630 if (!c2) break; // escape without anything following -> done
2631 else if (c2=='n') c='\n'; // internal line end
2632 else if (c2=='t') c='\t'; // internal tab
2633 else if (c2=='x') {
2634 // hex char spec
2635 uInt16 sh;
2636 text+=HexStrToUShort(text,sh,2);
2637 c=(appChar)sh;
2638 }
2639 else c=c2; // simply use char following the escape char
2640 }
2641 // now add
2642 if (c) itm+=c;
2643 }
2644 // code literal into token string
2645 aTScript+=TK_STRING_LITERAL0x11; // token
2646 aTScript+=(appChar)(itm.size()); // length of additional data
2647 aTScript.append(itm);
2648 }
2649 else if (isalpha(c)) {
2650 // identifier
2651 // - get identifier
2652 p=text-1;
2653 while (isidentchar(*text)) text++;
2654 // - now p=start of identified, text=end
2655 uInt16 il=text-p;
2656 // - skip whitespace following identifier
2657 while (*text==' ' || *text=='\t') text++;
2658 // - check language keywords
2659 if (strucmp(p,"IF",il)==0) token=TK_IF0x80;
2660 else if (strucmp(p,"ELSE",il)==0) token=TK_ELSE0x81;
2661 else if (strucmp(p,"LOOP",il)==0) token=TK_LOOP0x82;
2662 else if (strucmp(p,"WHILE",il)==0) token=TK_WHILE0x86;
2663 else if (strucmp(p,"BREAK",il)==0) token=TK_BREAK0x83;
2664 else if (strucmp(p,"CONTINUE",il)==0) token=TK_CONTINUE0x84;
2665 else if (strucmp(p,"RETURN",il)==0) token=TK_RETURN0x85;
2666 // - check special constants
2667 else if (strucmp(p,"EMPTY",il)==0) token=TK_EMPTY0x90;
2668 else if (strucmp(p,"UNASSIGNED",il)==0) token=TK_UNASSIGNED0x91;
2669 else if (strucmp(p,"TRUE",il)==0) token=TK_TRUE0x92;
2670 else if (strucmp(p,"FALSE",il)==0) token=TK_FALSE0x93;
2671 // - check types
2672 else if (StrToEnum(ItemFieldTypeNames,numFieldTypes,enu,p,il)) {
2673 // check if declaration and if allowed
2674 if (aNoDeclarations && lasttoken!=TK_OPEN_PARANTHESIS'(')
2675 SYSYNC_THROW(TTokenizeException(aScriptName, "no local variable declarations allowed in this script",textstart,text-textstart,line))throw TTokenizeException(aScriptName, "no local variable declarations allowed in this script"
,textstart,text-textstart,line)
;
2676 // code type into token
2677 aTScript+=TK_TYPEDEF0x18; // token
2678 aTScript+=1; // length of additional data
2679 aTScript+=enu; // type
2680 }
2681 // - check function calls if in body
2682 else if (*text=='(' && !aFuncHeader) {
2683 // identifier followed by ( must be function call (if not in header of function itself)
2684 // - check for built-in function
2685 sInt16 k=0;
2686 while (k<BuiltInFuncTable.numFuncs) {
2687 if (strucmp(p,BuiltInFuncDefs[k].fFuncName,il)==0) {
2688 // built-in
2689 aTScript+=TK_FUNCTION0x15; // token
2690 aTScript+=1; // length of additional data
2691 aTScript+=k; // built-in function index
2692 k=-1; // found flag
2693 break;
2694 }
2695 k++;
2696 }
2697 if (k>=0) {
2698 // no built-in base function, could be context-related function
2699 k=0; // function index (may span several chain links)
2700 // - start with passed functable
2701 TFuncTable *functableP = (TFuncTable *)aContextFuncs;
2702 while(functableP) {
2703 // get the function table properties
2704 sInt16 fidx,numfuncs = functableP->numFuncs;
2705 const TBuiltInFuncDef *funcs = functableP->funcDefs;
2706 // process this func table
2707 for (fidx=0; fidx<numfuncs; fidx++) {
2708 if (strucmp(p,funcs[fidx].fFuncName,il)==0) {
2709 // built-in
2710 aTScript+=TK_CONTEXTFUNCTION0x17; // token
2711 aTScript+=1; // length of additional data
2712 aTScript+=k+fidx; // built-in function index
2713 k=-1; // found flag
2714 break;
2715 }
2716 }
2717 // exit loop if found
2718 if (k<0) break;
2719 // not found, try to chain
2720 if (functableP->chainFunc) {
2721 // obtain next function table (caller context pointer is irrelevant here)
2722 k+=numfuncs; // index for next chained table starts at end of indexes for current table
2723 void *ctx=NULL__null;
2724 functableP=(TFuncTable *)functableP->chainFunc(ctx);
2725 }
2726 else {
2727 functableP=NULL__null; // end chaining loop
2728 }
2729 } // while
2730 if (k>=0) {
2731 // no built-in nor context-built-in found, assume user-defined
2732 aTScript+=TK_USERFUNCTION0x16; // token
2733 aTScript+=(appChar)(il+1); // length of additional data
2734 aTScript+=VARIDX_UNDEFINED-128; // no function index defined yet
2735 aTScript.append(p,il); // identifier name
2736 }
2737 }
2738 }
2739 // - check object qualifiers
2740 else if (*text=='.') {
2741 // must be qualifier
2742 uInt8 objidx=OBJ_AUTO0;
2743 if (strucmp(p,"LOCAL",il)==0) objidx=OBJ_LOCAL1;
2744 else if (strucmp(p,"OLD",il)==0) objidx=OBJ_REFERENCE3;
2745 else if (strucmp(p,"LOOSING",il)==0) objidx=OBJ_REFERENCE3;
2746 else if (strucmp(p,"REFERENCE",il)==0) objidx=OBJ_REFERENCE3;
2747 else if (strucmp(p,"NEW",il)==0) objidx=OBJ_TARGET2;
2748 else if (strucmp(p,"WINNING",il)==0) objidx=OBJ_TARGET2;
2749 else if (strucmp(p,"TARGET",il)==0) objidx=OBJ_TARGET2;
2750 else
2751 SYSYNC_THROW(TTokenizeException(aScriptName,"unknown object name",textstart,text-textstart,line))throw TTokenizeException(aScriptName,"unknown object name",textstart
,text-textstart,line)
;
2752 text++; // skip object qualifier
2753 aTScript+=TK_OBJECT0x14; // token
2754 aTScript+=1; // length of additional data
2755 aTScript+=objidx; // object index
2756 }
2757 else {
2758 // generic identifier, must be some kind of variable reference
2759 aTScript+=TK_IDENTIFIER0x12; // token
2760 aTScript+=(appChar)(il+1); // length of additional data
2761 aTScript+=VARIDX_UNDEFINED-128; // no variable index defined yet
2762 aTScript.append(p,il); // identifier name
2763 }
2764 } // if identifier
2765 else {
2766 // get next char for double-char tokens
2767 c2=*text;
2768 // check special single chars
2769 switch (c) {
2770 // - macro
2771 case '$': {
2772 // get macro name
2773 p=text;
2774 while (isidentchar(*text)) text++;
2775 if (text==p)
2776 SYSYNC_THROW(TTokenizeException(aScriptName,"missing macro name after $",textstart,text-textstart,line))throw TTokenizeException(aScriptName,"missing macro name after $"
,textstart,text-textstart,line)
;
2777 itm.assign(p,text-p);
2778 // see if we have such a macro
2779 TScriptConfig *cfgP = aAppBaseP->getRootConfig()->fScriptConfigP;
2780 TStringToStringMap::iterator pos = cfgP->fScriptMacros.find(itm);
2781 if (pos==cfgP->fScriptMacros.end())
2782 SYSYNC_THROW(TTokenizeException(aScriptName,"unknown macro",textstart,p-1-textstart,line))throw TTokenizeException(aScriptName,"unknown macro",textstart
,p-1-textstart,line)
;
2783 TMacroArgsArray macroArgs;
2784 // check for macro arguments
2785 if (*text=='(') {
2786 // Macro has Arguments
2787 text++;
2788 string arg;
2789 // Note: closing brackets and commas must be escaped when used as part of a macro argument
2790 while (*text) {
2791 c=*text++;
2792 if (c==',' || c==')') {
2793 // end of argument
2794 macroArgs.push_back(arg); // save it in array
2795 arg.erase();
2796 if (c==')') break; // end of macro
2797 continue; // skip comma, next arg
2798 }
2799 else if (c=='\\') {
2800 if (*text==0) break; // end of string
2801 // escaped - use next char w/o testing for , or )
2802 c=*text++;
2803 }
2804 // add to argument string
2805 arg += c;
2806 }
2807 }
2808 // continue tokenizing with macro text
2809 TScriptContext::Tokenize(
2810 aAppBaseP,
2811 itm.c_str(), // pass macro name as "script" name
2812 1, // line number relative to beginning of macro
2813 (*pos).second.c_str(), // use macro text as script text
2814 macro, // produce tokenized macro here
2815 aContextFuncs, // same context
2816 false, // not in function header
2817 aNoDeclarations, // same condition
2818 &macroArgs // macro arguments
2819 );
2820 // append tokenized macro to current script
2821 aTScript+=macro;
2822 // continue with normal text
2823 break;
2824 }
2825 // - grouping
2826 case '(': token=TK_OPEN_PARANTHESIS'('; break; // open subexpression/argument paranthesis
2827 case ')': token=TK_CLOSE_PARANTHESIS')'; break; // close subexpression/argument paranthesis
2828 case ',': token=TK_LIST_SEPARATOR','; break; // comma for separating arguments
2829 case '{': token=TK_BEGIN_BLOCK0x30; aFuncHeader=false; break; // begin block (and start of function body)
2830 case '}': token=TK_END_BLOCK0x31; break; // end block
2831 case ';': token=TK_END_STATEMENT';'; break; // end statement
2832 case '[': token=TK_OPEN_ARRAY0x32; break; // open array paranthesis
2833 case ']': token=TK_CLOSE_ARRAY0x33; break; // close array paranthesis
2834 // line ends
2835 case 0x0D:
2836 if (c2==0x0A) text++; // skip LF of CRLF sequence as well to make sure it is not counted twice
2837 // otherwise treat like LF
2838 case 0x0A:
2839 // new line begins : insert source line identification token (and source of next line, if any)
2840 line++;
2841 addSourceLine(line,text,aTScript,includesource,lastincludedline);
2842 break;
2843 // possible multi-char tokens
2844 case '/':
2845 if (c2=='/') {
2846 text++;
2847 // end-of-line comment, skip it
2848 do { c=*text; if (c==0 || c==0x0D || c==0x0A) break; text++; } while(true);
2849 }
2850 else if (c2=='*') {
2851 // C-style comment, skip until next '*/'
2852 text++;
2853 do {
2854 c=*text;
2855 if (c==0) break;
2856 text++; // next
2857 if (c=='*' && *text=='/') {
2858 // end of comment
2859 text++; // skip /
2860 break; // end of comment
2861 }
2862 else if (c==0x0D || c==0x0A) {
2863 if (*text==0x0A) text++; // skip LF of CRLF sequence as well to make sure it is not counted twice
2864 // new line begins : insert source line identification token (and source of next line, if any)
2865 line++;
2866 addSourceLine(line,text,aTScript,includesource,lastincludedline);
2867 }
2868 } while(true);
2869 }
2870 else
2871 token=TK_DIVIDE0x45; // simple division
2872 break;
2873 case '*': token=TK_MULTIPLY0x44; break; // multiply
2874 case '%': token=TK_MODULUS0x46; break; // modulus
2875 case '+': token=TK_PLUS0x48; break; // add
2876 case '-': token=TK_MINUS0x49; break; // subtract/unary minus
2877 case '^': token=TK_BITWISEXOR0x5C; break; // bitwise XOR
2878 case '~': token=TK_BITWISENOT0x40; break; // bitwise not (one's complement)
2879 case '!':
2880 if (c2=='=') { token=TK_NOTEQUAL0x55; text++; } // !=
2881 else token=TK_LOGICALNOT0x41; // !
2882 break;
2883 case '=':
2884 if (c2=='=') { token=TK_EQUAL0x54; text++; } // ==
2885 else token=TK_ASSIGN0x6C; // =
2886 break;
2887 case '>':
2888 if (c2=='=') { token=TK_GREATEREQUAL0x53; text++; } // >=
2889 else if (c2=='>') { token=TK_SHIFTRIGHT0x4D; text++; } // >>
2890 else token=TK_GREATERTHAN0x51; // >
2891 break;
2892 case '<':
2893 if (c2=='=') { token=TK_LESSEQUAL0x52; text++; } // <=
2894 else if (c2=='<') { token=TK_SHIFTLEFT0x4C; text++; } // <<
2895 else if (c2=='>') { token=TK_NOTEQUAL0x55; text++; } // <>
2896 else token=TK_LESSTHAN0x50; // <
2897 break;
2898 case '&':
2899 if (c2=='&') { token=TK_LOGICALAND0x64; text++; } // &&
2900 else token=TK_BITWISEAND0x58; // &
2901 break;
2902 case '|':
2903 if (c2=='|') { token=TK_LOGICALOR0x68; text++; } // ||
2904 else token=TK_BITWISEOR0x60; // |
2905 break;
2906 default:
2907 SYSYNC_THROW(TTokenizeException(aScriptName,"Syntax Error",textstart,text-textstart,line))throw TTokenizeException(aScriptName,"Syntax Error",textstart
,text-textstart,line)
;
2908 }
2909 }
2910 // add token if simple token found
2911 if (token) aTScript+=token;
2912 lasttoken=token; // save for differentiating casts from declarations etc.
2913 } // while more script text
2914 }
2915 SYSYNC_CATCH (...)catch(...) {
2916 // make sure that script with errors is not stored
2917 aTScript.erase();
2918 SYSYNC_RETHROWthrow;
2919 SYSYNC_ENDCATCH}
2920} // TScriptContext::Tokenize
2921
2922
2923// tokenize and resolve user-defined function
2924void TScriptContext::TokenizeAndResolveFunction(TSyncAppBase *aAppBaseP, sInt32 aLine, cAppCharP aScriptText, TUserScriptFunction &aFuncDef)
2925{
2926 TScriptContext *resolvecontextP=NULL__null;
2927
2928 Tokenize(aAppBaseP, NULL__null, aLine,aScriptText,aFuncDef.fFuncDef,NULL__null,true); // parse as function
2929 SYSYNC_TRYtry {
2930 // resolve identifiers
2931 resolvecontextP=new TScriptContext(aAppBaseP,NULL__null);
2932 resolvecontextP->ResolveIdentifiers(
2933 aFuncDef.fFuncDef,
2934 NULL__null, // no fields
2935 false, // not rebuild
2936 &aFuncDef.fFuncName // store name here
2937 );
2938 delete resolvecontextP;
2939 }
2940 SYSYNC_CATCH (exception &e)catch(exception &e) {
2941 delete resolvecontextP;
2942 SYSYNC_RETHROWthrow;
2943 SYSYNC_ENDCATCH}
2944} // TScriptContext::TokenizeAndResolveFunction
2945
2946
2947
2948// resolve identifiers in a script, if there is a context passed at all
2949void TScriptContext::resolveScript(TSyncAppBase *aAppBaseP, string &aTScript,TScriptContext *&aCtxP, TFieldListConfig *aFieldListConfigP)
2950{
2951 if (aTScript.empty()) return; // no resolving needed
2952 if (!aCtxP) {
2953 // we need a context, create one
2954 aCtxP = new TScriptContext(aAppBaseP, NULL__null);
2955 }
2956 aCtxP->ResolveIdentifiers(
2957 aTScript,
2958 aFieldListConfigP,
2959 false
2960 );
2961} // TScriptContext::ResolveScript
2962
2963
2964// link a script into a context with already instantiated variables.
2965// This is for "late bound" scripts (such as rulescript) that cannot be bound at config
2966// but are determined only later, when their context is already instantiated.
2967void TScriptContext::linkIntoContext(string &aTScript,TScriptContext *aCtxP, TSyncSession *aSessionP)
2968{
2969 if (aTScript.empty() || !aCtxP) return; // no resolving needed (no script or no context)
2970 // resolve identfiers (no new declarations possible)
2971 aCtxP->ResolveIdentifiers(
2972 aTScript,
2973 NULL__null, // no field list
2974 false, // do not rebuild
2975 NULL__null, // no function name
2976 false // no declarations allowed
2977 );
2978} // TScriptContext::linkIntoContext
2979
2980
2981// rebuild a script context for a script, if the script is not empty
2982// - Script must already be resolved with ResolveIdentifiers
2983// - If context already exists, adds new locals to existing ones
2984// - If aBuildVars is set, buildVars() will be called after rebuilding variable definitions
2985void TScriptContext::rebuildContext(TSyncAppBase *aAppBaseP, string &aTScript,TScriptContext *&aCtxP, TSyncSession *aSessionP, bool aBuildVars)
2986{
2987 // Optimization: Nop if script is empty and NOT build var requested for already existing context (=vars from other scripts!)
2988 if (aTScript.empty() && !(aBuildVars && aCtxP)) return;
2989 // Create context if there isn't one yet
2990 if (!aCtxP) {
2991 // we need a context, create one
2992 aCtxP = new TScriptContext(aAppBaseP,aSessionP);
2993 }
2994 SYSYNC_TRYtry {
2995 aCtxP->ResolveIdentifiers(
2996 aTScript,
2997 NULL__null,
2998 true
2999 );
3000 if (aBuildVars) {
3001 // call this one, too
3002 buildVars(aCtxP);
3003 }
3004 }
3005 SYSYNC_CATCH (TScriptErrorException &e)catch(TScriptErrorException &e) {
3006 // show error in log, but otherwise ignore it
3007 POBJDEBUGPRINTFX(aSessionP,DBG_ERROR,("Failed rebuilding script context: %s",e.what())){ if ((aSessionP) && (((0x00000002) & (aSessionP)
->getDbgMask()) == (0x00000002))) (aSessionP)->getDbgLogger
()->setNextMask(0x00000002).DebugPrintfLastMask ("Failed rebuilding script context: %s"
,e.what()); }
;
3008 SYSYNC_ENDCATCH}
3009 SYSYNC_CATCH (...)catch(...) {
3010 SYSYNC_ENDCATCH}
3011} // TScriptContext::rebuildContext
3012
3013
3014// Builds the local variables according to definitions (clears existing vars first)
3015void TScriptContext::buildVars(TScriptContext *&aCtxP)
3016{
3017 if (aCtxP) {
3018 // instantiate the variables
3019 aCtxP->PrepareLocals();
3020 }
3021} // TScriptContext::buildVars
3022
3023
3024// init parsing vars
3025void TScriptContext::initParse(const string &aTScript, bool aExecuting)
3026{
3027 executing=aExecuting;
3028 bp=(cUInt8P)aTScript.c_str(); // start of script
3029 ep=bp+aTScript.size(); // end of script
3030 p=bp; // cursor, start at beginning
3031 np=NULL__null; // no next token yet
3032 line=0; // no line yet
3033 linesource=NULL__null; // no source yet
3034 scriptname=NULL__null; // no name yet
3035 inComment=false; // not in comment yet
3036 if (p == ep)
3037 // empty script
3038 return;
3039
3040 // try to get start line
3041 if (ep>bp && *p!=TK_SOURCELINE0x13)
3042 SYSYNC_THROW(TScriptErrorException(DEBUGTEXT("Script does not start with TK_SOURCELINE","scri2"),line))throw TScriptErrorException("Script does not start with TK_SOURCELINE"
,line)
;
3043 do {
3044 // get script name or first source code line
3045 line = (((uInt8)*(p+2))<<8) + (uInt8)(*(p+3));
3046 nextline=line; // assume same
3047 #ifdef SYDEBUG2
3048 sInt16 n=(uInt8)*(p+1)-2;
3049 if (n>0) {
3050 linesource=(const char *)(p+4); // source for this line starts here
3051 if (line==0) {
3052 // this is the script name
3053 scriptname=linesource; // remember
3054 linesource=NULL__null;
3055 }
3056 }
3057 p+=2+2+n;
3058 #else
3059 p+=2+2; // script starts here
3060 #endif
3061 if (line==0) {
3062 // we have the name
3063 continue; // get first line now
3064 }
3065 break;
3066 } while(true);
3067} // TScriptContext::initParse
3068
3069
3070#ifdef SYDEBUG2
3071
3072// colorize source line (comments only so far)
3073static void colorizeSourceLine(cAppCharP aSource, string &aColorSource, bool &aComment, bool skipping)
3074{
3075 appChar c;
3076 bool start=true;
3077 bool incomment=aComment;
3078 bool lineendcomment=false;
3079
3080 aColorSource.erase();
3081 while ((c=*aSource++)) {
3082 // check for comment start
3083 if (!aComment && !skipping) {
3084 // not in comment
3085 if (c=='/') {
3086 if (*aSource=='*')
3087 { aComment=true; }
3088 else if (*aSource=='/')
3089 { aComment=true; lineendcomment=true; }
3090 }
3091 }
3092 // switch to new color if changes at this point
3093 if (start || incomment!=aComment) {
3094 aColorSource += "&html;";
3095 if (!start)
3096 aColorSource += "</span>"; // not start
3097 aColorSource += "<span class=\"";
3098 aColorSource += skipping ? "skipped" : (aComment ? "comment" : "source");
3099 aColorSource += "\">&html;";
3100 incomment = aComment;
3101 }
3102 // output current char
3103 if (c==' ')
3104 aColorSource += "&sp;"; // will convert to &nbsp; in HTML
3105 else
3106 aColorSource += c;
3107 // check for end of comment after this char
3108 if (aComment && !lineendcomment && !skipping) {
3109 // in C-style comment
3110 if (c=='/' && !start && *(aSource-2)=='*') {
3111 // this was end of C-comment
3112 aComment=false;
3113 }
3114 }
3115 // not start any more
3116 start=false;
3117 }
3118 // end of color
3119 aColorSource += "&html;</span>&html;";
3120 // end of line terminates //-style comment
3121 if (lineendcomment) aComment=false;
3122} // colorizeSourceLine
3123
3124#endif
3125
3126
3127// get token at p if not end of script, updates np to point to next token
3128uInt8 TScriptContext::gettoken(void)
3129{
3130 uInt8 tk;
3131 DBGSTRINGDEF(s)string s;
3132
3133 if (np) p=np; // advance to next token
3134 line=nextline; // advance to next line
3135 if (p>=ep) return 0; // end of script
3136 do {
3137 // get token and search next
3138 tk=*p; // get token
3139 if (tk>TK_MAX_MULTIBYTE0x1F) np=p+1; // single byte token, next is next char
3140 else np=p+2+*(p+1); // use length to find next token
3141 // show current line with source if we have it
3142 #ifdef SYDEBUG2
3143 // delay display of line when processing a end block statement to make sure
3144 // end-skipping decision is made BEFORE showing the line
3145 if (SCRIPTDBGTEST(debugon && fSessionP && (fSessionP->getDbgMask
() & 0x00001000))
&&
3146 linesource &&
3147 executing &&
3148 tk!=TK_END_STATEMENT';' &&
3149 tk!=TK_END_BLOCK0x31) {
3150 bool sk = skipping>0;
3151 colorizeSourceLine(linesource,s,inComment,sk);
3152 SCRIPTDBGMSG(("Line %4hd: %s",line,s.c_str())){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ("Line %4hd: %s",line,s.c_str()); }; } }
;
3153 linesource=NULL__null; // prevent showing same line twice
3154 }
3155 #endif
3156 // check if it is line token, which would be processed invisibly
3157 if (tk==TK_SOURCELINE0x13) {
3158 // get source code line number
3159 line = (((uInt8)*(p+2))<<8) + (uInt8)(*(p+3));
3160 // get source code itself, if present
3161 #ifdef SYDEBUG2
3162 sInt16 n=(uInt8)*(p+1)-2;
3163 if (n>0) linesource=(const char *)(p+4); // source for this line starts here
3164 #endif
3165 // go to next token
3166 p=np;
3167 continue; // fetch next
3168 }
3169 nextline=line; // by default, assume next token is on same line
3170 do {
3171 // check if next is line token, if yes, skip it as well
3172 if (*np==TK_SOURCELINE0x13) {
3173 // get next token's source code line
3174 #ifdef SYDEBUG2
3175 uInt16 showline = nextline; // current line is next that must be shown
3176 #endif
3177 nextline = (((uInt8)*(np+2))<<8) + (uInt8)(*(np+3));
3178 #ifdef SYDEBUG2
3179 // - show last line if we'll skip another line
3180 if (linesource && executing) {
3181 colorizeSourceLine(linesource,s,inComment,skipping>0);
3182 SCRIPTDBGMSG(("Line %4hd: %s",showline,s.c_str())){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ("Line %4hd: %s",showline,s.c_str()); }
; } }
;
3183 linesource=NULL__null; // prevent showing same line twice
3184 }
3185 // - get next line's number and text
3186 sInt16 n=(uInt8)*(np+1)-2;
3187 if (n>0) linesource=(const char *)(np+4); // source for this line starts here
3188 np=np+2+2+n;
3189 #else
3190 np=np+2+2; // skip token
3191 #endif
3192 continue; // test again
3193 }
3194 break;
3195 } while(true);
3196 break;
3197 } while(true);
3198 return tk;
3199} // TScriptContext::gettoken
3200
3201
3202// re-use last token fetched with gettoken()
3203void TScriptContext::reusetoken(void)
3204{
3205 // set pointer such that next gettoken will fetch the same token again
3206 np=p;
3207 nextline=line;
3208} // TScriptContext::reusetoken
3209
3210
3211
3212// Resolve local variable declarations, references and field references
3213// Note: does not clear the context, so multiple scripts can
3214// share the same context
3215// Note: modifies the aTScript passed (inserts identifier IDs)
3216void TScriptContext::ResolveIdentifiers(string &aTScript,TFieldListConfig *aFieldListConfigP, bool aRebuild, string *aFuncNameP, bool aNoNewLocals)
3217{
3218 uInt8 tk; // current token
3219
3220 TItemFieldTypes ty=fty_none;
3221 bool deftype=false;
3222 bool refdecl=false;
3223 bool funcparams=false;
3224 string ident;
3225 sInt16 objidx;
3226
3227 // init parsing
3228 initParse(aTScript);
3229 objidx=OBJ_AUTO0;
3230 while ((tk=gettoken())) {
3231 // check declaration syntax
3232 if (deftype && tk!=TK_IDENTIFIER0x12)
3233 SYSYNC_THROW(TScriptErrorException("Bad declaration",line))throw TScriptErrorException("Bad declaration",line);
3234 if (!funcparams && tk==TK_OPEN_PARANTHESIS'(') {
3235 // could be typecast
3236 if (*np==TK_TYPEDEF0x18) {
3237 // is a typecase
3238 gettoken(); // swallow type identifier
3239 // next must be closing paranthesis
3240 if (gettoken()!=TK_CLOSE_PARANTHESIS')')
3241 SYSYNC_THROW(TScriptErrorException("Invalid typecast",line))throw TScriptErrorException("Invalid typecast",line);
3242 }
3243 }
3244 // process token
3245 else if (tk==TK_TYPEDEF0x18) {
3246 // type definition (for variable declaration or function definition)
3247 ty = (TItemFieldTypes)(*(p+2));
3248 deftype=true; // we are in type definition mode now
3249 // check if we are declaring a variable reference
3250 if (*np==TK_BITWISEAND0x58) {
3251 gettoken();
3252 refdecl=true; // defining a reference
3253 if (!funcparams)
3254 SYSYNC_THROW(TScriptErrorException("Field reference declaration allowed for function parameters",line))throw TScriptErrorException("Field reference declaration allowed for function parameters"
,line)
;
3255 }
3256 }
3257 else if (tk==TK_OBJECT0x14) {
3258 objidx=*(p+2);
3259 continue; // avoid resetting to OBJ_AUTO at end of loop
3260 }
3261 else if (tk==TK_CLOSE_PARANTHESIS')') {
3262 if (funcparams) {
3263 // end of function parameters
3264 fNumParams=fVarDefs.size(); // locals declared up to here are parameters
3265 funcparams=false; // done with parameters
3266 }
3267 }
3268 else if (tk==TK_IDENTIFIER0x12) {
3269 // get identifier
3270 ident.assign((cAppCharP)(p+3),(size_t)(*(p+1)-1));
3271 // check for function definition
3272 if (!funcparams && *np==TK_OPEN_PARANTHESIS'(') {
3273 // this is a function declaration
3274 if (!aRebuild) {
3275 // - check if allowed
3276 if (!aFuncNameP)
3277 SYSYNC_THROW(TScriptErrorException("cannot declare function here",line))throw TScriptErrorException("cannot declare function here",line
)
;
3278 // - save name of function
3279 aFuncNameP->assign(ident);
3280 }
3281 // - determine return type
3282 if (deftype)
3283 fFuncType=ty; // save function return type
3284 else
3285 fFuncType=fty_none; // void function
3286 // - switch to function param parsing
3287 gettoken(); // get opening paranthesis
3288 funcparams=true;
3289 deftype=false;
3290 }
3291 else if (deftype) {
3292 // defining new local variable or parameter
3293 if (objidx!=OBJ_AUTO0 && objidx!=OBJ_LOCAL1)
3294 SYSYNC_THROW(TScriptErrorException("cannot declare non-local variable '%s'",line,ident.c_str()))throw TScriptErrorException("cannot declare non-local variable '%s'"
,line,ident.c_str())
;
3295 bool arr=false;
3296 // define new identifier(s)
3297 cUInt8P idp=p; // remember identifier token pointer
3298 // - check if this is an array definition
3299 if (*np==TK_OPEN_ARRAY0x32) {
3300 arr=true;
3301 gettoken();
3302 if (*np!=TK_CLOSE_ARRAY0x33)
3303 SYSYNC_THROW(TScriptErrorException("Invalid array declaration for '%s'",line,ident.c_str()))throw TScriptErrorException("Invalid array declaration for '%s'"
,line,ident.c_str())
;
3304 #ifndef ARRAYFIELD_SUPPORT1
3305 SYSYNC_THROW(TScriptErrorException("Arrays not available in this version",line))throw TScriptErrorException("Arrays not available in this version"
,line)
;
3306 #endif
3307 gettoken(); // swallow closing bracket
3308 }
3309 // - check if variable already defined
3310 TScriptVarDef *vardefP=NULL__null;
3311 #if SYDEBUG2>1
3312 // always get it by name and compare index
3313 vardefP = getVarDef(ident.c_str(),ident.size());
3314 #else
3315 if (!aRebuild || (sInt8)(*(idp+2))==VARIDX_UNDEFINED-128)
3316 // first time build or index not yet set for another reason: get by name
3317 vardefP = getVarDef(ident.c_str(),ident.size());
3318 else
3319 // on rebuild, get definition by already known index
3320 vardefP = getVarDef(-((sInt8)(*(idp+2))+1));
3321 #endif
3322 #ifndef RELEASE_VERSION
3323 DEBUGPRINTFX(DBG_SCRIPTS+DBG_EXOTIC,({ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "ident=%s, vardefP=0x%lX, vardefP->fIdx=%d, stored idx=%d (=vardef idx %d)"
, ident.c_str(), (long)vardefP, vardefP ? (int)vardefP->fIdx
: -128, (int)((sInt8)(*(idp+2))), (int)(-((sInt8)(*(idp+2))+
1)) ); }
3324 "ident=%s, vardefP=0x%lX, vardefP->fIdx=%d, stored idx=%d (=vardef idx %d)",{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "ident=%s, vardefP=0x%lX, vardefP->fIdx=%d, stored idx=%d (=vardef idx %d)"
, ident.c_str(), (long)vardefP, vardefP ? (int)vardefP->fIdx
: -128, (int)((sInt8)(*(idp+2))), (int)(-((sInt8)(*(idp+2))+
1)) ); }
3325 ident.c_str(),{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "ident=%s, vardefP=0x%lX, vardefP->fIdx=%d, stored idx=%d (=vardef idx %d)"
, ident.c_str(), (long)vardefP, vardefP ? (int)vardefP->fIdx
: -128, (int)((sInt8)(*(idp+2))), (int)(-((sInt8)(*(idp+2))+
1)) ); }
3326 (long)vardefP,{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "ident=%s, vardefP=0x%lX, vardefP->fIdx=%d, stored idx=%d (=vardef idx %d)"
, ident.c_str(), (long)vardefP, vardefP ? (int)vardefP->fIdx
: -128, (int)((sInt8)(*(idp+2))), (int)(-((sInt8)(*(idp+2))+
1)) ); }
3327 vardefP ? (int)vardefP->fIdx : VARIDX_UNDEFINED,{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "ident=%s, vardefP=0x%lX, vardefP->fIdx=%d, stored idx=%d (=vardef idx %d)"
, ident.c_str(), (long)vardefP, vardefP ? (int)vardefP->fIdx
: -128, (int)((sInt8)(*(idp+2))), (int)(-((sInt8)(*(idp+2))+
1)) ); }
3328 (int)((sInt8)(*(idp+2))),{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "ident=%s, vardefP=0x%lX, vardefP->fIdx=%d, stored idx=%d (=vardef idx %d)"
, ident.c_str(), (long)vardefP, vardefP ? (int)vardefP->fIdx
: -128, (int)((sInt8)(*(idp+2))), (int)(-((sInt8)(*(idp+2))+
1)) ); }
3329 (int)(-((sInt8)(*(idp+2))+1)){ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "ident=%s, vardefP=0x%lX, vardefP->fIdx=%d, stored idx=%d (=vardef idx %d)"
, ident.c_str(), (long)vardefP, vardefP ? (int)vardefP->fIdx
: -128, (int)((sInt8)(*(idp+2))), (int)(-((sInt8)(*(idp+2))+
1)) ); }
3330 )){ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "ident=%s, vardefP=0x%lX, vardefP->fIdx=%d, stored idx=%d (=vardef idx %d)"
, ident.c_str(), (long)vardefP, vardefP ? (int)vardefP->fIdx
: -128, (int)((sInt8)(*(idp+2))), (int)(-((sInt8)(*(idp+2))+
1)) ); }
;
3331 #endif
3332 // check for match
3333 if (vardefP) {
3334 // check if type matching
3335 if (ty!=vardefP->fVarType || arr!=vardefP->fIsArray || refdecl!=vardefP->fIsRef)
3336 SYSYNC_THROW(TScriptErrorException("Redefined '%s' to different type",line,ident.c_str()))throw TScriptErrorException("Redefined '%s' to different type"
,line,ident.c_str())
;
3337 }
3338 else {
3339 // not existing yet
3340 if (aNoNewLocals) {
3341 // Note: aNoNewLocals is useful when resolving a script into a context
3342 // with already existing variables (such as RuleScript). We cannot
3343 // add new variables once the VarDefs have been instantiated!
3344 SYSYNC_THROW(TScriptErrorException("Cannot declare variables in this script",line))throw TScriptErrorException("Cannot declare variables in this script"
,line)
;
3345 }
3346 else {
3347 // create new variable definition
3348 vardefP = new TScriptVarDef(ident.c_str(),fVarDefs.size(),ty,arr,refdecl,false);
3349 fVarDefs.push_back(vardefP);
3350 #ifndef RELEASE_VERSION
3351 DEBUGPRINTFX(DBG_SCRIPTS+DBG_EXOTIC,({ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "created new vardef, ident=%s, type=%s, vardefP=0x%lX, vardefs.size()=%ld"
, ident.c_str(), ItemFieldTypeNames[ty], (long)vardefP, (long
)fVarDefs.size() ); }
3352 "created new vardef, ident=%s, type=%s, vardefP=0x%lX, vardefs.size()=%ld",{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "created new vardef, ident=%s, type=%s, vardefP=0x%lX, vardefs.size()=%ld"
, ident.c_str(), ItemFieldTypeNames[ty], (long)vardefP, (long
)fVarDefs.size() ); }
3353 ident.c_str(),{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "created new vardef, ident=%s, type=%s, vardefP=0x%lX, vardefs.size()=%ld"
, ident.c_str(), ItemFieldTypeNames[ty], (long)vardefP, (long
)fVarDefs.size() ); }
3354 ItemFieldTypeNames[ty],{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "created new vardef, ident=%s, type=%s, vardefP=0x%lX, vardefs.size()=%ld"
, ident.c_str(), ItemFieldTypeNames[ty], (long)vardefP, (long
)fVarDefs.size() ); }
3355 (long)vardefP,{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "created new vardef, ident=%s, type=%s, vardefP=0x%lX, vardefs.size()=%ld"
, ident.c_str(), ItemFieldTypeNames[ty], (long)vardefP, (long
)fVarDefs.size() ); }
3356 (long)fVarDefs.size(){ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "created new vardef, ident=%s, type=%s, vardefP=0x%lX, vardefs.size()=%ld"
, ident.c_str(), ItemFieldTypeNames[ty], (long)vardefP, (long
)fVarDefs.size() ); }
3357 )){ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "created new vardef, ident=%s, type=%s, vardefP=0x%lX, vardefs.size()=%ld"
, ident.c_str(), ItemFieldTypeNames[ty], (long)vardefP, (long
)fVarDefs.size() ); }
;
3358 #endif
3359 }
3360 }
3361 if (!aRebuild) {
3362 // creating defs for the first time, set index
3363 aTScript[idp-bp+2]= -(sInt8)(vardefP->fIdx)-1;
3364 }
3365 #if SYDEBUG2>1
3366 else {
3367 // check existing index against new one
3368 if ((sInt8)(*(idp+2))!=-(sInt8)(vardefP->fIdx)-1)
3369 SYSYNC_THROW(TScriptErrorException(DEBUGTEXT("Rebuilding defs gives different index","scri1"),line))throw TScriptErrorException("Rebuilding defs gives different index"
,line)
;
3370 }
3371 #endif
3372 // - check what follows
3373 deftype=false; // default to nothing
3374 refdecl=false;
3375 if (funcparams) {
3376 if (*np==TK_LIST_SEPARATOR',') gettoken(); // ok, next param
3377 }
3378 else {
3379 if (*np==TK_LIST_SEPARATOR',') {
3380 deftype=true; // continue with more definitions of same type
3381 gettoken(); // consume separator
3382 }
3383 else if (*np!=TK_END_STATEMENT';')
3384 SYSYNC_THROW(TScriptErrorException("Missing ';' after declaration of '%s'",line,ident.c_str()))throw TScriptErrorException("Missing ';' after declaration of '%s'"
,line,ident.c_str())
;
3385 }
3386 }
3387 else if (!aRebuild) {
3388 // refer to identifier
3389 sInt16 i=getIdentifierIndex(objidx,aFieldListConfigP,ident.c_str(),ident.size());
3390 if (i==VARIDX_UNDEFINED-128)
3391 SYSYNC_THROW(TScriptErrorException("Undefined identifier '%s'",line,ident.c_str()))throw TScriptErrorException("Undefined identifier '%s'",line,
ident.c_str())
;
3392 // save field/var index in script
3393 aTScript[p-bp+2]= (sInt8)i;
3394 }
3395 }
3396 else {
3397 // Other non-declaration tokens
3398 // - if this is function param def, no other tokens are allowed
3399 if (funcparams) {
3400 SYSYNC_THROW(TScriptErrorException("Invalid function parameter declaration",line))throw TScriptErrorException("Invalid function parameter declaration"
,line)
;
3401 }
3402 else {
3403 // non-declaration stuff
3404 if (tk==TK_USERFUNCTION0x16 && !aRebuild) {
3405 // resolve function
3406 ident.assign((cAppCharP)(p+3),(size_t)(*(p+1)-1));
3407 sInt16 i=getSyncAppBase()->getRootConfig()->fScriptConfigP->getFunctionIndex(ident.c_str(),ident.size());
3408 if (i==VARIDX_UNDEFINED-128)
3409 SYSYNC_THROW(TScriptErrorException("Undefined function '%s'",line,ident.c_str()))throw TScriptErrorException("Undefined function '%s'",line,ident
.c_str())
;
3410 // save function index in script
3411 aTScript[p-bp+2]= (sInt8)i;
3412 }
3413 }
3414 }
3415 objidx=OBJ_AUTO0; // default to auto-select
3416 // process next
3417 }
3418 SHOWVARDEFS(aRebuild ? "Rebuilding" : "Resolving")showVarDefs(aRebuild ? "Rebuilding" : "Resolving");
3419} // TScriptContext::ResolveIdentifiers
3420
3421
3422// create local variable definitions for calling a built-in function
3423// This is called for built-in functions before calling PrepareLocals, and
3424// builds up local var definitions like ResolveIdentifiers does for
3425// script code.
3426void TScriptContext::defineBuiltInVars(const TBuiltInFuncDef *aFuncDefP)
3427{
3428 // get information and save it in the function's context
3429 fNumParams=aFuncDefP->fNumParams;
3430 fFuncType=aFuncDefP->fReturntype;
3431 // get params
3432 sInt16 i=0;
3433 while (i<fNumParams) {
3434 // create parameter definition
3435 uInt8 paramdef=aFuncDefP->fParamTypes[i];
3436 TItemFieldTypes ty= (TItemFieldTypes)(paramdef & PARAM_TYPEMASK0x1F);
3437 bool isref = paramdef & PARAM_REF0x40;
3438 bool isarr = paramdef & PARAM_ARR0x80;
3439 bool isopt = paramdef & PARAM_OPT0x20;
3440 TScriptVarDef *vardefP = new TScriptVarDef("", fVarDefs.size(), ty, isarr, isref, isopt);
3441 fVarDefs.push_back(vardefP);
3442 i++;
3443 }
3444} // TScriptContext::defineBuiltInVars
3445
3446
3447// execute built-in function
3448void TScriptContext::executeBuiltIn(TItemField *&aTermP, const TBuiltInFuncDef *aFuncDefP)
3449{
3450 TItemField *resultP=NULL__null;
3451
3452 // pre-create result field if type is known
3453 // Note: multi-typed functions might create result themselves depending on result type
3454 if (fFuncType!=fty_none)
3455 resultP = newItemField(fFuncType, getSessionZones());
3456 // call actual function routine
3457 (aFuncDefP->fFuncProc)(resultP,this);
3458 // return result
3459 if (aTermP) {
3460 // copy value to exiting result field
3461 if (resultP) {
3462 (*aTermP)=(*resultP);
3463 delete resultP;
3464 } else
3465 aTermP=NULL__null; // no result
3466 }
3467 else {
3468 // just pass back result field (or NULL)
3469 aTermP=resultP;
3470 }
3471} // TScriptContext::executeBuiltIn
3472
3473
3474#if SYDEBUG2>1
3475void TScriptContext::showVarDefs(cAppCharP aTxt)
3476{
3477 if (DEBUGTEST(DBG_SCRIPTS+DBG_EXOTIC)(((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000))
) {
3478 // Show var defs
3479 DEBUGPRINTFX(DBG_SCRIPTS+DBG_EXOTIC+DBG_HOT,("%s - %s, ctx=0x%lX, VarDefs:",aTxt,scriptname ? scriptname : "<name unknown>",(long)this)){ if (((0x00001000 +0x80000000 +0x00000001) & getDbgMask(
)) == (0x00001000 +0x80000000 +0x00000001)) getDbgLogger()->
setNextMask(0x00001000 +0x80000000 +0x00000001).DebugPrintfLastMask
("%s - %s, ctx=0x%lX, VarDefs:",aTxt,scriptname ? scriptname
: "<name unknown>",(long)this); }
;
3480 TVarDefs::iterator pos;
3481 for (pos=fVarDefs.begin(); pos!=fVarDefs.end(); pos++) {
3482 DEBUGPRINTFX(DBG_SCRIPTS+DBG_EXOTIC,({ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "%d: %s %s%s%s", (*pos)->fIdx, ItemFieldTypeNames
[(*pos)->fVarType], (*pos)->fIsRef ? "&" : "", (*pos
)->fVarName.c_str(), (*pos)->fIsArray ? "[]" : "" ); }
3483 "%d: %s %s%s%s",{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "%d: %s %s%s%s", (*pos)->fIdx, ItemFieldTypeNames
[(*pos)->fVarType], (*pos)->fIsRef ? "&" : "", (*pos
)->fVarName.c_str(), (*pos)->fIsArray ? "[]" : "" ); }
3484 (*pos)->fIdx,{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "%d: %s %s%s%s", (*pos)->fIdx, ItemFieldTypeNames
[(*pos)->fVarType], (*pos)->fIsRef ? "&" : "", (*pos
)->fVarName.c_str(), (*pos)->fIsArray ? "[]" : "" ); }
3485 ItemFieldTypeNames[(*pos)->fVarType],{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "%d: %s %s%s%s", (*pos)->fIdx, ItemFieldTypeNames
[(*pos)->fVarType], (*pos)->fIsRef ? "&" : "", (*pos
)->fVarName.c_str(), (*pos)->fIsArray ? "[]" : "" ); }
3486 (*pos)->fIsRef ? "&" : "",{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "%d: %s %s%s%s", (*pos)->fIdx, ItemFieldTypeNames
[(*pos)->fVarType], (*pos)->fIsRef ? "&" : "", (*pos
)->fVarName.c_str(), (*pos)->fIsArray ? "[]" : "" ); }
3487 (*pos)->fVarName.c_str(),{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "%d: %s %s%s%s", (*pos)->fIdx, ItemFieldTypeNames
[(*pos)->fVarType], (*pos)->fIsRef ? "&" : "", (*pos
)->fVarName.c_str(), (*pos)->fIsArray ? "[]" : "" ); }
3488 (*pos)->fIsArray ? "[]" : ""{ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "%d: %s %s%s%s", (*pos)->fIdx, ItemFieldTypeNames
[(*pos)->fVarType], (*pos)->fIsRef ? "&" : "", (*pos
)->fVarName.c_str(), (*pos)->fIsArray ? "[]" : "" ); }
3489 )){ if (((0x00001000 +0x80000000) & getDbgMask()) == (0x00001000
+0x80000000)) getDbgLogger()->setNextMask(0x00001000 +0x80000000
).DebugPrintfLastMask ( "%d: %s %s%s%s", (*pos)->fIdx, ItemFieldTypeNames
[(*pos)->fVarType], (*pos)->fIsRef ? "&" : "", (*pos
)->fVarName.c_str(), (*pos)->fIsArray ? "[]" : "" ); }
;
3490 }
3491 }
3492} // TScriptContext::showVarDefs
3493#endif
3494
3495
3496
3497// Prepare local variables (create local fields), according to definitions (re-)built with ResolveIdentifiers()
3498// Note: This is called after ResolveIdentifiers() for each script of that context
3499// have been called. Normally ResolveIdentifiers() was called already at config parse
3500// with an anonymous context that is lost when PrepareLocals needs to be called.
3501// Therefore, ResolveIdentifiers(aRebuild=true) can be called to rebuild the definitions from
3502// the script(s) involved. Care must be taken to call ResolveIdentifiers() for each script
3503// in the same order as at config parse to make sure already resolved indexes will match the variables (again).
3504// Note: Field references will have a NULL pointer (which must be filled when the function is
3505// called.
3506bool TScriptContext::PrepareLocals(void)
3507{
3508 // make sure old array is cleared
3509 SHOWVARDEFS("PrepareLocals")showVarDefs("PrepareLocals");
3510 clearFields();
3511 // create array of appropriate size for locals
3512 fNumVars=fVarDefs.size();
3513 SYSYNC_TRYtry {
3514 if (fNumVars) {
3515 fFieldsP=new TItemFieldP[fNumVars];
3516 if (fFieldsP==NULL__null) return false;
3517 // - init it with null pointers to make sure we can survive when field instantiation fails
3518 for (sInt16 i=0; i<fNumVars; i++) fFieldsP[i]=NULL__null;
3519 // - now instantiate local fields (but leave references = NULL)
3520 TVarDefs::iterator pos;
3521 for (pos=fVarDefs.begin(); pos!=fVarDefs.end(); pos++) {
3522 if (!(*pos)->fIsRef && ((*pos)->fVarType!=fty_none)) {
3523 // this is not a reference and not an untyped parameter:
3524 // instantiate a locally owned field
3525 fFieldsP[(*pos)->fIdx]=sysync::newItemField((*pos)->fVarType,getSessionZones(),(*pos)->fIsArray);
3526 }
3527 }
3528 }
3529 }
3530 SYSYNC_CATCH (...)catch(...) {
3531 // failed, remove fields again
3532 clearFields();
3533 return false;
3534 SYSYNC_ENDCATCH}
3535 return true;
3536} // TScriptContext::PrepareLocals
3537
3538
3539// execute a script returning a boolean
3540bool TScriptContext::executeTest(
3541 bool aDefaultAnswer, // result if no script is there or script returns no value
3542 TScriptContext *aCtxP,
3543 const string &aTScript,
3544 const TFuncTable *aFuncTableP, // context's function table, NULL if none
3545 void *aCallerContext, // free pointer possibly having a meaning for context functions and chain function
3546 TMultiFieldItem *aTargetItemP, // target (or "loosing") item
3547 bool aTargetWritable, // if set, target item may be modified
3548 TMultiFieldItem *aReferenceItemP, // reference for source (or "old" or "winning") item
3549 bool aRefWritable // if set, reference item may also be written
3550)
3551{
3552 // if no script (no context), return default answer
3553 if (!aCtxP) return aDefaultAnswer;
3554 // now call and evaluate boolean result
3555 TItemField *resP;
3556 bool res;
3557 res=aCtxP->ExecuteScript(
3558 aTScript,
3559 &resP, // we want a result
3560 false, // not a function
3561 aFuncTableP, // context function table
3562 aCallerContext, // context data
3563 aTargetItemP, // target (or "loosing") item
3564 aTargetWritable, // if set, target item may be modified
3565 aReferenceItemP, // reference for source (or "old" or "winning") item
3566 aRefWritable // if set, reference item may also be written
3567 );
3568 if (!res) {
3569 // script execution failed, do as if there was no script
3570 res=aDefaultAnswer;
3571 }
3572 else {
3573 // evaluate result
3574 if (resP) {
3575 res=resP->getAsBoolean();
3576 // get rid of result
3577 delete resP;
3578 }
3579 else {
3580 // no result, return default
3581 res=aDefaultAnswer;
3582 }
3583 }
3584 // return result
3585 return res;
3586} // TScriptContext::executeTest
3587
3588
3589// execute a script returning a result if there is a context for it
3590bool TScriptContext::executeWithResult(
3591 TItemField *&aResultField, // can be default result or NULL, will contain result or NULL if no result
3592 TScriptContext *aCtxP,
3593 const string &aTScript,
3594 const TFuncTable *aFuncTableP, // context's function table, NULL if none
3595 void *aCallerContext, // free pointer possibly having a meaning for context functions
3596 TMultiFieldItem *aTargetItemP, // target (or "loosing") item
3597 bool aTargetWritable, // if set, target item may be modified
3598 TMultiFieldItem *aReferenceItemP, // reference for source (or "old" or "winning") item
3599 bool aRefWritable, // if set, reference item may also be written
3600 bool aNoDebug // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
3601)
3602{
3603 // if no script (no context), return default answer
3604 if (!aCtxP) return true; // ok, leave aResultFieldP unmodified
3605 // now call and evaluate result
3606 return aCtxP->ExecuteScript(
3607 aTScript,
3608 &aResultField, // we want a result, if we pass a default it will be modified if script has a result
3609 false, // not a function
3610 aFuncTableP, // context function table
3611 aCallerContext, // context data
3612 aTargetItemP, // target (or "loosing") item
3613 aTargetWritable, // if set, target item may be modified
3614 aReferenceItemP, // reference for source (or "old" or "winning") item
3615 aRefWritable, // if set, reference item may also be written
3616 aNoDebug // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
3617 );
3618} // TScriptContext::executeTest
3619
3620
3621
3622
3623// execute a script if there is a context for it
3624bool TScriptContext::execute(
3625 TScriptContext *aCtxP,
3626 const string &aTScript,
3627 const TFuncTable *aFuncTableP, // context's function table, NULL if none
3628 void *aCallerContext, // free pointer possibly having a meaning for context functions
3629 TMultiFieldItem *aTargetItemP, // target (or "loosing") item
3630 bool aTargetWritable, // if set, target item may be modified
3631 TMultiFieldItem *aReferenceItemP, // reference for source (or "old" or "winning") item
3632 bool aRefWritable, // if set, reference item may also be written
3633 bool aNoDebug // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
3634)
3635{
3636 if (!aCtxP) return true; // no script, success
3637 // execute script in given context
3638 SYSYNC_TRYtry {
3639 bool r=aCtxP->ExecuteScript(
3640 aTScript,
3641 NULL__null, // we don't need a result
3642 false, // not a function
3643 aFuncTableP, // context specific function table
3644 aCallerContext, // caller's context private data pointer
3645 aTargetItemP, // target (or "loosing") item
3646 aTargetWritable, // if set, target item may be modified
3647 aReferenceItemP, // reference for source (or "old" or "winning") item
3648 aRefWritable, // if set, reference item may also be written
3649 aNoDebug // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
3650 );
3651 return r;
3652 }
3653 SYSYNC_CATCH (...)catch(...) {
3654 SYSYNC_RETHROWthrow;
3655 SYSYNC_ENDCATCH}
3656} // TScriptContext::execute
3657
3658
3659void TScriptContext::pushState(TScriptState aNewState, cUInt8P aBegin, uInt16 aLine)
3660{
3661 if (fStackEntries>=maxstackentries)
3662 SYSYNC_THROW(TScriptErrorException("too many IF/ELSE/WHILE/LOOP nested",line))throw TScriptErrorException("too many IF/ELSE/WHILE/LOOP nested"
,line)
;
3663 // use defaults
3664 if (aBegin==NULL__null) {
3665 aBegin = np; // next token after current token
3666 aLine = nextline;
3667 }
3668 // push current state
3669 fScriptstack[fStackEntries].state=aNewState; // new state
3670 fScriptstack[fStackEntries].begin=aBegin; // start of block
3671 fScriptstack[fStackEntries].line=aLine; // line where block starts
3672 fStackEntries++;
3673} // TScriptContext::pushState
3674
3675
3676// pop state from flow control state stack
3677void TScriptContext::popState(TScriptState aCurrentStateExpected)
3678{
3679 if (fScriptstack[fStackEntries-1].state!=aCurrentStateExpected)
3680 SYSYNC_THROW(TScriptErrorException("bad block/IF/ELSE/WHILE/LOOP nesting",line))throw TScriptErrorException("bad block/IF/ELSE/WHILE/LOOP nesting"
,line)
;
3681 // remove entry
3682 fStackEntries--; // remove stack entry
3683 if (fStackEntries<1)
3684 SYSYNC_THROW(TScriptErrorException(DEBUGTEXT("unbalanced control flow stack pop","scri3"),line))throw TScriptErrorException("unbalanced control flow stack pop"
,line)
;
3685} // TScriptContext::popState
3686
3687
3688// get variable specification, returns true if writable
3689bool TScriptContext::getVarField(TItemField *&aItemFieldP)
3690{
3691 sInt16 objnum, varidx, arridx;
3692 TMultiFieldItem *itemP=NULL__null;
3693 bool writeable=false;
3694 bool fidoffs=false;
3695 uInt8 tk;
3696
3697 aItemFieldP=NULL__null; // none by default
3698 tk=gettoken();
3699 objnum=OBJ_AUTO0; // default to automatic object selection
3700 // check for object specifier
3701 if (tk==TK_OBJECT0x14) {
3702 // object qualifier
3703 objnum=*(p+2);
3704 tk=gettoken();
3705 // must be followed by identifier
3706 if (tk!=TK_IDENTIFIER0x12)
3707 SYSYNC_THROW(TScriptErrorException("bad variable/field reference",line))throw TScriptErrorException("bad variable/field reference",line
)
;
3708 }
3709 // check for object itself
3710 if (tk==TK_IDENTIFIER0x12) {
3711 #ifdef SYDEBUG2
3712 cAppCharP idnam=(cAppCharP)(p+3);
3713 int idlen=*(p+1)-1;
3714 #endif
3715 // get index
3716 varidx=(sInt8)(*(p+2));
3717 if (varidx==VARIDX_UNDEFINED-128)
3718 SYSYNC_THROW(TScriptErrorException("Undefined identifier",line))throw TScriptErrorException("Undefined identifier",line);
3719 // check possible array index
3720 arridx=-1; // default to non-array
3721 if (*np==TK_OPEN_ARRAY0x32) {
3722 tk=gettoken(); // consume open bracket
3723 // check special field-offset access mode
3724 if (*np==TK_PLUS0x48) {
3725 fidoffs=true;
3726 gettoken(); // consume the plus
3727 }
3728 TItemField *fldP = new TIntegerField;
3729 SYSYNC_TRYtry {
3730 evalExpression(fldP,EXPRDBGTEST(debugon && fSessionP && ((fSessionP->getDbgMask
() & (0x00001000|0x00000800)) == (0x00001000|0x00000800))
)
,NULL__null); // do not show array index expression evaluation
3731 arridx=fldP->getAsInteger();
3732 delete fldP;
3733 }
3734 SYSYNC_CATCH (...)catch(...) {
3735 delete fldP;
3736 SYSYNC_RETHROWthrow;
3737 SYSYNC_ENDCATCH}
3738 // check closing array bracket
3739 tk=gettoken();
3740 if (tk!=TK_CLOSE_ARRAY0x33)
3741 SYSYNC_THROW(TScriptErrorException("missing ']' for array reference",line))throw TScriptErrorException("missing ']' for array reference"
,line)
;
3742 }
3743 // now access field
3744 #ifdef SYDEBUG2
3745 sInt16 oai=arridx;
3746 #endif
3747 // - determine object if not explicitly specified
3748 if (objnum==OBJ_AUTO0) {
3749 if (varidx<0) objnum=OBJ_LOCAL1;
3750 else objnum=OBJ_TARGET2;
3751 }
3752 // - now access (arridx==-1 if we don't want to access leaf element)
3753 if (objnum==OBJ_LOCAL1) {
3754 if (fidoffs) { varidx-=arridx; arridx=-1; } // use array index as offset within local fields (negative indices!)
3755 aItemFieldP=getFieldOrVar(NULL__null,varidx,arridx);
3756 writeable=true; // locals are always writeable
3757 }
3758 else {
3759 // prepare index/arrayindex to access
3760 if (fidoffs) { varidx+=arridx; arridx=-1; } // use array index as offset within field list
3761 // get item to access
3762 if (objnum==OBJ_TARGET2) { itemP=fTargetItemP; writeable=fTargetWritable; }
3763 else if (objnum==OBJ_REFERENCE3) { itemP=fReferenceItemP; writeable=fRefWritable; }
3764 if (itemP)
3765 aItemFieldP=getFieldOrVar(itemP,varidx,arridx);
3766 else
3767 SYSYNC_THROW(TScriptErrorException("field not accessible in this context",line))throw TScriptErrorException("field not accessible in this context"
,line)
;
3768 }
3769 // error if no field
3770 DBGSTRINGDEF(s)string s;
3771 DBGSTRINGDEF(sa)string sa;
3772 DBGVALUESHOW(s,aItemFieldP)dbgValueShow(s,aItemFieldP);
3773 if (EXPRDBGTEST(debugon && fSessionP && ((fSessionP->getDbgMask
() & (0x00001000|0x00000800)) == (0x00001000|0x00000800))
)
) {
3774 if (oai>=0) {
3775 if (arridx!=-1)
3776 StringObjPrintf(sa,"[%hd]",oai);
3777 else
3778 StringObjPrintf(sa,"[+%hd]",oai);
3779 }
3780 else {
3781 // no array access
3782 sa.erase();
3783 }
3784 SCRIPTDBGMSG(({ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %s: %.*s%s = %s", objnum==1 ? "Local Variable"
: (objnum==3 ? "OLD/LOOSING Field " : "Field"), idlen,idnam,
sa.c_str(), s.c_str() ); }; } }
3785 "- %s: %.*s%s = %s",{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %s: %.*s%s = %s", objnum==1 ? "Local Variable"
: (objnum==3 ? "OLD/LOOSING Field " : "Field"), idlen,idnam,
sa.c_str(), s.c_str() ); }; } }
3786 objnum==OBJ_LOCAL ? "Local Variable" :{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %s: %.*s%s = %s", objnum==1 ? "Local Variable"
: (objnum==3 ? "OLD/LOOSING Field " : "Field"), idlen,idnam,
sa.c_str(), s.c_str() ); }; } }
3787 (objnum==OBJ_REFERENCE ? "OLD/LOOSING Field " : "Field"),{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %s: %.*s%s = %s", objnum==1 ? "Local Variable"
: (objnum==3 ? "OLD/LOOSING Field " : "Field"), idlen,idnam,
sa.c_str(), s.c_str() ); }; } }
3788 idlen,idnam,{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %s: %.*s%s = %s", objnum==1 ? "Local Variable"
: (objnum==3 ? "OLD/LOOSING Field " : "Field"), idlen,idnam,
sa.c_str(), s.c_str() ); }; } }
3789 sa.c_str(),{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %s: %.*s%s = %s", objnum==1 ? "Local Variable"
: (objnum==3 ? "OLD/LOOSING Field " : "Field"), idlen,idnam,
sa.c_str(), s.c_str() ); }; } }
3790 s.c_str(){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %s: %.*s%s = %s", objnum==1 ? "Local Variable"
: (objnum==3 ? "OLD/LOOSING Field " : "Field"), idlen,idnam,
sa.c_str(), s.c_str() ); }; } }
3791 )){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %s: %.*s%s = %s", objnum==1 ? "Local Variable"
: (objnum==3 ? "OLD/LOOSING Field " : "Field"), idlen,idnam,
sa.c_str(), s.c_str() ); }; } }
;
3792 }
3793 if (!aItemFieldP)
3794 SYSYNC_THROW(TScriptErrorException("undefined identifier, bad array index or offset",line))throw TScriptErrorException("undefined identifier, bad array index or offset"
,line)
;
3795 // return field pointer we've found, if any
3796 return writeable;
3797 }
3798 else
3799 SYSYNC_THROW(TScriptErrorException("expected identifier",line))throw TScriptErrorException("expected identifier",line);
3800} // TScriptContext::getVarField
3801
3802
3803
3804// evaluate function parameters
3805void TScriptContext::evalParams(TScriptContext *aFuncContextP)
3806{
3807 uInt8 tk;
3808 sInt16 paramidx;
3809 TScriptVarDef *vardefP;
3810 TItemField *fldP;
3811
3812 tk=gettoken();
3813 if (tk!=TK_OPEN_PARANTHESIS'(')
3814 SYSYNC_THROW(TScriptErrorException("missing '(' of function parameter list",line))throw TScriptErrorException("missing '(' of function parameter list"
,line)
;
3815 paramidx=0;
3816 // special check for function with only optional params
3817 if (aFuncContextP->getNumParams()>0 && aFuncContextP->getVarDef((short)0)->fIsOpt) {
3818 if (*np==TK_CLOSE_PARANTHESIS')') goto noparams; // first is optional already -> ok to see closing paranthesis here
3819 }
3820 // at least one param
3821 while(paramidx<aFuncContextP->getNumParams()) {
3822 vardefP=aFuncContextP->getVarDef(paramidx);
3823 // check what param is expected next
3824 if (vardefP->fIsRef) {
3825 // by reference, we must have a writable lvalue here
3826 fldP=NULL__null;
3827 if (*np==TK_IDENTIFIER0x12 || *np==TK_OBJECT0x14) {
3828 // get writable field/var of caller
3829 if (!getVarField(fldP))
3830 SYSYNC_THROW(TScriptErrorException("expected writable by-reference parameter",line))throw TScriptErrorException("expected writable by-reference parameter"
,line)
;
3831 // type must match (or destination must be untyped)
3832 if (vardefP->fVarType!=fldP->getType() && vardefP->fVarType!=fty_none)
3833 SYSYNC_THROW(TScriptErrorException("expected by-reference parameter of type '%s'",line,ItemFieldTypeNames[vardefP->fVarType]))throw TScriptErrorException("expected by-reference parameter of type '%s'"
,line,ItemFieldTypeNames[vardefP->fVarType])
;
3834 // type must match (or destination must be untyped)
3835 if (vardefP->fIsArray && !(fldP->isArray()))
3836 SYSYNC_THROW(TScriptErrorException("expected array as by-reference parameter",line))throw TScriptErrorException("expected array as by-reference parameter"
,line)
;
3837 // assign field to function context's local var list
3838 aFuncContextP->setLocalVar(paramidx,fldP);
3839 }
3840 else
3841 SYSYNC_THROW(TScriptErrorException("expected by-reference parameter",line))throw TScriptErrorException("expected by-reference parameter"
,line)
;
3842 }
3843 else {
3844 // by value
3845 // - get field where to assign value
3846 if (vardefP->fVarType!=fty_none) {
3847 // already prepared typed parameter
3848 fldP=aFuncContextP->getLocalVar(paramidx);
3849 // - evaluate expression into local var
3850 evalExpression(fldP,EXPRDBGTEST(debugon && fSessionP && ((fSessionP->getDbgMask
() & (0x00001000|0x00000800)) == (0x00001000|0x00000800))
)
); // do not show expression, as we will show param
3851 }
3852 else {
3853 // untyped param, let evaluation create correct type
3854 fldP=NULL__null;
3855 evalExpression(fldP,EXPRDBGTEST(debugon && fSessionP && ((fSessionP->getDbgMask
() & (0x00001000|0x00000800)) == (0x00001000|0x00000800))
)
); // do not show expression, as we will show param
3856 // untyped params are not yet instantiated, assign expression result now
3857 aFuncContextP->setLocalVar(paramidx,fldP);
3858 }
3859 }
3860 DBGSTRINGDEF(s)string s;
3861 DBGVALUESHOW(s,fldP)dbgValueShow(s,fldP);
3862 SCRIPTDBGMSG(({ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- Parameter #%d (by %s) = %s", paramidx
+1, vardefP->fIsRef ? "reference" : "value", s.c_str() ); }
; } }
3863 "- Parameter #%d (by %s) = %s",{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- Parameter #%d (by %s) = %s", paramidx
+1, vardefP->fIsRef ? "reference" : "value", s.c_str() ); }
; } }
3864 paramidx+1,{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- Parameter #%d (by %s) = %s", paramidx
+1, vardefP->fIsRef ? "reference" : "value", s.c_str() ); }
; } }
3865 vardefP->fIsRef ? "reference" : "value",{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- Parameter #%d (by %s) = %s", paramidx
+1, vardefP->fIsRef ? "reference" : "value", s.c_str() ); }
; } }
3866 s.c_str(){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- Parameter #%d (by %s) = %s", paramidx
+1, vardefP->fIsRef ? "reference" : "value", s.c_str() ); }
; } }
3867 )){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- Parameter #%d (by %s) = %s", paramidx
+1, vardefP->fIsRef ? "reference" : "value", s.c_str() ); }
; } }
;
3868 // got param
3869 paramidx++;
3870 // check parameter delimiters
3871 if (paramidx<aFuncContextP->getNumParams()) {
3872 // function potentially has more params
3873 tk=gettoken();
3874 if (tk!=TK_LIST_SEPARATOR',') {
3875 // - check if next is an optional parameter
3876 if (aFuncContextP->getVarDef(paramidx)->fIsOpt)
3877 goto endofparams; // yes, optional -> check for end of parameter list
3878 else
3879 SYSYNC_THROW(TScriptErrorException("expected ',' (more parameters)",line))throw TScriptErrorException("expected ',' (more parameters)",
line)
; // non-optional parameter missing
3880 }
3881 }
3882 }
3883 // check end of parameter list
3884noparams:
3885 tk=gettoken();
3886endofparams:
3887 if (tk!=TK_CLOSE_PARANTHESIS')')
3888 SYSYNC_THROW(TScriptErrorException("missing ')' of function parameter list",line))throw TScriptErrorException("missing ')' of function parameter list"
,line)
;
3889} // TScriptContext::evalParams
3890
3891
3892
3893// evaluate term, return caller-owned field containing term data
3894TItemField *TScriptContext::evalTerm(TItemFieldTypes aResultType)
3895{
3896 TItemFieldTypes termtype=aResultType; // default to result type
3897 TItemField *termP=NULL__null;
3898 uInt8 tk;
3899 TScriptContext *funccontextP;
3900 string *funcscript;
3901 const char *funcname;
3902 uInt16 funcnamelen;
3903
3904 // Evaluate term. A term is
3905 // - a subexpression in paranthesis
3906 // - a variable reference
3907 // - a builtin function call
3908 // - a user defined function call
3909 // - a literal, including the special EMPTY and UNASSIGNED ones
3910 // A term can be preceeded by a typecast
3911 do {
3912 tk=gettoken();
3913 if (tk==TK_OPEN_PARANTHESIS'(') {
3914 // typecast or subexpression
3915 if (*np==TK_TYPEDEF0x18) {
3916 // this is a typecast
3917 gettoken(); // actually get type
3918 termtype=(TItemFieldTypes)(*(p+2)); // set new term target type
3919 if (gettoken()!=TK_CLOSE_PARANTHESIS')')
3920 SYSYNC_THROW(TScriptErrorException("missing ')' after typecast",line))throw TScriptErrorException("missing ')' after typecast",line
)
;
3921 if (aResultType!=fty_none && aResultType!=termtype)
3922 SYSYNC_THROW(TScriptErrorException("invalid typecast",line))throw TScriptErrorException("invalid typecast",line);
3923 continue; // now get term
3924 }
3925 else {
3926 // process subexpression into term
3927 if (termtype!=fty_none) {
3928 // prepare distinct result type field
3929 termP=newItemField(termtype, getSessionZones());
3930 }
3931 // puts result in a caller-owned termP (creates new one ONLY if caller passes NULL)
3932 evalExpression(termP,EXPRDBGTEST(debugon && fSessionP && ((fSessionP->getDbgMask
() & (0x00001000|0x00000800)) == (0x00001000|0x00000800))
)
); // do not show subexpression results
3933 if (gettoken()!=TK_CLOSE_PARANTHESIS')')
3934 SYSYNC_THROW(TScriptErrorException("missing ')' after expression",line))throw TScriptErrorException("missing ')' after expression",line
)
;
3935 }
3936 }
3937 else if (tk==TK_FUNCTION0x15 || tk==TK_CONTEXTFUNCTION0x17) {
3938 // get function definition table reference
3939 const TFuncTable *functableP =
3940 (tk==TK_FUNCTION0x15) ?
3941 &BuiltInFuncTable : // globally available functions
3942 fFuncTableP; // context specific functions
3943 // get the function index
3944 sInt16 funcid=*(p+2);
3945 // now find the function definition record in the chain of function tables
3946 void *callerContext = fCallerContext; // start with caller context of current script
3947 while(functableP && funcid>=functableP->numFuncs) {
3948 // function is in a chained table
3949 // - adjust id to get offset based on next table
3950 funcid-=functableP->numFuncs;
3951 // - get next table and next caller context
3952 if (functableP->chainFunc)
3953 functableP = (TFuncTable *)functableP->chainFunc(callerContext);
3954 else
3955 functableP = NULL__null;
3956 }
3957 if (!functableP)
3958 SYSYNC_THROW(TScriptErrorException("undefined context function",line))throw TScriptErrorException("undefined context function",line
)
;
3959 // found function table and caller context, now get funcdef
3960 const TBuiltInFuncDef *funcdefP = &(functableP->funcDefs[funcid]);
3961 // execute built-in function call
3962 funcname=funcdefP->fFuncName;
3963 funcnamelen=100; // enough
3964 SCRIPTDBGMSG(("- %s() built-in function call:",funcname)){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ("- %s() built-in function call:",funcname
); }; } }
;
3965 // - get context for built-in function
3966 funccontextP=new TScriptContext(fAppBaseP,fSessionP);
3967 SYSYNC_TRYtry {
3968 // define built-in parameters of function context
3969 funccontextP->defineBuiltInVars(funcdefP);
3970 // prepare local variables of function context
3971 funccontextP->PrepareLocals();
3972 // prepare parameters
3973 evalParams(funccontextP);
3974 // copy current line for reference (as builtins have no own line number
3975 funccontextP->line=line;
3976 // copy caller's context pointer (possibly modified by function table chaining)
3977 funccontextP->fCallerContext=callerContext;
3978 funccontextP->fParentContextP=this; // link to calling script context
3979 // copy target and reference item vars
3980 funccontextP->fTargetItemP = fTargetItemP;
3981 funccontextP->fTargetWritable = fTargetWritable;
3982 funccontextP->fReferenceItemP = fReferenceItemP;
3983 funccontextP->fRefWritable = fRefWritable;
3984 // execute function
3985 funccontextP->executeBuiltIn(termP,funcdefP);
3986 // show by-ref parameters after call
3987 if (SCRIPTDBGTEST(debugon && fSessionP && (fSessionP->getDbgMask
() & 0x00001000))
) {
3988 // show by-ref variables
3989 for (uInt16 i=0; i<funcdefP->fNumParams; i++) {
3990 if (funcdefP->fParamTypes[i] & PARAM_REF0x40) {
3991 // is a parameter passed by reference, possibly changed by function call
3992 DBGSTRINGDEF(s)string s;
3993 DBGVALUESHOW(s,funccontextP->getLocalVar(i))dbgValueShow(s,funccontextP->getLocalVar(i));
3994 SCRIPTDBGMSG(({ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- return value of by-ref parameter #%d = %s"
, i+1, s.c_str() ); }; } }
3995 "- return value of by-ref parameter #%d = %s",{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- return value of by-ref parameter #%d = %s"
, i+1, s.c_str() ); }; } }
3996 i+1, s.c_str(){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- return value of by-ref parameter #%d = %s"
, i+1, s.c_str() ); }; } }
3997 )){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- return value of by-ref parameter #%d = %s"
, i+1, s.c_str() ); }; } }
;
3998 }
3999 }
4000 }
4001 // done
4002 delete funccontextP;
4003 }
4004 SYSYNC_CATCH (...)catch(...) {
4005 if (funccontextP) delete funccontextP;
4006 SYSYNC_RETHROWthrow;
4007 SYSYNC_ENDCATCH}
4008 goto funcresult;
4009 }
4010 else if (tk==TK_USERFUNCTION0x16) {
4011 // user defined function call
4012 funcname=(const char *)p+3;
4013 funcnamelen=*(p+1)-1;
4014 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- User-defined function %.*s call:",funcnamelen,funcname)){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000 +0x00000800) & (fSessionP)->
getDbgMask()) == (0x00001000 +0x80000000 +0x00000800))) (fSessionP
)->getDbgLogger()->setNextMask(0x00001000 +0x80000000 +
0x00000800).DebugPrintfLastMask ("- User-defined function %.*s call:"
,funcnamelen,funcname); }; } }
;
4015 // - get function text
4016 funcscript=getSyncAppBase()->getRootConfig()->fScriptConfigP->getFunctionScript(*(p+2));
4017 if (!funcscript)
4018 SYSYNC_THROW(TSyncException(DEBUGTEXT("invalid user function index","scri7")))throw TSyncException("invalid user function index");
4019 // %%% possibly add caching of function contexts here.
4020 // Now we rebuild a context for every function call. Not extremely efficient...
4021 funccontextP=NULL__null;
4022 rebuildContext(fAppBaseP,*funcscript,funccontextP,fSessionP,true);
4023 if (!funccontextP)
4024 SYSYNC_THROW(TSyncException(DEBUGTEXT("no context for user-defined function call","scri5")))throw TSyncException("no context for user-defined function call"
)
;
4025 SYSYNC_TRYtry {
4026 // prepare parameters
4027 evalParams(funccontextP);
4028 // pass parent context
4029 funccontextP->fParentContextP=this; // link to calling script context
4030 // process sub-script into term
4031 if (termtype!=fty_none)
4032 termP=newItemField(termtype, getSessionZones()); // we want a specific type, pre-define the field
4033 // execute function
4034 if (!funccontextP->ExecuteScript(
4035 *funcscript,
4036 &termP, // receives result, if any
4037 true, // is a function
4038 NULL__null, NULL__null, // no context functions/datapointer
4039 NULL__null, false, // no target fields
4040 NULL__null, false // no reference fields
4041 )) {
4042 SCRIPTDBGMSG(("- User-defined function failed to execute")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ("- User-defined function failed to execute"
); }; } }
;
4043 SYSYNC_THROW(TSyncException("User-defined function failed to execute properly"))throw TSyncException("User-defined function failed to execute properly"
)
;
4044 }
4045 // done
4046 if (funccontextP) delete funccontextP;
4047 }
4048 SYSYNC_CATCH (...)catch(...) {
4049 if (funccontextP) delete funccontextP;
4050 SYSYNC_RETHROWthrow;
4051 SYSYNC_ENDCATCH}
4052 funcresult:
4053 DBGSTRINGDEF(s)string s;
4054 DBGVALUESHOW(s,termP)dbgValueShow(s,termP);
4055 SCRIPTDBGMSG(({ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %.*s() function result = %s", funcnamelen
,funcname, s.c_str() ); }; } }
4056 "- %.*s() function result = %s",{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %.*s() function result = %s", funcnamelen
,funcname, s.c_str() ); }; } }
4057 funcnamelen,funcname,{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %.*s() function result = %s", funcnamelen
,funcname, s.c_str() ); }; } }
4058 s.c_str(){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %.*s() function result = %s", funcnamelen
,funcname, s.c_str() ); }; } }
4059 )){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- %.*s() function result = %s", funcnamelen
,funcname, s.c_str() ); }; } }
;
4060 }
4061 else if (tk==TK_EMPTY0x90) {
4062 termP=newItemField(fty_none, getSessionZones());
4063 termP->assignEmpty(); // make it EMPTY (that is, assigned)
4064 //SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Literal: EMPTY"));
4065 }
4066 else if (tk==TK_UNASSIGNED0x91) {
4067 termP=newItemField(fty_none, getSessionZones());
4068 termP->unAssign(); // make it (already is...) unassigned
4069 //SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Literal: UNASSIGNED"));
4070 }
4071 else if (tk==TK_TRUE0x92 || tk==TK_FALSE0x93) {
4072 termP=newItemField(fty_integer, getSessionZones());
4073 termP->setAsInteger(tk==TK_TRUE0x92 ? 1 : 0); // set 0 or 1
4074 //SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Literal BOOLEAN: %d",tk==TK_TRUE ? 1 : 0));
4075 }
4076 else if (tk==TK_NUMERIC_LITERAL0x10) {
4077 // %%% possibly add fty_float later
4078 // set type to integer if not another type requested
4079 //SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Literal number: %0.*s",*(p+1),p+2));
4080 if (termtype==fty_none) termtype=fty_integer;
4081 goto literalterm;
4082 }
4083 else if (tk==TK_STRING_LITERAL0x11) {
4084 if (termtype==fty_none) termtype=fty_string;
4085 //SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Literal string: \"%0.*s\"",*(p+1),p+2));
4086 literalterm:
4087 termP = newItemField(termtype, getSessionZones());
4088 termP->setAsString((cAppCharP)(p+2),*(p+1));
4089 }
4090 else {
4091 // must be identifier
4092 reusetoken();
4093 TItemField *varP=NULL__null;
4094 getVarField(varP);
4095 // copy and convert
4096 if (termtype==fty_none) termtype=varP->getType();
4097 termP=newItemField(termtype, getSessionZones());
4098 (*termP)=(*varP); // simply assign (automatically converts if needed)
4099 }
4100 break;
4101 } while(true); // repeat only for typecast
4102 return termP; // return caller-owned field
4103} // TScriptContext::evalTerm
4104
4105
4106// evaluate expression, creates new or fills passed caller-owned field with result
4107void TScriptContext::evalExpression(
4108 TItemField *&aResultFieldP, // result (created new if passed NULL, modified and casted if passed a field)
4109 bool aShowResult, // if set, this is the main expression (and we want to see the result in DBG)
4110 TItemField *aLeftTermP, // if not NULL, chain-evaluate rest of expression according to aBinaryOp and aPreviousOp. WILL BE CONSUMED
4111 uInt8 *aBinaryOpP, // operator to be applied between term passed in aLeftTermP and next term, will receive next operator that has same or lower precedence than aPreviousOp
4112 uInt8 aPreviousOp // if an operator of same or lower precedence than this is found, expression evaluation ends
4113)
4114{
4115 TItemFieldTypes termtype; // type of term
4116 TItemField *termP; // next term
4117 TItemField *resultP; // intermediate result
4118 string s; // temp string
4119 bool retainType=false;
4120
4121 uInt8 unaryop,binaryop,nextbinaryop;
4122
4123 fieldinteger_t a,b;
4124
4125 // defaults
4126 binaryop=0; // none by default
4127 if (aBinaryOpP) binaryop=*aBinaryOpP; // get one if one was passed
4128 termtype=fty_none; // first term can be anything
4129
4130 // init first term/operation if there is one
4131 if (binaryop && !aLeftTermP) binaryop=0; // security
4132
4133 // process expression
4134 #ifdef SYDEBUG2
4135 // - determine if subexpression
4136 if (!binaryop) {
4137 // starting new evaluation
4138 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Starting expression evaluation")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000 +0x00000800) & (fSessionP)->
getDbgMask()) == (0x00001000 +0x80000000 +0x00000800))) (fSessionP
)->getDbgLogger()->setNextMask(0x00001000 +0x80000000 +
0x00000800).DebugPrintfLastMask ("- Starting expression evaluation"
); }; } }
;
4139 }
4140 #endif
4141
4142 SYSYNC_TRYtry {
4143 // Note: if binaryop!=0, we have a valid aLeftTermP
4144 do {
4145 resultP=NULL__null;
4146 termP=NULL__null;
4147 // Get next term
4148 unaryop=0; // default to none
4149 // - check for unaries
4150 if (*np==TK_BITWISENOT0x40 || *np==TK_LOGICALNOT0x41 || *np==TK_MINUS0x49) {
4151 unaryop=gettoken();
4152 // evaluate to integer, as unary ops all only make sense with integers
4153 termtype=fty_integer;
4154 }
4155 // - get next term
4156 termP=evalTerm(termtype);
4157 // Apply unaries now
4158 if (unaryop) {
4159 // all unaries are integer ops
4160 if (termP->getCalcType()!=fty_integer)
4161 SYSYNC_THROW(TScriptErrorException("unary operator applied to non-integer",line))throw TScriptErrorException("unary operator applied to non-integer"
,line)
;
4162 fieldinteger_t ival = termP->getAsInteger();
4163 // apply op
4164 switch (unaryop) {
4165 case TK_MINUS0x49:
4166 ival=-ival;
4167 break;
4168 case TK_BITWISENOT0x40:
4169 ival= ~ival;
4170 break;
4171 case TK_LOGICALNOT0x41:
4172 ival= !termP->getAsBoolean();
4173 break;
4174 }
4175 // store back into term field
4176 termP->setAsInteger(ival);
4177 }
4178 // now we have a new term in termP (or NULL if term had no result)
4179 // - check for a next operator
4180 nextbinaryop=0;
4181 if (*np>=TK_BINOP_MIN0x44 && *np <=TK_BINOP_MAX0x68) {
4182 // this is a binary operator
4183 nextbinaryop=gettoken();
4184 }
4185 // - check for non-existing term
4186 if (!termP && (binaryop || nextbinaryop))
4187 SYSYNC_THROW(TScriptErrorException("non-value cannot be used in expression",line))throw TScriptErrorException("non-value cannot be used in expression"
,line)
;
4188 // - check if there is a previous operation pending
4189 if (binaryop) {
4190 fieldinteger_t intres;
4191 // There is an operation to perform between aLeftTermP and current term (or expression with
4192 // higher precedence)
4193 // - get precedences (make them minus to have lowerprec<higherprec)
4194 sInt8 currentprec=-(binaryop & TK_OP_PRECEDENCE_MASK0xFC);
4195 sInt8 nextprec=-(nextbinaryop & TK_OP_PRECEDENCE_MASK0xFC);
4196 // - we must evaluate part of the expression BEFORE applying binaryop if next operator
4197 // has higher precedence than current one
4198 if (nextbinaryop && nextprec>currentprec) {
4199 // next operator has higher precedence, evaluate everything up to next operator with same or
4200 // lower precedence as current one BEFORE applying binaryop
4201 evalExpression(
4202 resultP, // we'll receive the result here
4203 EXPRDBGTEST(debugon && fSessionP && ((fSessionP->getDbgMask
() & (0x00001000|0x00000800)) == (0x00001000|0x00000800))
)
, // do not normally show subexpression results
4204 termP, // left term of new expression, WILL BE CONSUMED
4205 &nextbinaryop, // operator, will be updated to receive next operator to apply
4206 binaryop // evaluation stops when expression reaches operator with same or lower precedence
4207 );
4208 // nextbinaryop now has next operation to perform AFTER doing binaryop
4209 termP=resultP; // original termP has been consumed, use result as new termP
4210 resultP=NULL__null;
4211 }
4212 // Now we can apply binaryop between lasttermP and termP
4213 // Note: this must CONSUME termP AND aLeftTermP and CREATE resultP
4214 // - check for operators that work with multiple types
4215 if (binaryop==TK_PLUS0x48) {
4216 if (aLeftTermP->getCalcType()!=fty_integer) {
4217 // treat plus as general append (defaults to string append for non-arrays)
4218 aLeftTermP->append(*termP);
4219 resultP=aLeftTermP; // we can simply pass the pointer
4220 aLeftTermP=NULL__null; // consume
4221 goto opdone; // operation done, get rid of termP
4222 }
4223 }
4224 else if (binaryop>=TK_LESSTHAN0x50 && binaryop<=TK_NOTEQUAL0x55) {
4225 // comparison operators
4226 sInt16 cmpres=-3; // will never happen
4227 bool neg=false;
4228 switch (binaryop) {
4229 // - comparison
4230 case TK_LESSTHAN0x50 : cmpres=-1; break;
4231 case TK_GREATERTHAN0x51 : cmpres=1; break;
4232 case TK_LESSEQUAL0x52 : cmpres=1; neg=true; break;
4233 case TK_GREATEREQUAL0x53 : cmpres=-1; neg=true; break;
4234 // - equality
4235 case TK_EQUAL0x54 : cmpres=0; break;
4236 case TK_NOTEQUAL0x55 : cmpres=0; neg=true; break;
4237 }
4238 // do comparison
4239 if (termP->getType()==fty_none) {
4240 // EMPTY or UNASSIGNED comparison
4241 if (termP->isUnassigned()) {
4242 intres = aLeftTermP->isUnassigned() ? 0 : 1; // if left is assigned, it is greater
4243 }
4244 else {
4245 intres = aLeftTermP->isEmpty() ? 0 : 1; // if left is not empty, it is greater
4246 }
4247 }
4248 else {
4249 // normal comparison
4250 intres=aLeftTermP->compareWith(*termP);
4251 }
4252 if (intres==SYSYNC_NOT_COMPARABLE-999) intres=0; // false
4253 else {
4254 intres=cmpres==intres;
4255 if (neg) intres=!intres;
4256 }
4257 retainType= aLeftTermP->getType()==fty_integer;; // comparisons must always have plain integer result
4258 goto intresult;
4259 }
4260 // - integer operators
4261 a=aLeftTermP->getAsInteger();
4262 b=termP->getAsInteger();
4263 // for integer math operators, retain type of left term if it can calculate as integer
4264 // (such that expressions like: "timestamp + integer" will have a result type of timestamp)
4265 retainType = aLeftTermP->getCalcType()==fty_integer;
4266 // now perform integer operation
4267 switch (binaryop) {
4268 // - multiply, divide
4269 case TK_MULTIPLY0x44 : intres=a*b; break;
4270 case TK_DIVIDE0x45 : intres=a/b; break;
4271 case TK_MODULUS0x46 : intres=a%b; break;
4272 // - add, subtract
4273 case TK_PLUS0x48 : intres=a+b; break;
4274 case TK_MINUS0x49 : intres=a-b; break;
4275 // - shift
4276 case TK_SHIFTLEFT0x4C : intres=a<<b; break;
4277 case TK_SHIFTRIGHT0x4D : intres=a>>b; break;
4278 // - bitwise AND
4279 case TK_BITWISEAND0x58 : intres=a&b; break;
4280 // - bitwise XOR
4281 case TK_BITWISEXOR0x5C : intres=a^b; break;
4282 // - bitwise OR
4283 case TK_BITWISEOR0x60 : intres=a|b; break;
4284 // - logical AND
4285 case TK_LOGICALAND0x64 : intres=a&&b; break;
4286 // - logical OR
4287 case TK_LOGICALOR0x68 : intres=a||b; break;
4288 default:
4289 SYSYNC_THROW(TScriptErrorException("operator not implemented",line))throw TScriptErrorException("operator not implemented",line);
4290 }
4291 intresult:
4292 // save integer result (optimized, generate new field only if aLeftTermP is not already integer-calc-type)
4293 if (retainType)
4294 resultP=aLeftTermP; // retain original left-side type (including extra information like time zone context and rendering type)
4295 else {
4296 // create new integer field
4297 resultP = newItemField(fty_integer, getSessionZones());
4298 delete aLeftTermP;
4299 }
4300 aLeftTermP=NULL__null;
4301 resultP->setAsInteger(intres);
4302 opdone:
4303 // check for special conditions when operating on timestamps
4304 if (resultP->isBasedOn(fty_timestamp) && termP->isBasedOn(fty_timestamp)) {
4305 // both based on timestamp.
4306 if (binaryop==TK_MINUS0x49 && !static_cast<TTimestampField *>(resultP)->isDuration() && !static_cast<TTimestampField *>(termP)->isDuration()) {
4307 // subtracted two points in time -> result is duration
4308 static_cast<TTimestampField *>(resultP)->makeDuration();
4309 }
4310 }
4311 // get rid of termP
4312 delete termP;
4313 termP=NULL__null;
4314 } // if binary op was pending
4315 else {
4316 // no binary op pending, simply pass term as result
4317 resultP=termP;
4318 termP=NULL__null;
4319 }
4320 // Now termP and aLeftTermP are consumed, resultP is alive
4321 // - determine next operation
4322 if (nextbinaryop) {
4323 // check precedence, if we can pass back to previous
4324 sInt8 lastprec=-(aPreviousOp & TK_OP_PRECEDENCE_MASK0xFC);
4325 sInt8 thisprec=-(nextbinaryop & TK_OP_PRECEDENCE_MASK0xFC);
4326 if (aPreviousOp && thisprec<=lastprec) break; // force exit, resultP is assigned
4327 }
4328 // result is going to be left term for next operation
4329 aLeftTermP=resultP;
4330 binaryop=nextbinaryop;
4331 } while(nextbinaryop); // as long as expression continues
4332 // show result
4333 #ifdef SYDEBUG2
4334 // show final result of main expression only
4335 if (!nextbinaryop && aShowResult) {
4336 DBGVALUESHOW(s,resultP)dbgValueShow(s,resultP);
4337 SCRIPTDBGMSG(({ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- Expression result: %s", s.c_str() )
; }; } }
4338 "- Expression result: %s",{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- Expression result: %s", s.c_str() )
; }; } }
4339 s.c_str(){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- Expression result: %s", s.c_str() )
; }; } }
4340 )){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ( "- Expression result: %s", s.c_str() )
; }; } }
;
4341 }
4342 #endif
4343 // convert result to desired type
4344 if (!aResultFieldP) {
4345 // no return field passed, just pass pointer (and ownership) of our resultP
4346 aResultFieldP=resultP;
4347 }
4348 else {
4349 // return field already exists, assign value (will convert type if needed)
4350 if (resultP) {
4351 (*aResultFieldP)=(*resultP);
4352 delete resultP; // not passed to caller, so we must get rid of it
4353 }
4354 else {
4355 // no result, unAssign
4356 aResultFieldP->unAssign();
4357 }
4358 }
4359 // return nextbinaryop if requested
4360 if (aBinaryOpP) *aBinaryOpP=nextbinaryop; // this is how we ended
4361 }
4362 SYSYNC_CATCH (...)catch(...) {
4363 // delete terms
4364 if (resultP) delete resultP;
4365 if (aLeftTermP) delete aLeftTermP;
4366 if (termP) delete termP;
4367 SYSYNC_RETHROWthrow;
4368 SYSYNC_ENDCATCH}
4369} // TScriptContext:evalExpression
4370
4371
4372
4373// execute script
4374// returns false if script execution was not successful
4375bool TScriptContext::ExecuteScript(
4376 const string &aTScript,
4377 TItemField **aResultPP, // if not NULL, a result field will be returned here (must be deleted by caller)
4378 bool aAsFunction, // if set, this is a function call
4379 const TFuncTable *aFuncTableP, // context's function table, NULL if none
4380 void *aCallerContext, // free pointer possibly having a meaning for context functions
4381 TMultiFieldItem *aTargetItemP, // target (or "loosing") item
4382 bool aTargetWritable, // if set, target item may be modified
4383 TMultiFieldItem *aReferenceItemP, // reference for source (or "old" or "winning") item
4384 bool aRefWritable, // if set, reference item may also be written
4385 bool aNoDebug // if set, debug output is suppressed even if DBG_SCRIPTS is generally on
4386)
4387{
4388 if (aResultPP) *aResultPP=NULL__null; // no result yet
4389 TItemField *resultP = NULL__null; // none yet
4390
4391 uInt8 tk; // current token
4392 TScriptState sta; // status
4393
4394 skipping=0; // skipping level
4395
4396 bool funchdr=aAsFunction; // flag if processing function header first
4397 if (funchdr) skipping=1; // skip stuff
4398
4399 // endless loop protection
4400 lineartime_t loopmaxtime=0; // not in a loop
4401
4402 // save context parameters
4403 fTargetItemP=aTargetItemP; // target (or "loosing") item
4404 fTargetWritable=aTargetWritable; // if set, target item may be modified
4405 fReferenceItemP=aReferenceItemP; // reference for source (or "old" or "winning") item
4406 fRefWritable=aRefWritable; // if set, reference item may also be written
4407 fFuncTableP=aFuncTableP; // context's function table, NULL if none
4408 fCallerContext=aCallerContext;
4409
4410 #ifdef SYDEBUG2
4411 debugon = !aNoDebug;
4412 #endif
4413
4414 // init parsing (for execute)
4415 initParse(aTScript,true);
4416 // test if there's something to execute at all
4417 if (ep>bp) {
4418 #ifdef SYDEBUG2
4419 if (aAsFunction) {
4420 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,("* Starting execution of user-defined function")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x00000001) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x00000001))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x00000001).DebugPrintfLastMask
("* Starting execution of user-defined function"); }; } }
;
4421 } else {
4422 SCRIPTDBGSTART(scriptname ? scriptname : "<unnamed>"){ if (debugon && fSessionP) { if (fSessionP->getDbgMask
() & 0x00001000) { fSessionP->getDbgLogger()->DebugOpenBlockCollapsed
("ScriptExecute","Start executing Script","name=%s",scriptname
? scriptname : "<unnamed>"); } else { { if ((fSessionP
) && (((0x00000080) & (fSessionP)->getDbgMask(
)) == (0x00000080))) (fSessionP)->getDbgLogger()->setNextMask
(0x00000080).DebugPrintfLastMask ("Executing Script '%s'",scriptname
? scriptname : "<unnamed>"); }; } } }
;
4423 }
4424 #endif
4425 SYSYNC_TRYtry {
4426 // init state stack
4427 fStackEntries=0; // stack is empty
4428 pushState(ssta_statement); // start with statement
4429
4430 // execute
4431 while ((tk=gettoken())) {
4432 resultP=NULL__null;
4433 // start of statement
4434 // - check for block
4435 if (tk==TK_BEGIN_BLOCK0x30) {
4436 // push current state to stack and start new block
4437 pushState(ssta_block);
4438 if (funchdr) {
4439 // start of first block = start of function body, stop skipping
4440 funchdr=false;
4441 skipping=0;
4442 }
4443 continue; // process next
4444 }
4445 else if (tk==TK_END_BLOCK0x31) {
4446 // pop block start, throw error if none there
4447 popState(ssta_block);
4448 // treat like end-of-statement, check for IF/ELSE/LOOP etc.
4449 goto endstatement;
4450 }
4451 else if (tk==TK_END_STATEMENT';') {
4452 goto endstatement;
4453 }
4454 // - handle skipping of conditionally excluded blocks
4455 if (skipping!=0) {
4456 // only IF and ELSE are of any relevance
4457 if (tk==TK_IF0x80) {
4458 pushState(ssta_if); // nested IF
4459 skipping++; // skip anyway
4460 }
4461 else if (tk==TK_ELSE0x81) {
4462 // %%% this case probably never happens, as it is pre-fetched at end-of-statement
4463 // only push ELSE if this is not a chained ELSE IF
4464 if (*np!=TK_IF0x80) pushState(ssta_else); // nested ELSE
4465 }
4466 // all others are irrelevant during skip
4467 continue;
4468 }
4469 else {
4470 // really executing
4471 // - check empty statement
4472 if (tk==TK_END_STATEMENT';') goto endstatement;
4473 // - check IF statement
4474 else if (tk==TK_IF0x80 || tk==TK_WHILE0x86) {
4475 // IF or WHILE conditional
4476 // - remember for WHILE case as condition must be re-evaluated for every loop
4477 cUInt8P condBeg = p;
4478 uInt16 condLine = line;
4479 // - process boolean expression
4480 tk=gettoken();
4481 if (tk!=TK_OPEN_PARANTHESIS'(')
4482 SYSYNC_THROW(TScriptErrorException("missing '(' after IF or WHILE",line))throw TScriptErrorException("missing '(' after IF or WHILE",line
)
;
4483 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- IF or WHILE, evaluating condition...")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000 +0x00000800) & (fSessionP)->
getDbgMask()) == (0x00001000 +0x80000000 +0x00000800))) (fSessionP
)->getDbgLogger()->setNextMask(0x00001000 +0x80000000 +
0x00000800).DebugPrintfLastMask ("- IF or WHILE, evaluating condition..."
); }; } }
;
4484 evalExpression(resultP,EXPRDBGTEST(debugon && fSessionP && ((fSessionP->getDbgMask
() & (0x00001000|0x00000800)) == (0x00001000|0x00000800))
)
);
4485 tk=gettoken();
4486 if (tk!=TK_CLOSE_PARANTHESIS')')
4487 SYSYNC_THROW(TScriptErrorException("missing ')' in IF or WHILE",line))throw TScriptErrorException("missing ')' in IF or WHILE",line
)
;
4488 // - determine which branch to process
4489 if (!(resultP && resultP->getAsBoolean()))
4490 skipping=1; // enter skip level 1
4491 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC,("- %s condition is %s", *condBeg==TK_WHILE ? "WHILE" : "IF", skipping ? "false" : "true")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x80000000))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x80000000).DebugPrintfLastMask
("- %s condition is %s", *condBeg==0x86 ? "WHILE" : "IF", skipping
? "false" : "true"); }; } }
;
4492 delete resultP;
4493 resultP=NULL__null;
4494 if (*condBeg==TK_WHILE0x86) {
4495 if (skipping) {
4496 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,("- WHILE condition is false -> skipping WHILE body")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x00000001) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x00000001))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x00000001).DebugPrintfLastMask
("- WHILE condition is false -> skipping WHILE body"); };
} }
;
4497 }
4498 // for every subsequent loop, WHILE must be reprocessed
4499 pushState(ssta_while,condBeg,condLine);
4500 goto startloop; // limit loop execution time
4501 }
4502 else {
4503 // process statement (block) after IF
4504 pushState(ssta_if);
4505 }
4506 continue;
4507 }
4508 // Note: ELSE is checked at end of statement only
4509 // Check loop beginning
4510 else if (tk==TK_LOOP0x82) {
4511 // initiate loop
4512 pushState(ssta_loop);
4513 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC,("- LOOP entered")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x80000000))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x80000000).DebugPrintfLastMask
("- LOOP entered"); }; } }
;
4514 startloop:
4515 // - remember entry time of outermost loop
4516 if (loopmaxtime==0) {
4517 uInt32 loopSecs = getSyncAppBase()->getRootConfig()->fScriptConfigP->fMaxLoopProcessingTime;
4518 if (loopSecs>0) {
4519 loopmaxtime=
4520 getSystemNowAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)), getSessionZones())+
4521 secondToLinearTimeFactor * loopSecs;
4522 }
4523 else {
4524 loopmaxtime=maxLinearTime; // virtually unlimited time
4525 }
4526 }
4527 continue;
4528 }
4529 else if (tk==TK_CONTINUE0x84 || tk==TK_BREAK0x83) {
4530 // see if there is a LOOP or WHILE on the stack
4531 bool inloop=false;
4532 bool inwhile=false;
4533 sInt16 sp=fStackEntries;
4534 while (sp>0) {
4535 sta = fScriptstack[sp-1].state;
4536 if (sta==ssta_loop) { inloop=true; break; } // we are in a LOOP
4537 if (sta==ssta_while) { inwhile=true; break; } // we are in a WHILE
4538 sp--;
4539 }
4540 // no loop found, error
4541 if (!inloop && !inwhile)
4542 SYSYNC_THROW(TScriptErrorException("BREAK or CONTINUE without enclosing LOOP or WHILE",line))throw TScriptErrorException("BREAK or CONTINUE without enclosing LOOP or WHILE"
,line)
;
4543 if (tk==TK_BREAK0x83) {
4544 // make sure we are skipping everything (including any number of nested if/else
4545 // and blocks up to reaching next end-of-loop). Note that if a LOOP or WHILE is in the
4546 // skipped part, it will not cause troubles because it is not recognized as
4547 // such while skipping.
4548 skipping=maxstackentries;
4549 }
4550 else {
4551 // continue, remove stack entries down to LOOP or WHILE and jump to beginning of loop
4552 // - same as if we had reached the bottom of the loop, but pop
4553 // open blocks, if's and else's first
4554 // - sta is ssta_loop or ssta_while to decide if we must pop the status or not
4555 goto loopcontinue;
4556 }
4557 }
4558 else if (tk==TK_TYPEDEF0x18) {
4559 // declaration, skip it
4560 do {
4561 if (*np!=TK_IDENTIFIER0x12)
4562 SYSYNC_THROW(TScriptErrorException("Invalid declaration",line))throw TScriptErrorException("Invalid declaration",line);
4563 tk=gettoken(); // swallow identifier
Value stored to 'tk' is never read
4564 if (*np==TK_OPEN_ARRAY0x32) {
4565 // must be array declaration
4566 tk=gettoken(); // swallow [
4567 if (*np==TK_CLOSE_ARRAY0x33)
4568 gettoken(); // swallow ]
4569 }
4570 if (*np!=TK_LIST_SEPARATOR',') break;
4571 gettoken(); // swallow separator
4572 } while(true);
4573 // end of statement should follow here, will be checked below.
4574 }
4575 else if (tk==TK_IDENTIFIER0x12 || tk==TK_OBJECT0x14) {
4576 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Starting assignment/unstored expression")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000 +0x00000800) & (fSessionP)->
getDbgMask()) == (0x00001000 +0x80000000 +0x00000800))) (fSessionP
)->getDbgLogger()->setNextMask(0x00001000 +0x80000000 +
0x00000800).DebugPrintfLastMask ("- Starting assignment/unstored expression"
); }; } }
;
4577 // could be assignment if identifier is followed by TK_ASSIGN
4578 // otherwise, it could be a simple expression evaluation
4579 cUInt8P ts=p; // remember start of statement
4580 uInt16 tl=line; // remember line as well
4581 TItemField *fldP;
4582 reusetoken();
4583 bool writeable=getVarField(fldP);
4584 // now check if this is an assignment
4585 if (*np==TK_ASSIGN0x6C) {
4586 gettoken(); // swallow TK_ASSIGN
4587 // must be writeable
4588 if (!writeable)
4589 SYSYNC_THROW(TScriptErrorException("Not allowed to assign to this field/variable",line))throw TScriptErrorException("Not allowed to assign to this field/variable"
,line)
;
4590 // evaluate expression into given field
4591 evalExpression(fldP,false); // we show the result ourselves
4592 DBGSTRINGDEF(s)string s;
4593 DBGVALUESHOW(s,fldP)dbgValueShow(s,fldP);
4594 SCRIPTDBGMSG(("- Assigned expression result = %s",s.c_str())){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000) & (fSessionP)->getDbgMask()) == (0x00001000
))) (fSessionP)->getDbgLogger()->setNextMask(0x00001000
).DebugPrintfLastMask ("- Assigned expression result = %s",s.
c_str()); }; } }
;
4595 }
4596 else {
4597 // must be plain expression
4598 np=ts; // back to start of statement;
4599 nextline=tl;
4600 evalExpression(resultP,true); // we always want to see the result
4601 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Evaluated unstored expression")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000 +0x00000800) & (fSessionP)->
getDbgMask()) == (0x00001000 +0x80000000 +0x00000800))) (fSessionP
)->getDbgLogger()->setNextMask(0x00001000 +0x80000000 +
0x00000800).DebugPrintfLastMask ("- Evaluated unstored expression"
); }; } }
;
4602 // - if nothing follows, script ends here with result of expression
4603 if (*np==0) break;
4604 // - otherwise, forget result
4605 delete resultP;
4606 resultP=NULL__null;
4607 }
4608 }
4609 else if (tk==TK_RETURN0x85) {
4610 // simply return if no expression follows
4611 resultP=NULL__null;
4612 if (*np==TK_END_STATEMENT';') {
4613 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,("- RETURN: ending %s without result",aAsFunction ? "function" : "script")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x00000001) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x00000001))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x00000001).DebugPrintfLastMask
("- RETURN: ending %s without result",aAsFunction ? "function"
: "script"); }; } }
;
4614 break;
4615 }
4616 // evaluate expression first
4617 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- RETURN: evaluating result expression...")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000 +0x00000800) & (fSessionP)->
getDbgMask()) == (0x00001000 +0x80000000 +0x00000800))) (fSessionP
)->getDbgLogger()->setNextMask(0x00001000 +0x80000000 +
0x00000800).DebugPrintfLastMask ("- RETURN: evaluating result expression..."
); }; } }
;
4618 evalExpression(resultP,false); // we always show the result ourselves
4619 DBGSTRINGDEF(s)string s;
4620 DBGVALUESHOW(s,resultP)dbgValueShow(s,resultP);
4621 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,({ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x00000001) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x00000001))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x00000001).DebugPrintfLastMask
( "- RETURN: ending %s with result = %s", aAsFunction ? "function"
: "script", s.c_str() ); }; } }
4622 "- RETURN: ending %s with result = %s",{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x00000001) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x00000001))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x00000001).DebugPrintfLastMask
( "- RETURN: ending %s with result = %s", aAsFunction ? "function"
: "script", s.c_str() ); }; } }
4623 aAsFunction ? "function" : "script",{ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x00000001) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x00000001))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x00000001).DebugPrintfLastMask
( "- RETURN: ending %s with result = %s", aAsFunction ? "function"
: "script", s.c_str() ); }; } }
4624 s.c_str(){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x00000001) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x00000001))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x00000001).DebugPrintfLastMask
( "- RETURN: ending %s with result = %s", aAsFunction ? "function"
: "script", s.c_str() ); }; } }
4625 )){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x00000001) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x00000001))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x00000001).DebugPrintfLastMask
( "- RETURN: ending %s with result = %s", aAsFunction ? "function"
: "script", s.c_str() ); }; } }
;
4626 // now end script
4627 break;
4628 }
4629 else {
4630 // everything else must be plain expression (e.g. function calls etc.)
4631 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Starting evaluating unstored expression")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000 +0x00000800) & (fSessionP)->
getDbgMask()) == (0x00001000 +0x80000000 +0x00000800))) (fSessionP
)->getDbgLogger()->setNextMask(0x00001000 +0x80000000 +
0x00000800).DebugPrintfLastMask ("- Starting evaluating unstored expression"
); }; } }
;
4632 reusetoken();
4633 evalExpression(resultP,false); // we always show the result ourselves
4634 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC+DBG_SCRIPTEXPR,("- Evaluated unstored expression")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000 +0x00000800) & (fSessionP)->
getDbgMask()) == (0x00001000 +0x80000000 +0x00000800))) (fSessionP
)->getDbgLogger()->setNextMask(0x00001000 +0x80000000 +
0x00000800).DebugPrintfLastMask ("- Evaluated unstored expression"
); }; } }
;
4635 /* %%% wrong, prevents script from returning NOTHING!
4636 to return a value, RETURN *must* be used!
4637 // - if nothing follows, script ends here with result of expression
4638 if (*np==0) {
4639 DBGSTRINGDEF(s);
4640 DBGVALUESHOW(s,resultP);
4641 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,(
4642 "- Script ends with result = %s",
4643 s.c_str()
4644 ));
4645 break;
4646 }
4647 */
4648 // - otherwise, forget result
4649 delete resultP;
4650 resultP=NULL__null;
4651 }
4652 } // not skipping
4653 // Statement executed, next must be end-of-statement
4654 tk=gettoken();
4655 if (tk!=TK_END_STATEMENT';')
4656 SYSYNC_THROW(TScriptErrorException("missing ';' after statement",line))throw TScriptErrorException("missing ';' after statement",line
)
;
4657 // Statement executed and properly terminated
4658 endstatement:
4659 // check state
4660 sta=fScriptstack[fStackEntries-1].state;
4661 // check end of IF block
4662 if (sta==ssta_if) {
4663 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC,("- End of %s IF",skipping ? "skipped" : "executed")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x80000000))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x80000000).DebugPrintfLastMask
("- End of %s IF",skipping ? "skipped" : "executed"); }; } }
;
4664 popState(sta); // end this state
4665 if (skipping) {
4666 // IF branch was not executed, check for else
4667 if (*np==TK_ELSE0x81) {
4668 // else follows
4669 // - swallow TK_ELSE
4670 gettoken();
4671 // - check for ELSE IF chain
4672 // (in this case, we must NOT push a state, but let chained IF push TK_IF again
4673 // and also increment skipping again, after it is decremented by one below)
4674 if (*np!=TK_IF0x80) {
4675 pushState(ssta_else); // end-of-chain ELSE: enter else state
4676 // keep skipping (compensate for decrement below) ONLY if end-of-chain ELSE
4677 // is not to be executed due to surrounding skip.
4678 if (skipping>1) skipping++;
4679 }
4680 }
4681 // no ELSE, just continue with next statement
4682 skipping--; // reduce skip level
4683 continue;
4684 }
4685 else {
4686 // IF branch was executed, check for else
4687 if (*np==TK_ELSE0x81) {
4688 // else follows, skip it (including all chained IFs)
4689 skipping=1;
4690 gettoken(); // swallow ELSE
4691 // - check for ELSE IF chain
4692 if (*np==TK_IF0x80) {
4693 // chained if while skipping
4694 gettoken(); // consume it (avoid regular parsing to see it and increment skipping)
4695 pushState(ssta_chainif); // chained IF, make sure nothing is executed up to and including end-of-chain else
4696 }
4697 else
4698 pushState(ssta_else); // process as skipped ELSE part
4699 }
4700 // no else, simply continue execution
4701 continue;
4702 }
4703 }
4704 else if (sta==ssta_chainif) {
4705 // only entered while skipping rest of chain
4706 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC,("- End of skipped ELSE IF")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x80000000))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x80000000).DebugPrintfLastMask
("- End of skipped ELSE IF"); }; } }
;
4707 if (*np==TK_ELSE0x81) {
4708 gettoken(); // consume it
4709 if (*np==TK_IF0x80) {
4710 // chained if while skipping
4711 gettoken(); // consume it (avoid regular parsing to see it and increment skipping)
4712 // stay in ssta_chainif
4713 }
4714 else {
4715 popState(ssta_chainif); // end of ELSE IF chain
4716 pushState(ssta_else); // rest is a normal skipped ELSE part
4717 }
4718 }
4719 else {
4720 // end of skipped chained ifs
4721 popState(ssta_chainif);
4722 skipping--;
4723 }
4724 }
4725 else if (sta==ssta_else) {
4726 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC,("- End of %s ELSE",skipping ? "skipped" : "executed")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x80000000))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x80000000).DebugPrintfLastMask
("- End of %s ELSE",skipping ? "skipped" : "executed"); }; }
}
;
4727 popState(ssta_else); // end of else state
4728 if (skipping) skipping--;
4729 }
4730 // check end of LOOP block
4731 else if (sta==ssta_loop || sta==ssta_while) {
4732 // Note: we'll never see that for a completely skipped loop/while, as
4733 // no ssta_loop/ssta_while is pushed while skipping.
4734 if (!skipping) goto loopcontinue; // not end of while or breaking out of loop, repeat
4735 // - end of loop reached
4736 popState(sta);
4737 skipping=0; // end of loop found after BREAK, continue normal execution
4738 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_EXOTIC,("- End of WHILE or LOOP")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x80000000) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x80000000))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x80000000).DebugPrintfLastMask
("- End of WHILE or LOOP"); }; } }
;
4739 }
4740 // nothing special, just execute next statement
4741 continue;
4742 loopcontinue:
4743 // continue at beginning of loop which is at top of state stack
4744 if (getSystemNowAs(TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)), getSessionZones())>loopmaxtime)
4745 SYSYNC_THROW(TScriptErrorException("loop execution aborted because <looptimeout> reached (endless loop?)",line))throw TScriptErrorException("loop execution aborted because <looptimeout> reached (endless loop?)"
,line)
;
4746 // go to beginning of loop: restore position of beginning of loop (including condition in case of WHILE)
4747 np=fScriptstack[fStackEntries-1].begin; // next token after current token
4748 nextline=fScriptstack[fStackEntries-1].line; // line of next token after current token
4749 linesource=NULL__null; // prevent showing source (which is that of NEXT line, not that of jump target's
4750 // for while, we must pop the stack entry as it will be recreated by re-excution of the WHILE statement
4751 if (sta==ssta_while)
4752 popState(sta);
4753 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,("- Starting next iteration of LOOP/WHILE -> jumping to line %hd",nextline)){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x00000001) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x00000001))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x00000001).DebugPrintfLastMask
("- Starting next iteration of LOOP/WHILE -> jumping to line %hd"
,nextline); }; } }
;
4754 continue;
4755 } // while more tokens
4756 } // try
4757 SYSYNC_CATCH (exception &e)catch(exception &e) {
4758 // make sure field is deleted
4759 if (resultP) delete resultP;
4760 // show error message
4761 SCRIPTDBGMSGX(DBG_ERROR,("Warning: TERMINATING SCRIPT WITH ERROR: %s",e.what())){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00000002) & (fSessionP)->getDbgMask()) == (0x00000002
))) (fSessionP)->getDbgLogger()->setNextMask(0x00000002
).DebugPrintfLastMask ("Warning: TERMINATING SCRIPT WITH ERROR: %s"
,e.what()); }; } }
;
4762 if (!aAsFunction) SCRIPTDBGEND(){ if (debugon && fSessionP && (fSessionP->
getDbgMask() & 0x00001000)) { fSessionP->getDbgLogger(
)->DebugCloseBlock( "ScriptExecute"); } }
;
4763 return false;
4764 SYSYNC_ENDCATCH}
4765 #ifdef SYDEBUG2
4766 if (aAsFunction) {
4767 SCRIPTDBGMSGX(DBG_SCRIPTS+DBG_HOT,("* Successfully finished execution of user-defined function")){ if (debugon && fSessionP) { { if ((fSessionP) &&
(((0x00001000 +0x00000001) & (fSessionP)->getDbgMask(
)) == (0x00001000 +0x00000001))) (fSessionP)->getDbgLogger
()->setNextMask(0x00001000 +0x00000001).DebugPrintfLastMask
("* Successfully finished execution of user-defined function"
); }; } }
;
4768 } else {
4769 SCRIPTDBGEND(){ if (debugon && fSessionP && (fSessionP->
getDbgMask() & 0x00001000)) { fSessionP->getDbgLogger(
)->DebugCloseBlock( "ScriptExecute"); } }
;
4770 }
4771 #endif
4772 } // if something to execute at all
4773 // return most recent evaluated expression (normally RETURN expression)
4774 if (aResultPP) {
4775 // caller wants to see result, pass it back
4776 if (*aResultPP && resultP) {
4777 // script result field already exists, assign value
4778 (**aResultPP)=(*resultP);
4779 delete resultP;
4780 }
4781 else {
4782 // script result field does not yet exist, just pass result (even if it is NULL)
4783 *aResultPP=resultP;
4784 }
4785 }
4786 else if (resultP) delete resultP; // nobody needs the result, delete it
4787 // execution successful
4788 return true;
4789} // TScriptContext::ExecuteScript
4790
4791
4792// get variable definition by name, NULL if none defined yet
4793TScriptVarDef *TScriptContext::getVarDef(cAppCharP aVarName,size_t len)
4794{
4795 TVarDefs::iterator pos;
4796 for (pos=fVarDefs.begin(); pos!=fVarDefs.end(); pos++) {
4797 if (strucmp((*pos)->fVarName.c_str(),aVarName,0,len)==0) return (*pos);
4798 }
4799 return NULL__null;
4800} // TScriptContext::getVarDef
4801
4802
4803// get variable definition by index, NULL if none defined yet
4804TScriptVarDef *TScriptContext::getVarDef(sInt16 aLocalVarIdx)
4805{
4806 if (aLocalVarIdx<0 || uInt32(aLocalVarIdx)>=fVarDefs.size()) return NULL__null;
4807 return fVarDefs[aLocalVarIdx];
4808} // TScriptContext::getVarDef
4809
4810
4811// - get identifier index (>=0: field index, <0: local var)
4812// returns VARIDX_UNDEFINED for unknown identifier
4813sInt16 TScriptContext::getIdentifierIndex(sInt16 aObjIndex, TFieldListConfig *aFieldListConfigP, cAppCharP aIdentifier,size_t aLen)
4814{
4815 // first look for local variable
4816 if (aObjIndex==OBJ_AUTO0 || aObjIndex==OBJ_LOCAL1) {
4817 TScriptVarDef *vardefP = getVarDef(aIdentifier,aLen);
4818 if (vardefP) {
4819 return - (sInt16)vardefP->fIdx-1;
4820 }
4821 }
4822 if (aObjIndex==OBJ_AUTO0 || aObjIndex==OBJ_TARGET2 || aObjIndex==OBJ_REFERENCE3) {
4823 // look for field with that name
4824 if (!aFieldListConfigP) return VARIDX_UNDEFINED-128; // no field list available here
4825 return aFieldListConfigP->fieldIndex(aIdentifier,aLen);
4826 }
4827 else return VARIDX_UNDEFINED-128; // unknown object, unknown index
4828} // TScriptContext::getIdentifierIndex
4829
4830
4831// get field by fid, can also be field of aItem, also resolves arrays
4832TItemField *TScriptContext::getFieldOrVar(TMultiFieldItem *aItemP, sInt16 aFid, sInt16 aArrIdx)
4833{
4834 TItemField *fldP = getFieldOrVar(aItemP,aFid);
4835 if (!fldP) return NULL__null;
4836 #ifdef ARRAYFIELD_SUPPORT1
4837 if (aArrIdx>=0)
4838 return fldP->getArrayField(aArrIdx);
4839 else
4840 return fldP; // return field without array resolution
4841 #else
4842 if (aArrIdx>0) return NULL__null;
4843 return fldP;
4844 #endif
4845} // TScriptContext::getFieldOrVar
4846
4847
4848// get field by fid, can be field of aItem (positive aFid) or local variable (negative fid)
4849TItemField *TScriptContext::getFieldOrVar(TMultiFieldItem *aItemP, sInt16 aFid)
4850{
4851 if (aFid<0) return getLocalVar(-aFid-1);
4852 if (!aItemP) return NULL__null;
4853 return aItemP->getField(aFid);
4854} // TScriptContext::getFieldOrVar
4855
4856
4857// get local var by local index
4858TItemField *TScriptContext::getLocalVar(sInt16 aVarIdx)
4859{
4860 if (aVarIdx<0 || aVarIdx>=fNumVars) return NULL__null;
4861 return fFieldsP[aVarIdx];
4862} // TScriptContext::getLocalVar
4863
4864
4865// set local var by local index (used for passing references)
4866void TScriptContext::setLocalVar(sInt16 aVarIdx, TItemField *aFieldP)
4867{
4868 if (aVarIdx<0 || aVarIdx>=fNumVars) return;
4869 fFieldsP[aVarIdx]=aFieldP; // set new
4870} // TScriptContext::setLocalVar
4871
4872
4873/* end of TScriptContext implementation */
4874
4875
4876#ifdef ENGINEINTERFACE_SUPPORT1
4877
4878// TScriptVarKey
4879// =============
4880
4881
4882// get FID for specified name
4883sInt16 TScriptVarKey::getFidFor(cAppCharP aName, stringSize aNameSz)
4884{
4885 if (fScriptContext==NULL__null) return VARIDX_UNDEFINED-128;
4886 // check for iterator commands first
4887 if (strucmp(aName,VALNAME_FIRST".FIRST")==0) {
4888 fIterator=0;
4889 if (fIterator>=sInt32(fScriptContext->fVarDefs.size())) return VARIDX_UNDEFINED-128;
4890 return -fIterator-1;
4891 }
4892 else if (strucmp(aName,VALNAME_NEXT".NEXT")==0) {
4893 fIterator++;
4894 if (fIterator>=sInt32(fScriptContext->fVarDefs.size())) return VARIDX_UNDEFINED-128;
4895 return -fIterator-1;
4896 }
4897 else
4898 return fScriptContext->getIdentifierIndex(OBJ_LOCAL1, NULL__null, aName, aNameSz);
4899} // TScriptVarKey::getFidFor
4900
4901
4902
4903// get base field from FID
4904TItemField *TScriptVarKey::getBaseFieldFromFid(sInt16 aFid)
4905{
4906 if (fScriptContext==NULL__null) return NULL__null;
4907 return fScriptContext->getFieldOrVar(NULL__null, aFid);
4908} // TScriptVarKey::getBaseFieldFromFid
4909
4910
4911// get field name from FID
4912bool TScriptVarKey::getFieldNameFromFid(sInt16 aFid, string &aFieldName)
4913{
4914 if (fScriptContext==NULL__null) return false;
4915 TScriptVarDef *vardefP = fScriptContext->getVarDef(-aFid-1);
4916 if (vardefP) {
4917 aFieldName = vardefP->fVarName;
4918 return true;
4919 }
4920 // none found
4921 return false;
4922} // TScriptVarKey::getFieldNameFromFid
4923
4924
4925
4926#endif // ENGINEINTERFACE_SUPPORT
4927
4928
4929} // namespace sysync
4930
4931
4932#endif // SCRIPT_SUPPORT
4933
4934// eof