inifile.cc revision 2632:1bb2f91485ea
17119Sgblack@eecs.umich.edu/*
27119Sgblack@eecs.umich.edu * Copyright (c) 2001-2005 The Regents of The University of Michigan
37119Sgblack@eecs.umich.edu * All rights reserved.
47119Sgblack@eecs.umich.edu *
57119Sgblack@eecs.umich.edu * Redistribution and use in source and binary forms, with or without
67119Sgblack@eecs.umich.edu * modification, are permitted provided that the following conditions are
77119Sgblack@eecs.umich.edu * met: redistributions of source code must retain the above copyright
87119Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer;
97119Sgblack@eecs.umich.edu * redistributions in binary form must reproduce the above copyright
107119Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer in the
117119Sgblack@eecs.umich.edu * documentation and/or other materials provided with the distribution;
127119Sgblack@eecs.umich.edu * neither the name of the copyright holders nor the names of its
137119Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from
147119Sgblack@eecs.umich.edu * this software without specific prior written permission.
157119Sgblack@eecs.umich.edu *
167119Sgblack@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
177119Sgblack@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
187119Sgblack@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
197119Sgblack@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
207119Sgblack@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
217119Sgblack@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
227119Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
237119Sgblack@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
247119Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
257119Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
267119Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
277119Sgblack@eecs.umich.edu */
287119Sgblack@eecs.umich.edu
297119Sgblack@eecs.umich.edu#define USE_CPP
307119Sgblack@eecs.umich.edu
317119Sgblack@eecs.umich.edu#ifdef USE_CPP
327119Sgblack@eecs.umich.edu#include <sys/signal.h>
337119Sgblack@eecs.umich.edu#include <sys/types.h>
347119Sgblack@eecs.umich.edu#include <sys/wait.h>
357119Sgblack@eecs.umich.edu
367119Sgblack@eecs.umich.edu#include <libgen.h>
377119Sgblack@eecs.umich.edu#include <stdio.h>
387119Sgblack@eecs.umich.edu#include <stdlib.h>
397119Sgblack@eecs.umich.edu#include <unistd.h>
407119Sgblack@eecs.umich.edu#endif
417119Sgblack@eecs.umich.edu
427119Sgblack@eecs.umich.edu#include <fstream>
437119Sgblack@eecs.umich.edu#include <iostream>
447119Sgblack@eecs.umich.edu
457119Sgblack@eecs.umich.edu#include <vector>
467119Sgblack@eecs.umich.edu#include <string>
477119Sgblack@eecs.umich.edu
487119Sgblack@eecs.umich.edu#include "base/inifile.hh"
497119Sgblack@eecs.umich.edu#include "base/str.hh"
507119Sgblack@eecs.umich.edu
517119Sgblack@eecs.umich.eduusing namespace std;
527119Sgblack@eecs.umich.edu
537119Sgblack@eecs.umich.eduIniFile::IniFile()
547119Sgblack@eecs.umich.edu{}
557119Sgblack@eecs.umich.edu
567128Sgblack@eecs.umich.eduIniFile::~IniFile()
577128Sgblack@eecs.umich.edu{
587128Sgblack@eecs.umich.edu    SectionTable::iterator i = table.begin();
597128Sgblack@eecs.umich.edu    SectionTable::iterator end = table.end();
607128Sgblack@eecs.umich.edu
617128Sgblack@eecs.umich.edu    while (i != end) {
627119Sgblack@eecs.umich.edu        delete (*i).second;
637119Sgblack@eecs.umich.edu        ++i;
647119Sgblack@eecs.umich.edu    }
657119Sgblack@eecs.umich.edu}
667119Sgblack@eecs.umich.edu
677119Sgblack@eecs.umich.edu
687119Sgblack@eecs.umich.edu#ifdef USE_CPP
697119Sgblack@eecs.umich.edubool
707119Sgblack@eecs.umich.eduIniFile::loadCPP(const string &file, vector<char *> &cppArgs)
717119Sgblack@eecs.umich.edu{
727119Sgblack@eecs.umich.edu    // Open the file just to verify that we can.  Otherwise if the
737119Sgblack@eecs.umich.edu    // file doesn't exist or has bad permissions the user will get
747119Sgblack@eecs.umich.edu    // confusing errors from cpp/g++.
757119Sgblack@eecs.umich.edu    ifstream tmpf(file.c_str());
767119Sgblack@eecs.umich.edu
777119Sgblack@eecs.umich.edu    if (!tmpf.is_open())
787119Sgblack@eecs.umich.edu        return false;
797119Sgblack@eecs.umich.edu
807119Sgblack@eecs.umich.edu    tmpf.close();
817119Sgblack@eecs.umich.edu
827119Sgblack@eecs.umich.edu    char *cfile = strncpy(new char[file.size() + 1], file.c_str(),
837119Sgblack@eecs.umich.edu                          file.size());
847119Sgblack@eecs.umich.edu    char *dir = dirname(cfile);
857119Sgblack@eecs.umich.edu    char *dir_arg = NULL;
867119Sgblack@eecs.umich.edu    if (*dir != '.') {
877119Sgblack@eecs.umich.edu        string arg = "-I";
887119Sgblack@eecs.umich.edu        arg += dir;
897119Sgblack@eecs.umich.edu
907119Sgblack@eecs.umich.edu        dir_arg = new char[arg.size() + 1];
917119Sgblack@eecs.umich.edu        strncpy(dir_arg, arg.c_str(), arg.size());
927119Sgblack@eecs.umich.edu    }
937119Sgblack@eecs.umich.edu
947119Sgblack@eecs.umich.edu    delete [] cfile;
957119Sgblack@eecs.umich.edu
967119Sgblack@eecs.umich.edu    char tempfile[] = "/tmp/configXXXXXX";
977119Sgblack@eecs.umich.edu    int tmp_fd = mkstemp(tempfile);
987119Sgblack@eecs.umich.edu
997119Sgblack@eecs.umich.edu    int pid = fork();
1007119Sgblack@eecs.umich.edu
1017119Sgblack@eecs.umich.edu    if (pid == -1)
1027119Sgblack@eecs.umich.edu        return false;
1037119Sgblack@eecs.umich.edu
1047119Sgblack@eecs.umich.edu    if (pid == 0) {
1057119Sgblack@eecs.umich.edu        char filename[FILENAME_MAX];
1067119Sgblack@eecs.umich.edu        string::size_type i = file.copy(filename, sizeof(filename) - 1);
1077119Sgblack@eecs.umich.edu        filename[i] = '\0';
1087119Sgblack@eecs.umich.edu
1097119Sgblack@eecs.umich.edu        int arg_count = cppArgs.size();
1107119Sgblack@eecs.umich.edu
1117119Sgblack@eecs.umich.edu        char **args = new char *[arg_count + 20];
1127119Sgblack@eecs.umich.edu
1137119Sgblack@eecs.umich.edu        int nextArg = 0;
1147119Sgblack@eecs.umich.edu        args[nextArg++] = "g++";
1157119Sgblack@eecs.umich.edu        args[nextArg++] = "-E";
1167119Sgblack@eecs.umich.edu        args[nextArg++] = "-P";
1177119Sgblack@eecs.umich.edu        args[nextArg++] = "-nostdinc";
1187119Sgblack@eecs.umich.edu        args[nextArg++] = "-nostdinc++";
1197119Sgblack@eecs.umich.edu        args[nextArg++] = "-x";
1207119Sgblack@eecs.umich.edu        args[nextArg++] = "c++";
1217119Sgblack@eecs.umich.edu        args[nextArg++] = "-undef";
1227119Sgblack@eecs.umich.edu
1237119Sgblack@eecs.umich.edu        for (int i = 0; i < arg_count; i++)
1247119Sgblack@eecs.umich.edu            args[nextArg++] = cppArgs[i];
1257128Sgblack@eecs.umich.edu
1267128Sgblack@eecs.umich.edu        if (dir_arg)
1277128Sgblack@eecs.umich.edu            args[nextArg++] = dir_arg;
1287128Sgblack@eecs.umich.edu
1297128Sgblack@eecs.umich.edu        args[nextArg++] = filename;
1307128Sgblack@eecs.umich.edu        args[nextArg++] = NULL;
1317128Sgblack@eecs.umich.edu
1327128Sgblack@eecs.umich.edu        close(STDOUT_FILENO);
1337128Sgblack@eecs.umich.edu        if (dup2(tmp_fd, STDOUT_FILENO) == -1)
1347128Sgblack@eecs.umich.edu            exit(1);
1357128Sgblack@eecs.umich.edu
1367128Sgblack@eecs.umich.edu        execvp("g++", args);
1377128Sgblack@eecs.umich.edu
1387128Sgblack@eecs.umich.edu        exit(0);
1397128Sgblack@eecs.umich.edu    }
1407128Sgblack@eecs.umich.edu
1417128Sgblack@eecs.umich.edu    int retval;
1427128Sgblack@eecs.umich.edu    waitpid(pid, &retval, 0);
1437128Sgblack@eecs.umich.edu
1447128Sgblack@eecs.umich.edu    delete [] dir_arg;
1457128Sgblack@eecs.umich.edu
1467128Sgblack@eecs.umich.edu    // check for normal completion of CPP
1477128Sgblack@eecs.umich.edu    if (!WIFEXITED(retval) || WEXITSTATUS(retval) != 0)
1487128Sgblack@eecs.umich.edu        return false;
1497128Sgblack@eecs.umich.edu
1507128Sgblack@eecs.umich.edu    close(tmp_fd);
1517128Sgblack@eecs.umich.edu
1527128Sgblack@eecs.umich.edu    bool status = false;
1537128Sgblack@eecs.umich.edu
1547128Sgblack@eecs.umich.edu    status = load(tempfile);
1557128Sgblack@eecs.umich.edu
1567128Sgblack@eecs.umich.edu    unlink(tempfile);
1577128Sgblack@eecs.umich.edu
1587128Sgblack@eecs.umich.edu    return status;
1597128Sgblack@eecs.umich.edu}
1607128Sgblack@eecs.umich.edu#endif
1617128Sgblack@eecs.umich.edu
1627128Sgblack@eecs.umich.edubool
1637128Sgblack@eecs.umich.eduIniFile::load(const string &file)
1647128Sgblack@eecs.umich.edu{
1657128Sgblack@eecs.umich.edu    ifstream f(file.c_str());
1667128Sgblack@eecs.umich.edu
1677128Sgblack@eecs.umich.edu    if (!f.is_open())
1687128Sgblack@eecs.umich.edu        return false;
1697128Sgblack@eecs.umich.edu
1707128Sgblack@eecs.umich.edu    return load(f);
1717128Sgblack@eecs.umich.edu}
1727128Sgblack@eecs.umich.edu
1737128Sgblack@eecs.umich.edu
1747128Sgblack@eecs.umich.educonst string &
1757128Sgblack@eecs.umich.eduIniFile::Entry::getValue() const
1767119Sgblack@eecs.umich.edu{
1777119Sgblack@eecs.umich.edu    referenced = true;
1787119Sgblack@eecs.umich.edu    return value;
1797119Sgblack@eecs.umich.edu}
1807119Sgblack@eecs.umich.edu
1817119Sgblack@eecs.umich.edu
1827119Sgblack@eecs.umich.eduvoid
1837119Sgblack@eecs.umich.eduIniFile::Section::addEntry(const std::string &entryName,
1847119Sgblack@eecs.umich.edu                           const std::string &value,
1857119Sgblack@eecs.umich.edu                           bool append)
1867119Sgblack@eecs.umich.edu{
1877119Sgblack@eecs.umich.edu    EntryTable::iterator ei = table.find(entryName);
1887119Sgblack@eecs.umich.edu
1897119Sgblack@eecs.umich.edu    if (ei == table.end()) {
1907128Sgblack@eecs.umich.edu        // new entry
1917128Sgblack@eecs.umich.edu        table[entryName] = new Entry(value);
1927128Sgblack@eecs.umich.edu    }
1937128Sgblack@eecs.umich.edu    else if (append) {
1947128Sgblack@eecs.umich.edu        // append new reult to old entry
1957128Sgblack@eecs.umich.edu        ei->second->appendValue(value);
1967128Sgblack@eecs.umich.edu    }
1977128Sgblack@eecs.umich.edu    else {
1987128Sgblack@eecs.umich.edu        // override old entry
1997128Sgblack@eecs.umich.edu        ei->second->setValue(value);
2007128Sgblack@eecs.umich.edu    }
2017128Sgblack@eecs.umich.edu}
2027128Sgblack@eecs.umich.edu
2037128Sgblack@eecs.umich.edu
2047119Sgblack@eecs.umich.edubool
2057119Sgblack@eecs.umich.eduIniFile::Section::add(const std::string &assignment)
2067119Sgblack@eecs.umich.edu{
2077119Sgblack@eecs.umich.edu    string::size_type offset = assignment.find('=');
2087119Sgblack@eecs.umich.edu    if (offset == string::npos) {
2097119Sgblack@eecs.umich.edu        // no '=' found
2107119Sgblack@eecs.umich.edu        cerr << "Can't parse .ini line " << assignment << endl;
2117119Sgblack@eecs.umich.edu        return false;
2127119Sgblack@eecs.umich.edu    }
2137119Sgblack@eecs.umich.edu
2147128Sgblack@eecs.umich.edu    // if "+=" rather than just "=" then append value
2157128Sgblack@eecs.umich.edu    bool append = (assignment[offset-1] == '+');
2167119Sgblack@eecs.umich.edu
217    string entryName = assignment.substr(0, append ? offset-1 : offset);
218    string value = assignment.substr(offset + 1);
219
220    eat_white(entryName);
221    eat_white(value);
222
223    addEntry(entryName, value, append);
224    return true;
225}
226
227
228IniFile::Entry *
229IniFile::Section::findEntry(const std::string &entryName) const
230{
231    referenced = true;
232
233    EntryTable::const_iterator ei = table.find(entryName);
234
235    return (ei == table.end()) ? NULL : ei->second;
236}
237
238
239IniFile::Section *
240IniFile::addSection(const string &sectionName)
241{
242    SectionTable::iterator i = table.find(sectionName);
243
244    if (i != table.end()) {
245        return i->second;
246    }
247    else {
248        // new entry
249        Section *sec = new Section();
250        table[sectionName] = sec;
251        return sec;
252    }
253}
254
255
256IniFile::Section *
257IniFile::findSection(const string &sectionName) const
258{
259    SectionTable::const_iterator i = table.find(sectionName);
260
261    return (i == table.end()) ? NULL : i->second;
262}
263
264
265// Take string of the form "<section>:<parameter>=<value>" and add to
266// database.  Return true if successful, false if parse error.
267bool
268IniFile::add(const string &str)
269{
270    // find ':'
271    string::size_type offset = str.find(':');
272    if (offset == string::npos)  // no ':' found
273        return false;
274
275    string sectionName = str.substr(0, offset);
276    string rest = str.substr(offset + 1);
277
278    eat_white(sectionName);
279    Section *s = addSection(sectionName);
280
281    return s->add(rest);
282}
283
284bool
285IniFile::load(istream &f)
286{
287    Section *section = NULL;
288
289    while (!f.eof()) {
290        f >> ws; // Eat whitespace
291        if (f.eof()) {
292            break;
293        }
294
295        string line;
296        getline(f, line);
297        if (line.size() == 0)
298            continue;
299
300        eat_end_white(line);
301        int last = line.size() - 1;
302
303        if (line[0] == '[' && line[last] == ']') {
304            string sectionName = line.substr(1, last - 1);
305            eat_white(sectionName);
306            section = addSection(sectionName);
307            continue;
308        }
309
310        if (section == NULL)
311            continue;
312
313        if (!section->add(line))
314            return false;
315    }
316
317    return true;
318}
319
320bool
321IniFile::find(const string &sectionName, const string &entryName,
322              string &value) const
323{
324    Section *section = findSection(sectionName);
325    if (section == NULL)
326        return false;
327
328    Entry *entry = section->findEntry(entryName);
329    if (entry == NULL)
330        return false;
331
332    value = entry->getValue();
333
334    return true;
335}
336
337bool
338IniFile::sectionExists(const string &sectionName) const
339{
340    return findSection(sectionName) != NULL;
341}
342
343
344bool
345IniFile::Section::printUnreferenced(const string &sectionName)
346{
347    bool unref = false;
348    bool search_unref_entries = false;
349    vector<string> unref_ok_entries;
350
351    Entry *entry = findEntry("unref_entries_ok");
352    if (entry != NULL) {
353        tokenize(unref_ok_entries, entry->getValue(), ' ');
354        if (unref_ok_entries.size()) {
355            search_unref_entries = true;
356        }
357    }
358
359    for (EntryTable::iterator ei = table.begin();
360         ei != table.end(); ++ei) {
361        const string &entryName = ei->first;
362        Entry *entry = ei->second;
363
364        if (entryName == "unref_section_ok" ||
365            entryName == "unref_entries_ok")
366        {
367            continue;
368        }
369
370        if (!entry->isReferenced()) {
371            if (search_unref_entries &&
372                (std::find(unref_ok_entries.begin(), unref_ok_entries.end(),
373                           entryName) != unref_ok_entries.end()))
374            {
375                continue;
376            }
377
378            cerr << "Parameter " << sectionName << ":" << entryName
379                 << " not referenced." << endl;
380            unref = true;
381        }
382    }
383
384    return unref;
385}
386
387
388bool
389IniFile::printUnreferenced()
390{
391    bool unref = false;
392
393    for (SectionTable::iterator i = table.begin();
394         i != table.end(); ++i) {
395        const string &sectionName = i->first;
396        Section *section = i->second;
397
398        if (!section->isReferenced()) {
399            if (section->findEntry("unref_section_ok") == NULL) {
400                cerr << "Section " << sectionName << " not referenced."
401                     << endl;
402                unref = true;
403            }
404        }
405        else {
406            if (section->printUnreferenced(sectionName)) {
407                unref = true;
408            }
409        }
410    }
411
412    return unref;
413}
414
415
416void
417IniFile::Section::dump(const string &sectionName)
418{
419    for (EntryTable::iterator ei = table.begin();
420         ei != table.end(); ++ei) {
421        cout << sectionName << ": " << (*ei).first << " => "
422             << (*ei).second->getValue() << "\n";
423    }
424}
425
426void
427IniFile::dump()
428{
429    for (SectionTable::iterator i = table.begin();
430         i != table.end(); ++i) {
431        i->second->dump(i->first);
432    }
433}
434