Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/lib/parser/ini_parser/ini_parser.cc @ 9880

Last change on this file since 9880 was 9880, checked in by bensch, 18 years ago

new implementation of the IniParser
Now it is in Full stl-style, with iterators, and it does not have a strange internal state, that makes absolutely no sense

File size: 17.1 KB
RevLine 
[4597]1/*
[2064]2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11   ### File Specific:
[5014]12   main-programmer: Benjamin Grauer
13   co-programmer: Christian Meyer
14
15   2005-08-14: complete reimplementation:
16               now the File is parsed at the initialisation,
17               and informations is gathered there.
[2064]18*/
19
20
21#include "ini_parser.h"
[4381]22
[5944]23#if HAVE_CONFIG_H
24#include <config.h>
[5938]25#endif
26
[9880]27#include <cassert>
28#include <algorithm>
29
[7729]30#ifdef DEBUG_LEVEL
[9869]31 #include "../../../lib/util/debug.h"
[5933]32#else
33 #define PRINTF(x) printf
[7729]34 #define PRINT(x) printf
[5933]35#endif
36
[2064]37
[9880]38/// /// /// /// /// ///
39/// INI-PARSER NODE ///
40/// /// /// /// /// ///
41IniParser::Node::Node(const std::string& name, const std::string& comment)
42{
43  this->_name = name;
44  this->_comment = comment;
45}
[9406]46
[9880]47/// /// /// /// /// ////
48/// INI-PARSER ENTRY ///
49/// /// /// /// /// ////
50IniParser::Entry::Entry(const std::string& name, const std::string& value, const std::string& comment)
51    : IniParser::Node(name, comment), _value(value)
52{}
53
54void IniParser::Entry::debug() const
[2064]55{
[9880]56  printf("   %s = %s\n", this->name().c_str(), this->_value.c_str());
57}
[5933]58
[9880]59
60/// /// /// /// /// /// ///
61/// INI-PARSER SECTION  ///
62/// /// /// /// /// /// ///
63IniParser::Section::Section(const std::string& sectionName, const std::string& comment)
64    : IniParser::Node(sectionName, comment)
65{}
66
67IniParser::Entry& IniParser::Section::addEntry(const std::string& entryName, const std::string& value, const std::string& comment)
68{
69  Entry::iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName);
70  if (entry == this->_entries.end())
71  {
72    this->_entries.push_back(Entry(entryName, value, comment));
73    entry = --this->_entries.end();
74  }
75  return *entry;
[2064]76}
77
[9880]78bool IniParser::Section::editEntry(const std::string& entryName, const std::string& value, bool createMissing)
79{
80  Entry::iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName);
81  if (entry == this->_entries.end())
82  {
83    if (createMissing)
84    {
85      this->addEntry(entryName, value);
86      return true;
87    }
88    else
89      return false;
90  }
91  (*entry).setValue(value);
92  return true;
93}
[5933]94
[9880]95const std::string& IniParser::Section::getValue(const std::string& entryName, const std::string& defaultValue) const
[2064]96{
[9880]97  Entry::const_iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName);
98  if (entry != this->_entries.end())
99    return (*entry).value();
100  else
101    return defaultValue;
[2064]102}
103
[9880]104bool IniParser::Section::setEntryComment(const std::string& entryName, const std::string& comment)
105{
106  Entry::iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName);
107  if (entry != this->_entries.end())
108  {
109    (*entry).setComment(comment);
110    return true;
111  }
112  return false;
113}
[5933]114
[9880]115const std::string& IniParser::Section::getEntryComment(const std::string& entryName) const
116{
117  Entry::const_iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName);
118  if (entry != this->_entries.end())
119  {
120    return (*entry).comment();
121  }
122  return IniParser::_emptyString;
123}
[7221]124
[9880]125
126IniParser::Entry* IniParser::Section::getEntry(const std::string& entryName)
[2064]127{
[9880]128  Entry::iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName);
129  if (entry != this->_entries.end())
130    return &(*entry);
131  else
132    return NULL;
133}
[5944]134
[9880]135IniParser::Entry::const_iterator IniParser::Section::getEntryIt(const std::string& entryName) const
136{
137  return std::find(this->_entries.begin(), this->_entries.end(), entryName);
[2064]138}
139
[9880]140void IniParser::Section::clear()
141{
142  this->_entries.clear();
143}
[5933]144
[9880]145void IniParser::Section::debug() const
146{
147  printf(" [%s]\n", this->name().c_str());
148  for(Entry::const_iterator entry = this->_entries.begin(); entry != this->_entries.end(); ++entry)
149    (*entry).debug();
150}
151
152
153
154/// /// /// /// /// /// ///
155/// INI-PARSER DOCUMENT ///
156/// /// /// /// /// /// ///
157IniParser::Document::Document(const std::string& fileName, const std::string& comment)
158    : IniParser::Node(fileName, comment)
159{}
160
161IniParser::Section& IniParser::Document::addSection(const std::string& sectionName, const std::string& comment)
162{
163  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
164  if (section == this->_sections.end())
165  {
166    this->_sections.push_back(Section(sectionName, comment));
167    return *(--_sections.end());
168  }
169  else
170    return *section;
171}
172
173bool IniParser::Document::removeSection(const std::string& sectionName)
174{
175  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
176  if (section != this->_sections.end())
177  {
178    this->_sections.erase(section);
179    return true;
180  }
181  else
182    return false;
183}
184
185bool IniParser::Document::setSectionComment(const std::string& sectionName, const std::string& comment)
186{
187  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
188  if (section != this->_sections.end())
189  {
190    (*section).setComment(comment);
191    return true;
192  }
193  else
194    return false;
195}
196
197
198
199IniParser::Section* IniParser::Document::getSection(const std::string& sectionName)
200{
201  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
202  if (section != this->_sections.end())
203  {
204    return &(*section);
205  }
206  else
207    return NULL;
208}
209
210IniParser::Section::const_iterator IniParser::Document::getSectionIt(const std::string& sectionName) const
211{
212  Section::const_iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
213  return section;
214}
215
216bool IniParser::Document::addEntry(const std::string& sectionName, const std::string& entryName, const std::string& value, const std::string& comment)
217{
218  // locating section
219  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
220  if (section == this->_sections.end())
221  {
222    // creating section if not found!!
223    this->_sections.push_back(Section(sectionName));
224    section = --_sections.end();
225  }
226
227  (*section).addEntry(entryName, value, comment);
228  return true;
229}
230
231bool IniParser::Document::editEntry(const std::string& sectionName, const std::string& entryName, const std::string& value, bool createMissing)
232{
233  // locating section
234  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
235  if (section == this->_sections.end())
236  {
237    // creating section if not found!!
238    if (createMissing)
239    {
240      this->_sections.push_back(Section(sectionName));
241      section = --_sections.end();
242    }
243    else
244      return false;
245  }
246
247  return (*section).editEntry(entryName, value, createMissing);
248}
249
250const std::string& IniParser::Document::getValue(const std::string& sectionName, const std::string& entryName, const std::string& defaultValue) const
251{
252  // locating section
253  Section::const_iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
254  if (section != this->_sections.end())
255    return (*section).getValue(entryName, defaultValue);
256  return defaultValue;
257}
258
259bool IniParser::Document::setEntryComment(const std::string& sectionName, const std::string& entryName, const std::string& comment)
260{
261  // locating section
262  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
263  if (section != this->_sections.end())
264    return (*section).setEntryComment(entryName, comment);
265  else
266    return false;
267}
268
269const std::string& IniParser::Document::getEntryComment(const std::string& sectionName, const std::string& entryName) const
270{
271  Section::const_iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
272  if (section != this->_sections.end())
273    return (*section).getEntryComment(entryName);
274  else
275    return IniParser::_emptyString;
276}
277
278void IniParser::Document::clear()
279{
280  this->_sections.clear();
281}
282
283void IniParser::Document::debug() const
284{
285  for(Section::const_iterator section = this->_sections.begin(); section != this->_sections.end(); ++section)
286    (*section).debug();
287}
288
289
290
291
292/// /// /// /// /// /// //
293/// INI-PARSER ITSELF ////
294/// /// /// /// /// /// //
295const std::string IniParser::_emptyString = "";
[2141]296/**
[9880]297 * @brief constructs an IniParser using a file
298 * @param fileName: the path and name of the file to parse
[5934]299 */
[9880]300IniParser::IniParser (const std::string& fileName)
301    : _document(fileName)
[5934]302{
[9880]303  this->_fileName = fileName;
304  if (!fileName.empty())
305    this->readFile(fileName);
[5934]306}
307
308
309/**
[9880]310 * @brief removes the IniParser from memory
311 */
312IniParser::~IniParser ()
313{}
314
315/**
[5944]316 * @brief opens a file to parse
[5017]317 * @param fileName: path and name of the new file to parse
[9880]318 * @param keepSettings if the Settings (if already some are set) should be kept (false by default)
[5014]319 * @return true on success false otherwise;
[5934]320 *
321 * If there was already an opened file, the file will be closed,
322 * and the new one will be opened.
[5933]323 */
[9880]324bool IniParser::readFile(const std::string& fileName, bool keepSettings)
[2064]325{
[5934]326  FILE*    stream;           //< The stream we use to read the file.
[9880]327  int      lineCount = 0;    //< The Count of lines read.
328  std::list<std::string>  commentList;     //< A list of Comments.
329  Section* currentSection = NULL;
[5934]330
[9880]331  if (!keepSettings)
332    this->_document.clear();
[7221]333
334  if( (stream = fopen (fileName.c_str(), "r")) == NULL)
[5014]335  {
[7661]336    PRINTF(1)("IniParser could not open %s for reading\n", fileName.c_str());
[5014]337    return false;
338  }
339  else
340  {
[9880]341    this->_fileName = fileName;
[5014]342
343    /////////////////////////////
344    // READING IN THE INI-FILE //
345    /////////////////////////////
[7221]346    char lineBuffer[PARSELINELENGHT+1];
347    char buffer[PARSELINELENGHT+1];
[5021]348    const char* lineBegin;
[5014]349    char* ptr;
350
[5319]351    while( fgets (lineBuffer, PARSELINELENGHT, stream))
[2551]352    {
[5021]353      lineBegin = lineBuffer;
[5014]354      // remove newline char, and \0-terminate
355      if( (ptr = strchr( lineBuffer, '\n')) != NULL)
356        *ptr = 0;
[7221]357      else
358        lineBuffer[PARSELINELENGHT] = 0;
[5021]359      // cut up to the beginning of the line.
360      while((*lineBegin == ' ' || *lineBegin == '\t') && lineBegin < lineBuffer + strlen(lineBuffer))
361        ++lineBegin;
[5946]362
[7221]363      if ( !strcmp( lineBegin, "" ) )
364        continue;
365
[5946]366      // check if we have a FileComment
367      if ( (*lineBegin == '#' || *lineBegin == ';'))
368      {
[9406]369        std::string newCommenLine = lineBegin;
[9880]370        commentList.push_back(newCommenLine);
[5946]371        continue;
372      }
[9880]373      if (lineCount == 0 && !commentList.empty())
[5946]374      {
[9880]375        this->setNodeComment(&this->_document, &commentList);
[5946]376        lineCount++;
377      }
378
[2551]379      // check for section identifyer
[5021]380      else if( sscanf (lineBegin, "[%s", buffer) == 1)
[5014]381      {
382        if( (ptr = strchr( buffer, ']')) != NULL)
[4597]383        {
[5014]384          *ptr = 0;
[9880]385          Section& node = this->_document.addSection(buffer);
386          setNodeComment(&node, &commentList);
387          currentSection = &node;
[4597]388        }
[5014]389      }
[5018]390      // check for Entry identifier (Entry = Value)
[5021]391      else if( (ptr = strchr( lineBegin, '=')) != NULL)
[5014]392      {
[9880]393        if (currentSection == NULL)
[5014]394        {
[5021]395          PRINTF(2)("Not in a Section yet for %s\n", lineBegin);
[5946]396          lineCount++;
[5014]397          continue;
398        }
[7221]399        if( ptr == lineBegin)
400        {
[5946]401          lineCount++;
[5014]402          continue;
[5946]403        }
[5014]404        char* valueBegin = ptr+1;
[5021]405        while ((*valueBegin == ' ' || *valueBegin == '\t') && valueBegin <= lineBegin + strlen(lineBegin))
[5014]406          ++valueBegin;
[5022]407        char* valueEnd = valueBegin + strlen(valueBegin)-1;
408        while ((*valueEnd == ' ' || *valueEnd == '\t') && valueEnd >= valueBegin)
409          --valueEnd;
410        valueEnd[1] = '\0';
[5018]411        char* nameEnd = ptr-1;
[5021]412        while ((*nameEnd == ' ' || *nameEnd == '\t' ) && nameEnd >= lineBegin)
[5014]413          --nameEnd;
414        nameEnd[1] = '\0';
[5018]415
[9880]416        Entry& node = currentSection->addEntry(lineBegin, valueBegin);
417        this->setNodeComment(&node, &commentList);
[5945]418
419        lineCount++;
[5014]420      }
[2551]421    }
[5014]422  }
423  fclose(stream);
424  return true;
[2064]425}
426
[5933]427
[2141]428/**
[5934]429 * @brief opens a file and writes to it
[5020]430 * @param fileName: path and name of the new file to write to
431 * @return true on success false otherwise
432 */
[7221]433bool IniParser::writeFile(const std::string& fileName) const
[5020]434{
435  FILE*    stream;           //!< The stream we use to read the file.
[7221]436  if( fileName.empty())
[5020]437    return false;
438
[7221]439  if( (stream = fopen (fileName.c_str(), "w")) == NULL)
[5020]440  {
[7661]441    PRINTF(1)("IniParser could not open %s for writing\n", fileName.c_str());
[5020]442    return false;
443  }
444  else
445  {
[9880]446    if (!this->_document.comment().empty())
447      fprintf(stream, "%s\n\n", this->_document.comment().c_str());
[5949]448
[9880]449    Section::const_iterator section;
450    for (section = this->_document.sections().begin(); section != this->_document.sections().end(); ++section)
[7221]451    {
[9880]452      if (!(*section).comment().empty())
453        fprintf(stream, "%s", (*section).comment().c_str());
454      fprintf(stream, "\n [%s]\n", (*section).name().c_str());
[7221]455
[9880]456      Entry::const_iterator entry;
457      for (entry = (*section).entries().begin(); entry != (*section).entries().end(); ++entry)
[5020]458      {
[9880]459        if (!(*entry).comment().empty())
460          fprintf(stream, "%s", (*entry).comment().c_str());
461        fprintf(stream, "   %s = %s\n", (*entry).name().c_str(), (*entry).value().c_str());
[5020]462      }
[7221]463    }
[5020]464  }
465  fclose(stream);
[9406]466  return true;
[5020]467}
468
[7221]469void IniParser::setFileComment(const std::string& fileComment)
[5945]470{
[9880]471  this->_document.setComment(fileComment);
[5945]472}
473
[5021]474/**
[5934]475 * @brief adds a section to the list of Sections,
[5021]476 * if no Section list is availiable, it will create it
477 * @param sectionName the Name of the section to add
478 * @return true on success... there is only success or segfault :)
479 */
[9880]480IniParser::Section& IniParser::addSection(const std::string& sectionName)
[5020]481{
[9880]482  return this->_document.addSection(sectionName);
[5020]483}
484
[5933]485
[5020]486/**
[5934]487 * @brief adds a new Entry to either the currentSection or the section called by sectionName
[5020]488 * @param entryName the Name of the Entry to add
489 * @param value the value to assign to this entry
490 * @param sectionName if NULL then this entry will be set to the currentSection
491 * otherwise to the section refered to by sectionName.
492 * If both are NULL no entry will be added
[9880]493 * @return The Entry, that was added.
[5020]494 */
[9880]495bool IniParser::addEntry(const std::string& sectionName, const std::string& entryName, const std::string& value, const std::string& comment)
[5020]496{
[9880]497  return this->_document.addEntry(sectionName, entryName, value, comment);
[5020]498}
499
[7256]500/**
501 * @brief edits the entry speciefied by entryName in sectionName/currentSection or creates it if it doesn't exist
502 * @param entryName the Name of the Entry to add
503 * @param value the value to assign to this entry
504 * @param sectionName if NULL then this entry will be set to the currentSection
505 * otherwise to the section refered to by sectionName.
506 * If both are NULL no entry will be added
507 * @return true if everything is ok false on error
508 */
[9880]509bool IniParser::editEntry(const std::string& sectionName, const std::string& entryName, const std::string& value, bool createMissing)
[7256]510{
[9880]511  return this->_document.editEntry(sectionName, entryName, value, createMissing);
[7256]512}
513
514
[5020]515/**
[5934]516 * @brief directly acesses an entry in a section
[9880]517 * @param sectionName: the section where the entry is to be found
[5014]518 * @param entryName: the name of the entry to find
519 * @param defaultValue: what should be returned in case the entry cannot be found
[9880]520 * @return The Value of the Entry.
521 */
522const std::string& IniParser::getValue(const std::string& sectionName, const std::string& entryName, const std::string& defaultValue) const
[2065]523{
[9880]524  return this->_document.getValue(sectionName, entryName, defaultValue);
[2065]525}
[5014]526
[5947]527/**
528 * Set the Comment of a specified Entry.
[5952]529 * @param comment the Comment to set
530 * @param entryName the Name of the Entry
531 * @param sectionName the Name of the Section
[5947]532 */
[9880]533void IniParser::setEntryComment(const std::string& sectionName, const std::string& entryName, const std::string& comment)
[5945]534{
[9880]535  this->_document.setEntryComment(sectionName, entryName, comment);
[5945]536}
537
[5947]538/**
[5952]539 * @param entryName the Entry to query for
540 * @param sectionName the Section to Query for
541 * @returns the queried Comment.
[5947]542 */
[9880]543const std::string& IniParser::getEntryComment(const std::string& sectionName, const std::string& entryName) const
[5945]544{
[9880]545  return this->_document.getEntryComment(sectionName, entryName);
[5945]546}
547
[5017]548/**
[9880]549 * @brief output the whole tree in a nice and easy way.
[5945]550 */
[9880]551void IniParser::debug() const
[5945]552{
[9880]553  PRINT(0)("Iniparser '%s' - debug\n", this->_fileName.c_str());
554  if (!this->_document.comment().empty())
555    PRINT(0)("FileComment:\n '%s'\n\n", this->_document.comment().c_str());
[5945]556
[9880]557  if (!this->_document.sections().empty())
558    this->_document.debug();
[5945]559  else
[9880]560    PRINTF(0)("no Sections Defined in this ini-file (%s).\n", _fileName.c_str());
[5945]561}
562
563
564/**
[9880]565 * takes lines together to form one NodeComment, ereasing the commentList
[5935]566 */
[9880]567void IniParser::setNodeComment(Node* node, std::list<std::string>* comments)
[5935]568{
[9880]569  assert(node != NULL);
570  assert(comments != NULL);
[5935]571
[9880]572  std::string comment;
573  if (comments->empty())
[7221]574  {
[9880]575    comment = "";
[5946]576  }
[9880]577  else
[5946]578  {
[9880]579    while (!comments->empty())
[5014]580    {
[9880]581      if (!comment.empty())
582        comment += "\n";
583      comment += comments->front();
584      comments->pop_front();
[5014]585    }
586  }
[9880]587  node->setComment(comment);
[5014]588}
[5938]589
[9880]590
Note: See TracBrowser for help on using the repository browser.