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