inifile.cc revision 304
16145SN/A/*
26386SN/A * Copyright (c) 2003 The Regents of The University of Michigan
37553SN/A * All rights reserved.
46386SN/A *
56386SN/A * Redistribution and use in source and binary forms, with or without
66386SN/A * modification, are permitted provided that the following conditions are
76386SN/A * met: redistributions of source code must retain the above copyright
86386SN/A * notice, this list of conditions and the following disclaimer;
96386SN/A * redistributions in binary form must reproduce the above copyright
106386SN/A * notice, this list of conditions and the following disclaimer in the
116386SN/A * documentation and/or other materials provided with the distribution;
126386SN/A * neither the name of the copyright holders nor the names of its
136386SN/A * contributors may be used to endorse or promote products derived from
146386SN/A * this software without specific prior written permission.
156386SN/A *
166386SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
176386SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
186386SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
196386SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
206386SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
216386SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
226386SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
236386SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
246386SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
256386SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
266386SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
276386SN/A */
286386SN/A
296145SN/A#define USE_CPP
307632SBrad.Beckmann@amd.com// #define CPP_PIPE
317632SBrad.Beckmann@amd.com
327632SBrad.Beckmann@amd.com
336145SN/A#ifdef USE_CPP
347553SN/A#include <sys/signal.h>
357553SN/A#include <sys/types.h>
366145SN/A#include <sys/wait.h>
377553SN/A
387553SN/A#include <stdio.h>
397553SN/A#include <stdlib.h>
407553SN/A#include <unistd.h>
417553SN/A#endif
426145SN/A
436145SN/A#include <fstream>
447553SN/A#include <iostream>
456145SN/A#if __GNUC__ >= 3
466145SN/A#include <ext/stdio_filebuf.h>
476145SN/A#endif
487553SN/A
497553SN/A#include <vector>
506145SN/A#include <string>
517553SN/A
527553SN/A#include "base/inifile.hh"
536145SN/A#include "base/str.hh"
547553SN/A
557553SN/Ausing namespace std;
567553SN/A
577553SN/AIniFile::IniFile()
587553SN/A{}
597553SN/A
607553SN/AIniFile::~IniFile()
617553SN/A{
627553SN/A    SectionTable::iterator i = table.begin();
637553SN/A    SectionTable::iterator end = table.end();
647553SN/A
657553SN/A    while (i != end) {
667553SN/A        delete (*i).second;
677553SN/A        ++i;
686145SN/A    }
697553SN/A}
707553SN/A
717553SN/A
727553SN/A#ifdef USE_CPP
736145SN/Abool
747553SN/AIniFile::loadCPP(const string &file, vector<char *> &cppArgs)
757553SN/A{
767553SN/A    int fd[2];
777553SN/A
787553SN/A    // Open the file just to verify that we can.  Otherwise if the
797553SN/A    // file doesn't exist or has bad permissions the user will get
807553SN/A    // confusing errors from cpp/g++.
817553SN/A    ifstream tmpf(file.c_str());
827553SN/A
837553SN/A    if (!tmpf.is_open())
847553SN/A        return false;
857553SN/A
867553SN/A    tmpf.close();
877553SN/A
886145SN/A    const char *cfile = file.c_str();
896145SN/A    char *dir = basename(cfile);
907553SN/A    char *dir_arg = NULL;
917553SN/A    if (*dir != '.' && dir != cfile) {
926145SN/A        string arg = "-I";
937553SN/A        arg += dir;
947553SN/A
957553SN/A        dir_arg = new char[arg.size() + 1];
966145SN/A        strcpy(dir_arg, arg.c_str());
977553SN/A    }
987553SN/A
997553SN/A#ifdef CPP_PIPE
1007553SN/A    if (pipe(fd) == -1)
1017553SN/A        return false;
1027553SN/A#else
1037553SN/A    char tempfile[] = "/tmp/configXXXXXX";
1047553SN/A    fd[0] = fd[1] = mkstemp(tempfile);
1057553SN/A#endif
1067553SN/A
1077553SN/A    int pid = fork();
1086145SN/A
1096145SN/A    if (pid == -1)
1107553SN/A        return 1;
1117553SN/A
1126145SN/A    if (pid == 0) {
1137553SN/A        char filename[FILENAME_MAX];
1146145SN/A        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