inifile.cc revision 304
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 const char *cfile = file.c_str(); 89 char *dir = basename(cfile); 90 char *dir_arg = NULL; 91 if (*dir != '.' && dir != cfile) { 92 string arg = "-I"; 93 arg += dir; 94 95 dir_arg = new char[arg.size() + 1]; 96 strcpy(dir_arg, arg.c_str()); 97 } 98 99#ifdef CPP_PIPE 100 if (pipe(fd) == -1) 101 return false; 102#else 103 char tempfile[] = "/tmp/configXXXXXX"; 104 fd[0] = fd[1] = mkstemp(tempfile); 105#endif 106 107 int pid = fork(); 108 109 if (pid == -1) 110 return 1; 111 112 if (pid == 0) { 113 char filename[FILENAME_MAX]; 114 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 §ionName) 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 §ionName) 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 §ionName, 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 §ionName) const 408{ 409 return findSection(sectionName) != NULL; 410} 411 412 413bool 414IniFile::Section::printUnreferenced(const string §ionName) 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 §ionName = 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 §ionName) 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