few more stat items to serialize
[gem5.git] / base / inifile.cc
1 /*
2 * Copyright (c) 2001-2004 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
31 #ifdef USE_CPP
32 #include <sys/signal.h>
33 #include <sys/types.h>
34 #include <sys/wait.h>
35
36 #include <libgen.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #endif
41
42 #include <fstream>
43 #include <iostream>
44
45 #include <vector>
46 #include <string>
47
48 #include "base/inifile.hh"
49 #include "base/str.hh"
50
51 using namespace std;
52
53 IniFile::IniFile()
54 {}
55
56 IniFile::~IniFile()
57 {
58 SectionTable::iterator i = table.begin();
59 SectionTable::iterator end = table.end();
60
61 while (i != end) {
62 delete (*i).second;
63 ++i;
64 }
65 }
66
67
68 #ifdef USE_CPP
69 bool
70 IniFile::loadCPP(const string &file, vector<char *> &cppArgs)
71 {
72 // Open the file just to verify that we can. Otherwise if the
73 // file doesn't exist or has bad permissions the user will get
74 // confusing errors from cpp/g++.
75 ifstream tmpf(file.c_str());
76
77 if (!tmpf.is_open())
78 return false;
79
80 tmpf.close();
81
82 char *cfile = strcpy(new char[file.size() + 1], file.c_str());
83 char *dir = dirname(cfile);
84 char *dir_arg = NULL;
85 if (*dir != '.') {
86 string arg = "-I";
87 arg += dir;
88
89 dir_arg = new char[arg.size() + 1];
90 strcpy(dir_arg, arg.c_str());
91 }
92
93 delete [] cfile;
94
95 char tempfile[] = "/tmp/configXXXXXX";
96 int tmp_fd = mkstemp(tempfile);
97
98 int pid = fork();
99
100 if (pid == -1)
101 return false;
102
103 if (pid == 0) {
104 char filename[FILENAME_MAX];
105 string::size_type i = file.copy(filename, sizeof(filename) - 1);
106 filename[i] = '\0';
107
108 int arg_count = cppArgs.size();
109
110 char **args = new char *[arg_count + 20];
111
112 int nextArg = 0;
113 args[nextArg++] = "g++";
114 args[nextArg++] = "-E";
115 args[nextArg++] = "-P";
116 args[nextArg++] = "-nostdinc";
117 args[nextArg++] = "-nostdinc++";
118 args[nextArg++] = "-x";
119 args[nextArg++] = "c++";
120 args[nextArg++] = "-undef";
121
122 for (int i = 0; i < arg_count; i++)
123 args[nextArg++] = cppArgs[i];
124
125 if (dir_arg)
126 args[nextArg++] = dir_arg;
127
128 args[nextArg++] = filename;
129 args[nextArg++] = NULL;
130
131 close(STDOUT_FILENO);
132 if (dup2(tmp_fd, STDOUT_FILENO) == -1)
133 exit(1);
134
135 execvp("g++", args);
136
137 exit(0);
138 }
139
140 int retval;
141 waitpid(pid, &retval, 0);
142
143 delete [] dir_arg;
144
145 // check for normal completion of CPP
146 if (!WIFEXITED(retval) || WEXITSTATUS(retval) != 0)
147 return false;
148
149 close(tmp_fd);
150
151 bool status = false;
152
153 status = load(tempfile);
154
155 unlink(tempfile);
156
157 return status;
158 }
159 #endif
160
161 bool
162 IniFile::load(const string &file)
163 {
164 ifstream f(file.c_str());
165
166 if (!f.is_open())
167 return false;
168
169 return load(f);
170 }
171
172
173 const string &
174 IniFile::Entry::getValue() const
175 {
176 referenced = true;
177 return value;
178 }
179
180
181 void
182 IniFile::Section::addEntry(const std::string &entryName,
183 const std::string &value,
184 bool append)
185 {
186 EntryTable::iterator ei = table.find(entryName);
187
188 if (ei == table.end()) {
189 // new entry
190 table[entryName] = new Entry(value);
191 }
192 else if (append) {
193 // append new reult to old entry
194 ei->second->appendValue(value);
195 }
196 else {
197 // override old entry
198 ei->second->setValue(value);
199 }
200 }
201
202
203 bool
204 IniFile::Section::add(const std::string &assignment)
205 {
206 string::size_type offset = assignment.find('=');
207 if (offset == string::npos) {
208 // no '=' found
209 cerr << "Can't parse .ini line " << assignment << endl;
210 return false;
211 }
212
213 // if "+=" rather than just "=" then append value
214 bool append = (assignment[offset-1] == '+');
215
216 string entryName = assignment.substr(0, append ? offset-1 : offset);
217 string value = assignment.substr(offset + 1);
218
219 eat_white(entryName);
220 eat_white(value);
221
222 addEntry(entryName, value, append);
223 return true;
224 }
225
226
227 IniFile::Entry *
228 IniFile::Section::findEntry(const std::string &entryName) const
229 {
230 referenced = true;
231
232 EntryTable::const_iterator ei = table.find(entryName);
233
234 return (ei == table.end()) ? NULL : ei->second;
235 }
236
237
238 IniFile::Section *
239 IniFile::addSection(const string &sectionName)
240 {
241 SectionTable::iterator i = table.find(sectionName);
242
243 if (i != table.end()) {
244 return i->second;
245 }
246 else {
247 // new entry
248 Section *sec = new Section();
249 table[sectionName] = sec;
250 return sec;
251 }
252 }
253
254
255 IniFile::Section *
256 IniFile::findSection(const string &sectionName) const
257 {
258 SectionTable::const_iterator i = table.find(sectionName);
259
260 return (i == table.end()) ? NULL : i->second;
261 }
262
263
264 // Take string of the form "<section>:<parameter>=<value>" and add to
265 // database. Return true if successful, false if parse error.
266 bool
267 IniFile::add(const string &str)
268 {
269 // find ':'
270 string::size_type offset = str.find(':');
271 if (offset == string::npos) // no ':' found
272 return false;
273
274 string sectionName = str.substr(0, offset);
275 string rest = str.substr(offset + 1);
276
277 eat_white(sectionName);
278 Section *s = addSection(sectionName);
279
280 return s->add(rest);
281 }
282
283 bool
284 IniFile::load(istream &f)
285 {
286 Section *section = NULL;
287
288 while (!f.eof()) {
289 f >> ws; // Eat whitespace
290 if (f.eof()) {
291 break;
292 }
293
294 string line;
295 getline(f, line);
296 if (line.size() == 0)
297 continue;
298
299 eat_end_white(line);
300 int last = line.size() - 1;
301
302 if (line[0] == '[' && line[last] == ']') {
303 string sectionName = line.substr(1, last - 1);
304 eat_white(sectionName);
305 section = addSection(sectionName);
306 continue;
307 }
308
309 if (section == NULL)
310 continue;
311
312 if (!section->add(line))
313 return false;
314 }
315
316 return true;
317 }
318
319 bool
320 IniFile::find(const string &sectionName, const string &entryName,
321 string &value) const
322 {
323 Section *section = findSection(sectionName);
324 if (section == NULL)
325 return false;
326
327 Entry *entry = section->findEntry(entryName);
328 if (entry == NULL)
329 return false;
330
331 value = entry->getValue();
332
333 return true;
334 }
335
336 bool
337 IniFile::findDefault(const string &_section, const string &entry,
338 string &value) const
339 {
340 string section = _section;
341 while (!findAppend(section, entry, value)) {
342 if (!find(section, "default", section)) {
343 return false;
344 }
345 }
346
347 return true;
348 }
349
350 bool
351 IniFile::findAppend(const string &_section, const string &entry,
352 string &value) const
353 {
354 string section = _section;
355 bool ret = false;
356 bool first = true;
357
358 do {
359 string val;
360 if (find(section, entry, val)) {
361 ret = true;
362 if (first) {
363 value = val;
364 first = false;
365 } else {
366 value += " ";
367 value += val;
368 }
369
370 }
371 } while (find(section, "append", section));
372
373 return ret;
374 }
375
376
377 bool
378 IniFile::sectionExists(const string &sectionName) const
379 {
380 return findSection(sectionName) != NULL;
381 }
382
383
384 bool
385 IniFile::Section::printUnreferenced(const string &sectionName)
386 {
387 bool unref = false;
388 bool search_unref_entries = false;
389 vector<string> unref_ok_entries;
390
391 Entry *entry = findEntry("unref_entries_ok");
392 if (entry != NULL) {
393 tokenize(unref_ok_entries, entry->getValue(), ' ');
394 if (unref_ok_entries.size()) {
395 search_unref_entries = true;
396 }
397 }
398
399 for (EntryTable::iterator ei = table.begin();
400 ei != table.end(); ++ei) {
401 const string &entryName = ei->first;
402 Entry *entry = ei->second;
403
404 if (entryName == "unref_section_ok" ||
405 entryName == "unref_entries_ok")
406 {
407 continue;
408 }
409
410 if (!entry->isReferenced()) {
411 if (search_unref_entries &&
412 (std::find(unref_ok_entries.begin(), unref_ok_entries.end(),
413 entryName) != unref_ok_entries.end()))
414 {
415 continue;
416 }
417
418 cerr << "Parameter " << sectionName << ":" << entryName
419 << " not referenced." << endl;
420 unref = true;
421 }
422 }
423
424 return unref;
425 }
426
427
428 bool
429 IniFile::printUnreferenced()
430 {
431 bool unref = false;
432
433 for (SectionTable::iterator i = table.begin();
434 i != table.end(); ++i) {
435 const string &sectionName = i->first;
436 Section *section = i->second;
437
438 if (!section->isReferenced()) {
439 if (section->findEntry("unref_section_ok") == NULL) {
440 cerr << "Section " << sectionName << " not referenced."
441 << endl;
442 unref = true;
443 }
444 }
445 else {
446 if (section->printUnreferenced(sectionName)) {
447 unref = true;
448 }
449 }
450 }
451
452 return unref;
453 }
454
455
456 void
457 IniFile::Section::dump(const string &sectionName)
458 {
459 for (EntryTable::iterator ei = table.begin();
460 ei != table.end(); ++ei) {
461 cout << sectionName << ": " << (*ei).first << " => "
462 << (*ei).second->getValue() << "\n";
463 }
464 }
465
466 void
467 IniFile::dump()
468 {
469 for (SectionTable::iterator i = table.begin();
470 i != table.end(); ++i) {
471 i->second->dump(i->first);
472 }
473 }