Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: more functionality to the ini_parser

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