Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

some movements

File size: 19.4 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.
[9883]18   2006-10-10: Complete reimplementation again :)
19               New STL-like style. The Parser has no state itself
20               Elements as Nodes
[2064]21*/
[9884]22/*!
23 * @file ini_parser.cc
24 */
[2064]25
26#include "ini_parser.h"
[4381]27
[9880]28#include <cassert>
29#include <algorithm>
30
[9881]31#define PARSELINELENGHT     1024       //!< how many chars to read at once
[5933]32
[2064]33
[9880]34/// /// /// /// /// ///
35/// INI-PARSER NODE ///
36/// /// /// /// /// ///
[9881]37/**
38 * @brief Constructs a Node
39 * @param name The name of the Node
40 * @param comment The comment of the Node.
41 */
[9880]42IniParser::Node::Node(const std::string& name, const std::string& comment)
43{
44  this->_name = name;
45  this->_comment = comment;
46}
[9406]47
[9880]48/// /// /// /// /// ////
49/// INI-PARSER ENTRY ///
50/// /// /// /// /// ////
[9881]51/**
52 * @brief Constructs a new Entry
53 * @param name the Name of the Entry
54 * @param value The name of the Value
55 * @param comment The Comment used for the Entry
56 */
[9880]57IniParser::Entry::Entry(const std::string& name, const std::string& value, const std::string& comment)
58    : IniParser::Node(name, comment), _value(value)
59{}
60
[9881]61/**
62 * @brief Displays some nice debug info.
63 */
[9880]64void IniParser::Entry::debug() const
[2064]65{
[9880]66  printf("   %s = %s\n", this->name().c_str(), this->_value.c_str());
67}
[5933]68
[9880]69
70/// /// /// /// /// /// ///
71/// INI-PARSER SECTION  ///
72/// /// /// /// /// /// ///
[9881]73/**
74 * @brief constructs a new Section
75 * @param sectionName The name of the Section
76 * @param comment The Comment for this section
77 */
[9880]78IniParser::Section::Section(const std::string& sectionName, const std::string& comment)
79    : IniParser::Node(sectionName, comment)
80{}
81
[9881]82/**
83 * @brief Adds a new Entry to this Section
84 * @param entryName The name of the Entry
85 * @param value The Value of the Section
86 * @param comment The Comment
87 * @returns Reference to the Entry added.
88 * @see IniParser::Entry::Entry
89 */
[9880]90IniParser::Entry& IniParser::Section::addEntry(const std::string& entryName, const std::string& value, const std::string& comment)
91{
92  Entry::iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName);
93  if (entry == this->_entries.end())
94  {
95    this->_entries.push_back(Entry(entryName, value, comment));
96    entry = --this->_entries.end();
97  }
98  return *entry;
[2064]99}
100
[9881]101/**
102 * @brief edits an Entry's Value
103 * @param entryName The Entry to edit
104 * @param value The Value to change
105 * @param createMissing If the Entry is missing it is created if true.
106 * @return true on success.
107 */
[9880]108bool IniParser::Section::editEntry(const std::string& entryName, const std::string& value, bool createMissing)
109{
110  Entry::iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName);
111  if (entry == this->_entries.end())
112  {
113    if (createMissing)
114    {
115      this->addEntry(entryName, value);
116      return true;
117    }
118    else
119      return false;
120  }
121  (*entry).setValue(value);
122  return true;
123}
[5933]124
[9881]125/**
126 * @param entryName The name of the entry to search for
127 * @param defaultValue The returned value, if the entry is not found
128 * @return The Value of the Entry, or defaultValue, if not found.
129 */
[9880]130const std::string& IniParser::Section::getValue(const std::string& entryName, const std::string& defaultValue) const
[2064]131{
[9880]132  Entry::const_iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName);
133  if (entry != this->_entries.end())
134    return (*entry).value();
135  else
136    return defaultValue;
[2064]137}
138
[9881]139/**
140 * @brief sets a Comment to an Entry
141 * @param entryName The Name of the Entry to set the comment to.
142 * @param comment the Comment.
143 * @return true on success (entry found and setup).
144 */
[9880]145bool IniParser::Section::setEntryComment(const std::string& entryName, const std::string& comment)
146{
147  Entry::iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName);
148  if (entry != this->_entries.end())
149  {
150    (*entry).setComment(comment);
151    return true;
152  }
153  return false;
154}
[5933]155
[9881]156/**
157 * @brief retrieves a Comment of an Entry
158 * @param entryName The Entry to get the comment of.
159 * @return The Comment, or "" if the Entry was not found.
160 */
[9880]161const std::string& IniParser::Section::getEntryComment(const std::string& entryName) const
162{
163  Entry::const_iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName);
164  if (entry != this->_entries.end())
165  {
166    return (*entry).comment();
167  }
168  return IniParser::_emptyString;
169}
[7221]170
[9880]171
[9881]172/**
173 * @brief retrieves a pointer to an Entry
174 * @param entryName The Name of the Entry.
175 * @return the located Entry or NULL!
176 * @note beware of NULL!
177 */
[9880]178IniParser::Entry* IniParser::Section::getEntry(const std::string& entryName)
[2064]179{
[9880]180  Entry::iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName);
181  if (entry != this->_entries.end())
182    return &(*entry);
183  else
184    return NULL;
185}
[5944]186
[9881]187/**
188 * @brief retrieves an Iterator to an Entry called entryName within this section
189 * @param entryName the name of the Entry
190 * @return The iterator to the position, or end();
191 * @see Section::end();
192 */
[9880]193IniParser::Entry::const_iterator IniParser::Section::getEntryIt(const std::string& entryName) const
194{
195  return std::find(this->_entries.begin(), this->_entries.end(), entryName);
[2064]196}
197
[9881]198/**
199 * @brief clears the Section's entries (flushes them)
200 */
[9880]201void IniParser::Section::clear()
202{
203  this->_entries.clear();
204}
[5933]205
[9881]206/**
207 * @brief print out some debug info
208 */
[9880]209void IniParser::Section::debug() const
210{
211  printf(" [%s]\n", this->name().c_str());
212  for(Entry::const_iterator entry = this->_entries.begin(); entry != this->_entries.end(); ++entry)
213    (*entry).debug();
214}
215
216
217
218/// /// /// /// /// /// ///
219/// INI-PARSER DOCUMENT ///
220/// /// /// /// /// /// ///
[9881]221/**
222 * @brief Constructs a new Document
223 * @param fileName The Name of the Document.
224 * @param comment Some Comment
225 */
[9880]226IniParser::Document::Document(const std::string& fileName, const std::string& comment)
227    : IniParser::Node(fileName, comment)
228{}
229
[9881]230/**
231 * @brief Adds a new Section to the Document
232 * @param sectionName The Name of the Section to add
233 * @param comment A comment for the section.
234 * @return A Reference to the newly added section.
235 */
[9880]236IniParser::Section& IniParser::Document::addSection(const std::string& sectionName, const std::string& comment)
237{
238  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
239  if (section == this->_sections.end())
240  {
241    this->_sections.push_back(Section(sectionName, comment));
242    return *(--_sections.end());
243  }
244  else
245    return *section;
246}
247
[9881]248/**
249 * @brief removes a Section from the Document.
250 * @param sectionName The section to remove
251 * @return true on success (section was found).
252 */
[9880]253bool IniParser::Document::removeSection(const std::string& sectionName)
254{
255  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
256  if (section != this->_sections.end())
257  {
258    this->_sections.erase(section);
259    return true;
260  }
261  else
262    return false;
263}
264
[9881]265/**
266 * @brief Sets a comment to a section
267 * @param sectionName The name of the section
268 * @param comment The Comment.
269 * @return True on success (section was found).
270 */
[9883]271bool IniParser::Document::setSectionsComment(const std::string& sectionName, const std::string& comment)
[9880]272{
273  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
274  if (section != this->_sections.end())
275  {
276    (*section).setComment(comment);
277    return true;
278  }
279  else
280    return false;
281}
282
[9883]283/**
284 * @brief Retrieves the comment for a specified section
285 * @param sectionName The name of the section
286 * @returns The Comment of the section.
287 */
288const std::string& IniParser::Document::getSectionsComment(const std::string& sectionName) const
289{
290  Section::const_iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
291  if (section != this->_sections.end())
292  {
293    return (*section).comment();
294  }
295  else
296    return IniParser::_emptyString;
297}
[9880]298
[9881]299/**
300 * @brief Queries for a Section within the document returning a Pointer to it
301 * @param sectionName The Section to search for.
302 * @return A pointer to the section, of NULL if not found.
303 * @brief beware of the NULL-pointer!
304 */
[9880]305IniParser::Section* IniParser::Document::getSection(const std::string& sectionName)
306{
307  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
308  if (section != this->_sections.end())
309  {
310    return &(*section);
311  }
312  else
313    return NULL;
314}
315
[9881]316/**
317 * @brief Queries for a Section within the document returning a Pointer to it
318 * @param sectionName The Section to search for.
319 * @return An iterator to the Section, or end()
320 * @see Section::end().
321 */
[9880]322IniParser::Section::const_iterator IniParser::Document::getSectionIt(const std::string& sectionName) const
323{
324  Section::const_iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
325  return section;
326}
327
[9881]328/**
329 * @brief adds a new Entry to a designated section.
330 * @param sectionName The name of the Section
331 * @param entryName The Name of the Entry to add
332 * @param value The Value to set for the entry
333 * @param comment optionally a comment.
334 * @return true on success (always true)
335 *
336 * @note the section will also be created on the go, if it did not exists so far!
337 */
[9880]338bool IniParser::Document::addEntry(const std::string& sectionName, const std::string& entryName, const std::string& value, const std::string& comment)
339{
340  // locating section
341  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
342  if (section == this->_sections.end())
343  {
344    // creating section if not found!!
345    this->_sections.push_back(Section(sectionName));
346    section = --_sections.end();
347  }
348
349  (*section).addEntry(entryName, value, comment);
350  return true;
351}
352
[9881]353/**
354 * @brief edits an Entry, and possibly creating it.
355 * @param sectionName The Section's name to edit the entry in.
356 * @param entryName The Name of the Entry to edit the value from
357 * @param value The new value for the Entry.
358 * @param createMissing if true, the Entry, (and the section) will be created.
359 * @return true on success, false otherwise.
360 */
[9880]361bool IniParser::Document::editEntry(const std::string& sectionName, const std::string& entryName, const std::string& value, bool createMissing)
362{
363  // locating section
364  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
365  if (section == this->_sections.end())
366  {
367    // creating section if not found!!
368    if (createMissing)
369    {
370      this->_sections.push_back(Section(sectionName));
371      section = --_sections.end();
372    }
373    else
374      return false;
375  }
376
377  return (*section).editEntry(entryName, value, createMissing);
378}
379
[9881]380/**
381 * @brief Retrieve a value from an Entry.
382 * @param sectionName The Name of the Section the enrty is in
383 * @param entryName The Name of the entry
384 * @param defaultValue A default value, if the entry is not found
385 * @return A string containing the value, or defaultValue, if the Section->Entry was not found.
386 */
[9880]387const std::string& IniParser::Document::getValue(const std::string& sectionName, const std::string& entryName, const std::string& defaultValue) const
388{
389  // locating section
390  Section::const_iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
391  if (section != this->_sections.end())
392    return (*section).getValue(entryName, defaultValue);
393  return defaultValue;
394}
395
[9881]396/**
397 * @brief Sets a Comment to an Entry.
398 * @param sectionName The name of the Section.
399 * @param entryName The name of the Entry.
400 * @param comment The comment to set to this Entry
401 * @return true on success (Section->Entry found).
402 */
[9880]403bool IniParser::Document::setEntryComment(const std::string& sectionName, const std::string& entryName, const std::string& comment)
404{
405  // locating section
406  Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
407  if (section != this->_sections.end())
408    return (*section).setEntryComment(entryName, comment);
409  else
410    return false;
411}
412
[9881]413/**
414 * @brief retrieved the comment of an Entry.
415 * @param sectionName The section.
416 * @param entryName The Entry to get the comment from
417 * @return the Comment of the Entry.
418 */
[9880]419const std::string& IniParser::Document::getEntryComment(const std::string& sectionName, const std::string& entryName) const
420{
421  Section::const_iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName);
422  if (section != this->_sections.end())
423    return (*section).getEntryComment(entryName);
424  else
425    return IniParser::_emptyString;
426}
427
[9881]428/**
429 * @brief clears all sections.
430 */
[9880]431void IniParser::Document::clear()
432{
433  this->_sections.clear();
434}
435
[9881]436/**
437 * @brief Print some nice debug output.
438 */
[9880]439void IniParser::Document::debug() const
440{
441  for(Section::const_iterator section = this->_sections.begin(); section != this->_sections.end(); ++section)
442    (*section).debug();
443}
444
445
446
447
448/// /// /// /// /// /// //
449/// INI-PARSER ITSELF ////
450/// /// /// /// /// /// //
451const std::string IniParser::_emptyString = "";
[2141]452/**
[9880]453 * @brief constructs an IniParser using a file
454 * @param fileName: the path and name of the file to parse
[5934]455 */
[9880]456IniParser::IniParser (const std::string& fileName)
457    : _document(fileName)
[5934]458{
[9880]459  this->_fileName = fileName;
460  if (!fileName.empty())
461    this->readFile(fileName);
[5934]462}
463
464
465/**
[9880]466 * @brief removes the IniParser from memory
467 */
468IniParser::~IniParser ()
469{}
470
471/**
[5944]472 * @brief opens a file to parse
[5017]473 * @param fileName: path and name of the new file to parse
[9880]474 * @param keepSettings if the Settings (if already some are set) should be kept (false by default)
[5014]475 * @return true on success false otherwise;
[5934]476 *
477 * If there was already an opened file, the file will be closed,
478 * and the new one will be opened.
[5933]479 */
[9880]480bool IniParser::readFile(const std::string& fileName, bool keepSettings)
[2064]481{
[5934]482  FILE*    stream;           //< The stream we use to read the file.
[9880]483  int      lineCount = 0;    //< The Count of lines read.
484  std::list<std::string>  commentList;     //< A list of Comments.
485  Section* currentSection = NULL;
[5934]486
[9880]487  if (!keepSettings)
488    this->_document.clear();
[7221]489
490  if( (stream = fopen (fileName.c_str(), "r")) == NULL)
[5014]491  {
[9881]492    printf("ERROR:: IniParser could not open %s for reading\n", fileName.c_str());
[5014]493    return false;
494  }
495  else
496  {
[9880]497    this->_fileName = fileName;
[5014]498
499    /////////////////////////////
500    // READING IN THE INI-FILE //
501    /////////////////////////////
[7221]502    char lineBuffer[PARSELINELENGHT+1];
503    char buffer[PARSELINELENGHT+1];
[5021]504    const char* lineBegin;
[5014]505    char* ptr;
506
[5319]507    while( fgets (lineBuffer, PARSELINELENGHT, stream))
[2551]508    {
[5021]509      lineBegin = lineBuffer;
[5014]510      // remove newline char, and \0-terminate
511      if( (ptr = strchr( lineBuffer, '\n')) != NULL)
512        *ptr = 0;
[7221]513      else
514        lineBuffer[PARSELINELENGHT] = 0;
[5021]515      // cut up to the beginning of the line.
516      while((*lineBegin == ' ' || *lineBegin == '\t') && lineBegin < lineBuffer + strlen(lineBuffer))
517        ++lineBegin;
[5946]518
[7221]519      if ( !strcmp( lineBegin, "" ) )
520        continue;
521
[5946]522      // check if we have a FileComment
523      if ( (*lineBegin == '#' || *lineBegin == ';'))
524      {
[9406]525        std::string newCommenLine = lineBegin;
[9880]526        commentList.push_back(newCommenLine);
[5946]527        continue;
528      }
[9880]529      if (lineCount == 0 && !commentList.empty())
[5946]530      {
[9880]531        this->setNodeComment(&this->_document, &commentList);
[5946]532        lineCount++;
533      }
534
[2551]535      // check for section identifyer
[5021]536      else if( sscanf (lineBegin, "[%s", buffer) == 1)
[5014]537      {
538        if( (ptr = strchr( buffer, ']')) != NULL)
[4597]539        {
[5014]540          *ptr = 0;
[9880]541          Section& node = this->_document.addSection(buffer);
542          setNodeComment(&node, &commentList);
543          currentSection = &node;
[4597]544        }
[5014]545      }
[5018]546      // check for Entry identifier (Entry = Value)
[5021]547      else if( (ptr = strchr( lineBegin, '=')) != NULL)
[5014]548      {
[9880]549        if (currentSection == NULL)
[5014]550        {
[9881]551          printf("WARNING:: Not in a Section yet for %s\n", lineBegin);
[5946]552          lineCount++;
[5014]553          continue;
554        }
[7221]555        if( ptr == lineBegin)
556        {
[5946]557          lineCount++;
[5014]558          continue;
[5946]559        }
[5014]560        char* valueBegin = ptr+1;
[5021]561        while ((*valueBegin == ' ' || *valueBegin == '\t') && valueBegin <= lineBegin + strlen(lineBegin))
[5014]562          ++valueBegin;
[5022]563        char* valueEnd = valueBegin + strlen(valueBegin)-1;
564        while ((*valueEnd == ' ' || *valueEnd == '\t') && valueEnd >= valueBegin)
565          --valueEnd;
566        valueEnd[1] = '\0';
[5018]567        char* nameEnd = ptr-1;
[5021]568        while ((*nameEnd == ' ' || *nameEnd == '\t' ) && nameEnd >= lineBegin)
[5014]569          --nameEnd;
570        nameEnd[1] = '\0';
[5018]571
[9880]572        Entry& node = currentSection->addEntry(lineBegin, valueBegin);
573        this->setNodeComment(&node, &commentList);
[5945]574
575        lineCount++;
[5014]576      }
[2551]577    }
[5014]578  }
579  fclose(stream);
580  return true;
[2064]581}
582
[5933]583
[2141]584/**
[9881]585 * @brief opens a file and writes to it.
586 * @param fileName: path and name of the new file to write to. If empty the internal value is used.
[5020]587 * @return true on success false otherwise
588 */
[7221]589bool IniParser::writeFile(const std::string& fileName) const
[5020]590{
[9881]591  std::string parseFile;
[5020]592  FILE*    stream;           //!< The stream we use to read the file.
[7221]593  if( fileName.empty())
[9881]594    parseFile = _fileName;
595  else
596    parseFile = fileName;
[5020]597
[9881]598  if( (stream = fopen (parseFile.c_str(), "w")) == NULL)
[5020]599  {
[9881]600    printf("ERROR:: IniParser could not open %s for writing\n", parseFile.c_str());
[5020]601    return false;
602  }
603  else
604  {
[9880]605    if (!this->_document.comment().empty())
606      fprintf(stream, "%s\n\n", this->_document.comment().c_str());
[5949]607
[9880]608    Section::const_iterator section;
609    for (section = this->_document.sections().begin(); section != this->_document.sections().end(); ++section)
[7221]610    {
[9880]611      if (!(*section).comment().empty())
612        fprintf(stream, "%s", (*section).comment().c_str());
613      fprintf(stream, "\n [%s]\n", (*section).name().c_str());
[7221]614
[9880]615      Entry::const_iterator entry;
616      for (entry = (*section).entries().begin(); entry != (*section).entries().end(); ++entry)
[5020]617      {
[9880]618        if (!(*entry).comment().empty())
619          fprintf(stream, "%s", (*entry).comment().c_str());
620        fprintf(stream, "   %s = %s\n", (*entry).name().c_str(), (*entry).value().c_str());
[5020]621      }
[7221]622    }
[5020]623  }
624  fclose(stream);
[9406]625  return true;
[5020]626}
627
[5945]628
[5021]629/**
[9880]630 * @brief output the whole tree in a nice and easy way.
[5945]631 */
[9880]632void IniParser::debug() const
[5945]633{
[9881]634  printf("Iniparser '%s' - debug\n", this->_fileName.c_str());
[9880]635  if (!this->_document.comment().empty())
[9881]636    printf("FileComment:\n '%s'\n\n", this->_document.comment().c_str());
[5945]637
[9880]638  if (!this->_document.sections().empty())
639    this->_document.debug();
[5945]640  else
[9881]641    printf("no Sections Defined in this ini-file (%s).\n", _fileName.c_str());
[5945]642}
643
644
645/**
[9881]646 * @brief takes lines together to form one NodeComment, ereasing the commentList
647 * @param node the Node to apply the Comment to.
648 * @param comments the CommentList to append.
[5935]649 */
[9880]650void IniParser::setNodeComment(Node* node, std::list<std::string>* comments)
[5935]651{
[9880]652  assert(node != NULL);
653  assert(comments != NULL);
[5935]654
[9880]655  std::string comment;
656  if (comments->empty())
[7221]657  {
[9880]658    comment = "";
[5946]659  }
[9880]660  else
[5946]661  {
[9880]662    while (!comments->empty())
[5014]663    {
[9880]664      if (!comment.empty())
665        comment += "\n";
666      comment += comments->front();
667      comments->pop_front();
[5014]668    }
669  }
[9880]670  node->setComment(comment);
[5014]671}
[5938]672
[9880]673
Note: See TracBrowser for help on using the repository browser.