File: | libsynthesis/src/sysync/scriptcontext.cpp |
Warning: | line 4563, column 15 Value stored to 'tk' is never read |
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 | |
61 | namespace sysync { |
62 | |
63 | #ifdef SYDEBUG2 |
64 | |
65 | // show value of a field |
66 | static 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 | |
88 | TTokenizeException::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 | |
105 | TScriptErrorException::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 |
129 | TScriptConfig::TScriptConfig(TConfigElement *aParentElementP) : |
130 | TConfigElement("scripting",aParentElementP) |
131 | { |
132 | clear(); |
133 | } // TScriptConfig::TScriptConfig |
134 | |
135 | |
136 | // config destructor |
137 | TScriptConfig::~TScriptConfig() |
138 | { |
139 | clear(); |
140 | } // TScriptConfig::~TScriptConfig |
141 | |
142 | |
143 | // init defaults |
144 | void 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 |
166 | bool 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 |
193 | void TScriptConfig::localResolve(bool aLastPass) |
194 | { |
195 | // resolve inherited |
196 | inherited::localResolve(aLastPass); |
197 | } // TScriptConfig::localResolve |
198 | |
199 | |
200 | // get script text |
201 | string *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 |
210 | sInt16 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 |
227 | TScriptVarDef::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 | |
238 | TScriptVarDef::~TScriptVarDef() |
239 | { |
240 | } // TScriptVarDef::~TScriptVarDef |
241 | |
242 | |
243 | |
244 | /* |
245 | * builtin function definitions |
246 | */ |
247 | |
248 | |
249 | class TBuiltinStdFuncs { |
250 | public: |
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 | |
2233 | const uInt8 param_oneTimestamp[] = { VAL(fty_timestamp)( (uInt8)fty_timestamp) }; |
2234 | const uInt8 param_oneInteger[] = { VAL(fty_integer)( (uInt8)fty_integer) }; |
2235 | const uInt8 param_oneString[] = { VAL(fty_string)( (uInt8)fty_string) }; |
2236 | const uInt8 param_oneVariant[] = { VAL(fty_none)( (uInt8)fty_none) }; |
2237 | const uInt8 param_oneOptInteger[] = { OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) }; |
2238 | |
2239 | const uInt8 param_Random[] = { VAL(fty_integer)( (uInt8)fty_integer), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) }; |
2240 | const uInt8 param_SetTimezone[] = { REF(fty_timestamp)(((uInt8)fty_timestamp)+0x40), VAL(fty_none)( (uInt8)fty_none) }; |
2241 | const uInt8 param_SetFloating[] = { REF(fty_timestamp)(((uInt8)fty_timestamp)+0x40) }; |
2242 | const uInt8 param_ConvertToZone[] = { VAL(fty_timestamp)( (uInt8)fty_timestamp), VAL(fty_none)( (uInt8)fty_none), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) }; |
2243 | const uInt8 param_ConvertToUserZone[] = { VAL(fty_timestamp)( (uInt8)fty_timestamp), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) }; |
2244 | const uInt8 param_Shellexecute[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) }; |
2245 | const uInt8 param_substr[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_integer)( (uInt8)fty_integer), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) }; |
2246 | const uInt8 param_size[] = { REF(fty_none)(((uInt8)fty_none)+0x40) }; |
2247 | const uInt8 param_Normalized[] = { REF(fty_none)(((uInt8)fty_none)+0x40) }; |
2248 | const uInt8 param_find[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) }; |
2249 | const uInt8 param_compare[] = { VAL(fty_none)( (uInt8)fty_none), VAL(fty_none)( (uInt8)fty_none) }; |
2250 | const uInt8 param_contains[] = { REF(fty_none)(((uInt8)fty_none)+0x40), VAL(fty_none)( (uInt8)fty_none), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) }; |
2251 | const uInt8 param_append[] = { REF(fty_none)(((uInt8)fty_none)+0x40), VAL(fty_none)( (uInt8)fty_none) }; |
2252 | const uInt8 param_swap[] = { REF(fty_none)(((uInt8)fty_none)+0x40), REF(fty_none)(((uInt8)fty_none)+0x40) }; |
2253 | const uInt8 param_isAvailable[] = { REF(fty_none)(((uInt8)fty_none)+0x40) }; |
2254 | const 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) }; |
2256 | const uInt8 param_SetSessionVar[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_none)( (uInt8)fty_none) }; |
2257 | const uInt8 param_SetDebugOptions[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_integer)( (uInt8)fty_integer) }; |
2258 | const 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) }; |
2259 | const 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) }; |
2260 | const 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) }; |
2261 | const 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) }; |
2262 | const 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) }; |
2263 | const uInt8 param_MakeAllday[] = { REF(fty_timestamp)(((uInt8)fty_timestamp)+0x40), REF(fty_timestamp)(((uInt8)fty_timestamp)+0x40), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) }; |
2264 | const 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) }; |
2265 | const uInt8 param_Explode[] = { VAL(fty_string)( (uInt8)fty_string), REFARR(fty_none)(((uInt8)fty_none)+0x40 +0x80) }; |
2266 | const uInt8 param_parseEmailSpec[] = { VAL(fty_string)( (uInt8)fty_string), REF(fty_string)(((uInt8)fty_string)+0x40), REF(fty_string)(((uInt8)fty_string)+0x40) }; |
2267 | const uInt8 param_makeEmailSpec[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string) }; |
2268 | const 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) }; |
2269 | const 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 |
2273 | const uInt8 param_regexfind[] = { VAL(fty_string)( (uInt8)fty_string), VAL(fty_string)( (uInt8)fty_string), OPTVAL(fty_integer)(((uInt8)fty_integer)+0x20) }; |
2274 | const 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) }; |
2275 | const 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) }; |
2276 | const 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 |
2280 | const 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 | |
2381 | const 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 | |
2395 | TScriptContext::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 | |
2413 | TScriptContext::~TScriptContext() |
2414 | { |
2415 | clear(); |
2416 | } // TScriptContext::~TScriptContext |
2417 | |
2418 | |
2419 | // Reset context (clear all variables and definitions) |
2420 | void 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 | |
2433 | GZones *TScriptContext::getSessionZones(void) |
2434 | { |
2435 | return |
2436 | fSessionP ? fSessionP->getSessionZones() : NULL__null; |
2437 | } // TScriptContext::getSessionZones |
2438 | |
2439 | |
2440 | #ifdef SYDEBUG2 |
2441 | // get debug logger |
2442 | TDebugLogger *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 | |
2452 | uInt32 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) |
2465 | void 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 |
2489 | static 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 |
2495 | static 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 |
2533 | void 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 | ¯oArgs // 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 |
2924 | void 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 |
2949 | void 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. |
2967 | void 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 |
2985 | void 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) |
3015 | void 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 |
3025 | void 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) |
3073 | static 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 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 |
3128 | uInt8 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() |
3203 | void 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) |
3216 | void 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. |
3426 | void 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 |
3448 | void 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 |
3475 | void 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. |
3506 | bool 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 |
3540 | bool 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 |
3590 | bool 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 |
3624 | bool 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 | |
3659 | void 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 |
3677 | void 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 |
3689 | bool 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 |
3805 | void 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 |
3884 | noparams: |
3885 | tk=gettoken(); |
3886 | endofparams: |
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 |
3894 | TItemField *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 |
4107 | void 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 |
4375 | bool 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 |
4793 | TScriptVarDef *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 |
4804 | TScriptVarDef *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 |
4813 | sInt16 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 |
4832 | TItemField *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) |
4849 | TItemField *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 |
4858 | TItemField *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) |
4866 | void 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 |
4883 | sInt16 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 |
4904 | TItemField *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 |
4912 | bool 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 |