inifile.cc revision 178
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#ifdef CPP_PIPE 89 if (pipe(fd) == -1) 90 return false; 91#else 92 char tempfile[] = "/tmp/configXXXXXX"; 93 fd[0] = fd[1] = mkstemp(tempfile); 94#endif 95 96 int pid = fork(); 97 98 if (pid == -1) 99 return 1; 100 101 if (pid == 0) { 102 char filename[FILENAME_MAX]; 103 string::size_type i = file.copy(filename, sizeof(filename) - 1); 104 filename[i] = '\0'; 105 106 int arg_count = cppArgs.size(); 107 108 char **args = new char *[arg_count + 20]; 109 110 int nextArg = 0; 111 args[nextArg++] = "g++"; 112 args[nextArg++] = "-E"; 113 args[nextArg++] = "-P"; 114 args[nextArg++] = "-nostdinc"; 115 args[nextArg++] = "-nostdinc++"; 116 args[nextArg++] = "-x"; 117 args[nextArg++] = "c++"; 118 args[nextArg++] = "-undef"; 119 120 for (int i = 0; i < arg_count; i++) 121 args[nextArg++] = cppArgs[i]; 122 123 args[nextArg++] = filename; 124 args[nextArg++] = NULL; 125 126 close(STDOUT_FILENO); 127 if (dup2(fd[1], STDOUT_FILENO) == -1) 128 return 1; 129 130 execvp("g++", args); 131 132 exit(1); 133 } 134 135 int retval; 136 waitpid(pid, &retval, 0); 137 138 // check for normal completion of CPP 139 if (!WIFEXITED(retval) || WEXITSTATUS(retval) != 0) 140 return false; 141 142#ifdef CPP_PIPE 143 close(fd[1]); 144#else 145 lseek(fd[0], 0, SEEK_SET); 146#endif 147 148 bool status = false; 149 150#if __GNUC__ >= 3 151 using namespace __gnu_cxx; 152 stdio_filebuf<char> fbuf(fd[0], ios_base::in, true, 153 static_cast<stdio_filebuf<char>::int_type>(BUFSIZ)); 154 155 if (fbuf.is_open()) { 156 istream f(&fbuf); 157 status = load(f); 158 } 159 160#else 161 ifstream f(fd[0]); 162 if (f.is_open()) 163 status = load(f); 164#endif 165 166#ifndef CPP_PIPE 167 unlink(tempfile); 168#endif 169 170 return status; 171} 172#endif 173 174bool 175IniFile::load(const string &file) 176{ 177 ifstream f(file.c_str()); 178 179 if (!f.is_open()) 180 return false; 181 182 return load(f); 183} 184 185 186const string & 187IniFile::Entry::getValue() const 188{ 189 referenced = true; 190 return value; 191} 192 193 194void 195IniFile::Section::addEntry(const std::string &entryName, 196 const std::string &value, 197 bool append) 198{ 199 EntryTable::iterator ei = table.find(entryName); 200 201 if (ei == table.end()) { 202 // new entry 203 table[entryName] = new Entry(value); 204 } 205 else if (append) { 206 // append new reult to old entry 207 ei->second->appendValue(value); 208 } 209 else { 210 // override old entry 211 ei->second->setValue(value); 212 } 213} 214 215 216bool 217IniFile::Section::add(const std::string &assignment) 218{ 219 string::size_type offset = assignment.find('='); 220 if (offset == string::npos) { 221 // no '=' found 222 cerr << "Can't parse .ini line " << assignment << endl; 223 return false; 224 } 225 226 // if "+=" rather than just "=" then append value 227 bool append = (assignment[offset-1] == '+'); 228 229 string entryName = assignment.substr(0, append ? offset-1 : offset); 230 string value = assignment.substr(offset + 1); 231 232 eat_white(entryName); 233 eat_white(value); 234 235 addEntry(entryName, value, append); 236 return true; 237} 238 239 240IniFile::Entry * 241IniFile::Section::findEntry(const std::string &entryName) const 242{ 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