Bug Summary

File:libsynthesis/src/sysync/configelement.cpp
Warning:line 434, column 13
Value stored to 'p' is never read

Annotated Source Code

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
25using 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
35const 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
79TConfigElement::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
102TConfigElement::~TConfigElement()
103{
104 clear();
105} // TConfigElement::~TConfigElement
106
107
108TSyncAppBase *TConfigElement::getSyncAppBase(void)
109{
110 return fRootElementP ? fRootElementP->fSyncAppBaseP : NULL__null;
111} // TConfigElement::getSyncAppBase
112
113
114// - convenience version for getting time
115lineartime_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
124const 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
140bool 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
151bool 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
160bool 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
169bool 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
178sInt16 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
187sInt16 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
198void TConfigElement::clear(void)
199{
200 // nop
201} // TConfigElement::clear
202
203
204// resolve (finish after all data is parsed)
205void 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
224TDebugLogger *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
231uInt32 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
243void 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
259void 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)
287bool 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
308bool 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
539void 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
568void 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
581void 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)
609bool 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
623bool 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
909void TRootConfigElement::resetError(void)
910{
911 fError=false;
912 fErrorMessage.erase();
913} // TRootConfigElement::ResetError
914
915
916// set error
917void 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
929const 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)
937void 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
952bool 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
970bool 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
1005void 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