Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/preferences/src/lib/parser/ini_parser/ini_parser.cc @ 7234

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

orxonox/branches/preferences: merged the old preferences here… some minor conflicts, hope it works

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