[4597] | 1 | /* |
---|
[2064] | 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: |
---|
[5014] | 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. |
---|
[2064] | 18 | */ |
---|
| 19 | |
---|
| 20 | |
---|
| 21 | #include "ini_parser.h" |
---|
[4381] | 22 | |
---|
[5031] | 23 | #include <stdlib.h> |
---|
| 24 | #include <string.h> |
---|
[2064] | 25 | |
---|
[5933] | 26 | #ifdef DEBUG |
---|
| 27 | #include "debug.h" |
---|
| 28 | #else |
---|
| 29 | #define PRINTF(x) printf |
---|
| 30 | #endif |
---|
| 31 | |
---|
[2064] | 32 | using namespace std; |
---|
| 33 | |
---|
[2141] | 34 | /** |
---|
[4836] | 35 | * constructs an IniParser using a file |
---|
[5017] | 36 | * @param fileName: the path and name of the file to parse |
---|
[5933] | 37 | */ |
---|
[5014] | 38 | IniParser::IniParser (const char* fileName) |
---|
[2064] | 39 | { |
---|
[5031] | 40 | this->fileName = NULL; |
---|
[5933] | 41 | |
---|
[5014] | 42 | if (fileName != NULL) |
---|
[5018] | 43 | this->readFile(fileName); |
---|
[2064] | 44 | } |
---|
| 45 | |
---|
[5933] | 46 | |
---|
[2141] | 47 | /** |
---|
[4836] | 48 | * removes the IniParser from memory |
---|
[5933] | 49 | */ |
---|
[2064] | 50 | IniParser::~IniParser () |
---|
| 51 | { |
---|
[5933] | 52 | this->deleteSections(); |
---|
[2064] | 53 | } |
---|
| 54 | |
---|
[5933] | 55 | |
---|
[5015] | 56 | /** |
---|
| 57 | * removes all the sections. This is like delete, but even cooler :) |
---|
| 58 | */ |
---|
[5014] | 59 | void IniParser::deleteSections() |
---|
[2064] | 60 | { |
---|
[5933] | 61 | while(!this->sections.empty()) |
---|
[5014] | 62 | { |
---|
[5933] | 63 | IniSection section = this->sections.front(); |
---|
| 64 | |
---|
| 65 | while(!section.entries.empty()) |
---|
[3231] | 66 | { |
---|
[5933] | 67 | IniEntry entry = section.entries.front(); |
---|
| 68 | delete []entry.name; |
---|
| 69 | delete []entry.value; |
---|
| 70 | section.entries.pop_front(); |
---|
| 71 | } |
---|
[5014] | 72 | |
---|
[5933] | 73 | delete []section.name; |
---|
| 74 | this->sections.pop_front(); |
---|
[5014] | 75 | } |
---|
[5933] | 76 | |
---|
| 77 | // this->currentEntry = NULL; |
---|
| 78 | // this->currentSection = NULL; |
---|
[5031] | 79 | this->setFileName(NULL); |
---|
[2064] | 80 | } |
---|
| 81 | |
---|
[5933] | 82 | |
---|
[2141] | 83 | /** |
---|
[5014] | 84 | * opens another file to parse |
---|
[5017] | 85 | * @param fileName: path and name of the new file to parse |
---|
[5014] | 86 | * @return true on success false otherwise; |
---|
[5933] | 87 | */ |
---|
[5018] | 88 | bool IniParser::readFile(const char* fileName) |
---|
[2064] | 89 | { |
---|
[5014] | 90 | FILE* stream; //!< The stream we use to read the file. |
---|
[5933] | 91 | if (!sections.empty()) |
---|
[5014] | 92 | deleteSections(); |
---|
[5015] | 93 | if( fileName == NULL) |
---|
| 94 | return false; |
---|
[5031] | 95 | this->setFileName(fileName); |
---|
[5014] | 96 | |
---|
[5015] | 97 | if( (stream = fopen (fileName, "r")) == NULL) |
---|
[5014] | 98 | { |
---|
[5015] | 99 | PRINTF(1)("IniParser could not open %s\n", fileName); |
---|
[5014] | 100 | return false; |
---|
| 101 | } |
---|
| 102 | else |
---|
| 103 | { |
---|
[5933] | 104 | this->currentEntry = 0;//this->sections.begin(); |
---|
| 105 | this->currentSection = this->sections.begin(); |
---|
[5014] | 106 | |
---|
| 107 | ///////////////////////////// |
---|
| 108 | // READING IN THE INI-FILE // |
---|
| 109 | ///////////////////////////// |
---|
| 110 | char lineBuffer[PARSELINELENGHT]; |
---|
| 111 | char buffer[PARSELINELENGHT]; |
---|
[5021] | 112 | const char* lineBegin; |
---|
[5014] | 113 | char* ptr; |
---|
| 114 | |
---|
[5319] | 115 | while( fgets (lineBuffer, PARSELINELENGHT, stream)) |
---|
[2551] | 116 | { |
---|
[5021] | 117 | lineBegin = lineBuffer; |
---|
[5014] | 118 | // remove newline char, and \0-terminate |
---|
| 119 | if( (ptr = strchr( lineBuffer, '\n')) != NULL) |
---|
| 120 | *ptr = 0; |
---|
[5021] | 121 | // cut up to the beginning of the line. |
---|
| 122 | while((*lineBegin == ' ' || *lineBegin == '\t') && lineBegin < lineBuffer + strlen(lineBuffer)) |
---|
| 123 | ++lineBegin; |
---|
| 124 | if (strlen(lineBegin) <= 1 || *lineBegin == '#' || *lineBegin == ';') |
---|
| 125 | continue;//printf("empty Line\n"); |
---|
[2551] | 126 | // check for section identifyer |
---|
[5021] | 127 | else if( sscanf (lineBegin, "[%s", buffer) == 1) |
---|
[5014] | 128 | { |
---|
| 129 | if( (ptr = strchr( buffer, ']')) != NULL) |
---|
[4597] | 130 | { |
---|
[5014] | 131 | *ptr = 0; |
---|
[5020] | 132 | this->addSection(buffer); |
---|
[4597] | 133 | } |
---|
[5014] | 134 | } |
---|
[5018] | 135 | // check for Entry identifier (Entry = Value) |
---|
[5021] | 136 | else if( (ptr = strchr( lineBegin, '=')) != NULL) |
---|
[5014] | 137 | { |
---|
| 138 | if (currentSection == NULL) |
---|
| 139 | { |
---|
[5021] | 140 | PRINTF(2)("Not in a Section yet for %s\n", lineBegin); |
---|
[5014] | 141 | continue; |
---|
| 142 | } |
---|
[5021] | 143 | if( ptr == lineBegin) |
---|
[5014] | 144 | continue; |
---|
| 145 | char* valueBegin = ptr+1; |
---|
[5021] | 146 | while ((*valueBegin == ' ' || *valueBegin == '\t') && valueBegin <= lineBegin + strlen(lineBegin)) |
---|
[5014] | 147 | ++valueBegin; |
---|
[5022] | 148 | char* valueEnd = valueBegin + strlen(valueBegin)-1; |
---|
| 149 | while ((*valueEnd == ' ' || *valueEnd == '\t') && valueEnd >= valueBegin) |
---|
| 150 | --valueEnd; |
---|
| 151 | valueEnd[1] = '\0'; |
---|
[5018] | 152 | char* nameEnd = ptr-1; |
---|
[5021] | 153 | while ((*nameEnd == ' ' || *nameEnd == '\t' ) && nameEnd >= lineBegin) |
---|
[5014] | 154 | --nameEnd; |
---|
| 155 | nameEnd[1] = '\0'; |
---|
[5018] | 156 | |
---|
[5021] | 157 | this->addVar(lineBegin, valueBegin); |
---|
[5014] | 158 | } |
---|
[2551] | 159 | } |
---|
[5014] | 160 | } |
---|
| 161 | fclose(stream); |
---|
| 162 | return true; |
---|
[2064] | 163 | } |
---|
| 164 | |
---|
[5933] | 165 | |
---|
[2141] | 166 | /** |
---|
[5020] | 167 | * opens a file and writes to it |
---|
| 168 | * @param fileName: path and name of the new file to write to |
---|
| 169 | * @return true on success false otherwise |
---|
| 170 | */ |
---|
| 171 | bool IniParser::writeFile(const char* fileName) |
---|
| 172 | { |
---|
| 173 | FILE* stream; //!< The stream we use to read the file. |
---|
| 174 | if( fileName == NULL) |
---|
| 175 | return false; |
---|
| 176 | |
---|
| 177 | if( (stream = fopen (fileName, "w")) == NULL) |
---|
| 178 | { |
---|
| 179 | PRINTF(1)("IniParser could not open %s\n", fileName); |
---|
| 180 | return false; |
---|
| 181 | } |
---|
| 182 | else |
---|
| 183 | { |
---|
[5933] | 184 | if (!this->sections.empty()) |
---|
[5020] | 185 | { |
---|
[5933] | 186 | std::list<IniSection>::iterator section; |
---|
| 187 | for (section = this->sections.begin(); section != this->sections.end(); section++) |
---|
[5020] | 188 | { |
---|
[5933] | 189 | fprintf(stream, "\n [%s]\n", (*section).name); |
---|
[5020] | 190 | |
---|
[5933] | 191 | std::list<IniEntry>::iterator entry; |
---|
| 192 | for (entry = (*section).entries.begin(); entry != (*section).entries.end(); entry++) |
---|
| 193 | fprintf(stream, " %s = %s\n", (*entry).name, (*entry).value); |
---|
[5020] | 194 | } |
---|
| 195 | } |
---|
| 196 | else |
---|
| 197 | PRINTF(1)("%s no sections defined yet\n", fileName); |
---|
| 198 | } |
---|
| 199 | fclose(stream); |
---|
| 200 | } |
---|
| 201 | |
---|
[5933] | 202 | |
---|
[5021] | 203 | /** |
---|
| 204 | * adds a section to the list of Sections, |
---|
| 205 | * if no Section list is availiable, it will create it |
---|
| 206 | * @param sectionName the Name of the section to add |
---|
| 207 | * @return true on success... there is only success or segfault :) |
---|
| 208 | */ |
---|
[5020] | 209 | bool IniParser::addSection(const char* sectionName) |
---|
| 210 | { |
---|
[5933] | 211 | this->sections.push_back(IniSection()); |
---|
[5020] | 212 | |
---|
[5933] | 213 | this->sections.back().name = new char[strlen(sectionName)+1]; |
---|
| 214 | strcpy(this->sections.back().name, sectionName); |
---|
| 215 | |
---|
| 216 | this->currentSection = --this->sections.end(); |
---|
[5031] | 217 | PRINTF(5)("Added Section %s\n", sectionName); |
---|
[5021] | 218 | return true; |
---|
[5020] | 219 | } |
---|
| 220 | |
---|
[5933] | 221 | |
---|
[5020] | 222 | /** |
---|
[5014] | 223 | * set the parsing cursor to the specified section |
---|
| 224 | * @param sectionName: the name of the section to set the cursor to |
---|
| 225 | * @return true on success or false if the section could not be found |
---|
[2141] | 226 | */ |
---|
[5014] | 227 | bool IniParser::getSection( const char* sectionName) |
---|
[2064] | 228 | { |
---|
[5933] | 229 | std::list<IniSection>::iterator section; |
---|
| 230 | for (section = this->sections.begin(); section != this->sections.end(); section++) |
---|
[5014] | 231 | { |
---|
[5933] | 232 | if (!strcmp((*section).name, sectionName)) |
---|
[3231] | 233 | { |
---|
[5933] | 234 | this->currentSection = section; |
---|
| 235 | this->currentEntry = (*section).entries.begin(); |
---|
[5014] | 236 | return true; |
---|
[3231] | 237 | } |
---|
[5014] | 238 | } |
---|
| 239 | return false; |
---|
| 240 | } |
---|
[4597] | 241 | |
---|
[5933] | 242 | |
---|
[5014] | 243 | /** |
---|
[5015] | 244 | * moves to the first section |
---|
| 245 | */ |
---|
| 246 | void IniParser::getFirstSection() |
---|
| 247 | { |
---|
[5933] | 248 | this->currentSection = this->sections.begin(); |
---|
| 249 | this->currentEntry = (*this->currentSection).entries.begin(); |
---|
[5015] | 250 | } |
---|
| 251 | |
---|
[5933] | 252 | |
---|
[5015] | 253 | /** |
---|
[5014] | 254 | * searches the next section |
---|
| 255 | * @returns the name of the section if found, NULL otherwise |
---|
| 256 | */ |
---|
| 257 | const char* IniParser::nextSection() |
---|
| 258 | { |
---|
[5933] | 259 | if (this->currentSection == this->sections.end()) |
---|
[5014] | 260 | return NULL; |
---|
| 261 | else |
---|
| 262 | { |
---|
[5933] | 263 | if (this->currentSection == this->sections.end()) |
---|
| 264 | return NULL; |
---|
| 265 | else |
---|
| 266 | this->currentSection++; |
---|
[5014] | 267 | } |
---|
[5015] | 268 | |
---|
[5014] | 269 | if (this->currentSection != NULL) |
---|
| 270 | return this->currentSection->name; |
---|
| 271 | else |
---|
| 272 | return NULL; |
---|
[2064] | 273 | } |
---|
| 274 | |
---|
[5933] | 275 | |
---|
[2141] | 276 | /** |
---|
[5015] | 277 | * moves to the first Variable of the current Section |
---|
| 278 | */ |
---|
| 279 | void IniParser::getFirstVar() |
---|
| 280 | { |
---|
[5933] | 281 | if (this->currentSection != this->sections.end()) |
---|
| 282 | this->currentEntry = (*this->currentSection).entries.begin(); |
---|
| 283 | // else |
---|
| 284 | // this->currentEntry = NULL; |
---|
[5015] | 285 | } |
---|
| 286 | |
---|
[5933] | 287 | |
---|
[5015] | 288 | /** |
---|
[5014] | 289 | * gets the next VarName=VarValue pair from the parsing stream |
---|
| 290 | * @return true on success, false otherwise (in the latter case name and value will be NULL) |
---|
| 291 | */ |
---|
| 292 | bool IniParser::nextVar() |
---|
| 293 | { |
---|
[5015] | 294 | if (this->currentSection == NULL |
---|
| 295 | || this->currentEntry == NULL |
---|
[5933] | 296 | || this->currentEntry == (*this->currentSection).entries.end()) |
---|
[5014] | 297 | { |
---|
| 298 | this->currentEntry = NULL; |
---|
| 299 | return false; |
---|
| 300 | } |
---|
[5933] | 301 | this->currentEntry++; |
---|
[5014] | 302 | |
---|
| 303 | if (this->currentEntry == NULL) |
---|
| 304 | return false; |
---|
| 305 | else |
---|
| 306 | return true; |
---|
| 307 | } |
---|
| 308 | |
---|
[5933] | 309 | |
---|
[5014] | 310 | /** |
---|
[5020] | 311 | * adds a new Entry to either the currentSection or the section called by sectionName |
---|
| 312 | * @param entryName the Name of the Entry to add |
---|
| 313 | * @param value the value to assign to this entry |
---|
| 314 | * @param sectionName if NULL then this entry will be set to the currentSection |
---|
| 315 | * otherwise to the section refered to by sectionName. |
---|
| 316 | * If both are NULL no entry will be added |
---|
| 317 | * @return true if everything is ok false on error |
---|
| 318 | */ |
---|
| 319 | bool IniParser::addVar(const char* entryName, const char* value, const char* sectionName) |
---|
| 320 | { |
---|
[5933] | 321 | std::list<IniSection>::iterator section = this->sections.end(); |
---|
| 322 | |
---|
[5020] | 323 | if (sectionName != NULL) |
---|
| 324 | { |
---|
[5933] | 325 | for (section = this->sections.begin(); section != this->sections.end(); section++) |
---|
| 326 | if (!strcmp((*section).name, sectionName)) |
---|
[5020] | 327 | break; |
---|
| 328 | } |
---|
| 329 | else |
---|
[5933] | 330 | section = this->currentSection; |
---|
[5020] | 331 | |
---|
[5933] | 332 | if (section == this->sections.end()) |
---|
[5020] | 333 | { |
---|
| 334 | PRINTF(2)("section not found for value %s\n", entryName); |
---|
| 335 | return false; |
---|
| 336 | } |
---|
| 337 | else |
---|
| 338 | { |
---|
[5933] | 339 | (*section).entries.push_back(IniEntry()); |
---|
| 340 | (*section).entries.back().name = new char[strlen (entryName)+1]; |
---|
| 341 | strcpy((*section).entries.back().name, entryName); |
---|
| 342 | (*section).entries.back().value = new char[strlen(value)+1]; |
---|
| 343 | strcpy((*section).entries.back().value, value); |
---|
| 344 | PRINTF(5)("Added Entry %s with Value '%s' to Section %s\n", entryName, value, (*section).name); |
---|
[5020] | 345 | return true; |
---|
| 346 | } |
---|
| 347 | } |
---|
| 348 | |
---|
[5933] | 349 | |
---|
[5020] | 350 | /** |
---|
[4836] | 351 | * directly acesses an entry in a section |
---|
[5014] | 352 | * @param entryName: the name of the entry to find |
---|
| 353 | * @param sectionName: the section where the entry is to be found |
---|
| 354 | * @param defaultValue: what should be returned in case the entry cannot be found |
---|
[4836] | 355 | * @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 |
---|
[5020] | 356 | * |
---|
| 357 | * 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 |
---|
| 358 | * lead to unwanted behaviour. |
---|
[2141] | 359 | */ |
---|
[5014] | 360 | const char* IniParser::getVar(const char* entryName, const char* sectionName, const char* defaultValue) const |
---|
[2065] | 361 | { |
---|
[5933] | 362 | if (fileName != NULL) |
---|
[5014] | 363 | { |
---|
[5933] | 364 | std::list<IniSection>::const_iterator section; |
---|
| 365 | for (section = this->sections.begin(); section != this->sections.end(); section++) |
---|
[3231] | 366 | { |
---|
[5933] | 367 | if (!strcmp((*section).name, sectionName)) |
---|
[5014] | 368 | { |
---|
[5933] | 369 | std::list<IniEntry>::const_iterator entry; |
---|
| 370 | for (entry = (*section).entries.begin(); entry != (*section).entries.end(); entry++) |
---|
| 371 | if (!strcmp((*entry).name, entryName)) |
---|
| 372 | return (*entry).value; |
---|
| 373 | PRINTF(2)("Entry %s in section %s not found.\n", entryName, sectionName); |
---|
| 374 | break; |
---|
[5014] | 375 | } |
---|
[3231] | 376 | } |
---|
[5014] | 377 | PRINTF(2)("Section %s that should be containing %s not found.\n", sectionName, entryName); |
---|
| 378 | } |
---|
| 379 | else |
---|
[5031] | 380 | PRINTF(1)("%s not opened\n", fileName); |
---|
[5014] | 381 | |
---|
| 382 | return defaultValue; |
---|
| 383 | |
---|
[2065] | 384 | } |
---|
[5014] | 385 | |
---|
[5031] | 386 | |
---|
| 387 | void IniParser::setFileName(const char* fileName) |
---|
| 388 | { |
---|
| 389 | if (this->fileName) |
---|
| 390 | delete []this->fileName; |
---|
| 391 | if (fileName) |
---|
| 392 | { |
---|
| 393 | this->fileName = new char[strlen(fileName)+1]; |
---|
| 394 | strcpy(this->fileName, fileName); |
---|
| 395 | } |
---|
| 396 | else |
---|
| 397 | this->fileName = NULL; |
---|
| 398 | } |
---|
| 399 | |
---|
| 400 | |
---|
[5017] | 401 | /** |
---|
| 402 | * output the whole tree in a nice and easy way. |
---|
| 403 | */ |
---|
[5014] | 404 | void IniParser::debug() const |
---|
| 405 | { |
---|
[5031] | 406 | PRINTF(0)("Iniparser %s - debug\n", this->fileName); |
---|
[5933] | 407 | if (this->fileName != NULL) |
---|
[5014] | 408 | { |
---|
[5933] | 409 | std::list<IniSection>::const_iterator section; |
---|
| 410 | for (section = this->sections.begin(); section != this->sections.end(); section++) |
---|
[5014] | 411 | { |
---|
[5933] | 412 | PRINTF(0)(" [%s]\n", (*section).name); |
---|
[5014] | 413 | |
---|
[5933] | 414 | std::list<IniEntry>::const_iterator entry; |
---|
| 415 | for (entry = (*section).entries.begin(); entry != (*section).entries.end(); entry++) |
---|
| 416 | PRINTF(0)(" '%s' -> '%s'\n", (*entry).name, (*entry).value); |
---|
[5014] | 417 | } |
---|
| 418 | } |
---|
| 419 | else |
---|
[5933] | 420 | PRINTF(1)("no opened ini-file.\n"); |
---|
[5014] | 421 | } |
---|