inifile.cc revision 56
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
55using namespace std;
56
57IniFile::IniFile()
58{}
59
60IniFile::~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
73bool
74IniFile::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
164bool
165IniFile::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
176const string &
177IniFile::Entry::getValue() const
178{
179    referenced = true;
180    return value;
181}
182
183
184void
185IniFile::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
201IniFile::Entry *
202IniFile::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
212IniFile::Section *
213IniFile::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
229IniFile::Section *
230IniFile::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.
240bool
241IniFile::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
268bool
269IniFile::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
310bool
311IniFile::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
327bool
328IniFile::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
341bool
342IniFile::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
385bool
386IniFile::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 0
404            if (section->findEntry("unref_entries_ok") == NULL) {
405                bool unrefEntries = section->printUnreferenced(sectionName);
406                unref = unref || unrefEntries;
407            }
408#else
409            if (section->printUnreferenced(sectionName)) {
410                unref = true;
411            }
412#endif
413        }
414    }
415
416    return unref;
417}
418
419
420void
421IniFile::Section::dump(const string &sectionName)
422{
423    for (EntryTable::iterator ei = table.begin();
424         ei != table.end(); ++ei) {
425        cout << sectionName << ": " << (*ei).first << " => "
426             << (*ei).second << "\n";
427    }
428}
429
430void
431IniFile::dump()
432{
433    for (ConfigTable::iterator ci = table.begin();
434         ci != table.end(); ++ci) {
435        ci->second->dump(ci->first);
436    }
437}
438