source: box/trunk/lib/common/Configuration.cpp @ 2696

Revision 2696, 21.1 KB checked in by chris, 2 years ago (diff)

Add support for account numbers greater than 0x7fffffff without wrapping.

  • Property svn:eol-style set to native
Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    Configuration.cpp
5//              Purpose: Reading configuration files
6//              Created: 2003/07/23
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#include <limits.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include <sstream>
17
18#include "Configuration.h"
19#include "CommonException.h"
20#include "Guards.h"
21#include "FdGetLine.h"
22
23#include "MemLeakFindOn.h"
24
25#include <cstring>
26
27// utility whitespace function
28inline bool iw(int c)
29{
30        return (c == ' ' || c == '\t' || c == '\v' || c == '\f'); // \r, \n are already excluded
31}
32
33// boolean values
34static const char *sValueBooleanStrings[] = {"yes", "true", "no", "false", 0};
35static const bool sValueBooleanValue[] = {true, true, false, false};
36
37ConfigurationVerifyKey::ConfigurationVerifyKey
38(
39        std::string name,
40        int flags,
41        void *testFunction
42)
43: mName(name),
44  mHasDefaultValue(false),
45  mFlags(flags),
46  mTestFunction(testFunction)
47{ }
48
49// to allow passing NULL for default ListenAddresses
50
51ConfigurationVerifyKey::ConfigurationVerifyKey
52(
53        std::string name,
54        int flags,
55        NoDefaultValue_t t,
56        void *testFunction
57)
58: mName(name),
59  mHasDefaultValue(false),
60  mFlags(flags),
61  mTestFunction(testFunction)
62{ }
63
64ConfigurationVerifyKey::ConfigurationVerifyKey
65(
66        std::string name,
67        int flags,
68        std::string defaultValue,
69        void *testFunction
70)
71: mName(name),
72  mDefaultValue(defaultValue),
73  mHasDefaultValue(true),
74  mFlags(flags),
75  mTestFunction(testFunction)
76{ }
77
78ConfigurationVerifyKey::ConfigurationVerifyKey
79(
80        std::string name,
81        int flags,
82        const char *defaultValue,
83        void *testFunction
84)
85: mName(name),
86  mDefaultValue(defaultValue),
87  mHasDefaultValue(true),
88  mFlags(flags),
89  mTestFunction(testFunction)
90{ }
91
92ConfigurationVerifyKey::ConfigurationVerifyKey
93(
94        std::string name,
95        int flags,
96        int defaultValue,
97        void *testFunction
98)
99: mName(name),
100  mHasDefaultValue(true),
101  mFlags(flags),
102  mTestFunction(testFunction)
103{
104        ASSERT(flags & ConfigTest_IsInt);
105        std::ostringstream val;
106        val << defaultValue;
107        mDefaultValue = val.str();
108}
109
110ConfigurationVerifyKey::ConfigurationVerifyKey
111(
112        std::string name,
113        int flags,
114        bool defaultValue,
115        void *testFunction
116)
117: mName(name),
118  mHasDefaultValue(true),
119  mFlags(flags),
120  mTestFunction(testFunction)
121{
122        ASSERT(flags & ConfigTest_IsBool);
123        mDefaultValue = defaultValue ? "yes" : "no";
124}
125
126ConfigurationVerifyKey::ConfigurationVerifyKey
127(
128        const ConfigurationVerifyKey& rToCopy
129)
130: mName(rToCopy.mName),
131  mDefaultValue(rToCopy.mDefaultValue),
132  mHasDefaultValue(rToCopy.mHasDefaultValue),
133  mFlags(rToCopy.mFlags),
134  mTestFunction(rToCopy.mTestFunction)
135{ }
136
137// --------------------------------------------------------------------------
138//
139// Function
140//              Name:    Configuration::Configuration(const std::string &)
141//              Purpose: Constructor
142//              Created: 2003/07/23
143//
144// --------------------------------------------------------------------------
145Configuration::Configuration(const std::string &rName)
146        : mName(rName)
147{
148}
149
150
151// --------------------------------------------------------------------------
152//
153// Function
154//              Name:    Configuration::Configuration(const Configuration &)
155//              Purpose: Copy constructor
156//              Created: 2003/07/23
157//
158// --------------------------------------------------------------------------
159Configuration::Configuration(const Configuration &rToCopy)
160        : mName(rToCopy.mName),
161          mKeys(rToCopy.mKeys),
162          mSubConfigurations(rToCopy.mSubConfigurations)
163{
164}
165
166
167// --------------------------------------------------------------------------
168//
169// Function
170//              Name:    Configuration::~Configuration()
171//              Purpose: Destructor
172//              Created: 2003/07/23
173//
174// --------------------------------------------------------------------------
175Configuration::~Configuration()
176{
177}
178
179
180// --------------------------------------------------------------------------
181//
182// Function
183//              Name:    Configuration::LoadAndVerify(const std::string &, const ConfigurationVerify *, std::string &)
184//              Purpose: Loads a configuration file from disc, checks it. Returns NULL if it was faulting, in which
185//                               case they'll be an error message.
186//              Created: 2003/07/23
187//
188// --------------------------------------------------------------------------
189std::auto_ptr<Configuration> Configuration::LoadAndVerify(
190        const std::string& rFilename,
191        const ConfigurationVerify *pVerify,
192        std::string &rErrorMsg)
193{
194        // Just to make sure
195        rErrorMsg.erase();
196       
197        // Open the file
198        FileHandleGuard<O_RDONLY> file(rFilename);
199       
200        // GetLine object
201        FdGetLine getline(file);
202       
203        // Object to create
204        std::auto_ptr<Configuration> apConfig(
205                new Configuration(std::string("<root>")));
206       
207        try
208        {
209                // Load
210                LoadInto(*apConfig, getline, rErrorMsg, true);
211
212                if(!rErrorMsg.empty())
213                {
214                        // An error occured, return now
215                        BOX_ERROR("Error in Configuration::LoadInto: " << 
216                                rErrorMsg);
217                        return std::auto_ptr<Configuration>(0);
218                }
219
220                // Verify?
221                if(pVerify)
222                {
223                        if(!apConfig->Verify(*pVerify, std::string(), rErrorMsg))
224                        {
225                                BOX_ERROR("Error verifying configuration: " <<
226                                        rErrorMsg);
227                                return std::auto_ptr<Configuration>(0);
228                        }
229                }
230        }
231        catch(...)
232        {
233                // Clean up
234                throw;
235        }
236       
237        // Success. Return result.
238        return apConfig;
239}
240
241
242// --------------------------------------------------------------------------
243//
244// Function
245//              Name:    LoadInto(Configuration &, FdGetLine &, std::string &, bool)
246//              Purpose: Private. Load configuration information from the file into the config object.
247//                               Returns 'abort' flag, if error, will be appended to rErrorMsg.
248//              Created: 2003/07/24
249//
250// --------------------------------------------------------------------------
251bool Configuration::LoadInto(Configuration &rConfig, FdGetLine &rGetLine, std::string &rErrorMsg, bool RootLevel)
252{
253        bool startBlockExpected = false;
254        std::string blockName;
255
256        //TRACE1("BLOCK: |%s|\n", rConfig.mName.c_str());
257
258        while(!rGetLine.IsEOF())
259        {
260                std::string line(rGetLine.GetLine(true));       /* preprocess out whitespace and comments */
261               
262                if(line.empty())
263                {
264                        // Ignore blank lines
265                        continue;
266                }
267               
268                // Line an open block string?
269                if(line == "{")
270                {
271                        if(startBlockExpected)
272                        {
273                                // New config object
274                                Configuration subConfig(blockName);
275                               
276                                // Continue processing into this block
277                                if(!LoadInto(subConfig, rGetLine, rErrorMsg, false))
278                                {
279                                        // Abort error
280                                        return false;
281                                }
282
283                                startBlockExpected = false;
284
285                                // Store...
286                                rConfig.AddSubConfig(blockName, subConfig);
287                        }
288                        else
289                        {
290                                rErrorMsg += "Unexpected start block in " +
291                                        rConfig.mName + "\n";
292                        }
293                }
294                else
295                {
296                        // Close block?
297                        if(line == "}")
298                        {
299                                if(RootLevel)
300                                {
301                                        // error -- root level doesn't have a close
302                                        rErrorMsg += "Root level has close block -- forgot to terminate subblock?\n";
303                                        // but otherwise ignore
304                                }
305                                else
306                                {
307                                        //TRACE0("ENDBLOCK\n");
308                                        return true;            // All very good and nice
309                                }
310                        }
311                        // Either a key, or a sub block beginning
312                        else
313                        {
314                                // Can't be a start block
315                                if(startBlockExpected)
316                                {
317                                        rErrorMsg += "Block " + blockName + " wasn't started correctly (no '{' on line of it's own)\n";
318                                        startBlockExpected = false;
319                                }
320
321                                // Has the line got an = in it?
322                                unsigned int equals = 0;
323                                for(; equals < line.size(); ++equals)
324                                {
325                                        if(line[equals] == '=')
326                                        {
327                                                // found!
328                                                break;
329                                        }
330                                }
331                                if(equals < line.size())
332                                {
333                                        // Make key value pair
334                                        unsigned int keyend = equals;
335                                        while(keyend > 0 && iw(line[keyend-1]))
336                                        {
337                                                keyend--;
338                                        }
339                                        unsigned int valuestart = equals+1;
340                                        while(valuestart < line.size() && iw(line[valuestart]))
341                                        {
342                                                valuestart++;
343                                        }
344                                        if(keyend > 0 && valuestart <= line.size())
345                                        {
346                                                std::string key(line.substr(0, keyend));
347                                                std::string value(line.substr(valuestart));
348                                                rConfig.AddKeyValue(key, value);
349                                        }
350                                        else
351                                        {
352                                                rErrorMsg += "Invalid configuration key: " + line + "\n";
353                                        }
354                                }
355                                else
356                                {
357                                        // Start of sub block
358                                        blockName = line;
359                                        startBlockExpected = true;
360                                }
361                        }
362                }       
363        }
364
365        // End of file?
366        if(!RootLevel && rGetLine.IsEOF())
367        {
368                // Error if EOF and this isn't the root level
369                rErrorMsg += "File ended without terminating all subblocks\n";
370        }
371       
372        return true;
373}
374
375void Configuration::AddKeyValue(const std::string& rKey,
376        const std::string& rValue)
377{
378        // Check for duplicate values
379        if(mKeys.find(rKey) != mKeys.end())
380        {
381                // Multi-values allowed here, but checked later on
382                mKeys[rKey] += MultiValueSeparator;
383                mKeys[rKey] += rValue;
384        }
385        else
386        {
387                // Store
388                mKeys[rKey] = rValue;
389        }       
390}
391
392void Configuration::AddSubConfig(const std::string& rName,
393        const Configuration& rSubConfig)
394{
395        mSubConfigurations.push_back(
396                std::pair<std::string, Configuration>(rName, rSubConfig));
397}
398
399
400// --------------------------------------------------------------------------
401//
402// Function
403//              Name:    Configuration::KeyExists(const std::string&)
404//              Purpose: Checks to see if a key exists
405//              Created: 2003/07/23
406//
407// --------------------------------------------------------------------------
408bool Configuration::KeyExists(const std::string& rKeyName) const
409{
410        return mKeys.find(rKeyName) != mKeys.end();
411}
412
413
414// --------------------------------------------------------------------------
415//
416// Function
417//              Name:    Configuration::GetKeyValue(const std::string&)
418//              Purpose: Returns the value of a configuration variable
419//              Created: 2003/07/23
420//
421// --------------------------------------------------------------------------
422const std::string &Configuration::GetKeyValue(const std::string& rKeyName) const
423{
424        std::map<std::string, std::string>::const_iterator i(mKeys.find(rKeyName));
425       
426        if(i == mKeys.end())
427        {
428                BOX_ERROR("Missing configuration key: " << rKeyName);
429                THROW_EXCEPTION(CommonException, ConfigNoKey)
430        }
431        else
432        {
433                return i->second;
434        }
435}
436
437
438// --------------------------------------------------------------------------
439//
440// Function
441//              Name:    Configuration::GetKeyValueInt(const std::string& rKeyName)
442//              Purpose: Gets a key value as an integer
443//              Created: 2003/07/23
444//
445// --------------------------------------------------------------------------
446int Configuration::GetKeyValueInt(const std::string& rKeyName) const
447{
448        std::map<std::string, std::string>::const_iterator i(mKeys.find(rKeyName));
449       
450        if(i == mKeys.end())
451        {
452                THROW_EXCEPTION(CommonException, ConfigNoKey)
453        }
454        else
455        {
456                long value = ::strtol((i->second).c_str(), NULL,
457                        0 /* C style handling */);
458                if(value == LONG_MAX || value == LONG_MIN)
459                {
460                        THROW_EXCEPTION(CommonException, ConfigBadIntValue)
461                }
462                return (int)value;
463        }
464}
465
466
467// --------------------------------------------------------------------------
468//
469// Function
470//              Name:    Configuration::GetKeyValueUint32(const std::string& rKeyName)
471//              Purpose: Gets a key value as a 32-bit unsigned integer
472//              Created: 2003/07/23
473//
474// --------------------------------------------------------------------------
475uint32_t Configuration::GetKeyValueUint32(const std::string& rKeyName) const
476{
477        std::map<std::string, std::string>::const_iterator i(mKeys.find(rKeyName));
478       
479        if(i == mKeys.end())
480        {
481                THROW_EXCEPTION(CommonException, ConfigNoKey)
482        }
483        else
484        {
485                errno = 0;
486                long value = ::strtoul((i->second).c_str(), NULL,
487                        0 /* C style handling */);
488                if(errno != 0)
489                {
490                        THROW_EXCEPTION(CommonException, ConfigBadIntValue)
491                }
492                return (int)value;
493        }
494}
495
496
497// --------------------------------------------------------------------------
498//
499// Function
500//              Name:    Configuration::GetKeyValueBool(const std::string&)
501//              Purpose: Gets a key value as a boolean
502//              Created: 17/2/04
503//
504// --------------------------------------------------------------------------
505bool Configuration::GetKeyValueBool(const std::string& rKeyName) const
506{
507        std::map<std::string, std::string>::const_iterator i(mKeys.find(rKeyName));
508       
509        if(i == mKeys.end())
510        {
511                THROW_EXCEPTION(CommonException, ConfigNoKey)
512        }
513        else
514        {
515                bool value = false;
516               
517                // Anything this is called for should have been verified as having a correct
518                // string in the verification section. However, this does default to false
519                // if it isn't in the string table.
520               
521                for(int l = 0; sValueBooleanStrings[l] != 0; ++l)
522                {
523                        if(::strcasecmp((i->second).c_str(), sValueBooleanStrings[l]) == 0)
524                        {
525                                // Found.
526                                value = sValueBooleanValue[l];
527                                break;
528                        }
529                }
530               
531                return value;
532        }
533
534}
535
536
537
538// --------------------------------------------------------------------------
539//
540// Function
541//              Name:    Configuration::GetKeyNames()
542//              Purpose: Returns list of key names
543//              Created: 2003/07/24
544//
545// --------------------------------------------------------------------------
546std::vector<std::string> Configuration::GetKeyNames() const
547{
548        std::map<std::string, std::string>::const_iterator i(mKeys.begin());
549       
550        std::vector<std::string> r;
551       
552        for(; i != mKeys.end(); ++i)
553        {
554                r.push_back(i->first);
555        }
556       
557        return r;
558}
559
560
561// --------------------------------------------------------------------------
562//
563// Function
564//              Name:    Configuration::SubConfigurationExists(const
565//                       std::string&)
566//              Purpose: Checks to see if a sub configuration exists
567//              Created: 2003/07/23
568//
569// --------------------------------------------------------------------------
570bool Configuration::SubConfigurationExists(const std::string& rSubName) const
571{
572        // Attempt to find it...
573        std::list<std::pair<std::string, Configuration> >::const_iterator i(mSubConfigurations.begin());
574       
575        for(; i != mSubConfigurations.end(); ++i)
576        {
577                // This the one?
578                if(i->first == rSubName)
579                {
580                        // Yes.
581                        return true;
582                }
583        }
584
585        // didn't find it.
586        return false;
587}
588
589
590// --------------------------------------------------------------------------
591//
592// Function
593//              Name:    Configuration::GetSubConfiguration(const
594//                       std::string&)
595//              Purpose: Gets a sub configuration
596//              Created: 2003/07/23
597//
598// --------------------------------------------------------------------------
599const Configuration &Configuration::GetSubConfiguration(const std::string&
600        rSubName) const
601{
602        // Attempt to find it...
603        std::list<std::pair<std::string, Configuration> >::const_iterator i(mSubConfigurations.begin());
604       
605        for(; i != mSubConfigurations.end(); ++i)
606        {
607                // This the one?
608                if(i->first == rSubName)
609                {
610                        // Yes.
611                        return i->second;
612                }
613        }
614
615        THROW_EXCEPTION(CommonException, ConfigNoSubConfig)
616}
617
618
619// --------------------------------------------------------------------------
620//
621// Function
622//              Name:    Configuration::GetSubConfiguration(const
623//                       std::string&)
624//              Purpose: Gets a sub configuration for editing
625//              Created: 2008/08/12
626//
627// --------------------------------------------------------------------------
628Configuration &Configuration::GetSubConfigurationEditable(const std::string&
629        rSubName)
630{
631        // Attempt to find it...
632       
633        for(SubConfigListType::iterator
634                i  = mSubConfigurations.begin();
635                i != mSubConfigurations.end(); ++i)
636        {
637                // This the one?
638                if(i->first == rSubName)
639                {
640                        // Yes.
641                        return i->second;
642                }
643        }
644
645        THROW_EXCEPTION(CommonException, ConfigNoSubConfig)
646}
647
648
649// --------------------------------------------------------------------------
650//
651// Function
652//              Name:    Configuration::GetSubConfigurationNames()
653//              Purpose: Return list of sub configuration names
654//              Created: 2003/07/24
655//
656// --------------------------------------------------------------------------
657std::vector<std::string> Configuration::GetSubConfigurationNames() const
658{
659        std::list<std::pair<std::string, Configuration> >::const_iterator i(mSubConfigurations.begin());
660       
661        std::vector<std::string> r;
662       
663        for(; i != mSubConfigurations.end(); ++i)
664        {
665                r.push_back(i->first);
666        }
667       
668        return r;
669}
670
671
672// --------------------------------------------------------------------------
673//
674// Function
675//              Name:    Configuration::Verify(const ConfigurationVerify &, const std::string &, std::string &)
676//              Purpose: Checks that the configuration is valid according to the
677//                       supplied verifier
678//              Created: 2003/07/24
679//
680// --------------------------------------------------------------------------
681bool Configuration::Verify(const ConfigurationVerify &rVerify,
682        const std::string &rLevel, std::string &rErrorMsg)
683{
684        bool ok = true;
685
686        // First... check the keys
687        if(rVerify.mpKeys != 0)
688        {
689                const ConfigurationVerifyKey *pvkey = rVerify.mpKeys;
690               
691                bool todo = true;
692                do
693                {
694                        // Can the key be found?
695                        if(KeyExists(pvkey->Name()))
696                        {
697                                // Get value
698                                const std::string &rval = GetKeyValue(pvkey->Name());
699                                const char *val = rval.c_str();
700
701                                // Check it's a number?
702                                if((pvkey->Flags() & ConfigTest_IsInt) == ConfigTest_IsInt)
703                                {                                       
704                                        // Test it...
705                                        char *end;
706                                        long r = ::strtol(val, &end, 0);
707                                        if(r == LONG_MIN || r == LONG_MAX || end != (val + rval.size()))
708                                        {
709                                                // not a good value
710                                                ok = false;
711                                                rErrorMsg += rLevel + mName + "." + pvkey->Name() + " (key) is not a valid integer.\n";
712                                        }
713                                }
714
715                                // Check it's a number?
716                                if(pvkey->Flags() & ConfigTest_IsUint32)
717                                {                                       
718                                        // Test it...
719                                        char *end;
720                                        errno = 0;
721                                        uint32_t r = ::strtoul(val, &end, 0);
722                                        if(errno != 0 || end != (val + rval.size()))
723                                        {
724                                                // not a good value
725                                                ok = false;
726                                                rErrorMsg += rLevel + mName + "." + pvkey->Name() + " (key) is not a valid unsigned 32-bit integer.\n";
727                                        }
728                                }
729                               
730                                // Check it's a bool?
731                                if((pvkey->Flags() & ConfigTest_IsBool) == ConfigTest_IsBool)
732                                {                               
733                                        // See if it's one of the allowed strings.
734                                        bool found = false;
735                                        for(int l = 0; sValueBooleanStrings[l] != 0; ++l)
736                                        {
737                                                if(::strcasecmp(val, sValueBooleanStrings[l]) == 0)
738                                                {
739                                                        // Found.
740                                                        found = true;
741                                                        break;
742                                                }
743                                        }
744                                       
745                                        // Error if it's not one of them.
746                                        if(!found)
747                                        {
748                                                ok = false;
749                                                rErrorMsg += rLevel + mName + "." + pvkey->Name() + " (key) is not a valid boolean value.\n";
750                                        }
751                                }
752                               
753                                // Check for multi valued statments where they're not allowed
754                                if((pvkey->Flags() & ConfigTest_MultiValueAllowed) == 0)
755                                {
756                                        // Check to see if this key is a multi-value -- it shouldn't be
757                                        if(rval.find(MultiValueSeparator) != rval.npos)
758                                        {
759                                                ok = false;
760                                                rErrorMsg += rLevel + mName +"." + pvkey->Name() + " (key) multi value not allowed (duplicated key?).\n";
761                                        }
762                                }                               
763                        }
764                        else
765                        {
766                                // Is it required to exist?
767                                if((pvkey->Flags() & ConfigTest_Exists) == ConfigTest_Exists)
768                                {
769                                        // Should exist, but doesn't.
770                                        ok = false;
771                                        rErrorMsg += rLevel + mName + "." + pvkey->Name() + " (key) is missing.\n";
772                                }
773                                else if(pvkey->HasDefaultValue())
774                                {
775                                        mKeys[pvkey->Name()] =
776                                                pvkey->DefaultValue();
777                                }
778                        }
779               
780                        if((pvkey->Flags() & ConfigTest_LastEntry) == ConfigTest_LastEntry)
781                        {
782                                // No more!
783                                todo = false;
784                        }
785                       
786                        // next
787                        pvkey++;
788                       
789                } while(todo);
790
791                // Check for additional keys
792                for(std::map<std::string, std::string>::const_iterator i = mKeys.begin();
793                        i != mKeys.end(); ++i)
794                {
795                        // Is the name in the list?
796                        const ConfigurationVerifyKey *scan = rVerify.mpKeys;
797                        bool found = false;
798                        while(scan)
799                        {
800                                if(scan->Name() == i->first)
801                                {
802                                        found = true;
803                                        break;
804                                }
805                               
806                                // Next?
807                                if((scan->Flags() & ConfigTest_LastEntry) == ConfigTest_LastEntry)
808                                {
809                                        break;
810                                }
811                                scan++;
812                        }
813                       
814                        if(!found)
815                        {
816                                // Shouldn't exist, but does.
817                                ok = false;
818                                rErrorMsg += rLevel + mName + "." + i->first + " (key) is not a known key. Check spelling and placement.\n";
819                        }
820                }
821        }
822       
823        // Then the sub configurations
824        if(rVerify.mpSubConfigurations)
825        {
826                // Find the wildcard entry, if it exists, and check that required subconfigs are there
827                const ConfigurationVerify *wildcardverify = 0;
828       
829                const ConfigurationVerify *scan = rVerify.mpSubConfigurations;
830                while(scan)
831                {
832                        if(scan->mName.length() > 0 && scan->mName[0] == '*')
833                        {
834                                wildcardverify = scan;
835                        }
836                       
837                        // Required?
838                        if((scan->Tests & ConfigTest_Exists) == ConfigTest_Exists)
839                        {
840                                if(scan->mName.length() > 0 &&
841                                        scan->mName[0] == '*')
842                                {
843                                        // Check something exists
844                                        if(mSubConfigurations.size() < 1)
845                                        {
846                                                // A sub config should exist, but doesn't.
847                                                ok = false;
848                                                rErrorMsg += rLevel + mName + ".* (block) is missing (a block must be present).\n";
849                                        }
850                                }
851                                else
852                                {
853                                        // Check real thing exists
854                                        if(!SubConfigurationExists(scan->mName))
855                                        {
856                                                // Should exist, but doesn't.
857                                                ok = false;
858                                                rErrorMsg += rLevel + mName + "." + scan->mName + " (block) is missing.\n";
859                                        }
860                                }
861                        }
862
863                        // Next?
864                        if((scan->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry)
865                        {
866                                break;
867                        }
868                        scan++;
869                }
870               
871                // Go through the sub configurations, one by one
872                for(SubConfigListType::iterator
873                        i  = mSubConfigurations.begin();
874                        i != mSubConfigurations.end(); ++i)
875                {
876                        // Can this be found?
877                        const ConfigurationVerify *subverify = 0;
878                       
879                        const ConfigurationVerify *scan = rVerify.mpSubConfigurations;
880                        const char *name = i->first.c_str();
881                        ASSERT(name);
882                        while(scan)
883                        {
884                                if(scan->mName == name)
885                                {
886                                        // found it!
887                                        subverify = scan;
888                                }
889                       
890                                // Next?
891                                if((scan->Tests & ConfigTest_LastEntry) == ConfigTest_LastEntry)
892                                {
893                                        break;
894                                }
895                                scan++;
896                        }
897                       
898                        // Use wildcard?
899                        if(subverify == 0)
900                        {
901                                subverify = wildcardverify;
902                        }
903                       
904                        // Verify
905                        if(subverify)
906                        {
907                                // override const-ness here...
908                                if(!i->second.Verify(*subverify, mName + '.',
909                                        rErrorMsg))
910                                {
911                                        ok = false;
912                                }
913                        }
914                }
915        }
916       
917        return ok;
918}
919
920
Note: See TracBrowser for help on using the repository browser.