inifile.cc revision 160
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) // no '=' found 221 return false; 222 223 // if "+=" rather than just "=" then append value 224 bool append = (assignment[offset-1] == '+'); 225 226 string entryName = assignment.substr(0, append ? offset-1 : offset); 227 string value = assignment.substr(offset + 1); 228 229 eat_white(entryName); 230 eat_white(value); 231 232 addEntry(entryName, value, append); 233 return true; 234} 235 236 237IniFile::Entry * 238IniFile::Section::findEntry(const std::string &entryName) const 239{ 240 referenced = true; 241 242 EntryTable::const_iterator ei = table.find(entryName); 243 244 return (ei == table.end()) ? NULL : ei->second; 245} 246 247 248IniFile::Section * 249IniFile::addSection(const string §ionName) 250{ 251 SectionTable::iterator i = table.find(sectionName); 252 253 if (i != table.end()) { 254 return i->second; 255 } 256 else { 257 // new entry 258 Section *sec = new Section(); 259 table[sectionName] = sec; 260 return sec; 261 } 262} 263 264 265IniFile::Section * 266IniFile::findSection(const string §ionName) const 267{ 268 SectionTable::const_iterator i = table.find(sectionName); 269 270 return (i == table.end()) ? NULL : i->second; 271} 272 273 274// Take string of the form "<section>:<parameter>=<value>" and add to 275// database. Return true if successful, false if parse error. 276bool 277IniFile::add(const string &str) 278{ 279 // find ':' 280 string::size_type offset = str.find(':'); 281 if (offset == string::npos) // no ':' found 282 return false; 283 284 string sectionName = str.substr(0, offset); 285 string rest = str.substr(offset + 1); 286 287 eat_white(sectionName); 288 Section *s = addSection(sectionName); 289 290 return s->add(rest); 291} 292 293bool 294IniFile::load(istream &f) 295{ 296 Section *section = NULL; 297 298 while (!f.eof()) { 299 f >> ws; // Eat whitespace 300 if (f.eof()) { 301 break; 302 } 303 304 string line; 305 getline(f, line); 306 if (line.size() == 0) 307 continue; 308 309 eat_end_white(line); 310 int last = line.size() - 1; 311 312 if (line[0] == '[' && line[last] == ']') { 313 string sectionName = line.substr(1, last - 1); 314 eat_white(sectionName); 315 section = addSection(sectionName); 316 continue; 317 } 318 319 if (section == NULL) 320 continue; 321 322 if (!section->add(line)) 323 return false; 324 } 325 326 return true; 327} 328 329bool 330IniFile::find(const string §ionName, const string &entryName, 331 string &value) const 332{ 333 Section *section = findSection(sectionName); 334 if (section == NULL) 335 return false; 336 337 Entry *entry = section->findEntry(entryName); 338 if (entry == NULL) 339 return false; 340 341 value = entry->getValue(); 342 343 return true; 344} 345 346bool 347IniFile::findDefault(const string &_section, const string &entry, 348 string &value) const 349{ 350 string section = _section; 351 while (!find(section, entry, value)) { 352 if (!find(section, "default", section)) 353 return false; 354 } 355 356 return true; 357} 358 359 360bool 361IniFile::Section::printUnreferenced(const string §ionName) 362{ 363 bool unref = false; 364 bool search_unref_entries = false; 365 vector<string> unref_ok_entries; 366 367 Entry *entry = findEntry("unref_entries_ok"); 368 if (entry != NULL) { 369 tokenize(unref_ok_entries, entry->getValue(), ' '); 370 if (unref_ok_entries.size()) { 371 search_unref_entries = true; 372 } 373 } 374 375 for (EntryTable::iterator ei = table.begin(); 376 ei != table.end(); ++ei) { 377 const string &entryName = ei->first; 378 Entry *entry = ei->second; 379 380 if (entryName == "unref_section_ok" || 381 entryName == "unref_entries_ok") 382 { 383 continue; 384 } 385 386 if (!entry->isReferenced()) { 387 if (search_unref_entries && 388 (std::find(unref_ok_entries.begin(), unref_ok_entries.end(), 389 entryName) != unref_ok_entries.end())) 390 { 391 continue; 392 } 393 394 cerr << "Parameter " << sectionName << ":" << entryName 395 << " not referenced." << endl; 396 unref = true; 397 } 398 } 399 400 return unref; 401} 402 403 404bool 405IniFile::printUnreferenced() 406{ 407 bool unref = false; 408 409 for (SectionTable::iterator i = table.begin(); 410 i != table.end(); ++i) { 411 const string §ionName = i->first; 412 Section *section = i->second; 413 414 if (!section->isReferenced()) { 415 if (section->findEntry("unref_section_ok") == NULL) { 416 cerr << "Section " << sectionName << " not referenced." 417 << endl; 418 unref = true; 419 } 420 } 421 else { 422 if (section->printUnreferenced(sectionName)) { 423 unref = true; 424 } 425 } 426 } 427 428 return unref; 429} 430 431 432void 433IniFile::Section::dump(const string §ionName) 434{ 435 for (EntryTable::iterator ei = table.begin(); 436 ei != table.end(); ++ei) { 437 cout << sectionName << ": " << (*ei).first << " => " 438 << (*ei).second->getValue() << "\n"; 439 } 440} 441 442void 443IniFile::dump() 444{ 445 for (SectionTable::iterator i = table.begin(); 446 i != table.end(); ++i) { 447 i->second->dump(i->first); 448 } 449} 450