Get rid of lots of obsolete mentions of stat_sdb_t.
[gem5.git] / base / inifile.cc
1 /*
2 * Copyright (c) 2003 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #define USE_CPP
30 // #define CPP_PIPE
31
32
33 #ifdef USE_CPP
34 #include <sys/signal.h>
35 #include <sys/types.h>
36 #include <sys/wait.h>
37
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #endif
42
43 #include <fstream>
44 #include <iostream>
45 #if __GNUC__ >= 3
46 #include <ext/stdio_filebuf.h>
47 #endif
48
49 #include <vector>
50 #include <string>
51
52 #include "base/inifile.hh"
53 #include "base/str.hh"
54
55 using namespace std;
56
57 IniFile::IniFile()
58 {}
59
60 IniFile::~IniFile()
61 {
62 SectionTable::iterator i = table.begin();
63 SectionTable::iterator end = table.end();
64
65 while (i != end) {
66 delete (*i).second;
67 ++i;
68 }
69 }
70
71
72 #ifdef USE_CPP
73 bool
74 IniFile::loadCPP(const string &file, vector<char *> &cppArgs)
75 {
76 int fd[2];
77
78 // Open the file just to verify that we can. Otherwise if the
79 // file doesn't exist or has bad permissions the user will get
80 // confusing errors from cpp/g++.
81 ifstream tmpf(file.c_str());
82
83 if (!tmpf.is_open())
84 return false;
85
86 tmpf.close();
87
88 const char *cfile = file.c_str();
89 char *dir = basename(cfile);
90 char *dir_arg = NULL;
91 if (*dir != '.' && dir != cfile) {
92 string arg = "-I";
93 arg += dir;
94
95 dir_arg = new char[arg.size() + 1];
96 strcpy(dir_arg, arg.c_str());
97 }
98
99 #ifdef CPP_PIPE
100 if (pipe(fd) == -1)
101 return false;
102 #else
103 char tempfile[] = "/tmp/configXXXXXX";
104 fd[0] = fd[1] = mkstemp(tempfile);
105 #endif
106
107 int pid = fork();
108
109 if (pid == -1)
110 return 1;
111
112 if (pid == 0) {
113 char filename[FILENAME_MAX];
114 string::size_type i = file.copy(filename, sizeof(filename) - 1);
115 filename[i] = '\0';
116
117 int arg_count = cppArgs.size();
118
119 char **args = new char *[arg_count + 20];
120
121 int nextArg = 0;
122 args[nextArg++] = "g++";
123 args[nextArg++] = "-E";
124 args[nextArg++] = "-P";
125 args[nextArg++] = "-nostdinc";
126 args[nextArg++] = "-nostdinc++";
127 args[nextArg++] = "-x";
128 args[nextArg++] = "c++";
129 args[nextArg++] = "-undef";
130
131 for (int i = 0; i < arg_count; i++)
132 args[nextArg++] = cppArgs[i];
133
134 if (dir_arg)
135 args[nextArg++] = dir_arg;
136
137 args[nextArg++] = filename;
138 args[nextArg++] = NULL;
139
140 close(STDOUT_FILENO);
141 if (dup2(fd[1], STDOUT_FILENO) == -1)
142 return 1;
143
144 execvp("g++", args);
145
146 exit(1);
147 }
148
149 int retval;
150 waitpid(pid, &retval, 0);
151
152 delete [] dir_arg;
153
154 // check for normal completion of CPP
155 if (!WIFEXITED(retval) || WEXITSTATUS(retval) != 0)
156 return false;
157
158 #ifdef CPP_PIPE
159 close(fd[1]);
160 #else
161 lseek(fd[0], 0, SEEK_SET);
162 #endif
163
164 bool status = false;
165
166 #if __GNUC__ >= 3
167 using namespace __gnu_cxx;
168 stdio_filebuf<char> fbuf(fd[0], ios_base::in, true,
169 static_cast<stdio_filebuf<char>::int_type>(BUFSIZ));
170
171 if (fbuf.is_open()) {
172 istream f(&fbuf);
173 status = load(f);
174 }
175
176 #else
177 ifstream f(fd[0]);
178 if (f.is_open())
179 status = load(f);
180 #endif
181
182 #ifndef CPP_PIPE
183 unlink(tempfile);
184 #endif
185
186 return status;
187 }
188 #endif
189
190 bool
191 IniFile::load(const string &file)
192 {
193 ifstream f(file.c_str());
194
195 if (!f.is_open())
196 return false;
197
198 return load(f);
199 }
200
201
202 const string &
203 IniFile::Entry::getValue() const
204 {
205 referenced = true;
206 return value;
207 }
208
209
210 void
211 IniFile::Section::addEntry(const std::string &entryName,
212 const std::string &value,
213 bool append)
214 {
215 EntryTable::iterator ei = table.find(entryName);
216
217 if (ei == table.end()) {
218 // new entry
219 table[entryName] = new Entry(value);
220 }
221 else if (append) {
222 // append new reult to old entry
223 ei->second->appendValue(value);
224 }
225 else {
226 // override old entry
227 ei->second->setValue(value);
228 }
229 }
230
231
232 bool
233 IniFile::Section::add(const std::string &assignment)
234 {
235 string::size_type offset = assignment.find('=');
236 if (offset == string::npos) {
237 // no '=' found
238 cerr << "Can't parse .ini line " << assignment << endl;
239 return false;
240 }
241
242 // if "+=" rather than just "=" then append value
243 bool append = (assignment[offset-1] == '+');
244
245 string entryName = assignment.substr(0, append ? offset-1 : offset);
246 string value = assignment.substr(offset + 1);
247
248 eat_white(entryName);
249 eat_white(value);
250
251 addEntry(entryName, value, append);
252 return true;
253 }
254
255
256 IniFile::Entry *
257 IniFile::Section::findEntry(const std::string &entryName) const
258 {
259 referenced = true;
260
261 EntryTable::const_iterator ei = table.find(entryName);
262
263 return (ei == table.end()) ? NULL : ei->second;
264 }
265
266
267 IniFile::Section *
268 IniFile::addSection(const string &sectionName)
269 {
270 SectionTable::iterator i = table.find(sectionName);
271
272 if (i != table.end()) {
273 return i->second;
274 }
275 else {
276 // new entry
277 Section *sec = new Section();
278 table[sectionName] = sec;
279 return sec;
280 }
281 }
282
283
284 IniFile::Section *
285 IniFile::findSection(const string &sectionName) const
286 {
287 SectionTable::const_iterator i = table.find(sectionName);
288
289 return (i == table.end()) ? NULL : i->second;
290 }
291
292
293 // Take string of the form "<section>:<parameter>=<value>" and add to
294 // database. Return true if successful, false if parse error.
295 bool
296 IniFile::add(const string &str)
297 {
298 // find ':'
299 string::size_type offset = str.find(':');
300 if (offset == string::npos) // no ':' found
301 return false;
302
303 string sectionName = str.substr(0, offset);
304 string rest = str.substr(offset + 1);
305
306 eat_white(sectionName);
307 Section *s = addSection(sectionName);
308
309 return s->add(rest);
310 }
311
312 bool
313 IniFile::load(istream &f)
314 {
315 Section *section = NULL;
316
317 while (!f.eof()) {
318 f >> ws; // Eat whitespace
319 if (f.eof()) {
320 break;
321 }
322
323 string line;
324 getline(f, line);
325 if (line.size() == 0)
326 continue;
327
328 eat_end_white(line);
329 int last = line.size() - 1;
330
331 if (line[0] == '[' && line[last] == ']') {
332 string sectionName = line.substr(1, last - 1);
333 eat_white(sectionName);
334 section = addSection(sectionName);
335 continue;
336 }
337
338 if (section == NULL)
339 continue;
340
341 if (!section->add(line))
342 return false;
343 }
344
345 return true;
346 }
347
348 bool
349 IniFile::find(const string &sectionName, const string &entryName,
350 string &value) const
351 {
352 Section *section = findSection(sectionName);
353 if (section == NULL)
354 return false;
355
356 Entry *entry = section->findEntry(entryName);
357 if (entry == NULL)
358 return false;
359
360 value = entry->getValue();
361
362 return true;
363 }
364
365 bool
366 IniFile::findDefault(const string &_section, const string &entry,
367 string &value) const
368 {
369 string section = _section;
370 while (!findAppend(section, entry, value)) {
371 if (!find(section, "default", section)) {
372 return false;
373 }
374 }
375
376 return true;
377 }
378
379 bool
380 IniFile::findAppend(const string &_section, const string &entry,
381 string &value) const
382 {
383 string section = _section;
384 bool ret = false;
385 bool first = true;
386
387 do {
388 string val;
389 if (find(section, entry, val)) {
390 ret = true;
391 if (first) {
392 value = val;
393 first = false;
394 } else {
395 value += " ";
396 value += val;
397 }
398
399 }
400 } while (find(section, "append", section));
401
402 return ret;
403 }
404
405 bool
406 IniFile::Section::printUnreferenced(const string &sectionName)
407 {
408 bool unref = false;
409 bool search_unref_entries = false;
410 vector<string> unref_ok_entries;
411
412 Entry *entry = findEntry("unref_entries_ok");
413 if (entry != NULL) {
414 tokenize(unref_ok_entries, entry->getValue(), ' ');
415 if (unref_ok_entries.size()) {
416 search_unref_entries = true;
417 }
418 }
419
420 for (EntryTable::iterator ei = table.begin();
421 ei != table.end(); ++ei) {
422 const string &entryName = ei->first;
423 Entry *entry = ei->second;
424
425 if (entryName == "unref_section_ok" ||
426 entryName == "unref_entries_ok")
427 {
428 continue;
429 }
430
431 if (!entry->isReferenced()) {
432 if (search_unref_entries &&
433 (std::find(unref_ok_entries.begin(), unref_ok_entries.end(),
434 entryName) != unref_ok_entries.end()))
435 {
436 continue;
437 }
438
439 cerr << "Parameter " << sectionName << ":" << entryName
440 << " not referenced." << endl;
441 unref = true;
442 }
443 }
444
445 return unref;
446 }
447
448
449 bool
450 IniFile::printUnreferenced()
451 {
452 bool unref = false;
453
454 for (SectionTable::iterator i = table.begin();
455 i != table.end(); ++i) {
456 const string &sectionName = i->first;
457 Section *section = i->second;
458
459 if (!section->isReferenced()) {
460 if (section->findEntry("unref_section_ok") == NULL) {
461 cerr << "Section " << sectionName << " not referenced."
462 << endl;
463 unref = true;
464 }
465 }
466 else {
467 if (section->printUnreferenced(sectionName)) {
468 unref = true;
469 }
470 }
471 }
472
473 return unref;
474 }
475
476
477 void
478 IniFile::Section::dump(const string &sectionName)
479 {
480 for (EntryTable::iterator ei = table.begin();
481 ei != table.end(); ++ei) {
482 cout << sectionName << ": " << (*ei).first << " => "
483 << (*ei).second->getValue() << "\n";
484 }
485 }
486
487 void
488 IniFile::dump()
489 {
490 for (SectionTable::iterator i = table.begin();
491 i != table.end(); ++i) {
492 i->second->dump(i->first);
493 }
494 }