Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

trunk: completely doxygened and finalized IniParser

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