Project

General

Profile

Statistics
| Branch: | Revision:

root / src / envir / sectionbasedconfig.cc @ e26d3d25

History | View | Annotate | Download (49.4 KB)

1 01873262 Georg Kunz
//==========================================================================
2
//  SECTIONBASEDCONFIG.CC - part of
3
//                     OMNeT++/OMNEST
4
//            Discrete System Simulation in C++
5
//
6
//  Author: Andras Varga
7
//
8
//==========================================================================
9
10
/*--------------------------------------------------------------*
11
  Copyright (C) 1992-2008 Andras Varga
12
  Copyright (C) 2006-2008 OpenSim Ltd.
13

14
  This file is distributed WITHOUT ANY WARRANTY. See the file
15
  `license' for details on this and other legal matters.
16
*--------------------------------------------------------------*/
17
18
#include <assert.h>
19
#include <algorithm>
20
#include <sstream>
21
#include "opp_ctype.h"
22
#include "sectionbasedconfig.h"
23
#include "cconfigreader.h"
24
#include "patternmatcher.h"
25
#include "valueiterator.h"
26
#include "cexception.h"
27
#include "scenario.h"
28
#include "globals.h"
29
#include "cconfigoption.h"
30
#include "stringtokenizer.h"
31
#include "timeutil.h"
32
#include "platmisc.h"   //getpid()
33
34
USING_NAMESPACE
35
36
//XXX error messages (exceptions) should contain file/line info!
37
//XXX make sure quoting "$\{" works!
38
//TODO optimize storage (now keys with wildcard suffix are stored multiple times, in several suffix groups)
39
40
41
Register_GlobalConfigOption(CFGID_SECTIONBASEDCONFIG_CONFIGREADER_CLASS, "sectionbasedconfig-configreader-class", CFG_STRING, "", "When configuration-class=SectionBasedConfiguration: selects the configuration reader C++ class, which must subclass from cConfigurationReader.");
42
Register_PerRunConfigOption(CFGID_DESCRIPTION, "description", CFG_STRING, NULL, "Descriptive name for the given simulation configuration. Descriptions get displayed in the run selection dialog.");
43
Register_PerRunConfigOption(CFGID_EXTENDS, "extends", CFG_STRING, NULL, "Name of the configuration this section is based on. Entries from that section will be inherited and can be overridden. In other words, configuration lookups will fall back to the base section.");
44
Register_PerRunConfigOption(CFGID_CONSTRAINT, "constraint", CFG_STRING, NULL, "For scenarios. Contains an expression that iteration variables (${} syntax) must satisfy for that simulation to run. Example: $i < $j+1.");
45
Register_PerRunConfigOption(CFGID_REPEAT, "repeat", CFG_INT, "1", "For scenarios. Specifies how many replications should be done with the same parameters (iteration variables). This is typically used to perform multiple runs with different random number seeds. The loop variable is available as ${repetition}. See also: seed-set= key.");
46
Register_PerRunConfigOption(CFGID_EXPERIMENT_LABEL, "experiment-label", CFG_STRING, "${configname}", "Identifies the simulation experiment (which consists of several, potentially repeated measurements). This string gets recorded into result files, and may be referred to during result analysis.");
47
Register_PerRunConfigOption(CFGID_MEASUREMENT_LABEL, "measurement-label", CFG_STRING, "${iterationvars}", "Identifies the measurement within the experiment. This string gets recorded into result files, and may be referred to during result analysis.");
48
Register_PerRunConfigOption(CFGID_REPLICATION_LABEL, "replication-label", CFG_STRING, "#${repetition}", "Identifies one replication of a measurement (see repeat= and measurement-label= as well). This string gets recorded into result files, and may be referred to during result analysis.");
49
Register_PerRunConfigOption(CFGID_RUNNUMBER_WIDTH, "runnumber-width", CFG_INT, "0", "Setting a nonzero value will cause the $runnumber variable to get padded with leading zeroes to the given length.");
50
51
extern cConfigOption *CFGID_NETWORK;
52
extern cConfigOption *CFGID_RESULT_DIR;
53
extern cConfigOption *CFGID_SEED_SET;
54
55
Register_Class(SectionBasedConfiguration);
56
57
// table to be kept consistent with scave/fields.cc
58
static struct ConfigVarDescription { const char *name, *description; } configVarDescriptions[] = {
59
    { CFGVAR_RUNID,            "A reasonably globally unique identifier for the run, produced by concatenating the configuration name, run number, date/time, etc." },
60
    { CFGVAR_INIFILE,          "Name of the (primary) inifile" },
61
    { CFGVAR_CONFIGNAME,       "Name of the active configuration" },
62
    { CFGVAR_RUNNUMBER,        "Sequence number of the current run within all runs in the active configuration" },
63
    { CFGVAR_NETWORK,          "Value of the \"network\" configuration option" },
64
    { CFGVAR_EXPERIMENT,       "Value of the \"experiment-label\" configuration option" },
65
    { CFGVAR_MEASUREMENT,      "Value of the \"measurement-label\" configuration option" },
66
    { CFGVAR_REPLICATION,      "Value of the \"replication-label\" configuration option" },
67
    { CFGVAR_PROCESSID,        "PID of the simulation process" },
68
    { CFGVAR_DATETIME,         "Date and time the simulation run was started" },
69
    { CFGVAR_RESULTDIR,        "Value of the \"result-dir\" configuration option" },
70
    { CFGVAR_REPETITION,       "The iteration number in 0..N-1, where N is the value of the \"repeat\" configuration option" },
71
    { CFGVAR_SEEDSET,          "Value of the \"seed-set\" configuration option" },
72
    { CFGVAR_ITERATIONVARS,    "Concatenation of all user-defined iteration variables in name=value form" },
73
    { CFGVAR_ITERATIONVARS2,   "Concatenation of all user-defined iteration variables in name=value form, plus ${repetition}" },
74
    { NULL,                    NULL }
75
};
76
77
#define VARPOS_PREFIX  std::string("&")
78
79
std::string SectionBasedConfiguration::KeyValue1::nullbasedir;
80
81
82
SectionBasedConfiguration::SectionBasedConfiguration()
83
{
84
    ini = NULL;
85
    activeRunNumber = 0;
86
}
87
88
SectionBasedConfiguration::~SectionBasedConfiguration()
89
{
90
    clear();
91
    delete ini;
92
}
93
94
void SectionBasedConfiguration::setConfigurationReader(cConfigurationReader *ini)
95
{
96
    clear();
97
    this->ini = ini;
98
    nullEntry.setBaseDirectory(ini->getDefaultBaseDirectory());
99
}
100
101
void SectionBasedConfiguration::setCommandLineConfigOptions(const std::map<std::string,std::string>& options)
102
{
103
    commandLineOptions.clear();
104
    for (StringMap::const_iterator it=options.begin(); it!=options.end(); it++)
105
    {
106
        // validate the key, then store the option
107
        //XXX we should better use the code in the validate() method...
108
        const char *key = it->first.c_str();
109
        const char *value = it->second.c_str();
110
        const char *option = strchr(key,'.') ? strrchr(key,'.')+1 : key; // check only the part after the last dot, i.e. recognize per-object keys as well
111
        cConfigOption *e = lookupConfigOption(option);
112
        if (!e)
113
            throw cRuntimeError("Unknown command-line configuration option --%s", key);
114
        if (!e->isPerObject() && key!=option)
115
            throw cRuntimeError("Wrong command-line configuration option --%s: %s is not a per-object option", key, e->getName());
116
        std::string tmp;
117
        if (e->isPerObject() && key==option)
118
            key = (tmp=std::string("**.")+key).c_str(); // prepend with "**." (XXX this should be done in inifile contents too)
119
        if (!value[0])
120
            throw cRuntimeError("Missing value for command-line configuration option --%s", key);
121
        commandLineOptions.push_back(KeyValue1(NULL, key, value));
122
    }
123
}
124
125
void SectionBasedConfiguration::clear()
126
{
127
    // note: this gets called between activateConfig() calls, so "ini" must NOT be NULL'ed out here
128
    activeConfig = "";
129
    activeRunNumber = 0;
130
    config.clear();
131
    suffixGroups.clear();
132
    wildcardSuffixGroup.entries.clear();
133
    variables.clear();
134
}
135
136
void SectionBasedConfiguration::initializeFrom(cConfiguration *bootConfig)
137
{
138
    std::string classname = bootConfig->getAsString(CFGID_SECTIONBASEDCONFIG_CONFIGREADER_CLASS);
139
    if (classname.empty())
140
        throw cRuntimeError("SectionBasedConfiguration: no configuration reader class specified (missing %s option)",
141
                             CFGID_SECTIONBASEDCONFIG_CONFIGREADER_CLASS->getName());
142
    cConfigurationReader *reader = dynamic_cast<cConfigurationReader *>(createOne(classname.c_str()));
143
    if (!reader)
144
        throw cRuntimeError("Class \"%s\" is not subclassed from cConfigurationReader", classname.c_str());
145
    reader->initializeFrom(bootConfig);
146
    setConfigurationReader(reader);
147
}
148
149
const char *SectionBasedConfiguration::getFileName() const
150
{
151
    return ini==NULL ? NULL : ini->getFileName();
152
}
153
154
const char *SectionBasedConfiguration::getActiveConfigName() const
155
{
156
    return activeConfig.c_str();
157
}
158
159
int SectionBasedConfiguration::getActiveRunNumber() const
160
{
161
    return activeRunNumber;
162
}
163
164
std::vector<std::string> SectionBasedConfiguration::getConfigNames()
165
{
166
    std::vector<std::string> result;
167
    for (int i=0; i<ini->getNumSections(); i++)
168
    {
169
        const char *section = ini->getSectionName(i);
170
        if (strcmp(section, "General")==0)
171
            result.push_back(section);
172
        else if (strncmp(section, "Config ", 7)==0)
173
            result.push_back(section+7);
174
        else
175
            ; // nothing - leave out bogus section names
176
    }
177
    return result;
178
}
179
180
std::string SectionBasedConfiguration::getConfigDescription(const char *configName) const
181
{
182
    int sectionId = resolveConfigName(configName);
183
    if (sectionId == -1)
184
        throw cRuntimeError("No such config: %s", configName);
185
186
    // determine the list of sections, from this one up to [General]
187
    std::vector<int> sectionChain = resolveSectionChain(sectionId);
188
    return opp_nulltoempty(internalGetValue(sectionChain, CFGID_DESCRIPTION->getName()));
189
}
190
191
std::string SectionBasedConfiguration::getBaseConfig(const char *configName) const
192
{
193
    int sectionId = resolveConfigName(configName);
194
    if (sectionId == -1)
195
        throw cRuntimeError("No such config: %s", configName);
196
    int entryId = internalFindEntry(sectionId, CFGID_EXTENDS->getName());
197
    std::string extends = entryId==-1 ? "" : ini->getEntry(sectionId, entryId).getValue();
198
    if (extends.empty())
199
        extends = "General";
200
    int baseSectionId = resolveConfigName(extends.c_str());
201
    return baseSectionId==-1 ? "" : extends;
202
}
203
204
int SectionBasedConfiguration::resolveConfigName(const char *configName) const
205
{
206
    if (!configName || !configName[0])
207
        throw cRuntimeError("Empty config name specified");
208
    int id = -1;
209
    if (!strcmp(configName, "General"))
210
        id = internalFindSection("General");
211
    if (id == -1)
212
        id = internalFindSection((std::string("Config ")+configName).c_str());
213
    return id;
214
}
215
216
static std::string opp_makedatetimestring()
217
{
218
    time_t t = time(NULL);
219
    struct tm tm = *localtime(&t);
220
    char timestr[32];
221
    sprintf(timestr, "%04d%02d%02d-%02d:%02d:%02d",
222
            1900+tm.tm_year, tm.tm_mon+1, tm.tm_mday,
223
            tm.tm_hour, tm.tm_min, tm.tm_sec);
224
    return timestr;
225
}
226
227
void SectionBasedConfiguration::activateConfig(const char *configName, int runNumber)
228
{
229
    clear();
230
231
    activeConfig = configName==NULL ? "" : configName;
232
    activeRunNumber = runNumber;
233
234
    int sectionId = resolveConfigName(configName);
235
    if (sectionId == -1 && !strcmp(configName, "General"))
236
        return;  // allow activating "General" even if it's empty
237
    if (sectionId == -1)
238
        throw cRuntimeError("No such config: %s", configName);
239
240
    // determine the list of sections, from this one up to [General]
241
    std::vector<int> sectionChain = resolveSectionChain(sectionId);
242
243
    // extract all iteration vars from values within this section
244
    std::vector<IterationVariable> itervars = collectIterationVariables(sectionChain);
245
246
    // see if there's a constraint given
247
    const char *constraint = internalGetValue(sectionChain, CFGID_CONSTRAINT->getName(), NULL);
248
249
    // determine the values to substitute into the iteration vars (${...})
250
    try
251
    {
252
        Scenario scenario(itervars, constraint);
253
        int numRuns = scenario.getNumRuns();
254
        if (runNumber<0 || runNumber>=numRuns)
255
            throw cRuntimeError("Run number %d is out of range for configuration `%s': it contains %d run(s)", runNumber, configName, numRuns);
256
257
        scenario.gotoRun(runNumber);
258
        setupVariables(getActiveConfigName(), getActiveRunNumber(), &scenario, sectionChain);
259
    }
260
    catch (std::exception& e)
261
    {
262
        throw cRuntimeError("Scenario generator: %s", e.what());
263
    }
264
265
    // walk the list of fallback sections, and add entries to our tables
266
    // (config[] and params[]). Meanwhile, substitute the iteration values.
267
    // Note: entries added first will have precedence over those added later.
268
    for (int i=0; i < (int)commandLineOptions.size(); i++)
269
    {
270
        addEntry(commandLineOptions[i]);
271
    }
272
    for (int i=0; i < (int)sectionChain.size(); i++)
273
    {
274
        int sectionId = sectionChain[i];
275
        for (int entryId=0; entryId<ini->getNumEntries(sectionId); entryId++)
276
        {
277
            // add entry to our tables
278
            addEntry(convert(sectionId, entryId));
279
        }
280
    }
281
}
282
283
void SectionBasedConfiguration::setupVariables(const char *configName, int runNumber, Scenario *scenario, const std::vector<int>& sectionChain)
284
{
285
    // create variables
286
    int runnumberWidth = std::max(0,atoi(opp_nulltoempty(internalGetValue(sectionChain, CFGID_RUNNUMBER_WIDTH->getName()))));
287
    variables[CFGVAR_INIFILE] = opp_nulltoempty(getFileName());
288
    variables[CFGVAR_CONFIGNAME] = configName;
289
    variables[CFGVAR_RUNNUMBER] = opp_stringf("%0*d", runnumberWidth, runNumber);
290
    variables[CFGVAR_NETWORK] = opp_nulltoempty(internalGetValue(sectionChain, CFGID_NETWORK->getName()));
291
    variables[CFGVAR_PROCESSID] = opp_stringf("%d", (int) getpid());
292
    variables[CFGVAR_DATETIME] = opp_makedatetimestring();
293
    variables[CFGVAR_RESULTDIR] = opp_nulltoempty(internalGetValue(sectionChain, CFGID_RESULT_DIR->getName(), CFGID_RESULT_DIR->getDefaultValue()));
294
    variables[CFGVAR_RUNID] = runId = variables[CFGVAR_CONFIGNAME]+"-"+variables[CFGVAR_RUNNUMBER]+"-"+variables[CFGVAR_DATETIME]+"-"+variables[CFGVAR_PROCESSID];
295
296
    // store iteration variables, and also their "positions" (iteration count) as "&varid"
297
    const std::vector<IterationVariable>& itervars = scenario->getIterationVariables();
298
    for (int i=0; i<(int)itervars.size(); i++)
299
    {
300
        const char *varid = itervars[i].varid.c_str();
301
        variables[varid] = scenario->getVariable(varid);
302
        variables[VARPOS_PREFIX+varid] = opp_stringf("%d", scenario->getIteratorPosition(varid));
303
    }
304
305
    // assemble ${iterationvars}
306
    std::string iterationvars, iterationvars2;
307
    for (int i=0; i<(int)itervars.size(); i++)
308
    {
309
        std::string txt = "$" + itervars[i].varname + "=" + scenario->getVariable(itervars[i].varid.c_str());
310
        if (itervars[i].varname != CFGVAR_REPETITION)
311
            iterationvars += std::string(i>0?", ":"") + txt;
312
        iterationvars2 += std::string(i>0?", ":"") + txt;
313
    }
314
    variables[CFGVAR_ITERATIONVARS] = iterationvars;
315
    variables[CFGVAR_ITERATIONVARS2] = iterationvars2;
316
317
    // experiment/measurement/replication must be done as last, because they may depend on the above vars
318
    variables[CFGVAR_SEEDSET] = substituteVariables(internalGetValue(sectionChain, CFGID_SEED_SET->getName(), CFGID_SEED_SET->getDefaultValue()), -1, -1);
319
    variables[CFGVAR_EXPERIMENT] = substituteVariables(internalGetValue(sectionChain, CFGID_EXPERIMENT_LABEL->getName(), CFGID_EXPERIMENT_LABEL->getDefaultValue()), -1, -1);
320
    variables[CFGVAR_MEASUREMENT] = substituteVariables(internalGetValue(sectionChain, CFGID_MEASUREMENT_LABEL->getName(), CFGID_MEASUREMENT_LABEL->getDefaultValue()), -1, -1);
321
    variables[CFGVAR_REPLICATION] = substituteVariables(internalGetValue(sectionChain, CFGID_REPLICATION_LABEL->getName(), CFGID_REPLICATION_LABEL->getDefaultValue()), -1, -1);
322
}
323
324
int SectionBasedConfiguration::getNumRunsInConfig(const char *configName) const
325
{
326
    int sectionId = resolveConfigName(configName);
327
    if (sectionId == -1)
328
        return 0;  // no such config
329
330
    // extract all iteration vars from values within this section
331
    std::vector<int> sectionChain = resolveSectionChain(sectionId);
332
    std::vector<IterationVariable> v = collectIterationVariables(sectionChain);
333
334
    // see if there's a constraint given
335
    const char *constraint = internalGetValue(sectionChain, CFGID_CONSTRAINT->getName(), NULL);
336
337
    // count the runs and return the result
338
    try {
339
        return Scenario(v, constraint).getNumRuns();
340
    }
341
    catch (std::exception& e) {
342
        throw cRuntimeError("Scenario generator: %s", e.what());
343
    }
344
}
345
346
std::vector<std::string> SectionBasedConfiguration::unrollConfig(const char *configName, bool detailed) const
347
{
348
    int sectionId = resolveConfigName(configName);
349
    if (sectionId == -1)
350
        throw cRuntimeError("No such config: %s", configName);
351
352
    // extract all iteration vars from values within this section
353
    std::vector<int> sectionChain = resolveSectionChain(sectionId);
354
    std::vector<IterationVariable> itervars = collectIterationVariables(sectionChain);
355
356
    // see if there's a constraint given
357
    const char *constraint = internalGetValue(sectionChain, CFGID_CONSTRAINT->getName(), NULL);
358
359
    // setupVariables() overwrites variables[], so we need to save/restore it
360
    StringMap savedVariables = variables;
361
362
    // iterate over all runs in the scenario
363
    try {
364
        Scenario scenario(itervars, constraint);
365
        std::vector<std::string> result;
366
        if (scenario.restart())
367
        {
368
            for (;;)
369
            {
370
                // generate a string for the current run
371
                std::string runstring;
372
                if (!detailed)
373
                {
374
                    runstring = scenario.str();
375
                }
376
                else
377
                {
378
                    // itervars, plus all entries that contain ${..}
379
                    runstring += std::string("\t# ") + scenario.str() + "\n";
380
                    (const_cast<SectionBasedConfiguration *>(this))->setupVariables(configName, result.size(), &scenario, sectionChain);
381
                    for (int i=0; i<(int)sectionChain.size(); i++)
382
                    {
383
                        int sectionId = sectionChain[i];
384
                        for (int entryId=0; entryId<ini->getNumEntries(sectionId); entryId++)
385
                        {
386
                            // add entry to our tables
387
                            const cConfigurationReader::KeyValue& entry = ini->getEntry(sectionId, entryId);
388
                            if (strstr(entry.getValue(), "${")!=NULL)
389
                            {
390
                                std::string actualValue = substituteVariables(entry.getValue(), sectionId, entryId);
391
                                runstring += std::string("\t") + entry.getKey() + " = " + actualValue + "\n";
392
                            }
393
                        }
394
                    }
395
                }
396
                result.push_back(runstring);
397
398
                // move to the next run
399
                if (!scenario.next())
400
                    break;
401
            }
402
        }
403
        (const_cast<SectionBasedConfiguration *>(this))->variables = savedVariables;
404
        return result;
405
    }
406
    catch (std::exception& e)
407
    {
408
        (const_cast<SectionBasedConfiguration *>(this))->variables = savedVariables;
409
        throw cRuntimeError("Scenario generator: %s", e.what());
410
    }
411
}
412
413
std::vector<SectionBasedConfiguration::IterationVariable> SectionBasedConfiguration::collectIterationVariables(const std::vector<int>& sectionChain) const
414
{
415
    std::vector<IterationVariable> v;
416
    int unnamedCount = 0;
417
    for (int i=0; i<(int)sectionChain.size(); i++)
418
    {
419
        int sectionId = sectionChain[i];
420
        for (int entryId=0; entryId<ini->getNumEntries(sectionId); entryId++)
421
        {
422
            const cConfigurationReader::KeyValue& entry = ini->getEntry(sectionId, entryId);
423
            const char *pos = entry.getValue();
424
            int k = 0;
425
            while ((pos = strstr(pos, "${")) != NULL)
426
            {
427
                IterationVariable loc;
428
                try {
429
                    parseVariable(pos, loc.varname, loc.value, loc.parvar, pos);
430
                } catch (std::exception& e) {
431
                    throw cRuntimeError("Scenario generator: %s at %s=%s", e.what(), entry.getKey(), entry.getValue());
432
                }
433
434
                if (!loc.value.empty() && loc.parvar.empty())
435
                {
436
                    // store variable
437
                    if (!loc.varname.empty())
438
                    {
439
                        // check it does not conflict with other iteration variables or predefined variables
440
                        for (int j=0; j<(int)v.size(); j++)
441
                            if (v[j].varname==loc.varname)
442
                                throw cRuntimeError("Scenario generator: redefinition of iteration variable ${%s} in the configuration", loc.varname.c_str());
443
                        if (isPredefinedVariable(loc.varname.c_str()))
444
                            throw cRuntimeError("Scenario generator: ${%s} is a predefined variable and cannot be changed", loc.varname.c_str());
445
                        // use name for id
446
                        loc.varid = loc.varname;
447
                    }
448
                    else
449
                    {
450
                        // unnamed variable: generate id (identifies location) and name ($0,$1,$2,etc)
451
                        loc.varid = opp_stringf("%d-%d-%d", sectionId, entryId, k);
452
                        loc.varname = opp_stringf("%d", unnamedCount++);
453
                    }
454
                    v.push_back(loc);
455
                }
456
                k++;
457
            }
458
        }
459
    }
460
461
    // register ${repetition}, based on the repeat= config entry
462
    const char *repeat = internalGetValue(sectionChain, CFGID_REPEAT->getName());
463
    int repeatCount = (int) parseLong(repeat, NULL, 1);
464
    IterationVariable repetition;
465
    repetition.varid = repetition.varname = CFGVAR_REPETITION;
466
    repetition.value = opp_stringf("0..%d", repeatCount-1);
467
    v.push_back(repetition);
468
469
    return v;
470
}
471
472
void SectionBasedConfiguration::parseVariable(const char *pos, std::string& outVarname, std::string& outValue, std::string& outParvar, const char *&outEndPos)
473
{
474
    Assert(pos[0]=='$' && pos[1]=='{'); // this is the way we've got to be invoked
475
    outEndPos = strchr(pos, '}');
476
    if (!outEndPos)
477
        throw cRuntimeError("missing '}' for '${'");
478
479
    // parse what's inside the ${...}
480
    const char *varbegin = NULL;
481
    const char *varend = NULL;
482
    const char *valuebegin = NULL;
483
    const char *valueend = NULL;
484
    const char *parvarbegin = NULL;
485
    const char *parvarend = NULL;
486
487
    const char *s = pos+2;
488
    while (opp_isspace(*s)) s++;
489
    if (opp_isalpha(*s))
490
    {
491
        // must be a variable or a variable reference
492
        varbegin = varend = s;
493
        while (opp_isalnum(*varend)) varend++;
494
        s = varend;
495
        while (opp_isspace(*s)) s++;
496
        if (*s=='}') {
497
            // ${x} syntax -- OK
498
        }
499
        else if (*s=='=' && *(s+1)!='=') {
500
            // ${x=...} syntax -- OK
501
            valuebegin = s+1;
502
        }
503
        else if (strchr(s,',')) {
504
            // part of a valuelist -- OK
505
            valuebegin = varbegin;
506
            varbegin = varend = NULL;
507
        }
508
        else {
509
            throw cRuntimeError("missing '=' after '${varname'");
510
        }
511
    } else {
512
        valuebegin = s;
513
    }
514
    valueend = outEndPos;
515
516
    if (valuebegin)
517
    {
518
        // try to parse parvar, present when value ends in "! variable"
519
        const char *exclamationMark = strrchr(valuebegin, '!');
520
        if (exclamationMark)
521
        {
522
            const char *s = exclamationMark+1;
523
            while (opp_isspace(*s)) s++;
524
            if (opp_isalpha(*s))
525
            {
526
                parvarbegin = s;
527
                while (opp_isalnum(*s)) s++;
528
                parvarend = s;
529
                while (opp_isspace(*s)) s++;
530
                if (s!=valueend)
531
                {
532
                    parvarbegin = parvarend = NULL; // no parvar after all
533
                }
534
            }
535
            if (parvarbegin)  {
536
                valueend = exclamationMark;  // chop off "!parvarname"
537
            }
538
        }
539
    }
540
541
    if (varbegin && parvarbegin)
542
        throw cRuntimeError("the ${var=...} and ${...!var} syntaxes cannot be used together");
543
544
    outVarname = outValue = outParvar = "";
545
    if (varbegin)
546
        outVarname.assign(varbegin, varend-varbegin);
547
    if (valuebegin)
548
        outValue.assign(valuebegin, valueend-valuebegin);
549
    if (parvarbegin)
550
        outParvar.assign(parvarbegin, parvarend-parvarbegin);
551
    //printf("DBG: var=`%s', value=`%s', parvar=`%s'\n", outVarname.c_str(), outValue.c_str(), outParvar.c_str());
552
}
553
554
std::string SectionBasedConfiguration::substituteVariables(const char *text, int sectionId, int entryId) const
555
{
556
    std::string result = opp_nulltoempty(text);
557
    int k = 0;  // counts "${" occurrences
558
    const char *pos, *endPos;
559
    while ((pos = strstr(result.c_str(), "${")) != NULL)
560
    {
561
        std::string varname, iterationstring, parvar;
562
        parseVariable(pos, varname, iterationstring, parvar, endPos);
563
        std::string value;
564
        if (parvar.empty())
565
        {
566
            // handle named and unnamed iteration variable references
567
            std::string varid = !varname.empty() ? varname : opp_stringf("%d-%d-%d", sectionId, entryId, k);
568
            StringMap::const_iterator it = variables.find(varid.c_str());
569
            if (it==variables.end())
570
                throw cRuntimeError("no such variable: ${%s}", varid.c_str());
571
            value = it->second;
572
        }
573
        else
574
        {
575
            // handle parallel iterations: if parvar is at its kth value,
576
            // we should take the kth value from iterationstring as well
577
            StringMap::const_iterator it = variables.find(VARPOS_PREFIX+parvar);
578
            if (it==variables.end())
579
                throw cRuntimeError("no such variable: ${%s}", parvar.c_str());
580
            int parvarPos = atoi(it->second.c_str());
581
            ValueIterator v(iterationstring.c_str());
582
            if (parvarPos >= v.length())
583
                throw cRuntimeError("parallel iterator ${...!%s} does not have enough values", parvar.c_str());
584
            value = v.get(parvarPos);
585
        }
586
        result.replace(pos-result.c_str(), endPos-pos+1, value);
587
        k++;
588
    }
589
    return result;
590
}
591
592
const char *SectionBasedConfiguration::substituteVariables(const char *value)
593
{
594
    if (value==NULL || strstr(value, "${")==NULL)
595
        return value;
596
597
    // returned string needs to be stringpooled
598
    std::string result = substituteVariables(value, -1, -1);
599
    return stringPool.get(result.c_str());
600
}
601
602
const char *SectionBasedConfiguration::getVariable(const char *varname) const
603
{
604
    StringMap::const_iterator it = variables.find(varname);
605
    return it==variables.end() ? NULL : it->second.c_str();
606
}
607
608
std::vector<const char *> SectionBasedConfiguration::getIterationVariableNames() const
609
{
610
    std::vector<const char *> result;
611
    for (StringMap::const_iterator it = variables.begin(); it!=variables.end(); ++it)
612
        if (opp_isalpha(it->first[0]) && !isPredefinedVariable(it->first.c_str()))  // skip unnamed and predefined ones
613
            result.push_back(it->first.c_str());
614
    return result;
615
}
616
617
std::vector<const char *> SectionBasedConfiguration::getPredefinedVariableNames() const
618
{
619
    std::vector<const char *> result;
620
    for (int i=0; configVarDescriptions[i].name; i++)
621
        result.push_back(configVarDescriptions[i].name);
622
    return result;
623
}
624
625
const char *SectionBasedConfiguration::getVariableDescription(const char *varname) const
626
{
627
    for (int i=0; configVarDescriptions[i].name; i++)
628
        if (strcmp(varname, configVarDescriptions[i].name)==0)
629
            return configVarDescriptions[i].description;
630
    if (!opp_isempty(getVariable(varname)))
631
        return "User-defined iteration variable";
632
    return NULL;
633
}
634
635
bool SectionBasedConfiguration::isPredefinedVariable(const char *varname) const
636
{
637
    for (int i=0; configVarDescriptions[i].name; i++)
638
        if (strcmp(varname, configVarDescriptions[i].name)==0)
639
            return true;
640
    return false;
641
}
642
643
std::vector<int> SectionBasedConfiguration::resolveSectionChain(int sectionId) const
644
{
645
    return resolveSectionChain(ini->getSectionName(sectionId));
646
}
647
648
std::vector<int> SectionBasedConfiguration::resolveSectionChain(const char *sectionName) const
649
{
650
    // determine the list of sections, from this one following the "extends" chain up to [General]
651
    std::vector<int> sectionChain;
652
    int generalSectionId = internalFindSection("General");
653
    int sectionId = internalGetSectionId(sectionName);
654
    while (true)
655
    {
656
        if (std::find(sectionChain.begin(), sectionChain.end(), sectionId) != sectionChain.end())
657
            throw cRuntimeError("Cycle detected in section fallback chain at: [%s]", ini->getSectionName(sectionId));
658
        sectionChain.push_back(sectionId);
659
        int entryId = internalFindEntry(sectionId, CFGID_EXTENDS->getName());
660
        std::string extends = entryId==-1 ? "" : ini->getEntry(sectionId, entryId).getValue();
661
        if (extends.empty() && generalSectionId!=-1 && sectionId!=generalSectionId)
662
            extends = "General";
663
        if (extends.empty())
664
            break;
665
        sectionId = resolveConfigName(extends.c_str());
666
        if (sectionId == -1)
667
            break; // wrong config name
668
    }
669
670
    return sectionChain;
671
}
672
673
void SectionBasedConfiguration::addEntry(const KeyValue1& entry)
674
{
675
    const std::string& key = entry.key;
676
    const char *lastDot = strrchr(key.c_str(), '.');
677
    if (!lastDot && !PatternMatcher::containsWildcards(key.c_str()))
678
    {
679
        // config: add if not already in there
680
        if (config.find(key)==config.end())
681
            config[key] = entry;
682
    }
683
    else
684
    {
685
        // key contains wildcard or dot: parameter or per-object configuration
686
        // (example: "**", "**.param", "**.partition-id")
687
        // Note: since the last part of they key might contain wildcards, it is not really possible
688
        // to distinguish the two. Cf "vector-recording", "vector-*" and "vector*"
689
690
        // analyze key and create appropriate entry
691
        std::string ownerName;
692
        std::string suffix;
693
        splitKey(key.c_str(), ownerName, suffix);
694
        bool suffixContainsWildcards = PatternMatcher::containsWildcards(suffix.c_str());
695
696
        KeyValue2 entry2(entry);
697
        if (!ownerName.empty())
698
            entry2.ownerPattern = new PatternMatcher(ownerName.c_str(), true, true, true);
699
         else
700
            entry2.fullPathPattern = new PatternMatcher(key.c_str(), true, true, true);
701
        entry2.suffixPattern = suffixContainsWildcards ? new PatternMatcher(suffix.c_str(), true, true, true) : NULL;
702
703
        // find which group it should go into
704
        if (!suffixContainsWildcards)
705
        {
706
            // no wildcard in suffix (=group name)
707
            if (suffixGroups.find(suffix)==suffixGroups.end()) {
708
                // suffix group not yet exists, create it
709
                SuffixGroup& group = suffixGroups[suffix];
710
711
                // initialize group with matching wildcard keys seen so far
712
                for (int k=0; k<(int)wildcardSuffixGroup.entries.size(); k++)
713
                    if (wildcardSuffixGroup.entries[k].suffixPattern->matches(suffix.c_str()))
714
                        group.entries.push_back(wildcardSuffixGroup.entries[k]);
715
            }
716
            suffixGroups[suffix].entries.push_back(entry2);
717
        }
718
        else
719
        {
720
            // suffix contains wildcards: we need to add it to all existing suffix groups it matches
721
            // Note: if suffix also contains a hyphen, that's actually illegal (per-object
722
            // config entry names cannot be wildcarded, ie. "foo.bar.cmdenv-*" is illegal),
723
            // but causes no harm, because getPerObjectConfigEntry() won't look into the
724
            // wildcard group
725
            wildcardSuffixGroup.entries.push_back(entry2);
726
            for (std::map<std::string,SuffixGroup>::iterator it = suffixGroups.begin(); it!=suffixGroups.end(); it++)
727
                if (entry2.suffixPattern->matches(it->first.c_str()))
728
                    (it->second).entries.push_back(entry2);
729
        }
730
    }
731
}
732
733
void SectionBasedConfiguration::splitKey(const char *key, std::string& outOwnerName, std::string& outGroupName)
734
{
735
    std::string tmp = key;
736
737
    const char *lastDotPos = strrchr(key, '.');
738
    const char *doubleAsterisk = !lastDotPos ? NULL : strstr(lastDotPos, "**");
739
740
    if (!lastDotPos || doubleAsterisk)
741
    {
742
        // complicated special case: there's a "**" after the last dot
743
        // (or there's no dot at all). Examples: "**baz", "net.**.foo**",
744
        // "net.**.foo**bar**baz"
745
        // Problem with this: are "foo" and "bar" part of the paramname (=group)
746
        // or the module name (=owner)? Can be either way. Only feasible solution
747
        // is to force matching of the full path (modulepath.paramname) against
748
        // the full pattern. Group name can be "*" plus segment of the pattern
749
        // after the last "**". (For example, for "net.**foo**bar", the group name
750
        // will be "*bar".)
751
752
        // find last "**"
753
        while (doubleAsterisk && strstr(doubleAsterisk+1, "**"))
754
            doubleAsterisk = strstr(doubleAsterisk+1, "**");
755
        outOwnerName = ""; // empty owner means "do fullPath match"
756
        outGroupName = !doubleAsterisk ? "*" : doubleAsterisk+1;
757
    }
758
    else
759
    {
760
        // normal case: group is the part after the last dot
761
        outOwnerName.assign(key, lastDotPos - key);
762
        outGroupName.assign(lastDotPos+1);
763
    }
764
}
765
766
SectionBasedConfiguration::KeyValue1 SectionBasedConfiguration::convert(int sectionId, int entryId)
767
{
768
    const cConfigurationReader::KeyValue& e = ini->getEntry(sectionId, entryId);
769
    std::string value = substituteVariables(e.getValue(), sectionId, entryId);
770
771
    StringSet::iterator it = basedirs.find(e.getBaseDirectory());
772
    if (it == basedirs.end()) {
773
        basedirs.insert(e.getBaseDirectory());
774
        it = basedirs.find(e.getBaseDirectory());
775
    }
776
    const std::string *basedirRef = &(*it);
777
    return KeyValue1(basedirRef, e.getKey(), value.c_str());
778
}
779
780
int SectionBasedConfiguration::internalFindSection(const char *section) const
781
{
782
    // not very efficient (linear search), but we only invoke it a few times during activateConfig()
783
    for (int i=0; i<ini->getNumSections(); i++)
784
        if (strcmp(section, ini->getSectionName(i))==0)
785
            return i;
786
    return -1;
787
}
788
789
int SectionBasedConfiguration::internalGetSectionId(const char *section) const
790
{
791
    int sectionId = internalFindSection(section);
792
    if (sectionId == -1)
793
        throw cRuntimeError("no such section: %s", section);
794
    return sectionId;
795
}
796
797
int SectionBasedConfiguration::internalFindEntry(int sectionId, const char *key) const
798
{
799
    // not very efficient (linear search), but we only invoke from activateConfig(),
800
    // and only once per section
801
    for (int i=0; i<ini->getNumEntries(sectionId); i++)
802
        if (strcmp(key, ini->getEntry(sectionId, i).getKey())==0)
803
            return i;
804
    return -1;
805
}
806
807
const char *SectionBasedConfiguration::internalGetValue(const std::vector<int>& sectionChain, const char *key, const char *fallbackValue) const
808
{
809
    for (int i=0; i<(int)commandLineOptions.size(); i++)
810
        if (strcmp(key, commandLineOptions[i].getKey())==0)
811
            return commandLineOptions[i].getValue();
812
813
    for (int i=0; i<(int)sectionChain.size(); i++)
814
    {
815
        int sectionId = sectionChain[i];
816
        int entryId = internalFindEntry(sectionId, key);
817
        if (entryId != -1)
818
            return ini->getEntry(sectionId, entryId).getValue();
819
    }
820
    return fallbackValue;
821
}
822
823
static int findInArray(const char *s, const char **array)
824
{
825
    for (int i=0; array[i]!=NULL; i++)
826
        if (!strcmp(s, array[i]))
827
            return i;
828
    return -1;
829
}
830
831
void SectionBasedConfiguration::validate(const char *ignorableConfigKeys) const
832
{
833
    const char *obsoleteSections[] = {
834
        "Parameters", "Cmdenv", "Tkenv", "OutVectors", "Partitioning", "DisplayStrings", NULL
835
    };
836
    const char *cmdenvNames[] = {
837
        "autoflush", "event-banner-details", "event-banners", "express-mode",
838
        "message-trace", "module-messages", "output-file", "performance-display",
839
        "runs-to-execute", "status-frequency", NULL
840
    };
841
    const char *tkenvNames[] = {
842
        "anim-methodcalls", "animation-enabled", "animation-msgclassnames",
843
        "animation-msgcolors", "animation-msgnames", "animation-speed",
844
        "default-run", "expressmode-autoupdate", "image-path", "methodcalls-delay",
845
        "next-event-markers", "penguin-mode", "plugin-path", "print-banners",
846
        "senddirect-arrows", "show-bubbles", "show-layouting", "slowexec-delay",
847
        "update-freq-express", "update-freq-fast", "use-mainwindow",
848
        "use-new-layouter", NULL
849
    };
850
851
    // warn for obsolete section names and config keys
852
    for (int i=0; i<ini->getNumSections(); i++)
853
    {
854
        const char *section = ini->getSectionName(i);
855
        if (findInArray(section, obsoleteSections) != -1)
856
            throw cRuntimeError("Obsolete section name [%s] found, please convert the ini file to 4.x format", section);
857
858
        int numEntries = ini->getNumEntries(i);
859
        for (int j=0; j<numEntries; j++)
860
        {
861
            const char *key = ini->getEntry(i, j).getKey();
862
            if (findInArray(key, cmdenvNames) != -1 || findInArray(key, tkenvNames) != -1)
863
                throw cRuntimeError("Obsolete configuration key %s= found, please convert the ini file to 4.x format", key);
864
        }
865
    }
866
867
    // check section names
868
    std::set<std::string> configNames;
869
    for (int i=0; i<ini->getNumSections(); i++)
870
    {
871
        const char *section = ini->getSectionName(i);
872
        const char *configName = NULL;
873
        if (strcmp(section, "General")==0)
874
            ; // OK
875
        else if (strncmp(section, "Config ", 7)==0)
876
            configName  = section+7;
877
        else
878
            throw cRuntimeError("Invalid section name [%s], should be [General] or [Config <name>]", section);
879
        if (configName)
880
        {
881
            if (*configName == ' ')
882
                throw cRuntimeError("Invalid section name [%s]: too many spaces", section);
883
            if (!opp_isalpha(*configName) && *configName!='_')
884
                throw cRuntimeError("Invalid section name [%s]: config name must begin with a letter or underscore", section);
885
            for (const char *s=configName; *s; s++)
886
                if (!opp_isalnum(*s) && strchr("-_@", *s)==NULL)
887
                    throw cRuntimeError("Invalid section name [%s], contains illegal character '%c'", section, *s);
888
            if (configNames.find(configName)!=configNames.end())
889
                throw cRuntimeError("Configuration name '%s' not unique", configName, section);
890
            configNames.insert(configName);
891
        }
892
893
    }
894
895
    // check keys
896
    for (int i=0; i<ini->getNumSections(); i++)
897
    {
898
        const char *section = ini->getSectionName(i);
899
        int numEntries = ini->getNumEntries(i);
900
        for (int j=0; j<numEntries; j++)
901
        {
902
            const char *key = ini->getEntry(i, j).getKey();
903
            bool containsDot = strchr(key, '.')!=NULL;
904
905
            if (!containsDot && !PatternMatcher::containsWildcards(key))
906
            {
907
                // warn for unrecognized (or misplaced) config keys
908
                // NOTE: values don't need to be validated here, that will be
909
                // done when the config gets actually used
910
                cConfigOption *e = lookupConfigOption(key);
911
                if (!e && isIgnorableConfigKey(ignorableConfigKeys, key))
912
                    continue;
913
                if (!e)
914
                    throw cRuntimeError("Unknown configuration key: %s", key);
915
                if (e->isPerObject())
916
                    throw cRuntimeError("Configuration key %s should be specified per object, try **.%s=", key, key);
917
                if (e->isGlobal() && strcmp(section, "General")!=0)
918
                    throw cRuntimeError("Configuration key %s may only occur in the [General] section", key);
919
920
                // check section hierarchy
921
                if (strcmp(key, CFGID_EXTENDS->getName())==0)
922
                {
923
                    if (strcmp(section, "General")==0)
924
                        throw cRuntimeError("The [General] section cannot extend other sections");
925
926
                    // warn for invalid "extends" names
927
                    const char *value = ini->getEntry(i, j).getValue();
928
                    if (configNames.find(value)==configNames.end())
929
                        throw cRuntimeError("No such config: %s", value);
930
931
                    // check for section cycles
932
                    resolveSectionChain(section);  //XXX move that check here?
933
                }
934
            }
935
            else
936
            {
937
                // check for per-object configuration subkeys (".ev-enabled", ".record-interval")
938
                std::string ownerName;
939
                std::string suffix;
940
                splitKey(key, ownerName, suffix);
941
                bool containsHyphen = strchr(suffix.c_str(), '-')!=NULL;
942
                if (containsHyphen)
943
                {
944
                    // this is a per-object config
945
                    //XXX suffix (probably) should not contain wildcard; but surely not "**" !!!!
946
                    cConfigOption *e = lookupConfigOption(suffix.c_str());
947
                    if (!e && isIgnorableConfigKey(ignorableConfigKeys, suffix.c_str()))
948
                        continue;
949
                    if (!e || !e->isPerObject())
950
                        throw cRuntimeError("Unknown per-object configuration key `%s' in %s", suffix.c_str(), key);
951
                }
952
            }
953
        }
954
    }
955
}
956
957
cConfigOption *SectionBasedConfiguration::lookupConfigOption(const char *key)
958
{
959
    cConfigOption *e = (cConfigOption *) configOptions.getInstance()->lookup(key);
960
    if (e)
961
        return e;  // found it, great
962
963
    // Maybe it matches on a cConfigOption which has '*' or '%' in its name,
964
    // such as "seed-1-mt" matches on the "seed-%-mt" cConfigOption.
965
    // We have to iterate over all cConfigOptions to verify this.
966
    // "%" means "any number" in config keys.
967
    int n = configOptions.getInstance()->size();
968
    for (int i=0; i<n; i++)
969
    {
970
        cConfigOption *e = (cConfigOption *) configOptions.getInstance()->get(i);
971
        if (PatternMatcher::containsWildcards(e->getName()) || strchr(e->getName(),'%')!=NULL)
972
        {
973
            std::string pattern = opp_replacesubstring(e->getName(), "%", "{..}", true);
974
            if (PatternMatcher(pattern.c_str(), false, true, true).matches(key))
975
                return e;
976
        }
977
    }
978
    return NULL;
979
}
980
981
bool SectionBasedConfiguration::isIgnorableConfigKey(const char *ignoredKeyPatterns, const char *key)
982
{
983
    // see if any element in ignoredKeyPatterns matches it
984
    StringTokenizer tokenizer(ignoredKeyPatterns ? ignoredKeyPatterns : "");
985
    while (tokenizer.hasMoreTokens())
986
        if (PatternMatcher(tokenizer.nextToken(), true, true, true).matches(key))
987
            return true;
988
    return false;
989
}
990
991
const char *SectionBasedConfiguration::getConfigValue(const char *key) const
992
{
993
    std::map<std::string,KeyValue1>::const_iterator it = config.find(key);
994
    return it==config.end() ? NULL : it->second.value.c_str();
995
}
996
997
const cConfiguration::KeyValue& SectionBasedConfiguration::getConfigEntry(const char *key) const
998
{
999
    std::map<std::string,KeyValue1>::const_iterator it = config.find(key);
1000
    return it==config.end() ? (KeyValue&)nullEntry : (KeyValue&)it->second;
1001
}
1002
1003
std::vector<const char *> SectionBasedConfiguration::getMatchingConfigKeys(const char *pattern) const
1004
{
1005
    std::vector<const char *> result;
1006
    PatternMatcher matcher(pattern, true, true, true);
1007
1008
    // iterate over the map -- this is going to be sloooow...
1009
    for (std::map<std::string,KeyValue1>::const_iterator it = config.begin(); it != config.end(); ++it)
1010
        if (matcher.matches(it->first.c_str()))
1011
            result.push_back(it->first.c_str());
1012
    return result;
1013
}
1014
1015
const char *SectionBasedConfiguration::getParameterValue(const char *moduleFullPath, const char *paramName, bool hasDefaultValue) const
1016
{
1017
    const SectionBasedConfiguration::KeyValue2& entry = (KeyValue2&) getParameterEntry(moduleFullPath, paramName, hasDefaultValue);
1018
    return entry.getKey()==NULL ? NULL : entry.value.c_str();
1019
}
1020
1021
const cConfiguration::KeyValue& SectionBasedConfiguration::getParameterEntry(const char *moduleFullPath, const char *paramName, bool hasDefaultValue) const
1022
{
1023
    // look up which group; paramName serves as suffix (ie. group name)
1024
    std::map<std::string,SuffixGroup>::const_iterator it = suffixGroups.find(paramName);
1025
    const SuffixGroup *group = it==suffixGroups.end() ? &wildcardSuffixGroup : &it->second;
1026
1027
    // find first match in the group
1028
    for (int i=0; i<(int)group->entries.size(); i++)
1029
    {
1030
        const KeyValue2& entry = group->entries[i];
1031
        if (entryMatches(entry, moduleFullPath, paramName))
1032
            if (hasDefaultValue || entry.value != "default")
1033
                return entry;
1034
    }
1035
    return nullEntry; // not found
1036
}
1037
1038
bool SectionBasedConfiguration::entryMatches(const KeyValue2& entry, const char *moduleFullPath, const char *paramName)
1039
{
1040
    if (!entry.fullPathPattern)
1041
    {
1042
        // typical
1043
        return entry.ownerPattern->matches(moduleFullPath) && (entry.suffixPattern==NULL || entry.suffixPattern->matches(paramName));
1044
    }
1045
    else
1046
    {
1047
        // less efficient, but very rare
1048
        std::string paramFullPath = std::string(moduleFullPath) + "." + paramName;
1049
        return entry.fullPathPattern->matches(paramFullPath.c_str());
1050
    }
1051
}
1052
1053
std::vector<const char *> SectionBasedConfiguration::getParameterKeyValuePairs() const
1054
{
1055
    std::vector<const char *> result;
1056
    //FIXME TBD
1057
    return result;
1058
}
1059
1060
const char *SectionBasedConfiguration::getPerObjectConfigValue(const char *objectFullPath, const char *keySuffix) const
1061
{
1062
    const SectionBasedConfiguration::KeyValue2& entry = (KeyValue2&) getPerObjectConfigEntry(objectFullPath, keySuffix);
1063
    return entry.getKey()==NULL ? NULL : entry.value.c_str();
1064
}
1065
1066
const cConfiguration::KeyValue& SectionBasedConfiguration::getPerObjectConfigEntry(const char *objectFullPath, const char *keySuffix) const
1067
{
1068
    // look up which group; keySuffix serves as group name
1069
    // Note: we do not accept wildcards in the config key's name (ie. "**.record-*" is invalid),
1070
    // so we ignore the wildcard group.
1071
    std::map<std::string,SuffixGroup>::const_iterator it = suffixGroups.find(keySuffix);
1072
    if (it==suffixGroups.end())
1073
        return nullEntry; // no such group
1074
1075
    const SuffixGroup *suffixGroup = &it->second;
1076
1077
    // find first match in the group
1078
    for (int i=0; i<(int)suffixGroup->entries.size(); i++)
1079
    {
1080
        const KeyValue2& entry = suffixGroup->entries[i];
1081
        if (entryMatches(entry, objectFullPath, keySuffix))
1082
            return entry;  // found value
1083
    }
1084
    return nullEntry; // not found
1085
}
1086
1087
static const char *partAfterLastDot(const char *s)
1088
{
1089
    const char *lastDotPos = strrchr(s, '.');
1090
    return lastDotPos==NULL ? NULL : lastDotPos+1;
1091
}
1092
1093
std::vector<const char *> SectionBasedConfiguration::getMatchingPerObjectConfigKeys(const char *objectFullPathPattern, const char *keySuffixPattern) const
1094
{
1095
    std::vector<const char *> result;
1096
1097
    // only concrete objects or "**" is accepted, because we are not prepared
1098
    // to handle the "pattern matches pattern" case (see below as well).
1099
    bool anyObject = strcmp(objectFullPathPattern, "**")==0;
1100
    if (!anyObject && PatternMatcher::containsWildcards(objectFullPathPattern))
1101
        throw cRuntimeError("getMatchingPerObjectConfigKeys: invalid objectFullPath parameter: the only wildcard pattern accepted is '**'");
1102
1103
    // check all suffix groups whose name matches the pattern
1104
    PatternMatcher suffixMatcher(keySuffixPattern, true, true, true);
1105
    for (std::map<std::string,SuffixGroup>::const_iterator it = suffixGroups.begin(); it != suffixGroups.end(); ++it)
1106
    {
1107
        const char *suffix = it->first.c_str();
1108
        if (suffixMatcher.matches(suffix))
1109
        {
1110
            // find all matching entries from this suffix group.
1111
            // We'll have a little problem where key ends in wildcard (i.e. entry.suffixPattern!=NULL);
1112
            // there we'd have to determine whether two *patterns* match. We resolve this
1113
            // by checking whether one pattern matches the other one as string, and vica versa.
1114
            const SuffixGroup& group = it->second;
1115
            for (int i=0; i<(int)group.entries.size(); i++)
1116
            {
1117
                const KeyValue2& entry = group.entries[i];
1118
                if ((anyObject || entry.ownerPattern->matches(objectFullPathPattern))
1119
                    &&
1120
                    (entry.suffixPattern==NULL ||
1121
                     suffixMatcher.matches(partAfterLastDot(entry.key.c_str())) ||
1122
                     entry.suffixPattern->matches(keySuffixPattern)))
1123
                    result.push_back(entry.key.c_str());
1124
            }
1125
        }
1126
    }
1127
    return result;
1128
}
1129
1130
std::vector<const char *> SectionBasedConfiguration::getMatchingPerObjectConfigKeySuffixes(const char *objectFullPath, const char *keySuffixPattern) const
1131
{
1132
    std::vector<const char *> result = getMatchingPerObjectConfigKeys(objectFullPath, keySuffixPattern);
1133
    for (int i=0; i<(int)result.size(); i++)
1134
        result[i] = partAfterLastDot(result[i]);
1135
    return result;
1136
}
1137
1138
void SectionBasedConfiguration::dump() const
1139
{
1140
    printf("Config:\n");
1141
    for (std::map<std::string,KeyValue1>::const_iterator it = config.begin(); it!=config.end(); it++)
1142
        printf("  %s = %s\n", it->first.c_str(), it->second.value.c_str());
1143
1144
    for (std::map<std::string,SuffixGroup>::const_iterator it = suffixGroups.begin(); it!=suffixGroups.end(); it++)
1145
    {
1146
        const std::string& suffix = it->first;
1147
        const SuffixGroup& group = it->second;
1148
        printf("Suffix Group %s:\n", suffix.c_str());
1149
        for (int i=0; i<(int)group.entries.size(); i++)
1150
            printf("  %s = %s\n", group.entries[i].key.c_str(), group.entries[i].value.c_str());
1151
    }
1152
    printf("Wildcard Suffix Group:\n");
1153
    for (int i=0; i<(int)wildcardSuffixGroup.entries.size(); i++)
1154
        printf("  %s = %s\n", wildcardSuffixGroup.entries[i].key.c_str(), wildcardSuffixGroup.entries[i].value.c_str());
1155
}
1156