inifile.cc revision 1762
1/* 2 * Copyright (c) 2001-2005 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 31#ifdef USE_CPP 32#include <sys/signal.h> 33#include <sys/types.h> 34#include <sys/wait.h> 35 36#include <libgen.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <unistd.h> 40#endif 41 42#include <fstream> 43#include <iostream> 44 45#include <vector> 46#include <string> 47 48#include "base/inifile.hh" 49#include "base/str.hh" 50 51using namespace std; 52 53IniFile::IniFile() 54{} 55 56IniFile::~IniFile() 57{ 58 SectionTable::iterator i = table.begin(); 59 SectionTable::iterator end = table.end(); 60 61 while (i != end) { 62 delete (*i).second; 63 ++i; 64 } 65} 66 67 68#ifdef USE_CPP 69bool 70IniFile::loadCPP(const string &file, vector<char *> &cppArgs) 71{ 72 // Open the file just to verify that we can. Otherwise if the 73 // file doesn't exist or has bad permissions the user will get 74 // confusing errors from cpp/g++. 75 ifstream tmpf(file.c_str()); 76 77 if (!tmpf.is_open()) 78 return false; 79 80 tmpf.close(); 81 82 char *cfile = strncpy(new char[file.size() + 1], file.c_str(), 83 file.size()); 84 char *dir = dirname(cfile); 85 char *dir_arg = NULL; 86 if (*dir != '.') { 87 string arg = "-I"; 88 arg += dir; 89 90 dir_arg = new char[arg.size() + 1]; 91 strncpy(dir_arg, arg.c_str(), arg.size()); 92 } 93 94 delete [] cfile; 95 96 char tempfile[] = "/tmp/configXXXXXX"; 97 int tmp_fd = mkstemp(tempfile); 98 99 int pid = fork(); 100 101 if (pid == -1) 102 return false; 103 104 if (pid == 0) { 105 char filename[FILENAME_MAX]; 106 string::size_type i = file.copy(filename, sizeof(filename) - 1); 107 filename[i] = '\0'; 108 109 int arg_count = cppArgs.size(); 110 111 char **args = new char *[arg_count + 20]; 112 113 int nextArg = 0; 114 args[nextArg++] = "g++"; 115 args[nextArg++] = "-E"; 116 args[nextArg++] = "-P"; 117 args[nextArg++] = "-nostdinc"; 118 args[nextArg++] = "-nostdinc++"; 119 args[nextArg++] = "-x"; 120 args[nextArg++] = "c++"; 121 args[nextArg++] = "-undef"; 122 123 for (int i = 0; i < arg_count; i++) 124 args[nextArg++] = cppArgs[i]; 125 126 if (dir_arg) 127 args[nextArg++] = dir_arg; 128 129 args[nextArg++] = filename; 130 args[nextArg++] = NULL; 131 132 close(STDOUT_FILENO); 133 if (dup2(tmp_fd, STDOUT_FILENO) == -1) 134 exit(1); 135 136 execvp("g++", args); 137 138 exit(0); 139 } 140 141 int retval; 142 waitpid(pid, &retval, 0); 143 144 delete [] dir_arg; 145 146 // check for normal completion of CPP 147 if (!WIFEXITED(retval) || WEXITSTATUS(retval) != 0) 148 return false; 149 150 close(tmp_fd); 151 152 bool status = false; 153 154 status = load(tempfile); 155 156 unlink(tempfile); 157 158 return status; 159} 160#endif 161 162bool 163IniFile::load(const string &file) 164{ 165 ifstream f(file.c_str()); 166 167 if (!f.is_open()) 168 return false; 169 170 return load(f); 171} 172 173 174const string & 175IniFile::Entry::getValue() const 176{ 177 referenced = true; 178 return value; 179} 180 181 182void 183IniFile::Section::addEntry(const std::string &entryName, 184 const std::string &value, 185 bool append) 186{ 187 EntryTable::iterator ei = table.find(entryName); 188 189 if (ei == table.end()) { 190 // new entry 191 table[entryName] = new Entry(value); 192 } 193 else if (append) { 194 // append new reult to old entry 195 ei->second->appendValue(value); 196 } 197 else { 198 // override old entry 199 ei->second->setValue(value); 200 } 201} 202 203 204bool 205IniFile::Section::add(const std::string &assignment) 206{ 207 string::size_type offset = assignment.find('='); 208 if (offset == string::npos) { 209 // no '=' found 210 cerr << "Can't parse .ini line " << assignment << endl; 211 return false; 212 } 213 214 // if "+=" rather than just "=" then append value 215 bool append = (assignment[offset-1] == '+'); 216 217 string entryName = assignment.substr(0, append ? offset-1 : offset); 218 string value = assignment.substr(offset + 1); 219 220 eat_white(entryName); 221 eat_white(value); 222 223 addEntry(entryName, value, append); 224 return true; 225} 226 227 228IniFile::Entry * 229IniFile::Section::findEntry(const std::string &entryName) const 230{ 231 referenced = true; 232 233 EntryTable::const_iterator ei = table.find(entryName); 234 235 return (ei == table.end()) ? NULL : ei->second; 236} 237 238 239IniFile::Section * 240IniFile::addSection(const string §ionName) 241{ 242 SectionTable::iterator i = table.find(sectionName); 243 244 if (i != table.end()) { 245 return i->second; 246 } 247 else { 248 // new entry 249 Section *sec = new Section(); 250 table[sectionName] = sec; 251 return sec; 252 } 253} 254 255 256IniFile::Section * 257IniFile::findSection(const string §ionName) const 258{ 259 SectionTable::const_iterator i = table.find(sectionName); 260 261 return (i == table.end()) ? NULL : i->second; 262} 263 264 265// Take string of the form "<section>:<parameter>=<value>" and add to 266// database. Return true if successful, false if parse error. 267bool 268IniFile::add(const string &str) 269{ 270 // find ':' 271 string::size_type offset = str.find(':'); 272 if (offset == string::npos) // no ':' found 273 return false; 274 275 string sectionName = str.substr(0, offset); 276 string rest = str.substr(offset + 1); 277 278 eat_white(sectionName); 279 Section *s = addSection(sectionName); 280 281 return s->add(rest); 282} 283 284bool 285IniFile::load(istream &f) 286{ 287 Section *section = NULL; 288 289 while (!f.eof()) { 290 f >> ws; // Eat whitespace 291 if (f.eof()) { 292 break; 293 } 294 295 string line; 296 getline(f, line); 297 if (line.size() == 0) 298 continue; 299 300 eat_end_white(line); 301 int last = line.size() - 1; 302 303 if (line[0] == '[' && line[last] == ']') { 304 string sectionName = line.substr(1, last - 1); 305 eat_white(sectionName); 306 section = addSection(sectionName); 307 continue; 308 } 309 310 if (section == NULL) 311 continue; 312 313 if (!section->add(line)) 314 return false; 315 } 316 317 return true; 318} 319 320bool 321IniFile::find(const string §ionName, const string &entryName, 322 string &value) const 323{ 324 Section *section = findSection(sectionName); 325 if (section == NULL) 326 return false; 327 328 Entry *entry = section->findEntry(entryName); 329 if (entry == NULL) 330 return false; 331 332 value = entry->getValue(); 333 334 return true; 335} 336 337bool 338IniFile::sectionExists(const string §ionName) const 339{ 340 return findSection(sectionName) != NULL; 341} 342 343 344bool 345IniFile::Section::printUnreferenced(const string §ionName) 346{ 347 bool unref = false; 348 bool search_unref_entries = false; 349 vector<string> unref_ok_entries; 350 351 Entry *entry = findEntry("unref_entries_ok"); 352 if (entry != NULL) { 353 tokenize(unref_ok_entries, entry->getValue(), ' '); 354 if (unref_ok_entries.size()) { 355 search_unref_entries = true; 356 } 357 } 358 359 for (EntryTable::iterator ei = table.begin(); 360 ei != table.end(); ++ei) { 361 const string &entryName = ei->first; 362 Entry *entry = ei->second; 363 364 if (entryName == "unref_section_ok" || 365 entryName == "unref_entries_ok") 366 { 367 continue; 368 } 369 370 if (!entry->isReferenced()) { 371 if (search_unref_entries && 372 (std::find(unref_ok_entries.begin(), unref_ok_entries.end(), 373 entryName) != unref_ok_entries.end())) 374 { 375 continue; 376 } 377 378 cerr << "Parameter " << sectionName << ":" << entryName 379 << " not referenced." << endl; 380 unref = true; 381 } 382 } 383 384 return unref; 385} 386 387 388bool 389IniFile::printUnreferenced() 390{ 391 bool unref = false; 392 393 for (SectionTable::iterator i = table.begin(); 394 i != table.end(); ++i) { 395 const string §ionName = i->first; 396 Section *section = i->second; 397 398 if (!section->isReferenced()) { 399 if (section->findEntry("unref_section_ok") == NULL) { 400 cerr << "Section " << sectionName << " not referenced." 401 << endl; 402 unref = true; 403 } 404 } 405 else { 406 if (section->printUnreferenced(sectionName)) { 407 unref = true; 408 } 409 } 410 } 411 412 return unref; 413} 414 415 416void 417IniFile::Section::dump(const string §ionName) 418{ 419 for (EntryTable::iterator ei = table.begin(); 420 ei != table.end(); ++ei) { 421 cout << sectionName << ": " << (*ei).first << " => " 422 << (*ei).second->getValue() << "\n"; 423 } 424} 425 426void 427IniFile::dump() 428{ 429 for (SectionTable::iterator i = table.begin(); 430 i != table.end(); ++i) { 431 i->second->dump(i->first); 432 } 433} 434