Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

many doxygen tags

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