Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

some movements

File size: 19.4 KB
Line 
1/*
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:
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.
18   2006-10-10: Complete reimplementation again :)
19               New STL-like style. The Parser has no state itself
20               Elements as Nodes
21*/
22/*!
23 * @file ini_parser.cc
24 */
25
26#include "ini_parser.h"
27
28#include <cassert>
29#include <algorithm>
30
31#define PARSELINELENGHT     1024       //!< how many chars to read at once
32
33
34/// /// /// /// /// ///
35/// INI-PARSER NODE ///
36/// /// /// /// /// ///
37/**
38 * @brief Constructs a Node
39 * @param name The name of the Node
40 * @param comment The comment of the Node.
41 */
42IniParser::Node::Node(const std::string& name, const std::string& comment)
43{
44  this->_name = name;
45  this->_comment = comment;
46}
47
48/// /// /// /// /// ////
49/// INI-PARSER ENTRY ///
50/// /// /// /// /// ////
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 */
57IniParser::Entry::Entry(const std::string& name, const std::string& value, const std::string& comment)
58    : IniParser::Node(name, comment), _value(value)
59{}
60
61/**
62 * @brief Displays some nice debug info.
63 */
64void IniParser::Entry::debug() const
65{
66  printf("   %s = %s\n", this->name().c_str(), this->_value.c_str());
67}
68
69
70/// /// /// /// /// /// ///
71/// INI-PARSER SECTION  ///
72/// /// /// /// /// /// ///
73/**
74 * @brief constructs a new Section
75 * @param sectionName The name of the Section
76 * @param comment The Comment for this section
77 */
78IniParser::Section::Section(const std::string& sectionName, const std::string& comment)
79    : IniParser::Node(sectionName, comment)
80{}
81
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 */
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;
99}
100
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 */
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}
124
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 */
130const std::string& IniParser::Section::getValue(const std::string& entryName, const std::string& defaultValue) const
131{
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;
137}
138
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 */
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}
155
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 */
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}
170
171
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 */
178IniParser::Entry* IniParser::Section::getEntry(const std::string& entryName)
179{
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}
186
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 */
193IniParser::Entry::const_iterator IniParser::Section::getEntryIt(const std::string& entryName) const
194{
195  return std::find(this->_entries.begin(), this->_entries.end(), entryName);
196}
197
198/**
199 * @brief clears the Section's entries (flushes them)
200 */
201void IniParser::Section::clear()
202{
203  this->_entries.clear();
204}
205
206/**
207 * @brief print out some debug info
208 */
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/// /// /// /// /// /// ///
221/**
222 * @brief Constructs a new Document
223 * @param fileName The Name of the Document.
224 * @param comment Some Comment
225 */
226IniParser::Document::Document(const std::string& fileName, const std::string& comment)
227    : IniParser::Node(fileName, comment)
228{}
229
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 */
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
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 */
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
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 */
271bool IniParser::Document::setSectionsComment(const std::string& sectionName, const std::string& comment)
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
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}
298
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 */
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
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 */
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
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 */
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
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 */
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
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 */
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
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 */
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
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 */
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
428/**
429 * @brief clears all sections.
430 */
431void IniParser::Document::clear()
432{
433  this->_sections.clear();
434}
435
436/**
437 * @brief Print some nice debug output.
438 */
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 = "";
452/**
453 * @brief constructs an IniParser using a file
454 * @param fileName: the path and name of the file to parse
455 */
456IniParser::IniParser (const std::string& fileName)
457    : _document(fileName)
458{
459  this->_fileName = fileName;
460  if (!fileName.empty())
461    this->readFile(fileName);
462}
463
464
465/**
466 * @brief removes the IniParser from memory
467 */
468IniParser::~IniParser ()
469{}
470
471/**
472 * @brief opens a file to parse
473 * @param fileName: path and name of the new file to parse
474 * @param keepSettings if the Settings (if already some are set) should be kept (false by default)
475 * @return true on success false otherwise;
476 *
477 * If there was already an opened file, the file will be closed,
478 * and the new one will be opened.
479 */
480bool IniParser::readFile(const std::string& fileName, bool keepSettings)
481{
482  FILE*    stream;           //< The stream we use to read the file.
483  int      lineCount = 0;    //< The Count of lines read.
484  std::list<std::string>  commentList;     //< A list of Comments.
485  Section* currentSection = NULL;
486
487  if (!keepSettings)
488    this->_document.clear();
489
490  if( (stream = fopen (fileName.c_str(), "r")) == NULL)
491  {
492    printf("ERROR:: IniParser could not open %s for reading\n", fileName.c_str());
493    return false;
494  }
495  else
496  {
497    this->_fileName = fileName;
498
499    /////////////////////////////
500    // READING IN THE INI-FILE //
501    /////////////////////////////
502    char lineBuffer[PARSELINELENGHT+1];
503    char buffer[PARSELINELENGHT+1];
504    const char* lineBegin;
505    char* ptr;
506
507    while( fgets (lineBuffer, PARSELINELENGHT, stream))
508    {
509      lineBegin = lineBuffer;
510      // remove newline char, and \0-terminate
511      if( (ptr = strchr( lineBuffer, '\n')) != NULL)
512        *ptr = 0;
513      else
514        lineBuffer[PARSELINELENGHT] = 0;
515      // cut up to the beginning of the line.
516      while((*lineBegin == ' ' || *lineBegin == '\t') && lineBegin < lineBuffer + strlen(lineBuffer))
517        ++lineBegin;
518
519      if ( !strcmp( lineBegin, "" ) )
520        continue;
521
522      // check if we have a FileComment
523      if ( (*lineBegin == '#' || *lineBegin == ';'))
524      {
525        std::string newCommenLine = lineBegin;
526        commentList.push_back(newCommenLine);
527        continue;
528      }
529      if (lineCount == 0 && !commentList.empty())
530      {
531        this->setNodeComment(&this->_document, &commentList);
532        lineCount++;
533      }
534
535      // check for section identifyer
536      else if( sscanf (lineBegin, "[%s", buffer) == 1)
537      {
538        if( (ptr = strchr( buffer, ']')) != NULL)
539        {
540          *ptr = 0;
541          Section& node = this->_document.addSection(buffer);
542          setNodeComment(&node, &commentList);
543          currentSection = &node;
544        }
545      }
546      // check for Entry identifier (Entry = Value)
547      else if( (ptr = strchr( lineBegin, '=')) != NULL)
548      {
549        if (currentSection == NULL)
550        {
551          printf("WARNING:: Not in a Section yet for %s\n", lineBegin);
552          lineCount++;
553          continue;
554        }
555        if( ptr == lineBegin)
556        {
557          lineCount++;
558          continue;
559        }
560        char* valueBegin = ptr+1;
561        while ((*valueBegin == ' ' || *valueBegin == '\t') && valueBegin <= lineBegin + strlen(lineBegin))
562          ++valueBegin;
563        char* valueEnd = valueBegin + strlen(valueBegin)-1;
564        while ((*valueEnd == ' ' || *valueEnd == '\t') && valueEnd >= valueBegin)
565          --valueEnd;
566        valueEnd[1] = '\0';
567        char* nameEnd = ptr-1;
568        while ((*nameEnd == ' ' || *nameEnd == '\t' ) && nameEnd >= lineBegin)
569          --nameEnd;
570        nameEnd[1] = '\0';
571
572        Entry& node = currentSection->addEntry(lineBegin, valueBegin);
573        this->setNodeComment(&node, &commentList);
574
575        lineCount++;
576      }
577    }
578  }
579  fclose(stream);
580  return true;
581}
582
583
584/**
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.
587 * @return true on success false otherwise
588 */
589bool IniParser::writeFile(const std::string& fileName) const
590{
591  std::string parseFile;
592  FILE*    stream;           //!< The stream we use to read the file.
593  if( fileName.empty())
594    parseFile = _fileName;
595  else
596    parseFile = fileName;
597
598  if( (stream = fopen (parseFile.c_str(), "w")) == NULL)
599  {
600    printf("ERROR:: IniParser could not open %s for writing\n", parseFile.c_str());
601    return false;
602  }
603  else
604  {
605    if (!this->_document.comment().empty())
606      fprintf(stream, "%s\n\n", this->_document.comment().c_str());
607
608    Section::const_iterator section;
609    for (section = this->_document.sections().begin(); section != this->_document.sections().end(); ++section)
610    {
611      if (!(*section).comment().empty())
612        fprintf(stream, "%s", (*section).comment().c_str());
613      fprintf(stream, "\n [%s]\n", (*section).name().c_str());
614
615      Entry::const_iterator entry;
616      for (entry = (*section).entries().begin(); entry != (*section).entries().end(); ++entry)
617      {
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());
621      }
622    }
623  }
624  fclose(stream);
625  return true;
626}
627
628
629/**
630 * @brief output the whole tree in a nice and easy way.
631 */
632void IniParser::debug() const
633{
634  printf("Iniparser '%s' - debug\n", this->_fileName.c_str());
635  if (!this->_document.comment().empty())
636    printf("FileComment:\n '%s'\n\n", this->_document.comment().c_str());
637
638  if (!this->_document.sections().empty())
639    this->_document.debug();
640  else
641    printf("no Sections Defined in this ini-file (%s).\n", _fileName.c_str());
642}
643
644
645/**
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.
649 */
650void IniParser::setNodeComment(Node* node, std::list<std::string>* comments)
651{
652  assert(node != NULL);
653  assert(comments != NULL);
654
655  std::string comment;
656  if (comments->empty())
657  {
658    comment = "";
659  }
660  else
661  {
662    while (!comments->empty())
663    {
664      if (!comment.empty())
665        comment += "\n";
666      comment += comments->front();
667      comments->pop_front();
668    }
669  }
670  node->setComment(comment);
671}
672
673
Note: See TracBrowser for help on using the repository browser.