2 * File: ConfigElement.cpp
4 * Author: Lukas Zeller (luz@synthesis.ch)
7 * Element of hierarchical configuration
9 * Copyright (c) 2001-2009 by Synthesis AG (www.synthesis.ch)
11 * 2001-11-14 : luz : created
15 #include "prefix_file.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"
25 using namespace sysync;
27 #ifndef RELEASE_VERSION
28 // if defined, parsing debug info goes to console
33 #define CONSDBGPRINTF(m) CONSOLEPRINTF(m)
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
47 "script", // tokenized script
48 "functiondef", // script function definition
51 "path", // string is updated such that a filename can be appended directly
67 #define CONSDBGPRINTF(m)
73 * Implementation of TConfigElement
76 /* public TConfigElement members */
79 TConfigElement::TConfigElement(const char *aElementName, TConfigElement *aParentElementP)
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();
91 // base element cannot be root
94 #ifndef HARDCODED_CONFIG
99 } // TConfigElement::TConfigElement
102 TConfigElement::~TConfigElement()
105 } // TConfigElement::~TConfigElement
108 TSyncAppBase *TConfigElement::getSyncAppBase(void)
110 return fRootElementP ? fRootElementP->fSyncAppBaseP : NULL;
111 } // TConfigElement::getSyncAppBase
114 // - convenience version for getting time
115 lineartime_t TConfigElement::getSystemNowAs(timecontext_t aContext)
117 return sysync::getSystemNowAs(aContext,getSyncAppBase()->getAppZones());
118 } // TConfigElement::getSystemNowAs
121 #ifndef HARDCODED_CONFIG
123 // static helper, returns attribute value or NULL if none
124 const char *TConfigElement::getAttr(const char **aAttributes, const char *aAttrName)
126 if (!aAttributes) return NULL;
128 while ((attname=*aAttributes)!=0) {
129 if (strucmp(attname,aAttrName)==0) {
130 return *(++aAttributes); // found, return value
132 aAttributes+=2; // skip value, go to next name
134 return NULL; // not found
135 } // TConfigElement::getAttr
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)
142 cAppCharP val = getAttr(aAttributes, aAttrName);
143 if (!val) return false; // no value
145 getSyncAppBase()->expandConfigVars(aValue, aDefaultExpand ? 1 : fCfgVarExp, this, getName());
147 } // TConfigElement::getAttrExpanded
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)
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
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)
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
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)
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
177 #ifdef SCRIPT_SUPPORT
178 sInt16 TConfigElement::getFieldIndex(cAppCharP aFieldName, TFieldListConfig *aFieldListP, TScriptContext *aScriptContextP)
180 // fields or local script variables (if any) can be mapped
182 return aScriptContextP->getIdentifierIndex(OBJ_AUTO, aFieldListP,aFieldName);
184 return aFieldListP ? aFieldListP->fieldIndex(aFieldName) : VARIDX_UNDEFINED;
187 sInt16 TConfigElement::getFieldIndex(cAppCharP aFieldName, TFieldListConfig *aFieldListP)
189 // only direct mapping of MultiFieldItem fields
190 return aFieldListP ? aFieldListP->fieldIndex(aFieldName) : VARIDX_UNDEFINED;
194 #endif // HARDCODED_CONFIG
198 void TConfigElement::clear(void)
201 } // TConfigElement::clear
204 // resolve (finish after all data is parsed)
205 void TConfigElement::Resolve(bool aLastPass)
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);
216 // hardcoded config is never resolved early
217 localResolve(aLastPass);
219 }; // TConfigElement::Resolve
224 TDebugLogger *TConfigElement::getDbgLogger(void)
226 // commands log to session's logger
227 TSyncAppBase *appBase = getSyncAppBase();
228 return appBase ? appBase->getDbgLogger() : NULL;
229 } // TConfigElement::getDbgLogger
231 uInt32 TConfigElement::getDbgMask(void)
233 TSyncAppBase *appBase = getSyncAppBase();
234 if (!appBase) return 0; // no session, no debug
235 return appBase->getDbgMask();
236 } // TConfigElement::getDbgMask
241 #ifndef HARDCODED_CONFIG
243 void TConfigElement::ResetParsing(void)
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;
255 } // TConfigElement::ResetParsing(void)
259 void TConfigElement::ReportError(bool aFatal, const char *aMessage, ...)
261 const sInt32 maxmsglen=1024;
266 va_start(args, aMessage);
275 // assemble the message string
276 vsnprintf(p, maxmsglen-n, aMessage, args);
279 TRootConfigElement *rootP = getRootElement();
281 SYSYNC_THROW(TConfigParseException("Element without root"));
282 rootP->setError(aFatal,msg);
283 } // TConfigElement::ReportError
286 // fail in parsing (short form of ReportError)
287 bool TConfigElement::fail(const char *aMessage, ...)
289 const sInt32 maxmsglen=1024;
294 va_start(args, aMessage);
295 // assemble the message string
296 vsnprintf(msg, maxmsglen, aMessage, args);
299 ReportError(true,msg);
304 } // TConfigElement::fail
307 // start of element, this config element decides who processes this element
308 bool TConfigElement::startElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
311 // parsing in a sub-level, delegate
312 return fChildParser->startElement(aElementName,aAttributes,aLine);
316 "'%s' starts in configElement='%s' with parsemode='%s' nest=%hd at source line=%ld",
319 ParseModeNames[fParseMode],
323 #ifdef SYSER_REGISTRATION
324 // check for locked elements or subtrees
325 // - copy parent's lock status
326 if (getParentElement()) fLockedElement=getParentElement()->fLockedElement;
327 // - if not already locked, see if locked section starts with this element
328 if (!fLockedElement) {
329 getAttrBool(aAttributes,"locked",fLockedElement,true);
331 // Perform CRC sum if locked element
332 if (fLockedElement) {
334 "'%s' is locked and is included in LockCRC = 0x%04hX",
336 getRootElement()->getConfigLockCRC()
338 // CRC over opening element name
339 getRootElement()->addToLockCRC(aElementName);
340 // CRC over all attributes
342 const char **attrs=aAttributes;
344 while ((attelem=*attrs++)!=0) {
345 // alternating names and values
346 getRootElement()->addToLockCRC(attelem);
351 // check conditional parsing
352 bool condmet=true; // assume parsing
355 // check platform filter
357 cAppCharP pf=getAttr(aAttributes,"platform");
358 if (pf && strucmp(pf,SYSYNC_PLATFORM_NAME)!=0) {
359 // tag is not for this platform, ignore
363 // check for config var conditionals
365 cond=getAttr(aAttributes,"if");
367 // check for value comparison
369 // - determine comparison
370 cAppCharP p2,p = cond;
371 int cmpres = 2; // 2: invalid, 1: var > cond, -1: var < cond, 0: var=cond
377 if (*p=='=') p++; // = and == are equivalent
378 cmpres=0; // must be equal
381 else if (c=='!' && *p=='=') {
384 neg=true; // must not be equal
390 cmpres=-1; // var >= cond is equal to NOT val < cond
394 cmpres=1; // var > cond
401 cmpres=1; // var <= cond is equal to NOT val > cond
405 cmpres=-1; // var < cond
410 // now value is at *p or we have not found a comparison op
412 nam.assign(cond,p2-cond);
419 // get var - if it does not exist, comparison always renders false
420 if (nam=="version") {
421 // special comparison mode for version
422 // - parse reference into version components
423 uInt16 maj=0,min=0,rev=0,bld=0;
426 p+=StrToUShort(p,maj); if (*p++!='.') break;
427 p+=StrToUShort(p,min); if (*p++!='.') break;
428 p+=StrToUShort(p,rev); if (*p++!='.') break;
429 p+=StrToUShort(p,bld);
431 // - compare with hexversion
432 StringObjPrintf(val,"%02X%02X%02X%02X",maj,min,rev,bld);
435 // get config var to perform condition check
436 condmet=getSyncAppBase()->getConfigVar(nam.c_str(),vv);
438 // var exists, perform comparison
440 // no comparison, but just boolean check. Non-bool but empty=false, non-bool-non-empty=true
441 condmet = !vv.empty();
442 if (condmet) StrToBool(vv.c_str(), condmet);
445 int res = strcmp(vv.c_str(),val.c_str());
446 res = res>0 ? 1 : (res<0 ? -1 : 0);
447 condmet = neg != (cmpres==res);
454 cond=getAttr(aAttributes,"ifdef");
456 condmet = getSyncAppBase()->getConfigVar(cond,vv);
460 cond=getAttr(aAttributes,"ifndef");
462 condmet = !getSyncAppBase()->getConfigVar(cond,vv);
464 // skip this tag if conditions not met
466 // skip everything inside
467 CONSDBGPRINTF(("Condition for parsing not met - skipping tag"));
469 return true; // ok, go on
471 // Now we know that we must actually parse this tag
472 // check configvar expansion mode
473 fCfgVarExp=0; // default to automatic (i.e. certain content such as paths or macrostrings will be expanded without "expand" attr)
474 cAppCharP cm=getAttr(aAttributes,"expand");
476 // explicit expand directive for this tag (includes expandable attributes of this tag,
477 // but only attributes queried with getAttrExpanded() can expand at all)
479 if (strucmp(cm,"single")==0) fCfgVarExp=1;
480 else if (StrToBool(cm,b)) fCfgVarExp = b ? 2 : -1;
482 // check for use-everywhere special tags
483 if (strucmp(aElementName,"configmsg")==0) {
485 cAppCharP msg=getAttr(aAttributes,"error");
487 msg=getAttr(aAttributes,"warning");
490 if (!msg) msg="Error: found <configmsg>";
491 ReportError(iserr,msg);
493 return false; // generate error message
495 // check if we are re-entering the same object again (trying to overwrite)
497 ReportError(true,"Duplicate definition of <%s>",aElementName);
499 return false; // not allowed, generate error message
501 if (fParseMode==pamo_element || fParseMode==pamo_nested) {
504 if (localStartElement(aElementName,aAttributes,aLine)) {
505 // element known and parsing initialized ok
506 #ifdef SYSER_REGISTRATION
507 if (fLockedElement && fChildParser==NULL && fParseMode!=pamo_element && fParseMode!=pamo_nested)
508 fHadLockedSubs=true; // flag that this element has processed non-child subelements in locked mode
513 // unknown element: read over all its contents
514 ReportError(false,"invalid tag <%s>",aElementName);
516 return false; // element not known, generate error message
519 else if (fParseMode==pamo_all) {
520 // read over contents
524 ReportError(false,"no XML tag expected here");
528 } // TConfigElement::startElement
531 // character data of current element
532 void TConfigElement::charData(const char *aCharData, sInt32 aNumChars)
535 // parsing in a sub-level, delegate
536 return fChildParser->charData(aCharData,aNumChars);
539 if (fParseMode==pamo_all /* %%% not needed, I think || fNest>0 */) {
542 else if (fParseMode==pamo_element || fParseMode==pamo_nested || fParseMode==pamo_end || fParseMode==pamo_endnested) {
543 // only whitespace allowed here
544 while (aNumChars--) {
545 if (!isspace(*aCharData++)) {
546 ReportError(false,"no character data expected");
552 // accumulate char data in string
553 fTempString.append(aCharData,aNumChars);
556 } // TConfigElement::charData
560 // read over all contents of current TConfigElement
561 void TConfigElement::expectAll(void)
563 // we are already in an element but have no non-decrementing
564 // parse mode set like expectEmpty() or expectString() etc.
565 // so nest count must be incremented to balance decrement occurring
566 // at next element end.
567 fExpectAllNestStart = fParseMode==pamo_nested ? fNest : -1; // remember where we left nested mode and entered ignoring mode
570 } // TConfigElement::expectAll
573 // expect Enum element
574 void TConfigElement::expectEnum(sInt16 aDestSize,void *aPtr, const char * const aEnumNames[], sInt16 aNumEnums)
577 fParseEnumArray = aEnumNames;
578 fParseEnumNum = aNumEnums;
579 // determine mode and destination
582 fParseMode=pamo_enum1by;
583 fResultPtr.fCharP=(char *)aPtr;
586 fParseMode=pamo_enum2by;
587 fResultPtr.fShortP=(sInt16 *)aPtr;
590 fParseMode=pamo_enum4by;
591 fResultPtr.fLongP=(sInt32 *)aPtr;
594 SYSYNC_THROW(TConfigParseException("expectEnum: invalid enum size"));
596 } // TConfigElement::expectEnum
599 // delegate parsing of a single element to another config element
600 // after processing aElementName and all subtags, processing will return to this object
601 // (rather than waiting for an end tag in aConfigElemP)
602 bool TConfigElement::delegateParsingTo(TConfigElement *aConfigElemP, const char *aElementName, const char **aAttributes, sInt32 aLine)
605 expectChildParsing(*aConfigElemP);
606 fParseMode=pamo_delegated;
607 // let child parse the current tag right away
608 return aConfigElemP->localStartElement(aElementName,aAttributes,aLine);
611 return false; // if we have no delegate, we can't understand this tag
612 } // TConfigElement::delegateParsingTo
615 // end of element, returns true when this config element is done parsing
616 bool TConfigElement::endElement(const char *aElementName, bool aIsDelegated)
619 // parsing in a real or simulated (delegateParsingTo) sub-level
620 if (fChildParser->endElement(aElementName,fParseMode==pamo_delegated)) {
621 // child has finished parsing
622 fChildParser=NULL; // handle next element myself
624 // - we were delegated to process a single element from another element.
625 // So, we nust not continue parsing here, but pass back to parent
629 // otherwise, wait here for next element to start (or encosing element to end)
630 fParseMode=pamo_element; // expect another element or end of myself
633 // child is still parsing, so am I
639 "'%s' ends in configElement='%s' with parsemode='%s' nest=%hd, aIsDelegated=%d",
642 ParseModeNames[fParseMode],
646 #ifdef SYSER_REGISTRATION
647 // Perform CRC sum if locked element
648 if (fLockedElement) {
650 getRootElement()->addToLockCRC(fTempString.c_str());
653 const char *p; // BCPPB: declaring vars in case does not work.
657 // expand macros in string first
660 fCfgVarExp = fParseMode==pamo_path || fParseMode==pamo_macrostring ? 2 : -1;
662 // do config variable expansion now (or not, according to fCfgVarExp)
663 getSyncAppBase()->expandConfigVars(fTempString,fCfgVarExp,this,aElementName);
665 switch (fParseMode) {
669 // Note: normal empty elements are NOT considered nested elements by default, only if
670 // they request endnested mode explicitly
671 if (fParseMode==pamo_endnested)
672 nestedElementEnd(); // inform possible parser of nested element that a nested element ends here
674 fParseMode=pamo_nested;
676 else fParseMode=pamo_element; // back to normal element parsing
677 return false; // do not exit
681 // not yet finished with startlevel, continue with pamo_all/pamo_nested
683 if (fExpectAllNestStart>0) {
684 // we are in expectAll mode within pamo_nested
685 if(fNest==fExpectAllNestStart) {
686 // reached level where we started ignoring contents before
687 fExpectAllNestStart = -1; // processed now
688 fParseMode = pamo_nested; // back to nested (but active) parsing, like in Mime-Dir profile
691 // NOP here - do NOT call nestedElementEnd()
695 // end of active nested element
696 nestedElementEnd(); // inform possible parser of nested element
698 // if back on nest level 0, switch to pamo_element
699 fParseMode = pamo_element;
702 return false; // stay in this element
704 // nested or all at level 0 cause handling like pamo_element
706 // end of element while looking for elements:
707 // this is end of this config element itself
708 // - flag completion of this element
709 fCompleted=true; // prevents re-entry
710 // Resolve if this is element says it is self-contained (no references to other elements
711 // that might follow later in the config file)
712 if (fResolveImmediately) {
713 // resolve element NOW, last pass
716 // - return parsing authority to caller
719 // interpret C-type escapes (only \t,\r,\n and \xXX, no octal)
720 #ifdef SYSER_REGISTRATION
721 if (fHadLockedSubs && !fResultPtr.fStringP->empty())
722 ReportError(true,"Duplicate definition of <%s>",aElementName);
724 fResultPtr.fStringP->erase();
725 CStrToStrAppend(fTempString.c_str(), *(fResultPtr.fStringP));
728 case pamo_macrostring:
730 #ifdef SYSER_REGISTRATION
731 if (fHadLockedSubs && !fResultPtr.fStringP->empty())
732 ReportError(true,"Duplicate definition of <%s>",aElementName);
734 // remove spaces at beginning and end
735 fResultPtr.fStringP->erase();
736 p = fTempString.c_str();
737 // - skip leading spaces
738 while (*p && isspace(*p)) ++p;
739 // - copy chars and convert all wsp to spaces
744 fResultPtr.fStringP->append(" ");
746 fResultPtr.fStringP->append(p,1);
747 lnwsp=n; // remember last non-whitespace
751 // - remove trailing spaces
752 fResultPtr.fStringP->resize(lnwsp);
753 // - if path requested, shape it up if needed
754 if (fParseMode==pamo_path) {
755 makeOSDirPath(*(fResultPtr.fStringP));
759 #ifdef SYSER_REGISTRATION
760 if (fHadLockedSubs && !fResultPtr.fStringP->empty())
761 ReportError(true,"Duplicate definition of <%s>",aElementName);
763 (*(fResultPtr.fStringP))=fTempString;
766 #ifdef SCRIPT_SUPPORT
767 *(fResultPtr.fShortP) = getFieldIndex(fTempString.c_str(),fFieldListP,fScriptContextP);
769 *(fResultPtr.fShortP) = getFieldIndex(fTempString.c_str(),fFieldListP);
772 #ifdef SCRIPT_SUPPORT
774 case pamo_functiondef:
776 if (fParseMode==pamo_functiondef)
777 TScriptContext::TokenizeAndResolveFunction(getSyncAppBase(),fExpectLine,fTempString.c_str(),(*(fResultPtr.fFuncDefP)));
779 #ifdef SYSER_REGISTRATION
780 if (fHadLockedSubs && !fResultPtr.fStringP->empty())
781 ReportError(true,"Duplicate definition of <%s>",aElementName);
783 TScriptContext::Tokenize(getSyncAppBase(),aElementName,fExpectLine,fTempString.c_str(),(*(fResultPtr.fStringP)),fExpectContextFuncs,false,fExpectNoDeclarations);
786 SYSYNC_CATCH (exception &e)
787 ReportError(true,"Script Error: %s",e.what());
792 // expect timestamp, store as UTC
793 if (ISO8601StrToTimestamp(fTempString.c_str(), *(fResultPtr.fTimestampP), tctx)==0)
794 ReportError(false,"bad ISO8601 timestamp value");
795 if (TCTX_IS_UNKNOWN(tctx)) tctx=TCTX_SYSTEM;
796 TzConvertTimestamp(*(fResultPtr.fTimestampP),tctx,TCTX_UTC,getSyncAppBase()->getAppZones());
800 if (!TimeZoneNameToContext(fTempString.c_str(), *(fResultPtr.fTimeContextP), getSyncAppBase()->getAppZones()))
801 ReportError(false,"invalid/unknown timezone name");
804 // definition of custom time zone in vTimezone format
805 if (!VTIMEZONEtoInternal(fTempString.c_str(), tctx, fResultPtr.fGZonesP, getDbgLogger()))
806 ReportError(false,"invalid vTimezone defintion");
809 if (!StrToBool(fTempString.c_str(),*(fResultPtr.fBoolP)))
810 ReportError(false,"bad boolean value");
813 if (fTempString.empty() || fTempString=="unspecified" || fTempString=="default")
814 *(fResultPtr.fByteP)=-1; // unspecified
816 if (!StrToBool(fTempString.c_str(),hlp))
817 ReportError(false,"bad boolean value");
819 *(fResultPtr.fByteP)= hlp ? 1 : 0;
823 if (!StrToLongLong(fTempString.c_str(),*(fResultPtr.fLongLongP)))
824 ReportError(false,"bad integer (64bit) value");
827 if (!StrToLong(fTempString.c_str(),*(fResultPtr.fLongP)))
828 ReportError(false,"bad integer (32bit) value");
831 if (fTempString.size()>1)
832 ReportError(false,"single char or nothing (=NUL char) expected");
833 if (fTempString.size()==0)
834 *(fResultPtr.fCharP) = 0;
836 *(fResultPtr.fCharP) = fTempString[0];
839 // Palm/MacOS-type 4-char code
840 if (fTempString.size()!=4)
841 ReportError(false,"id code must be exactly 4 characters");
842 *(fResultPtr.fLongP) =
843 ((uInt32)fTempString[0] << 24) +
844 ((uInt32)fTempString[1] << 16) +
845 ((uInt16)fTempString[2] << 8) +
846 ((uInt8)fTempString[3]);
849 if (!StrToShort(fTempString.c_str(),*(fResultPtr.fShortP)))
850 ReportError(false,"bad integer (16bit) value");
856 if (!StrToEnum(fParseEnumArray,fParseEnumNum,tempenum,fTempString.c_str()))
857 ReportError(false,"bad enumeration value '%s'",fTempString.c_str());
860 if (fParseMode==pamo_enum1by) *fResultPtr.fCharP = tempenum;
861 else if (fParseMode==pamo_enum2by) *fResultPtr.fShortP = tempenum;
862 else if (fParseMode==pamo_enum4by) *fResultPtr.fLongP = tempenum;
866 SYSYNC_THROW(TConfigParseException(DEBUGTEXT("Unknown parse mode","ce1")));
868 // normal case: end of simple element parsed at same level as parent
870 // - we were delegated to process a single element from another element.
871 // So, we nust not continue parsing here, but pass back to parent
876 // - expect next element
877 fParseMode=pamo_element;
880 // end of embedded element, but not of myself
881 #ifdef SYSER_REGISTRATION
883 if (fLockedElement) {
885 "'%s' was locked and was included in LockCRC = 0x%04hX",
887 getRootElement()->getConfigLockCRC()
891 // - back to parent's lock status (or false if no parent)
892 fLockedElement=getParentElement() ? getParentElement()->fLockedElement : false;
894 // do not return to parent config element
896 } // TConfigElement::endElement
901 void TRootConfigElement::resetError(void)
904 fErrorMessage.erase();
905 } // TRootConfigElement::ResetError
909 void TRootConfigElement::setError(bool aFatal, const char *aMsg)
911 if (!fErrorMessage.empty())
912 fErrorMessage += '\n'; // multiple messages on multiple lines
913 fErrorMessage.append(aMsg);
914 if (aFatal && fFatalError==LOCERR_OK)
915 fFatalError=LOCERR_CFGPARSE; // this is a config parse error
917 } // TRootConfigElement::setError
920 // check for error message
921 const char *TRootConfigElement::getErrorMsg(void)
923 if (!fError) return NULL;
924 return fErrorMessage.c_str();
925 } // TRootConfigElement::GetErrorMsg
928 // reset parsing (=reset error and fatal errors)
929 void TRootConfigElement::ResetParsing(void)
933 fFatalError=LOCERR_OK;
934 #ifdef SYSER_REGISTRATION
935 fLockCRC=0; // no CRC lock sum yet
937 TConfigElement::ResetParsing();
938 } // TRootConfigElement::ResetParsing
943 // resolve config tree and catch errors
944 bool TRootConfigElement::ResolveAll(void)
950 SYSYNC_CATCH (TConfigParseException &e)
951 #ifndef HARDCODED_CONFIG
952 ReportError(true,e.what());
956 } // TRootConfigElement::ResolveAll
959 #ifndef HARDCODED_CONFIG
961 // start of element, this config element decides who processes this element
962 bool TRootConfigElement::startElement(const char *aElementName, const char **aAttributes, sInt32 aLine)
965 // document not yet started
966 if (strucmp(aElementName,XMLCONFIG_DOCNAME)==0) {
968 const char* vers = getAttr(aAttributes,"version");
970 ReportError(true,"Missing version attribute for document");
971 expectAll(); // ignore everything
973 else if (strucmp(vers,XMLCONFIG_DOCVERSION)!=0) {
975 "Bad config document version (expected %s, found %s)",
976 XMLCONFIG_DOCVERSION,
979 expectAll(); // ignore everything inside that element
981 fDocStarted=true; // started normally
990 return TConfigElement::startElement(aElementName,aAttributes,aLine);
992 } // TRootConfigElement::startElement
995 #ifdef SYSER_REGISTRATION
996 // add config text to locking CRC
997 void TRootConfigElement::addToLockCRC(const char *aCharData, size_t aNumChars)
999 size_t n = aNumChars ? aNumChars : strlen(aCharData);
1001 bool lastwasctrl=false;
1004 // compact multiple whitespace to single char
1005 if ((uInt8)c<=' ') {
1006 if (lastwasctrl) continue;
1008 // treat them all as spaces
1012 lastwasctrl=false; // this is a non-space
1015 fLockCRC=sysync::sysync_crc16(fLockCRC,(uInt8)c);
1017 } // TRootConfigElement::addToLockCRC
1020 #endif // no hardcode config
1023 /* end of TConfigElement implementation */