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