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
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#if HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <cassert>
28#include <algorithm>
29
30#ifdef DEBUG_LEVEL
31 #include "../../../lib/util/debug.h"
32#else
33 #define PRINTF(x) printf
34 #define PRINT(x) printf
35#endif
36
37
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}
46
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
55{
56  printf("   %s = %s\n", this->name().c_str(), this->_value.c_str());
57}
58
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;
76}
77
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}
94
95const std::string& IniParser::Section::getValue(const std::string& entryName, const std::string& defaultValue) const
96{
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;
102}
103
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}
114
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}
124
125
126IniParser::Entry* IniParser::Section::getEntry(const std::string& entryName)
127{
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}
134
135IniParser::Entry::const_iterator IniParser::Section::getEntryIt(const std::string& entryName) const
136{
137  return std::find(this->_entries.begin(), this->_entries.end(), entryName);
138}
139
140void IniParser::Section::clear()
141{
142  this->_entries.clear();
143}
144
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 = "";
296/**
297 * @brief constructs an IniParser using a file
298 * @param fileName: the path and name of the file to parse
299 */
300IniParser::IniParser (const std::string& fileName)
301    : _document(fileName)
302{
303  this->_fileName = fileName;
304  if (!fileName.empty())
305    this->readFile(fileName);
306}
307
308
309/**
310 * @brief removes the IniParser from memory
311 */
312IniParser::~IniParser ()
313{}
314
315/**
316 * @brief opens a file to parse
317 * @param fileName: path and name of the new file to parse
318 * @param keepSettings if the Settings (if already some are set) should be kept (false by default)
319 * @return true on success false otherwise;
320 *
321 * If there was already an opened file, the file will be closed,
322 * and the new one will be opened.
323 */
324bool IniParser::readFile(const std::string& fileName, bool keepSettings)
325{
326  FILE*    stream;           //< The stream we use to read the file.
327  int      lineCount = 0;    //< The Count of lines read.
328  std::list<std::string>  commentList;     //< A list of Comments.
329  Section* currentSection = NULL;
330
331  if (!keepSettings)
332    this->_document.clear();
333
334  if( (stream = fopen (fileName.c_str(), "r")) == NULL)
335  {
336    PRINTF(1)("IniParser could not open %s for reading\n", fileName.c_str());
337    return false;
338  }
339  else
340  {
341    this->_fileName = fileName;
342
343    /////////////////////////////
344    // READING IN THE INI-FILE //
345    /////////////////////////////
346    char lineBuffer[PARSELINELENGHT+1];
347    char buffer[PARSELINELENGHT+1];
348    const char* lineBegin;
349    char* ptr;
350
351    while( fgets (lineBuffer, PARSELINELENGHT, stream))
352    {
353      lineBegin = lineBuffer;
354      // remove newline char, and \0-terminate
355      if( (ptr = strchr( lineBuffer, '\n')) != NULL)
356        *ptr = 0;
357      else
358        lineBuffer[PARSELINELENGHT] = 0;
359      // cut up to the beginning of the line.
360      while((*lineBegin == ' ' || *lineBegin == '\t') && lineBegin < lineBuffer + strlen(lineBuffer))
361        ++lineBegin;
362
363      if ( !strcmp( lineBegin, "" ) )
364        continue;
365
366      // check if we have a FileComment
367      if ( (*lineBegin == '#' || *lineBegin == ';'))
368      {
369        std::string newCommenLine = lineBegin;
370        commentList.push_back(newCommenLine);
371        continue;
372      }
373      if (lineCount == 0 && !commentList.empty())
374      {
375        this->setNodeComment(&this->_document, &commentList);
376        lineCount++;
377      }
378
379      // check for section identifyer
380      else if( sscanf (lineBegin, "[%s", buffer) == 1)
381      {
382        if( (ptr = strchr( buffer, ']')) != NULL)
383        {
384          *ptr = 0;
385          Section& node = this->_document.addSection(buffer);
386          setNodeComment(&node, &commentList);
387          currentSection = &node;
388        }
389      }
390      // check for Entry identifier (Entry = Value)
391      else if( (ptr = strchr( lineBegin, '=')) != NULL)
392      {
393        if (currentSection == NULL)
394        {
395          PRINTF(2)("Not in a Section yet for %s\n", lineBegin);
396          lineCount++;
397          continue;
398        }
399        if( ptr == lineBegin)
400        {
401          lineCount++;
402          continue;
403        }
404        char* valueBegin = ptr+1;
405        while ((*valueBegin == ' ' || *valueBegin == '\t') && valueBegin <= lineBegin + strlen(lineBegin))
406          ++valueBegin;
407        char* valueEnd = valueBegin + strlen(valueBegin)-1;
408        while ((*valueEnd == ' ' || *valueEnd == '\t') && valueEnd >= valueBegin)
409          --valueEnd;
410        valueEnd[1] = '\0';
411        char* nameEnd = ptr-1;
412        while ((*nameEnd == ' ' || *nameEnd == '\t' ) && nameEnd >= lineBegin)
413          --nameEnd;
414        nameEnd[1] = '\0';
415
416        Entry& node = currentSection->addEntry(lineBegin, valueBegin);
417        this->setNodeComment(&node, &commentList);
418
419        lineCount++;
420      }
421    }
422  }
423  fclose(stream);
424  return true;
425}
426
427
428/**
429 * @brief opens a file and writes to it
430 * @param fileName: path and name of the new file to write to
431 * @return true on success false otherwise
432 */
433bool IniParser::writeFile(const std::string& fileName) const
434{
435  FILE*    stream;           //!< The stream we use to read the file.
436  if( fileName.empty())
437    return false;
438
439  if( (stream = fopen (fileName.c_str(), "w")) == NULL)
440  {
441    PRINTF(1)("IniParser could not open %s for writing\n", fileName.c_str());
442    return false;
443  }
444  else
445  {
446    if (!this->_document.comment().empty())
447      fprintf(stream, "%s\n\n", this->_document.comment().c_str());
448
449    Section::const_iterator section;
450    for (section = this->_document.sections().begin(); section != this->_document.sections().end(); ++section)
451    {
452      if (!(*section).comment().empty())
453        fprintf(stream, "%s", (*section).comment().c_str());
454      fprintf(stream, "\n [%s]\n", (*section).name().c_str());
455
456      Entry::const_iterator entry;
457      for (entry = (*section).entries().begin(); entry != (*section).entries().end(); ++entry)
458      {
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());
462      }
463    }
464  }
465  fclose(stream);
466  return true;
467}
468
469void IniParser::setFileComment(const std::string& fileComment)
470{
471  this->_document.setComment(fileComment);
472}
473
474/**
475 * @brief adds a section to the list of Sections,
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 */
480IniParser::Section& IniParser::addSection(const std::string& sectionName)
481{
482  return this->_document.addSection(sectionName);
483}
484
485
486/**
487 * @brief adds a new Entry to either the currentSection or the section called by sectionName
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
493 * @return The Entry, that was added.
494 */
495bool IniParser::addEntry(const std::string& sectionName, const std::string& entryName, const std::string& value, const std::string& comment)
496{
497  return this->_document.addEntry(sectionName, entryName, value, comment);
498}
499
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 */
509bool IniParser::editEntry(const std::string& sectionName, const std::string& entryName, const std::string& value, bool createMissing)
510{
511  return this->_document.editEntry(sectionName, entryName, value, createMissing);
512}
513
514
515/**
516 * @brief directly acesses an entry in a section
517 * @param sectionName: the section where the entry is to be found
518 * @param entryName: the name of the entry to find
519 * @param defaultValue: what should be returned in case the entry cannot be found
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
523{
524  return this->_document.getValue(sectionName, entryName, defaultValue);
525}
526
527/**
528 * Set the Comment of a specified Entry.
529 * @param comment the Comment to set
530 * @param entryName the Name of the Entry
531 * @param sectionName the Name of the Section
532 */
533void IniParser::setEntryComment(const std::string& sectionName, const std::string& entryName, const std::string& comment)
534{
535  this->_document.setEntryComment(sectionName, entryName, comment);
536}
537
538/**
539 * @param entryName the Entry to query for
540 * @param sectionName the Section to Query for
541 * @returns the queried Comment.
542 */
543const std::string& IniParser::getEntryComment(const std::string& sectionName, const std::string& entryName) const
544{
545  return this->_document.getEntryComment(sectionName, entryName);
546}
547
548/**
549 * @brief output the whole tree in a nice and easy way.
550 */
551void IniParser::debug() const
552{
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());
556
557  if (!this->_document.sections().empty())
558    this->_document.debug();
559  else
560    PRINTF(0)("no Sections Defined in this ini-file (%s).\n", _fileName.c_str());
561}
562
563
564/**
565 * takes lines together to form one NodeComment, ereasing the commentList
566 */
567void IniParser::setNodeComment(Node* node, std::list<std::string>* comments)
568{
569  assert(node != NULL);
570  assert(comments != NULL);
571
572  std::string comment;
573  if (comments->empty())
574  {
575    comment = "";
576  }
577  else
578  {
579    while (!comments->empty())
580    {
581      if (!comment.empty())
582        comment += "\n";
583      comment += comments->front();
584      comments->pop_front();
585    }
586  }
587  node->setComment(comment);
588}
589
590
Note: See TracBrowser for help on using the repository browser.