inifile.cc revision 178
113553Sjavier.bueno@metempsy.com/* 213553Sjavier.bueno@metempsy.com * Copyright (c) 2003 The Regents of The University of Michigan 313553Sjavier.bueno@metempsy.com * All rights reserved. 413553Sjavier.bueno@metempsy.com * 513553Sjavier.bueno@metempsy.com * Redistribution and use in source and binary forms, with or without 613553Sjavier.bueno@metempsy.com * modification, are permitted provided that the following conditions are 713553Sjavier.bueno@metempsy.com * met: redistributions of source code must retain the above copyright 813553Sjavier.bueno@metempsy.com * notice, this list of conditions and the following disclaimer; 913553Sjavier.bueno@metempsy.com * redistributions in binary form must reproduce the above copyright 1013553Sjavier.bueno@metempsy.com * notice, this list of conditions and the following disclaimer in the 1113553Sjavier.bueno@metempsy.com * documentation and/or other materials provided with the distribution; 1213553Sjavier.bueno@metempsy.com * neither the name of the copyright holders nor the names of its 1313553Sjavier.bueno@metempsy.com * contributors may be used to endorse or promote products derived from 1413553Sjavier.bueno@metempsy.com * this software without specific prior written permission. 1513553Sjavier.bueno@metempsy.com * 1613553Sjavier.bueno@metempsy.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1713553Sjavier.bueno@metempsy.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1813553Sjavier.bueno@metempsy.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1913553Sjavier.bueno@metempsy.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2013553Sjavier.bueno@metempsy.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2113553Sjavier.bueno@metempsy.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2213553Sjavier.bueno@metempsy.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2313553Sjavier.bueno@metempsy.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2413553Sjavier.bueno@metempsy.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2513553Sjavier.bueno@metempsy.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2613553Sjavier.bueno@metempsy.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2713553Sjavier.bueno@metempsy.com */ 2813553Sjavier.bueno@metempsy.com 2913553Sjavier.bueno@metempsy.com#define USE_CPP 3013553Sjavier.bueno@metempsy.com// #define CPP_PIPE 3113553Sjavier.bueno@metempsy.com 3213553Sjavier.bueno@metempsy.com 3313553Sjavier.bueno@metempsy.com#ifdef USE_CPP 3413553Sjavier.bueno@metempsy.com#include <sys/signal.h> 3513553Sjavier.bueno@metempsy.com#include <sys/types.h> 3613553Sjavier.bueno@metempsy.com#include <sys/wait.h> 3713553Sjavier.bueno@metempsy.com 3813553Sjavier.bueno@metempsy.com#include <stdio.h> 3913553Sjavier.bueno@metempsy.com#include <stdlib.h> 4013553Sjavier.bueno@metempsy.com#include <unistd.h> 4113553Sjavier.bueno@metempsy.com#endif 4213553Sjavier.bueno@metempsy.com 4313553Sjavier.bueno@metempsy.com#include <fstream> 4413553Sjavier.bueno@metempsy.com#include <iostream> 4513553Sjavier.bueno@metempsy.com#if __GNUC__ >= 3 4613553Sjavier.bueno@metempsy.com#include <ext/stdio_filebuf.h> 4713553Sjavier.bueno@metempsy.com#endif 4813553Sjavier.bueno@metempsy.com 4913553Sjavier.bueno@metempsy.com#include <vector> 5013553Sjavier.bueno@metempsy.com#include <string> 5113553Sjavier.bueno@metempsy.com 5213553Sjavier.bueno@metempsy.com#include "base/inifile.hh" 5313553Sjavier.bueno@metempsy.com#include "base/str.hh" 5413553Sjavier.bueno@metempsy.com 5513553Sjavier.bueno@metempsy.comusing namespace std; 5613553Sjavier.bueno@metempsy.com 5713553Sjavier.bueno@metempsy.comIniFile::IniFile() 5813553Sjavier.bueno@metempsy.com{} 5913553Sjavier.bueno@metempsy.com 6013553Sjavier.bueno@metempsy.comIniFile::~IniFile() 6113553Sjavier.bueno@metempsy.com{ 6213553Sjavier.bueno@metempsy.com SectionTable::iterator i = table.begin(); 6313553Sjavier.bueno@metempsy.com SectionTable::iterator end = table.end(); 6413553Sjavier.bueno@metempsy.com 6513553Sjavier.bueno@metempsy.com while (i != end) { 6613553Sjavier.bueno@metempsy.com delete (*i).second; 6713553Sjavier.bueno@metempsy.com ++i; 6813553Sjavier.bueno@metempsy.com } 6913553Sjavier.bueno@metempsy.com} 7013553Sjavier.bueno@metempsy.com 7113553Sjavier.bueno@metempsy.com 7213553Sjavier.bueno@metempsy.com#ifdef USE_CPP 7313553Sjavier.bueno@metempsy.combool 7413553Sjavier.bueno@metempsy.comIniFile::loadCPP(const string &file, vector<char *> &cppArgs) 7513553Sjavier.bueno@metempsy.com{ 7613553Sjavier.bueno@metempsy.com int fd[2]; 7713553Sjavier.bueno@metempsy.com 7813553Sjavier.bueno@metempsy.com // Open the file just to verify that we can. Otherwise if the 7913553Sjavier.bueno@metempsy.com // file doesn't exist or has bad permissions the user will get 8013553Sjavier.bueno@metempsy.com // confusing errors from cpp/g++. 8113553Sjavier.bueno@metempsy.com ifstream tmpf(file.c_str()); 8213553Sjavier.bueno@metempsy.com 8313553Sjavier.bueno@metempsy.com if (!tmpf.is_open()) 8413553Sjavier.bueno@metempsy.com return false; 8513553Sjavier.bueno@metempsy.com 8613553Sjavier.bueno@metempsy.com tmpf.close(); 8713553Sjavier.bueno@metempsy.com 8813553Sjavier.bueno@metempsy.com#ifdef CPP_PIPE 8913553Sjavier.bueno@metempsy.com if (pipe(fd) == -1) 9013553Sjavier.bueno@metempsy.com return false; 9113553Sjavier.bueno@metempsy.com#else 9213553Sjavier.bueno@metempsy.com char tempfile[] = "/tmp/configXXXXXX"; 9313553Sjavier.bueno@metempsy.com fd[0] = fd[1] = mkstemp(tempfile); 9413553Sjavier.bueno@metempsy.com#endif 9513553Sjavier.bueno@metempsy.com 9613553Sjavier.bueno@metempsy.com int pid = fork(); 9713553Sjavier.bueno@metempsy.com 9813553Sjavier.bueno@metempsy.com if (pid == -1) 9913553Sjavier.bueno@metempsy.com return 1; 10013553Sjavier.bueno@metempsy.com 10113553Sjavier.bueno@metempsy.com if (pid == 0) { 10213553Sjavier.bueno@metempsy.com char filename[FILENAME_MAX]; 10313553Sjavier.bueno@metempsy.com string::size_type i = file.copy(filename, sizeof(filename) - 1); 10413553Sjavier.bueno@metempsy.com filename[i] = '\0'; 10513553Sjavier.bueno@metempsy.com 10613553Sjavier.bueno@metempsy.com int arg_count = cppArgs.size(); 10713553Sjavier.bueno@metempsy.com 10813553Sjavier.bueno@metempsy.com char **args = new char *[arg_count + 20]; 10913553Sjavier.bueno@metempsy.com 11013553Sjavier.bueno@metempsy.com int nextArg = 0; 11113553Sjavier.bueno@metempsy.com args[nextArg++] = "g++"; 11213553Sjavier.bueno@metempsy.com args[nextArg++] = "-E"; 11313553Sjavier.bueno@metempsy.com args[nextArg++] = "-P"; 11413553Sjavier.bueno@metempsy.com args[nextArg++] = "-nostdinc"; 11513553Sjavier.bueno@metempsy.com args[nextArg++] = "-nostdinc++"; 11613553Sjavier.bueno@metempsy.com args[nextArg++] = "-x"; 11713553Sjavier.bueno@metempsy.com args[nextArg++] = "c++"; 11813553Sjavier.bueno@metempsy.com args[nextArg++] = "-undef"; 11913553Sjavier.bueno@metempsy.com 12013553Sjavier.bueno@metempsy.com for (int i = 0; i < arg_count; i++) 12113553Sjavier.bueno@metempsy.com args[nextArg++] = cppArgs[i]; 12213553Sjavier.bueno@metempsy.com 12313553Sjavier.bueno@metempsy.com args[nextArg++] = filename; 12413553Sjavier.bueno@metempsy.com args[nextArg++] = NULL; 12513553Sjavier.bueno@metempsy.com 12613553Sjavier.bueno@metempsy.com close(STDOUT_FILENO); 12713553Sjavier.bueno@metempsy.com if (dup2(fd[1], STDOUT_FILENO) == -1) 12813553Sjavier.bueno@metempsy.com return 1; 12913553Sjavier.bueno@metempsy.com 13013553Sjavier.bueno@metempsy.com execvp("g++", args); 13113553Sjavier.bueno@metempsy.com 13213553Sjavier.bueno@metempsy.com exit(1); 13313553Sjavier.bueno@metempsy.com } 13413553Sjavier.bueno@metempsy.com 13513553Sjavier.bueno@metempsy.com int retval; 13613553Sjavier.bueno@metempsy.com waitpid(pid, &retval, 0); 13713553Sjavier.bueno@metempsy.com 13813553Sjavier.bueno@metempsy.com // check for normal completion of CPP 13913553Sjavier.bueno@metempsy.com if (!WIFEXITED(retval) || WEXITSTATUS(retval) != 0) 14013553Sjavier.bueno@metempsy.com return false; 14113553Sjavier.bueno@metempsy.com 14213553Sjavier.bueno@metempsy.com#ifdef CPP_PIPE 14313553Sjavier.bueno@metempsy.com close(fd[1]); 14413553Sjavier.bueno@metempsy.com#else 14513553Sjavier.bueno@metempsy.com lseek(fd[0], 0, SEEK_SET); 14613553Sjavier.bueno@metempsy.com#endif 14713553Sjavier.bueno@metempsy.com 14813553Sjavier.bueno@metempsy.com bool status = false; 14913553Sjavier.bueno@metempsy.com 15013553Sjavier.bueno@metempsy.com#if __GNUC__ >= 3 15113553Sjavier.bueno@metempsy.com using namespace __gnu_cxx; 15213553Sjavier.bueno@metempsy.com stdio_filebuf<char> fbuf(fd[0], ios_base::in, true, 15313553Sjavier.bueno@metempsy.com static_cast<stdio_filebuf<char>::int_type>(BUFSIZ)); 15413553Sjavier.bueno@metempsy.com 15513553Sjavier.bueno@metempsy.com if (fbuf.is_open()) { 15613553Sjavier.bueno@metempsy.com istream f(&fbuf); 15713553Sjavier.bueno@metempsy.com status = load(f); 15813553Sjavier.bueno@metempsy.com } 15913553Sjavier.bueno@metempsy.com 16013553Sjavier.bueno@metempsy.com#else 16113553Sjavier.bueno@metempsy.com ifstream f(fd[0]); 16213553Sjavier.bueno@metempsy.com if (f.is_open()) 16313553Sjavier.bueno@metempsy.com status = load(f); 16413553Sjavier.bueno@metempsy.com#endif 16513553Sjavier.bueno@metempsy.com 16613553Sjavier.bueno@metempsy.com#ifndef CPP_PIPE 16713553Sjavier.bueno@metempsy.com unlink(tempfile); 16813553Sjavier.bueno@metempsy.com#endif 16913553Sjavier.bueno@metempsy.com 17013553Sjavier.bueno@metempsy.com return status; 17113553Sjavier.bueno@metempsy.com} 17213553Sjavier.bueno@metempsy.com#endif 17313553Sjavier.bueno@metempsy.com 17413553Sjavier.bueno@metempsy.combool 17513553Sjavier.bueno@metempsy.comIniFile::load(const string &file) 17613553Sjavier.bueno@metempsy.com{ 17713553Sjavier.bueno@metempsy.com ifstream f(file.c_str()); 17813553Sjavier.bueno@metempsy.com 17913553Sjavier.bueno@metempsy.com if (!f.is_open()) 18013553Sjavier.bueno@metempsy.com return false; 18113553Sjavier.bueno@metempsy.com 18213553Sjavier.bueno@metempsy.com return load(f); 18313553Sjavier.bueno@metempsy.com} 18413553Sjavier.bueno@metempsy.com 18513553Sjavier.bueno@metempsy.com 18613553Sjavier.bueno@metempsy.comconst string & 18713553Sjavier.bueno@metempsy.comIniFile::Entry::getValue() const 18813553Sjavier.bueno@metempsy.com{ 18913553Sjavier.bueno@metempsy.com referenced = true; 19013553Sjavier.bueno@metempsy.com return value; 19113553Sjavier.bueno@metempsy.com} 19213553Sjavier.bueno@metempsy.com 19313553Sjavier.bueno@metempsy.com 19413553Sjavier.bueno@metempsy.comvoid 19513553Sjavier.bueno@metempsy.comIniFile::Section::addEntry(const std::string &entryName, 19613553Sjavier.bueno@metempsy.com const std::string &value, 19713553Sjavier.bueno@metempsy.com bool append) 19813707Sjavier.bueno@metempsy.com{ 19913707Sjavier.bueno@metempsy.com EntryTable::iterator ei = table.find(entryName); 20013707Sjavier.bueno@metempsy.com 20113707Sjavier.bueno@metempsy.com if (ei == table.end()) { 20213707Sjavier.bueno@metempsy.com // new entry 20313707Sjavier.bueno@metempsy.com table[entryName] = new Entry(value); 20413707Sjavier.bueno@metempsy.com } 20513707Sjavier.bueno@metempsy.com else if (append) { 20613707Sjavier.bueno@metempsy.com // append new reult to old entry 20713707Sjavier.bueno@metempsy.com ei->second->appendValue(value); 20813707Sjavier.bueno@metempsy.com } 20913707Sjavier.bueno@metempsy.com else { 21013707Sjavier.bueno@metempsy.com // override old entry 21113707Sjavier.bueno@metempsy.com ei->second->setValue(value); 21213707Sjavier.bueno@metempsy.com } 21313707Sjavier.bueno@metempsy.com} 21413707Sjavier.bueno@metempsy.com 21513707Sjavier.bueno@metempsy.com 21613707Sjavier.bueno@metempsy.combool 21713707Sjavier.bueno@metempsy.comIniFile::Section::add(const std::string &assignment) 21813707Sjavier.bueno@metempsy.com{ 21913707Sjavier.bueno@metempsy.com string::size_type offset = assignment.find('='); 22013707Sjavier.bueno@metempsy.com if (offset == string::npos) { 22113707Sjavier.bueno@metempsy.com // no '=' found 22213707Sjavier.bueno@metempsy.com cerr << "Can't parse .ini line " << assignment << endl; 22313707Sjavier.bueno@metempsy.com return false; 22413707Sjavier.bueno@metempsy.com } 22513707Sjavier.bueno@metempsy.com 22613707Sjavier.bueno@metempsy.com // if "+=" rather than just "=" then append value 22713707Sjavier.bueno@metempsy.com bool append = (assignment[offset-1] == '+'); 22813707Sjavier.bueno@metempsy.com 22913707Sjavier.bueno@metempsy.com string entryName = assignment.substr(0, append ? offset-1 : offset); 23013707Sjavier.bueno@metempsy.com string value = assignment.substr(offset + 1); 23113707Sjavier.bueno@metempsy.com 23213707Sjavier.bueno@metempsy.com eat_white(entryName); 23313707Sjavier.bueno@metempsy.com eat_white(value); 23413707Sjavier.bueno@metempsy.com 23513707Sjavier.bueno@metempsy.com addEntry(entryName, value, append); 23613707Sjavier.bueno@metempsy.com return true; 23713707Sjavier.bueno@metempsy.com} 23813707Sjavier.bueno@metempsy.com 23913707Sjavier.bueno@metempsy.com 24013553Sjavier.bueno@metempsy.comIniFile::Entry * 24113553Sjavier.bueno@metempsy.comIniFile::Section::findEntry(const std::string &entryName) const 24213553Sjavier.bueno@metempsy.com{ 243 referenced = true; 244 245 EntryTable::const_iterator ei = table.find(entryName); 246 247 return (ei == table.end()) ? NULL : ei->second; 248} 249 250 251IniFile::Section * 252IniFile::addSection(const string §ionName) 253{ 254 SectionTable::iterator i = table.find(sectionName); 255 256 if (i != table.end()) { 257 return i->second; 258 } 259 else { 260 // new entry 261 Section *sec = new Section(); 262 table[sectionName] = sec; 263 return sec; 264 } 265} 266 267 268IniFile::Section * 269IniFile::findSection(const string §ionName) const 270{ 271 SectionTable::const_iterator i = table.find(sectionName); 272 273 return (i == table.end()) ? NULL : i->second; 274} 275 276 277// Take string of the form "<section>:<parameter>=<value>" and add to 278// database. Return true if successful, false if parse error. 279bool 280IniFile::add(const string &str) 281{ 282 // find ':' 283 string::size_type offset = str.find(':'); 284 if (offset == string::npos) // no ':' found 285 return false; 286 287 string sectionName = str.substr(0, offset); 288 string rest = str.substr(offset + 1); 289 290 eat_white(sectionName); 291 Section *s = addSection(sectionName); 292 293 return s->add(rest); 294} 295 296bool 297IniFile::load(istream &f) 298{ 299 Section *section = NULL; 300 301 while (!f.eof()) { 302 f >> ws; // Eat whitespace 303 if (f.eof()) { 304 break; 305 } 306 307 string line; 308 getline(f, line); 309 if (line.size() == 0) 310 continue; 311 312 eat_end_white(line); 313 int last = line.size() - 1; 314 315 if (line[0] == '[' && line[last] == ']') { 316 string sectionName = line.substr(1, last - 1); 317 eat_white(sectionName); 318 section = addSection(sectionName); 319 continue; 320 } 321 322 if (section == NULL) 323 continue; 324 325 if (!section->add(line)) 326 return false; 327 } 328 329 return true; 330} 331 332bool 333IniFile::find(const string §ionName, const string &entryName, 334 string &value) const 335{ 336 Section *section = findSection(sectionName); 337 if (section == NULL) 338 return false; 339 340 Entry *entry = section->findEntry(entryName); 341 if (entry == NULL) 342 return false; 343 344 value = entry->getValue(); 345 346 return true; 347} 348 349bool 350IniFile::findDefault(const string &_section, const string &entry, 351 string &value) const 352{ 353 string section = _section; 354 while (!find(section, entry, value)) { 355 if (!find(section, "default", section)) 356 return false; 357 } 358 359 return true; 360} 361 362 363bool 364IniFile::Section::printUnreferenced(const string §ionName) 365{ 366 bool unref = false; 367 bool search_unref_entries = false; 368 vector<string> unref_ok_entries; 369 370 Entry *entry = findEntry("unref_entries_ok"); 371 if (entry != NULL) { 372 tokenize(unref_ok_entries, entry->getValue(), ' '); 373 if (unref_ok_entries.size()) { 374 search_unref_entries = true; 375 } 376 } 377 378 for (EntryTable::iterator ei = table.begin(); 379 ei != table.end(); ++ei) { 380 const string &entryName = ei->first; 381 Entry *entry = ei->second; 382 383 if (entryName == "unref_section_ok" || 384 entryName == "unref_entries_ok") 385 { 386 continue; 387 } 388 389 if (!entry->isReferenced()) { 390 if (search_unref_entries && 391 (std::find(unref_ok_entries.begin(), unref_ok_entries.end(), 392 entryName) != unref_ok_entries.end())) 393 { 394 continue; 395 } 396 397 cerr << "Parameter " << sectionName << ":" << entryName 398 << " not referenced." << endl; 399 unref = true; 400 } 401 } 402 403 return unref; 404} 405 406 407bool 408IniFile::printUnreferenced() 409{ 410 bool unref = false; 411 412 for (SectionTable::iterator i = table.begin(); 413 i != table.end(); ++i) { 414 const string §ionName = i->first; 415 Section *section = i->second; 416 417 if (!section->isReferenced()) { 418 if (section->findEntry("unref_section_ok") == NULL) { 419 cerr << "Section " << sectionName << " not referenced." 420 << endl; 421 unref = true; 422 } 423 } 424 else { 425 if (section->printUnreferenced(sectionName)) { 426 unref = true; 427 } 428 } 429 } 430 431 return unref; 432} 433 434 435void 436IniFile::Section::dump(const string §ionName) 437{ 438 for (EntryTable::iterator ei = table.begin(); 439 ei != table.end(); ++ei) { 440 cout << sectionName << ": " << (*ei).first << " => " 441 << (*ei).second->getValue() << "\n"; 442 } 443} 444 445void 446IniFile::dump() 447{ 448 for (SectionTable::iterator i = table.begin(); 449 i != table.end(); ++i) { 450 i->second->dump(i->first); 451 } 452} 453