Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: merged the new_class_id branche back to the trunk.
merged with command:
svn merge https://svn.orxonox.net/orxonox/branches/new_class_id trunk -r9683:HEAD
no conflicts… puh..

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