Merge zizzer:/bk/m5 into zower.eecs.umich.edu:/z/hsul/bk/clean
[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 ConfigTable::iterator i = table.begin();
63 ConfigTable::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 #ifdef CPP_PIPE
79 if (pipe(fd) == -1)
80 return false;
81 #else
82 char tempfile[] = "/tmp/configXXXXXX";
83 fd[0] = fd[1] = mkstemp(tempfile);
84 #endif
85
86 int pid = fork();
87
88 if (pid == -1)
89 return 1;
90
91 if (pid == 0) {
92 char filename[FILENAME_MAX];
93 string::size_type i = file.copy(filename, sizeof(filename) - 1);
94 filename[i] = '\0';
95
96 int arg_count = cppArgs.size();
97
98 char **args = new char *[arg_count + 20];
99
100 int nextArg = 0;
101 args[nextArg++] = "g++";
102 args[nextArg++] = "-E";
103 args[nextArg++] = "-P";
104 args[nextArg++] = "-nostdinc";
105 args[nextArg++] = "-nostdinc++";
106 args[nextArg++] = "-x";
107 args[nextArg++] = "c++";
108 args[nextArg++] = "-undef";
109
110 for (int i = 0; i < arg_count; i++)
111 args[nextArg++] = cppArgs[i];
112
113 args[nextArg++] = filename;
114 args[nextArg++] = NULL;
115
116 close(STDOUT_FILENO);
117 if (dup2(fd[1], STDOUT_FILENO) == -1)
118 return 1;
119
120 execvp("g++", args);
121
122 exit(1);
123 }
124
125 int retval;
126 waitpid(pid, &retval, 0);
127
128 // check for normal completion of CPP
129 if (!WIFEXITED(retval) || WEXITSTATUS(retval) != 0)
130 return false;
131
132 #ifdef CPP_PIPE
133 close(fd[1]);
134 #else
135 lseek(fd[0], 0, SEEK_SET);
136 #endif
137
138 bool status = false;
139
140 #if __GNUC__ >= 3
141 using namespace __gnu_cxx;
142 stdio_filebuf<char> fbuf(fd[0], ios_base::in, true,
143 static_cast<stdio_filebuf<char>::int_type>(BUFSIZ));
144
145 if (fbuf.is_open()) {
146 istream f(&fbuf);
147 status = load(f);
148 }
149
150 #else
151 ifstream f(fd[0]);
152 if (f.is_open())
153 status = load(f);
154 #endif
155
156 #ifndef CPP_PIPE
157 unlink(tempfile);
158 #endif
159
160 return status;
161 }
162 #endif
163
164 bool
165 IniFile::load(const string &file)
166 {
167 ifstream f(file.c_str());
168
169 if (!f.is_open())
170 return false;
171
172 return load(f);
173 }
174
175
176 const string &
177 IniFile::Entry::getValue() const
178 {
179 referenced = true;
180 return value;
181 }
182
183
184 void
185 IniFile::Section::addEntry(const std::string &entryName,
186 const std::string &value)
187 {
188 EntryTable::iterator ei = table.find(entryName);
189
190 if (ei == table.end()) {
191 // new entry
192 table[entryName] = new Entry(value);
193 }
194 else {
195 // override old entry
196 ei->second->setValue(value);
197 }
198 }
199
200
201 IniFile::Entry *
202 IniFile::Section::findEntry(const std::string &entryName) const
203 {
204 referenced = true;
205
206 EntryTable::const_iterator ei = table.find(entryName);
207
208 return (ei == table.end()) ? NULL : ei->second;
209 }
210
211
212 IniFile::Section *
213 IniFile::addSection(const string &sectionName)
214 {
215 ConfigTable::iterator ci = table.find(sectionName);
216
217 if (ci != table.end()) {
218 return ci->second;
219 }
220 else {
221 // new entry
222 Section *sec = new Section();
223 table[sectionName] = sec;
224 return sec;
225 }
226 }
227
228
229 IniFile::Section *
230 IniFile::findSection(const string &sectionName) const
231 {
232 ConfigTable::const_iterator ci = table.find(sectionName);
233
234 return (ci == table.end()) ? NULL : ci->second;
235 }
236
237
238 // Take string of the form "<section>:<parameter>=<value>" and add to
239 // database. Return true if successful, false if parse error.
240 bool
241 IniFile::add(const string &str)
242 {
243 // find ':'
244 string::size_type offset = str.find(':');
245 if (offset == string::npos) // no ':' found
246 return false;
247
248 string sectionName = str.substr(0, offset);
249 string rest = str.substr(offset + 1);
250
251 offset = rest.find('=');
252 if (offset == string::npos) // no '='found
253 return false;
254
255 string entryName = rest.substr(0, offset);
256 string value = rest.substr(offset + 1);
257
258 eat_white(sectionName);
259 eat_white(entryName);
260 eat_white(value);
261
262 Section *s = addSection(sectionName);
263 s->addEntry(entryName, value);
264
265 return true;
266 }
267
268 bool
269 IniFile::load(istream &f)
270 {
271 Section *section = NULL;
272
273 while (!f.eof()) {
274 f >> ws; // Eat whitespace
275 if (f.eof()) {
276 break;
277 }
278
279 string line;
280 getline(f, line);
281 if (line.size() == 0)
282 continue;
283
284 eat_end_white(line);
285 int last = line.size() - 1;
286
287 if (line[0] == '[' && line[last] == ']') {
288 string sectionName = line.substr(1, last - 1);
289 eat_white(sectionName);
290 section = addSection(sectionName);
291 continue;
292 }
293
294 if (section == NULL)
295 continue;
296
297 string::size_type offset = line.find('=');
298 string entryName = line.substr(0, offset);
299 string value = line.substr(offset + 1);
300
301 eat_white(entryName);
302 eat_white(value);
303
304 section->addEntry(entryName, value);
305 }
306
307 return true;
308 }
309
310 bool
311 IniFile::find(const string &sectionName, const string &entryName,
312 string &value) const
313 {
314 Section *section = findSection(sectionName);
315 if (section == NULL)
316 return false;
317
318 Entry *entry = section->findEntry(entryName);
319 if (entry == NULL)
320 return false;
321
322 value = entry->getValue();
323
324 return true;
325 }
326
327 bool
328 IniFile::findDefault(const string &_section, const string &entry,
329 string &value) const
330 {
331 string section = _section;
332 while (!find(section, entry, value)) {
333 if (!find(section, "default", section))
334 return false;
335 }
336
337 return true;
338 }
339
340
341 bool
342 IniFile::Section::printUnreferenced(const string &sectionName)
343 {
344 bool unref = false;
345 bool search_unref_entries = false;
346 vector<string> unref_ok_entries;
347
348 Entry *entry = findEntry("unref_entries_ok");
349 if (entry != NULL) {
350 tokenize(unref_ok_entries, entry->getValue(), ' ');
351 if (unref_ok_entries.size()) {
352 search_unref_entries = true;
353 }
354 }
355
356 for (EntryTable::iterator ei = table.begin();
357 ei != table.end(); ++ei) {
358 const string &entryName = ei->first;
359 Entry *entry = ei->second;
360
361 if (entryName == "unref_section_ok" ||
362 entryName == "unref_entries_ok")
363 {
364 continue;
365 }
366
367 if (!entry->isReferenced()) {
368 if (search_unref_entries &&
369 (std::find(unref_ok_entries.begin(), unref_ok_entries.end(),
370 entryName) != unref_ok_entries.end()))
371 {
372 continue;
373 }
374
375 cerr << "Parameter " << sectionName << ":" << entryName
376 << " not referenced." << endl;
377 unref = true;
378 }
379 }
380
381 return unref;
382 }
383
384
385 bool
386 IniFile::printUnreferenced()
387 {
388 bool unref = false;
389
390 for (ConfigTable::iterator ci = table.begin();
391 ci != table.end(); ++ci) {
392 const string &sectionName = ci->first;
393 Section *section = ci->second;
394
395 if (!section->isReferenced()) {
396 if (section->findEntry("unref_section_ok") == NULL) {
397 cerr << "Section " << sectionName << " not referenced."
398 << endl;
399 unref = true;
400 }
401 }
402 else {
403 if (section->printUnreferenced(sectionName)) {
404 unref = true;
405 }
406 }
407 }
408
409 return unref;
410 }
411
412
413 void
414 IniFile::Section::dump(const string &sectionName)
415 {
416 for (EntryTable::iterator ei = table.begin();
417 ei != table.end(); ++ei) {
418 cout << sectionName << ": " << (*ei).first << " => "
419 << (*ei).second << "\n";
420 }
421 }
422
423 void
424 IniFile::dump()
425 {
426 for (ConfigTable::iterator ci = table.begin();
427 ci != table.end(); ++ci) {
428 ci->second->dump(ci->first);
429 }
430 }