inifile.cc revision 304
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    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
190bool
191IniFile::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
202const string &
203IniFile::Entry::getValue() const
204{
205    referenced = true;
206    return value;
207}
208
209
210void
211IniFile::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
232bool
233IniFile::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
256IniFile::Entry *
257IniFile::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
267IniFile::Section *
268IniFile::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
284IniFile::Section *
285IniFile::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.
295bool
296IniFile::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
312bool
313IniFile::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
348bool
349IniFile::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
365bool
366IniFile::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
379bool
380IniFile::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
406bool
407IniFile::sectionExists(const string &sectionName) const
408{
409    return findSection(sectionName) != NULL;
410}
411
412
413bool
414IniFile::Section::printUnreferenced(const string &sectionName)
415{
416    bool unref = false;
417    bool search_unref_entries = false;
418    vector<string> unref_ok_entries;
419
420    Entry *entry = findEntry("unref_entries_ok");
421    if (entry != NULL) {
422        tokenize(unref_ok_entries, entry->getValue(), ' ');
423        if (unref_ok_entries.size()) {
424            search_unref_entries = true;
425        }
426    }
427
428    for (EntryTable::iterator ei = table.begin();
429         ei != table.end(); ++ei) {
430        const string &entryName = ei->first;
431        Entry *entry = ei->second;
432
433        if (entryName == "unref_section_ok" ||
434            entryName == "unref_entries_ok")
435        {
436            continue;
437        }
438
439        if (!entry->isReferenced()) {
440            if (search_unref_entries &&
441                (std::find(unref_ok_entries.begin(), unref_ok_entries.end(),
442                           entryName) != unref_ok_entries.end()))
443            {
444                continue;
445            }
446
447            cerr << "Parameter " << sectionName << ":" << entryName
448                 << " not referenced." << endl;
449            unref = true;
450        }
451    }
452
453    return unref;
454}
455
456
457bool
458IniFile::printUnreferenced()
459{
460    bool unref = false;
461
462    for (SectionTable::iterator i = table.begin();
463         i != table.end(); ++i) {
464        const string &sectionName = i->first;
465        Section *section = i->second;
466
467        if (!section->isReferenced()) {
468            if (section->findEntry("unref_section_ok") == NULL) {
469                cerr << "Section " << sectionName << " not referenced."
470                     << endl;
471                unref = true;
472            }
473        }
474        else {
475            if (section->printUnreferenced(sectionName)) {
476                unref = true;
477            }
478        }
479    }
480
481    return unref;
482}
483
484
485void
486IniFile::Section::dump(const string &sectionName)
487{
488    for (EntryTable::iterator ei = table.begin();
489         ei != table.end(); ++ei) {
490        cout << sectionName << ": " << (*ei).first << " => "
491             << (*ei).second->getValue() << "\n";
492    }
493}
494
495void
496IniFile::dump()
497{
498    for (SectionTable::iterator i = table.begin();
499         i != table.end(); ++i) {
500        i->second->dump(i->first);
501    }
502}
503