File: | libsynthesis/src/sysync/configelement.cpp |
Warning: | line 434, column 13 Value stored to 'p' is never read |
1 | /* |
2 | * File: ConfigElement.cpp |
3 | * |
4 | * Author: Lukas Zeller (luz@plan44.ch) |
5 | * |
6 | * TConfigElement |
7 | * Element of hierarchical configuration |
8 | * |
9 | * Copyright (c) 2001-2011 by Synthesis AG + plan44.ch |
10 | * |
11 | * 2001-11-14 : luz : created |
12 | */ |
13 | |
14 | // includes |
15 | #include "prefix_file.h" |
16 | #include "sysync.h" |
17 | #include "configelement.h" |
18 | #include "syncappbase.h" |
19 | #include "scriptcontext.h" |
20 | #include "multifielditem.h" |
21 | #include "sysync_crc16.h" |
22 | #include "vtimezone.h" |
23 | |
24 | |
25 | using namespace sysync; |
26 | |
27 | #ifndef RELEASE_VERSION |
28 | // if defined, parsing debug info goes to console |
29 | //#define CONSDEBUG |
30 | #endif |
31 | |
32 | #ifdef CONSDEBUG |
33 | #define CONSDBGPRINTF(m) CONSOLEPRINTF(m)CONSOLE_PRINTF_VARARGS m |
34 | |
35 | const char * const ParseModeNames[numParseModes] = { |
36 | "element", // normal, expecting sub-elements |
37 | "nested", // like pamo_element, but scanning nested elements in same TConfigElement |
38 | "delegated", // I have delegated parsing of a single sub-element of mine to another element (without XML nesting) |
39 | "end", // expecting end of element |
40 | "endnested", // expecting end of nested element (i.e. will call nestedElementEnd()) |
41 | "all", // read over all content |
42 | "string", // string, but with all WSP converted to space and removed at beginning an end |
43 | "rawstring", // string without any changes |
44 | "cstring", // string with \\, \t,\n,\r and \xXX escape conversion |
45 | "macrostring", // string which can contain macros to substitute config vars in $xxx or $() format |
46 | #ifdef SCRIPT_SUPPORT1 |
47 | "script", // tokenized script |
48 | "functiondef", // script function definition |
49 | #endif |
50 | "field", |
51 | "path", // string is updated such that a filename can be appended directly |
52 | "boolean", |
53 | "tristate", |
54 | "timestamp", |
55 | "timezone", |
56 | "vtimezone", |
57 | "idcode", |
58 | "char", |
59 | "int64", |
60 | "int32", |
61 | "int16", |
62 | "enum1by", |
63 | "enum2by", |
64 | "enum4by" |
65 | }; |
66 | #else |
67 | #define CONSDBGPRINTF(m) |
68 | #endif |
69 | |
70 | |
71 | |
72 | /* |
73 | * Implementation of TConfigElement |
74 | */ |
75 | |
76 | /* public TConfigElement members */ |
77 | |
78 | |
79 | TConfigElement::TConfigElement(const char *aElementName, TConfigElement *aParentElementP) |
80 | { |
81 | // set element name |
82 | fElementName=aElementName; |
83 | fResolveImmediately=false; // by default, elements do not resolve immediately, but when entire config is read |
84 | // set parent and root element pointers |
85 | fParentElementP=aParentElementP; |
86 | if (fParentElementP) { |
87 | // has parent, get root from parent |
88 | fRootElementP=fParentElementP->getRootElement(); |
89 | } |
90 | else { |
91 | // base element cannot be root |
92 | fRootElementP=NULL__null; |
93 | } |
94 | #ifndef HARDCODED_CONFIG |
95 | // init parsing |
96 | ResetParsing(); |
97 | #endif |
98 | fCfgVarExp=0; |
99 | } // TConfigElement::TConfigElement |
100 | |
101 | |
102 | TConfigElement::~TConfigElement() |
103 | { |
104 | clear(); |
105 | } // TConfigElement::~TConfigElement |
106 | |
107 | |
108 | TSyncAppBase *TConfigElement::getSyncAppBase(void) |
109 | { |
110 | return fRootElementP ? fRootElementP->fSyncAppBaseP : NULL__null; |
111 | } // TConfigElement::getSyncAppBase |
112 | |
113 | |
114 | // - convenience version for getting time |
115 | lineartime_t TConfigElement::getSystemNowAs(timecontext_t aContext) |
116 | { |
117 | return sysync::getSystemNowAs(aContext,getSyncAppBase()->getAppZones()); |
118 | } // TConfigElement::getSystemNowAs |
119 | |
120 | |
121 | #ifndef HARDCODED_CONFIG |
122 | |
123 | // static helper, returns attribute value or NULL if none |
124 | const char *TConfigElement::getAttr(const char **aAttributes, const char *aAttrName) |
125 | { |
126 | if (!aAttributes) return NULL__null; |
127 | const char *attname; |
128 | while ((attname=*aAttributes)!=0) { |
129 | if (strucmp(attname,aAttrName)==0) { |
130 | return *(++aAttributes); // found, return value |
131 | } |
132 | aAttributes+=2; // skip value, go to next name |
133 | } |
134 | return NULL__null; // not found |
135 | } // TConfigElement::getAttr |
136 | |
137 | |
138 | // get attribute value, check for macro expansion |
139 | // @param aDefaultExpand : if set, non-recursive expansion is done anyway, otherwise, a "expand" attribute is required |
140 | bool TConfigElement::getAttrExpanded(const char **aAttributes, const char *aAttrName, string &aValue, bool aDefaultExpand) |
141 | { |
142 | cAppCharP val = getAttr(aAttributes, aAttrName); |
143 | if (!val) return false; // no value |
144 | aValue = val; |
145 | getSyncAppBase()->expandConfigVars(aValue, aDefaultExpand ? 1 : fCfgVarExp, this, getName()); |
146 | return true; |
147 | } // TConfigElement::getAttrExpanded |
148 | |
149 | |
150 | // static helper, returns true if attribute has valid (or none, if aOpt) bool value |
151 | bool TConfigElement::getAttrBool(const char **aAttributes, const char *aAttrName, bool &aBool, bool aOpt) |
152 | { |
153 | const char *v = getAttr(aAttributes,aAttrName); |
154 | if (!v) return aOpt; // not existing, is ok if optional |
155 | return StrToBool(v,aBool); |
156 | } // TConfigElement::getAttrBool |
157 | |
158 | |
159 | // static helper, returns true if attribute has valid (or none, if aOpt) short value |
160 | bool TConfigElement::getAttrShort(const char **aAttributes, const char *aAttrName, sInt16 &aShort, bool aOpt) |
161 | { |
162 | const char *v = getAttr(aAttributes,aAttrName); |
163 | if (!v) return aOpt; // not existing, is ok if optional |
164 | return StrToShort(v,aShort); |
165 | } // TConfigElement::getAttrShort |
166 | |
167 | |
168 | // static helper, returns true if attribute has valid (or none, if aOpt) short value |
169 | bool TConfigElement::getAttrLong(const char **aAttributes, const char *aAttrName, sInt32 &aLong, bool aOpt) |
170 | { |
171 | const char *v = getAttr(aAttributes,aAttrName); |
172 | if (!v) return aOpt; // not existing, is ok if optional |
173 | return StrToLong(v,aLong); |
174 | } // TConfigElement::getAttrLong |
175 | |
176 | |
177 | #ifdef SCRIPT_SUPPORT1 |
178 | sInt16 TConfigElement::getFieldIndex(cAppCharP aFieldName, TFieldListConfig *aFieldListP, TScriptContext *aScriptContextP) |
179 | { |
180 | // fields or local script variables (if any) can be mapped |
181 | if (aScriptContextP) |
182 | return aScriptContextP->getIdentifierIndex(OBJ_AUTO0, aFieldListP,aFieldName); |
183 | else |
184 | return aFieldListP ? aFieldListP->fieldIndex(aFieldName) : VARIDX_UNDEFINED-128; |
185 | } |
186 | #else |
187 | sInt16 TConfigElement::getFieldIndex(cAppCharP aFieldName, TFieldListConfig *aFieldListP) |
188 | { |
189 | // only direct mapping of MultiFieldItem fields |
190 | return aFieldListP ? aFieldListP->fieldIndex(aFieldName) : VARIDX_UNDEFINED-128; |
191 | } |
192 | #endif |
193 | |
194 | #endif // HARDCODED_CONFIG |
195 | |
196 | |
197 | |
198 | void TConfigElement::clear(void) |
199 | { |
200 | // nop |
201 | } // TConfigElement::clear |
202 | |
203 | |
204 | // resolve (finish after all data is parsed) |
205 | void TConfigElement::Resolve(bool aLastPass) |
206 | { |
207 | #ifndef HARDCODED_CONFIG |
208 | // Only resolve if element was not already resolved when it finished parsing |
209 | // or if it was not parsed at all (that is, it did not appear in the config |
210 | // at all and only contains default values) |
211 | if (!fResolveImmediately || !fCompleted) { |
212 | // try to finally resolve private stuff now that all children are resolved |
213 | localResolve(aLastPass); |
214 | } |
215 | #else |
216 | // hardcoded config is never resolved early |
217 | localResolve(aLastPass); |
218 | #endif |
219 | }; // TConfigElement::Resolve |
220 | |
221 | |
222 | #ifdef SYDEBUG2 |
223 | |
224 | TDebugLogger *TConfigElement::getDbgLogger(void) |
225 | { |
226 | // commands log to session's logger |
227 | TSyncAppBase *appBase = getSyncAppBase(); |
228 | return appBase ? appBase->getDbgLogger() : NULL__null; |
229 | } // TConfigElement::getDbgLogger |
230 | |
231 | uInt32 TConfigElement::getDbgMask(void) |
232 | { |
233 | TSyncAppBase *appBase = getSyncAppBase(); |
234 | if (!appBase) return 0; // no session, no debug |
235 | return appBase->getDbgMask(); |
236 | } // TConfigElement::getDbgMask |
237 | |
238 | #endif |
239 | |
240 | |
241 | #ifndef HARDCODED_CONFIG |
242 | |
243 | void TConfigElement::ResetParsing(void) |
244 | { |
245 | fChildParser=NULL__null; |
246 | fParseMode=pamo_element; // expecting elements |
247 | fNest=0; // normal elements start with Nest=0 |
248 | fExpectAllNestStart=-1; // no expectAll called yet |
249 | fCompleted=false; // not yet completed parsing |
250 | fTempString.erase(); // nothing accumulated yet |
251 | #ifdef SYSER_REGISTRATION |
252 | fLockedElement=false; |
253 | fHadLockedSubs=false; |
254 | #endif |
255 | } // TConfigElement::ResetParsing(void) |
256 | |
257 | |
258 | // report error |
259 | void TConfigElement::ReportError(bool aFatal, const char *aMessage, ...) |
260 | { |
261 | const sInt32 maxmsglen=1024; |
262 | char msg[maxmsglen]; |
263 | va_list args; |
264 | |
265 | msg[0]='\0'; |
266 | va_start(args, aMessage)__builtin_va_start(args, aMessage); |
267 | char *p = msg; |
268 | int n=0; |
269 | // show fatal flag |
270 | if (aFatal) { |
271 | strcat(p,"Fatal: "); |
272 | n=strlen(p); |
273 | p+=n; |
274 | } |
275 | // assemble the message string |
276 | vsnprintf(p, maxmsglen-n, aMessage, args); |
277 | va_end(args)__builtin_va_end(args); |
278 | // set the message |
279 | TRootConfigElement *rootP = getRootElement(); |
280 | if (!rootP) |
281 | SYSYNC_THROW(TConfigParseException("Element without root"))throw TConfigParseException("Element without root"); |
282 | rootP->setError(aFatal,msg); |
283 | } // TConfigElement::ReportError |
284 | |
285 | |
286 | // fail in parsing (short form of ReportError) |
287 | bool TConfigElement::fail(const char *aMessage, ...) |
288 | { |
289 | const sInt32 maxmsglen=1024; |
290 | char msg[maxmsglen]; |
291 | va_list args; |
292 | |
293 | msg[0]='\0'; |
294 | va_start(args, aMessage)__builtin_va_start(args, aMessage); |
295 | // assemble the message string |
296 | vsnprintf(msg, maxmsglen, aMessage, args); |
297 | va_end(args)__builtin_va_end(args); |
298 | // report the error |
299 | ReportError(true,msg); |
300 | // skip the rest |
301 | expectAll(); |
302 | // return value |
303 | return true; |
304 | } // TConfigElement::fail |
305 | |
306 | |
307 | // start of element, this config element decides who processes this element |
308 | bool TConfigElement::startElement(const char *aElementName, const char **aAttributes, sInt32 aLine) |
309 | { |
310 | if (fChildParser) { |
311 | // parsing in a sub-level, delegate |
312 | return fChildParser->startElement(aElementName,aAttributes,aLine); |
313 | } |
314 | else { |
315 | CONSDBGPRINTF(( |
316 | "'%s' starts in configElement='%s' with parsemode='%s' nest=%hd at source line=%ld", |
317 | aElementName, |
318 | getName(), |
319 | ParseModeNames[fParseMode], |
320 | fNest, |
321 | aLine |
322 | )); |
323 | if (fParseMode==pamo_all) { |
324 | // read over contents, no matter what is inside |
325 | fNest++; |
326 | return true; |
327 | } |
328 | #ifdef SYSER_REGISTRATION |
329 | // check for locked elements or subtrees |
330 | // - copy parent's lock status |
331 | if (getParentElement()) fLockedElement=getParentElement()->fLockedElement; |
332 | // - if not already locked, see if locked section starts with this element |
333 | if (!fLockedElement) { |
334 | getAttrBool(aAttributes,"locked",fLockedElement,true); |
335 | } |
336 | // Perform CRC sum if locked element |
337 | if (fLockedElement) { |
338 | CONSDBGPRINTF(( |
339 | "'%s' is locked and is included in LockCRC = 0x%04hX", |
340 | aElementName, |
341 | getRootElement()->getConfigLockCRC() |
342 | )); |
343 | // CRC over opening element name |
344 | getRootElement()->addToLockCRC(aElementName); |
345 | // CRC over all attributes |
346 | if (aAttributes) { |
347 | const char **attrs=aAttributes; |
348 | const char *attelem; |
349 | while ((attelem=*attrs++)!=0) { |
350 | // alternating names and values |
351 | getRootElement()->addToLockCRC(attelem); |
352 | } |
353 | } |
354 | } |
355 | #endif |
356 | // check conditional parsing |
357 | bool condmet=true; // assume parsing |
358 | cAppCharP cond; |
359 | string vv,val; |
360 | // check platform filter |
361 | if (condmet) { |
362 | cAppCharP pf=getAttr(aAttributes,"platform"); |
363 | if (pf && strucmp(pf,SYSYNC_PLATFORM_NAME"Linux")!=0) { |
364 | // tag is not for this platform, ignore |
365 | condmet=false; |
366 | } |
367 | } |
368 | // check for config var conditionals |
369 | if (condmet) { |
370 | cond=getAttr(aAttributes,"if"); |
371 | if (cond) { |
372 | // check for value comparison |
373 | string nam; |
374 | // - determine comparison |
375 | cAppCharP p2,p = cond; |
376 | int cmpres = 2; // 2: invalid, 1: var > cond, -1: var < cond, 0: var=cond |
377 | bool neg=false; |
378 | appChar c; |
379 | while ((c=*p)) { |
380 | p2=p++; |
381 | if (c=='=') { |
382 | if (*p=='=') p++; // = and == are equivalent |
383 | cmpres=0; // must be equal |
384 | break; |
385 | } |
386 | else if (c=='!' && *p=='=') { |
387 | p++; |
388 | cmpres=0; |
389 | neg=true; // must not be equal |
390 | break; |
391 | } |
392 | else if (c=='>') { |
393 | if (*p=='=') { |
394 | p++; |
395 | cmpres=-1; // var >= cond is equal to NOT val < cond |
396 | neg=true; |
397 | } |
398 | else { |
399 | cmpres=1; // var > cond |
400 | } |
401 | break; |
402 | } |
403 | else if (c=='<') { |
404 | if (*p=='=') { |
405 | p++; |
406 | cmpres=1; // var <= cond is equal to NOT val > cond |
407 | neg=true; |
408 | } |
409 | else { |
410 | cmpres=-1; // var < cond |
411 | } |
412 | break; |
413 | } |
414 | } |
415 | // now value is at *p or we have not found a comparison op |
416 | if (cmpres<2) { |
417 | nam.assign(cond,p2-cond); |
418 | val=p; |
419 | } |
420 | else { |
421 | nam=cond; |
422 | val.erase(); |
423 | } |
424 | // get var - if it does not exist, comparison always renders false |
425 | if (nam=="version") { |
426 | // special comparison mode for version |
427 | // - parse reference into version components |
428 | uInt16 maj=0,min=0,rev=0,bld=0; |
429 | p = val.c_str(); |
430 | do { |
431 | p+=StrToUShort(p,maj); if (*p++!='.') break; |
432 | p+=StrToUShort(p,min); if (*p++!='.') break; |
433 | p+=StrToUShort(p,rev); if (*p++!='.') break; |
434 | p+=StrToUShort(p,bld); |
Value stored to 'p' is never read | |
435 | } while(false); |
436 | // - compare with hexversion |
437 | StringObjPrintf(val,"%02X%02X%02X%02X",maj,min,rev,bld); |
438 | nam="hexversion"; |
439 | } |
440 | // get config var to perform condition check |
441 | condmet=getSyncAppBase()->getConfigVar(nam.c_str(),vv); |
442 | if (condmet) { |
443 | // var exists, perform comparison |
444 | if (cmpres>=2) { |
445 | // no comparison, but just boolean check. Non-bool but empty=false, non-bool-non-empty=true |
446 | condmet = !vv.empty(); |
447 | if (condmet) StrToBool(vv.c_str(), condmet); |
448 | } |
449 | else { |
450 | int res = strcmp(vv.c_str(),val.c_str()); |
451 | res = res>0 ? 1 : (res<0 ? -1 : 0); |
452 | condmet = neg != (cmpres==res); |
453 | } |
454 | } |
455 | } |
456 | } |
457 | // check for ifdef |
458 | if (condmet) { |
459 | cond=getAttr(aAttributes,"ifdef"); |
460 | if (cond) |
461 | condmet = getSyncAppBase()->getConfigVar(cond,vv); |
462 | } |
463 | // check for ifndef |
464 | if (condmet) { |
465 | cond=getAttr(aAttributes,"ifndef"); |
466 | if (cond) |
467 | condmet = !getSyncAppBase()->getConfigVar(cond,vv); |
468 | } |
469 | // skip this tag if conditions not met |
470 | if (!condmet) { |
471 | // skip everything inside |
472 | CONSDBGPRINTF(("Condition for parsing not met - skipping tag")); |
473 | expectAll(); |
474 | return true; // ok, go on |
475 | } |
476 | // Now we know that we must actually parse this tag |
477 | // check configvar expansion mode |
478 | fCfgVarExp=0; // default to automatic (i.e. certain content such as paths or macrostrings will be expanded without "expand" attr) |
479 | cAppCharP cm=getAttr(aAttributes,"expand"); |
480 | if (cm) { |
481 | // explicit expand directive for this tag (includes expandable attributes of this tag, |
482 | // but only attributes queried with getAttrExpanded() can expand at all) |
483 | bool b; |
484 | if (strucmp(cm,"single")==0) fCfgVarExp=1; |
485 | else if (StrToBool(cm,b)) fCfgVarExp = b ? 2 : -1; |
486 | } |
487 | // check for use-everywhere special tags |
488 | if (strucmp(aElementName,"configmsg")==0) { |
489 | bool iserr=true; |
490 | cAppCharP msg=getAttr(aAttributes,"error"); |
491 | if (!msg) { |
492 | msg=getAttr(aAttributes,"warning"); |
493 | iserr=false; |
494 | } |
495 | if (!msg) msg="Error: found <configmsg>"; |
496 | ReportError(iserr,msg); |
497 | expectAll(); |
498 | return false; // generate error message |
499 | } |
500 | // check if we are re-entering the same object again (trying to overwrite) |
501 | if (fCompleted) { |
502 | ReportError(true,"Duplicate definition of <%s>",aElementName); |
503 | expectAll(); |
504 | return false; // not allowed, generate error message |
505 | } |
506 | if (fParseMode==pamo_element || fParseMode==pamo_nested) { |
507 | // expecting element |
508 | fTempString.erase(); |
509 | if (localStartElement(aElementName,aAttributes,aLine)) { |
510 | // element known and parsing initialized ok |
511 | #ifdef SYSER_REGISTRATION |
512 | if (fLockedElement && fChildParser==NULL__null && fParseMode!=pamo_element && fParseMode!=pamo_nested) |
513 | fHadLockedSubs=true; // flag that this element has processed non-child subelements in locked mode |
514 | #endif |
515 | return true; |
516 | } |
517 | else { |
518 | // unknown element: read over all its contents |
519 | ReportError(false,"invalid tag <%s>",aElementName); |
520 | expectAll(); |
521 | return false; // element not known, generate error message |
522 | } |
523 | } |
524 | /* %%% moved up to beginning |
525 | else if (fParseMode==pamo_all) { |
526 | // read over contents |
527 | fNest++; |
528 | } |
529 | */ |
530 | else { |
531 | ReportError(false,"no XML tag expected here"); |
532 | } |
533 | return true; |
534 | } |
535 | } // TConfigElement::startElement |
536 | |
537 | |
538 | // character data of current element |
539 | void TConfigElement::charData(const char *aCharData, sInt32 aNumChars) |
540 | { |
541 | if (fChildParser) { |
542 | // parsing in a sub-level, delegate |
543 | return fChildParser->charData(aCharData,aNumChars); |
544 | } |
545 | else { |
546 | if (fParseMode==pamo_all /* %%% not needed, I think || fNest>0 */) { |
547 | // just ignore |
548 | } |
549 | else if (fParseMode==pamo_element || fParseMode==pamo_nested || fParseMode==pamo_end || fParseMode==pamo_endnested) { |
550 | // only whitespace allowed here |
551 | while (aNumChars--) { |
552 | if (!isspace(*aCharData++)) { |
553 | ReportError(false,"no character data expected"); |
554 | break; |
555 | } |
556 | } |
557 | } |
558 | else { |
559 | // accumulate char data in string |
560 | fTempString.append(aCharData,aNumChars); |
561 | } |
562 | } |
563 | } // TConfigElement::charData |
564 | |
565 | |
566 | |
567 | // read over all contents of current TConfigElement |
568 | void TConfigElement::expectAll(void) |
569 | { |
570 | // we are already in an element but have no non-decrementing |
571 | // parse mode set like expectEmpty() or expectString() etc. |
572 | // so nest count must be incremented to balance decrement occurring |
573 | // at next element end. |
574 | fExpectAllNestStart = fParseMode==pamo_nested ? fNest : -1; // remember where we left nested mode and entered ignoring mode |
575 | fNest++; |
576 | fParseMode=pamo_all; |
577 | } // TConfigElement::expectAll |
578 | |
579 | |
580 | // expect Enum element |
581 | void TConfigElement::expectEnum(sInt16 aDestSize,void *aPtr, const char * const aEnumNames[], sInt16 aNumEnums) |
582 | { |
583 | // save params |
584 | fParseEnumArray = aEnumNames; |
585 | fParseEnumNum = aNumEnums; |
586 | // determine mode and destination |
587 | switch (aDestSize) { |
588 | case 1 : |
589 | fParseMode=pamo_enum1by; |
590 | fResultPtr.fCharP=(char *)aPtr; |
591 | break; |
592 | case 2 : |
593 | fParseMode=pamo_enum2by; |
594 | fResultPtr.fShortP=(sInt16 *)aPtr; |
595 | break; |
596 | case 4 : |
597 | fParseMode=pamo_enum4by; |
598 | fResultPtr.fLongP=(sInt32 *)aPtr; |
599 | break; |
600 | default: |
601 | SYSYNC_THROW(TConfigParseException("expectEnum: invalid enum size"))throw TConfigParseException("expectEnum: invalid enum size"); |
602 | } |
603 | } // TConfigElement::expectEnum |
604 | |
605 | |
606 | // delegate parsing of a single element to another config element |
607 | // after processing aElementName and all subtags, processing will return to this object |
608 | // (rather than waiting for an end tag in aConfigElemP) |
609 | bool TConfigElement::delegateParsingTo(TConfigElement *aConfigElemP, const char *aElementName, const char **aAttributes, sInt32 aLine) |
610 | { |
611 | if (aConfigElemP) { |
612 | expectChildParsing(*aConfigElemP); |
613 | fParseMode=pamo_delegated; |
614 | // let child parse the current tag right away |
615 | return aConfigElemP->localStartElement(aElementName,aAttributes,aLine); |
616 | } |
617 | else |
618 | return false; // if we have no delegate, we can't understand this tag |
619 | } // TConfigElement::delegateParsingTo |
620 | |
621 | |
622 | // end of element, returns true when this config element is done parsing |
623 | bool TConfigElement::endElement(const char *aElementName, bool aIsDelegated) |
624 | { |
625 | if (fChildParser) { |
626 | // parsing in a real or simulated (delegateParsingTo) sub-level |
627 | if (fChildParser->endElement(aElementName,fParseMode==pamo_delegated)) { |
628 | // child has finished parsing |
629 | fChildParser=NULL__null; // handle next element myself |
630 | if (aIsDelegated) { |
631 | // - we were delegated to process a single element from another element. |
632 | // So, we nust not continue parsing here, but pass back to parent |
633 | // for next element |
634 | return true; |
635 | } |
636 | // otherwise, wait here for next element to start (or encosing element to end) |
637 | fParseMode=pamo_element; // expect another element or end of myself |
638 | } |
639 | else { |
640 | // child is still parsing, so am I |
641 | return false; |
642 | } |
643 | } |
644 | else { |
645 | CONSDBGPRINTF(( |
646 | "'%s' ends in configElement='%s' with parsemode='%s' nest=%hd, aIsDelegated=%d", |
647 | aElementName, |
648 | getName(), |
649 | ParseModeNames[fParseMode], |
650 | fNest, |
651 | aIsDelegated |
652 | )); |
653 | #ifdef SYSER_REGISTRATION |
654 | // Perform CRC sum if locked element |
655 | if (fLockedElement) { |
656 | // CRC over CharData |
657 | getRootElement()->addToLockCRC(fTempString.c_str()); |
658 | } |
659 | #endif |
660 | const char *p; // BCPPB: declaring vars in case does not work. |
661 | size_t n,lnwsp; |
662 | bool hlp; |
663 | timecontext_t tctx; |
664 | // expand macros in string first |
665 | if (fCfgVarExp==0) { |
666 | // auto mode |
667 | fCfgVarExp = fParseMode==pamo_path || fParseMode==pamo_macrostring ? 2 : -1; |
668 | } |
669 | // do config variable expansion now (or not, according to fCfgVarExp) |
670 | getSyncAppBase()->expandConfigVars(fTempString,fCfgVarExp,this,aElementName); |
671 | // now parse |
672 | switch (fParseMode) { |
673 | case pamo_end: |
674 | case pamo_endnested: |
675 | if (fNest>0) { |
676 | // Note: normal empty elements are NOT considered nested elements by default, only if |
677 | // they request endnested mode explicitly |
678 | if (fParseMode==pamo_endnested) |
679 | nestedElementEnd(); // inform possible parser of nested element that a nested element ends here |
680 | // back to nested |
681 | fParseMode=pamo_nested; |
682 | } |
683 | else |
684 | fParseMode=pamo_element; // back to normal element parsing |
685 | return false; // do not exit |
686 | case pamo_all: |
687 | case pamo_nested: |
688 | if (fNest>0) { |
689 | // not yet finished with startlevel, continue with pamo_all/pamo_nested |
690 | fNest--; |
691 | if (fExpectAllNestStart>0) { |
692 | // we are in expectAll mode within pamo_nested |
693 | if(fNest==fExpectAllNestStart) { |
694 | // reached level where we started ignoring contents before |
695 | fExpectAllNestStart = -1; // processed now |
696 | fParseMode = pamo_nested; // back to nested (but active) parsing, like in Mime-Dir profile |
697 | } |
698 | else { |
699 | // NOP here - do NOT call nestedElementEnd() |
700 | } |
701 | } |
702 | else { |
703 | // end of active nested element |
704 | nestedElementEnd(); // inform possible parser of nested element |
705 | if (fNest==0) { |
706 | // if back on nest level 0, switch to pamo_element |
707 | fParseMode = pamo_element; |
708 | } |
709 | } |
710 | return false; // stay in this element |
711 | } |
712 | // nested or all at level 0 cause handling like pamo_element |
713 | case pamo_element: |
714 | // end of element while looking for elements: |
715 | // this is end of this config element itself |
716 | // - flag completion of this element |
717 | fCompleted=true; // prevents re-entry |
718 | // Resolve if this is element says it is self-contained (no references to other elements |
719 | // that might follow later in the config file) |
720 | if (fResolveImmediately) { |
721 | // resolve element NOW, last pass |
722 | localResolve(true); |
723 | } |
724 | // - return parsing authority to caller |
725 | return true; |
726 | case pamo_cstring: |
727 | // interpret C-type escapes (only \t,\r,\n and \xXX, no octal) |
728 | #ifdef SYSER_REGISTRATION |
729 | if (fHadLockedSubs && !fResultPtr.fStringP->empty()) |
730 | ReportError(true,"Duplicate definition of <%s>",aElementName); |
731 | #endif |
732 | fResultPtr.fStringP->erase(); |
733 | CStrToStrAppend(fTempString.c_str(), *(fResultPtr.fStringP)); |
734 | break; |
735 | case pamo_string: |
736 | case pamo_macrostring: |
737 | case pamo_path: |
738 | #ifdef SYSER_REGISTRATION |
739 | if (fHadLockedSubs && !fResultPtr.fStringP->empty()) |
740 | ReportError(true,"Duplicate definition of <%s>",aElementName); |
741 | #endif |
742 | // remove spaces at beginning and end |
743 | fResultPtr.fStringP->erase(); |
744 | p = fTempString.c_str(); |
745 | // - skip leading spaces |
746 | while (*p && isspace(*p)) ++p; |
747 | // - copy chars and convert all wsp to spaces |
748 | n=0; lnwsp=0; |
749 | while (*p) { |
750 | ++n; // count char |
751 | if (isspace(*p)) |
752 | fResultPtr.fStringP->append(" "); |
753 | else { |
754 | fResultPtr.fStringP->append(p,1); |
755 | lnwsp=n; // remember last non-whitespace |
756 | } |
757 | ++p; |
758 | } |
759 | // - remove trailing spaces |
760 | fResultPtr.fStringP->resize(lnwsp); |
761 | // - if path requested, shape it up if needed |
762 | if (fParseMode==pamo_path) { |
763 | makeOSDirPath(*(fResultPtr.fStringP)); |
764 | } |
765 | break; |
766 | case pamo_rawstring: |
767 | #ifdef SYSER_REGISTRATION |
768 | if (fHadLockedSubs && !fResultPtr.fStringP->empty()) |
769 | ReportError(true,"Duplicate definition of <%s>",aElementName); |
770 | #endif |
771 | (*(fResultPtr.fStringP))=fTempString; |
772 | break; |
773 | case pamo_field: |
774 | #ifdef SCRIPT_SUPPORT1 |
775 | *(fResultPtr.fShortP) = getFieldIndex(fTempString.c_str(),fFieldListP,fScriptContextP); |
776 | #else |
777 | *(fResultPtr.fShortP) = getFieldIndex(fTempString.c_str(),fFieldListP); |
778 | #endif |
779 | break; |
780 | #ifdef SCRIPT_SUPPORT1 |
781 | case pamo_script: |
782 | case pamo_functiondef: |
783 | SYSYNC_TRYtry { |
784 | if (fParseMode==pamo_functiondef) |
785 | TScriptContext::TokenizeAndResolveFunction(getSyncAppBase(),fExpectLine,fTempString.c_str(),(*(fResultPtr.fFuncDefP))); |
786 | else { |
787 | #ifdef SYSER_REGISTRATION |
788 | if (fHadLockedSubs && !fResultPtr.fStringP->empty()) |
789 | ReportError(true,"Duplicate definition of <%s>",aElementName); |
790 | #endif |
791 | TScriptContext::Tokenize(getSyncAppBase(),aElementName,fExpectLine,fTempString.c_str(),(*(fResultPtr.fStringP)),fExpectContextFuncs,false,fExpectNoDeclarations); |
792 | } |
793 | } |
794 | SYSYNC_CATCH (exception &e)catch(exception &e) { |
795 | ReportError(true,"Script Error: %s",e.what()); |
796 | SYSYNC_ENDCATCH} |
797 | break; |
798 | #endif |
799 | case pamo_timestamp: |
800 | // expect timestamp, store as UTC |
801 | if (ISO8601StrToTimestamp(fTempString.c_str(), *(fResultPtr.fTimestampP), tctx)==0) |
802 | ReportError(false,"bad ISO8601 timestamp value"); |
803 | if (TCTX_IS_UNKNOWN(tctx)) tctx=TCTX_SYSTEM((timecontext_t) ((tctx_tz_system) | TCTX_SYMBOLIC_TZ)); |
804 | TzConvertTimestamp(*(fResultPtr.fTimestampP),tctx,TCTX_UTC((timecontext_t) ((tctx_tz_UTC) | TCTX_SYMBOLIC_TZ)),getSyncAppBase()->getAppZones()); |
805 | break; |
806 | case pamo_timezone: |
807 | // time zone by name (internal or olson) |
808 | if (!TimeZoneNameToContext(fTempString.c_str(), *(fResultPtr.fTimeContextP), getSyncAppBase()->getAppZones(), true)) |
809 | ReportError(false,"invalid/unknown timezone name"); |
810 | break; |
811 | case pamo_vtimezone: |
812 | // definition of custom time zone in vTimezone format |
813 | if (!VTIMEZONEtoInternal(fTempString.c_str(), tctx, fResultPtr.fGZonesP, getDbgLogger())) |
814 | ReportError(false,"invalid vTimezone defintion"); |
815 | break; |
816 | case pamo_boolean: |
817 | if (!StrToBool(fTempString.c_str(),*(fResultPtr.fBoolP))) |
818 | ReportError(false,"bad boolean value"); |
819 | break; |
820 | case pamo_tristate: |
821 | if (fTempString.empty() || fTempString=="unspecified" || fTempString=="default") |
822 | *(fResultPtr.fByteP)=-1; // unspecified |
823 | else { |
824 | if (!StrToBool(fTempString.c_str(),hlp)) |
825 | ReportError(false,"bad boolean value"); |
826 | else |
827 | *(fResultPtr.fByteP)= hlp ? 1 : 0; |
828 | } |
829 | break; |
830 | case pamo_int64: |
831 | if (!StrToLongLong(fTempString.c_str(),*(fResultPtr.fLongLongP))) |
832 | ReportError(false,"bad integer (64bit) value"); |
833 | break; |
834 | case pamo_int32: |
835 | if (!StrToLong(fTempString.c_str(),*(fResultPtr.fLongP))) |
836 | ReportError(false,"bad integer (32bit) value"); |
837 | break; |
838 | case pamo_char: |
839 | if (fTempString.size()>1) |
840 | ReportError(false,"single char or nothing (=NUL char) expected"); |
841 | if (fTempString.size()==0) |
842 | *(fResultPtr.fCharP) = 0; |
843 | else |
844 | *(fResultPtr.fCharP) = fTempString[0]; |
845 | break; |
846 | case pamo_idCode: |
847 | // Palm/MacOS-type 4-char code |
848 | if (fTempString.size()!=4) |
849 | ReportError(false,"id code must be exactly 4 characters"); |
850 | *(fResultPtr.fLongP) = |
851 | ((uInt32)fTempString[0] << 24) + |
852 | ((uInt32)fTempString[1] << 16) + |
853 | ((uInt16)fTempString[2] << 8) + |
854 | ((uInt8)fTempString[3]); |
855 | break; |
856 | case pamo_int16: |
857 | if (!StrToShort(fTempString.c_str(),*(fResultPtr.fShortP))) |
858 | ReportError(false,"bad integer (16bit) value"); |
859 | break; |
860 | case pamo_enum1by: |
861 | case pamo_enum2by: |
862 | case pamo_enum4by: |
863 | sInt16 tempenum; |
864 | if (!StrToEnum(fParseEnumArray,fParseEnumNum,tempenum,fTempString.c_str())) |
865 | ReportError(false,"bad enumeration value '%s'",fTempString.c_str()); |
866 | else { |
867 | // now assign enum |
868 | if (fParseMode==pamo_enum1by) *fResultPtr.fCharP = tempenum; |
869 | else if (fParseMode==pamo_enum2by) *fResultPtr.fShortP = tempenum; |
870 | else if (fParseMode==pamo_enum4by) *fResultPtr.fLongP = tempenum; |
871 | } |
872 | break; |
873 | default: |
874 | SYSYNC_THROW(TConfigParseException(DEBUGTEXT("Unknown parse mode","ce1")))throw TConfigParseException("Unknown parse mode"); |
875 | } |
876 | // normal case: end of simple element parsed at same level as parent |
877 | if (aIsDelegated) { |
878 | // - we were delegated to process a single element from another element. |
879 | // So, we nust not continue parsing here, but pass back to parent |
880 | // for next element |
881 | return true; |
882 | } |
883 | else { |
884 | // - expect next element |
885 | fParseMode=pamo_element; |
886 | } |
887 | } |
888 | // end of embedded element, but not of myself |
889 | #ifdef SYSER_REGISTRATION |
890 | #ifdef CONSDEBUG |
891 | if (fLockedElement) { |
892 | CONSDBGPRINTF(( |
893 | "'%s' was locked and was included in LockCRC = 0x%04hX", |
894 | aElementName, |
895 | getRootElement()->getConfigLockCRC() |
896 | )); |
897 | } |
898 | #endif |
899 | // - back to parent's lock status (or false if no parent) |
900 | fLockedElement=getParentElement() ? getParentElement()->fLockedElement : false; |
901 | #endif |
902 | // do not return to parent config element |
903 | return false; |
904 | } // TConfigElement::endElement |
905 | |
906 | |
907 | |
908 | // reset error |
909 | void TRootConfigElement::resetError(void) |
910 | { |
911 | fError=false; |
912 | fErrorMessage.erase(); |
913 | } // TRootConfigElement::ResetError |
914 | |
915 | |
916 | // set error |
917 | void TRootConfigElement::setError(bool aFatal, const char *aMsg) |
918 | { |
919 | if (!fErrorMessage.empty()) |
920 | fErrorMessage += '\n'; // multiple messages on multiple lines |
921 | fErrorMessage.append(aMsg); |
922 | if (aFatal && fFatalError==LOCERR_OK) |
923 | fFatalError=LOCERR_CFGPARSE; // this is a config parse error |
924 | fError=true; |
925 | } // TRootConfigElement::setError |
926 | |
927 | |
928 | // check for error message |
929 | const char *TRootConfigElement::getErrorMsg(void) |
930 | { |
931 | if (!fError) return NULL__null; |
932 | return fErrorMessage.c_str(); |
933 | } // TRootConfigElement::GetErrorMsg |
934 | |
935 | |
936 | // reset parsing (=reset error and fatal errors) |
937 | void TRootConfigElement::ResetParsing(void) |
938 | { |
939 | resetError(); |
940 | fDocStarted=false; |
941 | fFatalError=LOCERR_OK; |
942 | #ifdef SYSER_REGISTRATION |
943 | fLockCRC=0; // no CRC lock sum yet |
944 | #endif |
945 | TConfigElement::ResetParsing(); |
946 | } // TRootConfigElement::ResetParsing |
947 | |
948 | |
949 | #endif |
950 | |
951 | // resolve config tree and catch errors |
952 | bool TRootConfigElement::ResolveAll(void) |
953 | { |
954 | SYSYNC_TRYtry { |
955 | Resolve(true); |
956 | return true; |
957 | } |
958 | SYSYNC_CATCH (TConfigParseException &e)catch(TConfigParseException &e) { |
959 | #ifndef HARDCODED_CONFIG |
960 | ReportError(true,e.what()); |
961 | #endif |
962 | return false; |
963 | SYSYNC_ENDCATCH} |
964 | } // TRootConfigElement::ResolveAll |
965 | |
966 | |
967 | #ifndef HARDCODED_CONFIG |
968 | |
969 | // start of element, this config element decides who processes this element |
970 | bool TRootConfigElement::startElement(const char *aElementName, const char **aAttributes, sInt32 aLine) |
971 | { |
972 | if (!fDocStarted) { |
973 | // document not yet started |
974 | if (strucmp(aElementName,XMLCONFIG_DOCNAME"sysync_config")==0) { |
975 | // check version |
976 | const char* vers = getAttr(aAttributes,"version"); |
977 | if (!vers) { |
978 | ReportError(true,"Missing version attribute for document"); |
979 | expectAll(); // ignore everything |
980 | } |
981 | else if (strucmp(vers,XMLCONFIG_DOCVERSION"1.0")!=0) { |
982 | ReportError(true, |
983 | "Bad config document version (expected %s, found %s)", |
984 | XMLCONFIG_DOCVERSION"1.0", |
985 | vers |
986 | ); |
987 | expectAll(); // ignore everything inside that element |
988 | } |
989 | fDocStarted=true; // started normally |
990 | return true; |
991 | } |
992 | else { |
993 | // invalid element |
994 | return false; |
995 | } |
996 | } |
997 | else { |
998 | return TConfigElement::startElement(aElementName,aAttributes,aLine); |
999 | } |
1000 | } // TRootConfigElement::startElement |
1001 | |
1002 | |
1003 | #ifdef SYSER_REGISTRATION |
1004 | // add config text to locking CRC |
1005 | void TRootConfigElement::addToLockCRC(const char *aCharData, size_t aNumChars) |
1006 | { |
1007 | size_t n = aNumChars ? aNumChars : strlen(aCharData); |
1008 | char c; |
1009 | bool lastwasctrl=false; |
1010 | while (n--) { |
1011 | c=*aCharData++; |
1012 | // compact multiple whitespace to single char |
1013 | if ((uInt8)c<=' ') { |
1014 | if (lastwasctrl) continue; |
1015 | lastwasctrl=true; |
1016 | // treat them all as spaces |
1017 | c=' '; |
1018 | } |
1019 | else { |
1020 | lastwasctrl=false; // this is a non-space |
1021 | } |
1022 | // add to CRC |
1023 | fLockCRC=sysync::sysync_crc16(fLockCRC,(uInt8)c); |
1024 | } |
1025 | } // TRootConfigElement::addToLockCRC |
1026 | #endif |
1027 | |
1028 | #endif // no hardcode config |
1029 | |
1030 | |
1031 | /* end of TConfigElement implementation */ |
1032 | |
1033 | // eof |