From d19a9f5df5ca670857ae82ae8e02e065a0035974 Mon Sep 17 00:00:00 2001 From: alberth Date: Thu, 3 Mar 2011 20:50:24 +0000 Subject: (svn r22167) -Codechange: Extract IniLoadFile base class for loading ini files out of IniFile. --- projects/openttd_vs100.vcxproj | 1 + projects/openttd_vs100.vcxproj.filters | 3 + projects/openttd_vs80.vcproj | 4 + projects/openttd_vs90.vcproj | 4 + source.list | 1 + src/ini.cpp | 292 +------------------------------ src/ini_load.cpp | 311 +++++++++++++++++++++++++++++++++ src/ini_type.h | 16 +- 8 files changed, 336 insertions(+), 296 deletions(-) create mode 100644 src/ini_load.cpp diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj index c1bb317d3..d4e38e713 100644 --- a/projects/openttd_vs100.vcxproj +++ b/projects/openttd_vs100.vcxproj @@ -325,6 +325,7 @@ + diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters index 16031fa6c..1f4ba1c4f 100644 --- a/projects/openttd_vs100.vcxproj.filters +++ b/projects/openttd_vs100.vcxproj.filters @@ -195,6 +195,9 @@ Source Files + + Source Files + Source Files diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index 33701798d..640423250 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -570,6 +570,10 @@ RelativePath=".\..\src\ini.cpp" > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 4d6671c20..1f8b60b0a 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -567,6 +567,10 @@ RelativePath=".\..\src\ini.cpp" > + + diff --git a/source.list b/source.list index 5dfb06adb..45a5cf9a9 100644 --- a/source.list +++ b/source.list @@ -33,6 +33,7 @@ heightmap.cpp highscore.cpp hotkeys.cpp ini.cpp +ini_load.cpp landscape.cpp map.cpp misc.cpp diff --git a/src/ini.cpp b/src/ini.cpp index 35d8910c8..3dc1219bb 100644 --- a/src/ini.cpp +++ b/src/ini.cpp @@ -10,8 +10,6 @@ /** @file ini.cpp Definition of the IniItem class, related to reading/writing '*.ini' files. */ #include "stdafx.h" -#include "core/alloc_func.hpp" -#include "core/mem_func.hpp" #include "debug.h" #include "ini_type.h" #include "string_func.h" @@ -26,296 +24,8 @@ # include #endif -/** - * Construct a new in-memory item of an Ini file. - * @param parent the group we belong to - * @param name the name of the item - * @param len the length of the name of the item - */ -IniItem::IniItem(IniGroup *parent, const char *name, size_t len) : next(NULL), value(NULL), comment(NULL) -{ - if (len == 0) len = strlen(name); - - this->name = strndup(name, len); - *parent->last_item = this; - parent->last_item = &this->next; -} - -/** Free everything we loaded. */ -IniItem::~IniItem() -{ - free(this->name); - free(this->value); - free(this->comment); - - delete this->next; -} - -/** - * Replace the current value with another value. - * @param value the value to replace with. - */ -void IniItem::SetValue(const char *value) -{ - free(this->value); - this->value = strdup(value); -} - -/** - * Construct a new in-memory group of an Ini file. - * @param parent the file we belong to - * @param name the name of the group - * @param len the length of the name of the group - */ -IniGroup::IniGroup(IniFile *parent, const char *name, size_t len) : next(NULL), type(IGT_VARIABLES), item(NULL), comment(NULL) -{ - if (len == 0) len = strlen(name); - - this->name = strndup(name, len); - this->last_item = &this->item; - *parent->last_group = this; - parent->last_group = &this->next; - - if (parent->list_group_names == NULL) return; - - for (uint i = 0; parent->list_group_names[i] != NULL; i++) { - if (strcmp(this->name, parent->list_group_names[i]) == 0) { - this->type = IGT_LIST; - return; - } - } -} - -/** Free everything we loaded. */ -IniGroup::~IniGroup() -{ - free(this->name); - free(this->comment); - - delete this->item; - delete this->next; -} - -/** - * Get the item with the given name, and if it doesn't exist - * and create is true it creates a new item. - * @param name name of the item to find. - * @param create whether to create an item when not found or not. - * @return the requested item or NULL if not found. - */ -IniItem *IniGroup::GetItem(const char *name, bool create) -{ - for (IniItem *item = this->item; item != NULL; item = item->next) { - if (strcmp(item->name, name) == 0) return item; - } - - if (!create) return NULL; - - /* otherwise make a new one */ - return new IniItem(this, name, strlen(name)); -} - -/** - * Clear all items in the group - */ -void IniGroup::Clear() -{ - delete this->item; - this->item = NULL; - this->last_item = &this->item; -} - -/** - * Construct a new in-memory Ini file representation. - * @param list_group_names A NULL terminated list with groups that should be - * loaded as lists instead of variables. - */ -IniFile::IniFile(const char * const *list_group_names) : group(NULL), comment(NULL), list_group_names(list_group_names) -{ - this->last_group = &this->group; -} - -/** Free everything we loaded. */ -IniFile::~IniFile() -{ - free(this->comment); - delete this->group; -} - -/** - * Get the group with the given name, and if it doesn't exist - * create a new group. - * @param name name of the group to find. - * @param len the maximum length of said name. - * @return the requested group. - */ -IniGroup *IniFile::GetGroup(const char *name, size_t len) +IniFile::IniFile(const char * const *list_group_names) : IniLoadFile(list_group_names) { - if (len == 0) len = strlen(name); - - /* does it exist already? */ - for (IniGroup *group = this->group; group != NULL; group = group->next) { - if (!strncmp(group->name, name, len) && group->name[len] == 0) { - return group; - } - } - - /* otherwise make a new one */ - IniGroup *group = new IniGroup(this, name, len); - group->comment = strdup("\n"); - return group; -} - -/** - * Remove the group with the given name. - * @param name name of the group to remove. - */ -void IniFile::RemoveGroup(const char *name) -{ - size_t len = strlen(name); - IniGroup *prev = NULL; - IniGroup *group; - - /* does it exist already? */ - for (group = this->group; group != NULL; prev = group, group = group->next) { - if (strncmp(group->name, name, len) == 0) { - break; - } - } - - if (group == NULL) return; - - if (prev != NULL) { - prev->next = prev->next->next; - if (this->last_group == &group->next) this->last_group = &prev->next; - } else { - this->group = this->group->next; - if (this->last_group == &group->next) this->last_group = &this->group; - } - - group->next = NULL; - delete group; -} - -/** - * Load the Ini file's data from the disk. - * @param filename the file to load. - * @pre nothing has been loaded yet. - */ -void IniFile::LoadFromDisk(const char *filename) -{ - assert(this->last_group == &this->group); - - char buffer[1024]; - IniGroup *group = NULL; - - char *comment = NULL; - uint comment_size = 0; - uint comment_alloc = 0; - - size_t end; - /* - * Now we are going to open a file that contains no more than simple - * plain text. That would raise the question: "why open the file as - * if it is a binary file?". That's simple... Microsoft, in all - * their greatness and wisdom decided it would be useful if ftell - * is aware of '\r\n' and "sees" that as a single character. The - * easiest way to test for that situation is by searching for '\n' - * and decrease the value every time you encounter a '\n'. This will - * thus also make ftell "see" the '\r' when it is not there, so the - * result of ftell will be highly unreliable. So to work around this - * marvel of wisdom we have to open in as a binary file. - */ - FILE *in = FioFOpenFile(filename, "rb", DATA_DIR, &end); - if (in == NULL) return; - - end += ftell(in); - - /* for each line in the file */ - while ((size_t)ftell(in) < end && fgets(buffer, sizeof(buffer), in)) { - char c, *s; - /* trim whitespace from the left side */ - for (s = buffer; *s == ' ' || *s == '\t'; s++) {} - - /* trim whitespace from right side. */ - char *e = s + strlen(s); - while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--; - *e = '\0'; - - /* skip comments and empty lines */ - if (*s == '#' || *s == ';' || *s == '\0') { - uint ns = comment_size + (e - s + 1); - uint a = comment_alloc; - /* add to comment */ - if (ns > a) { - a = max(a, 128U); - do a *= 2; while (a < ns); - comment = ReallocT(comment, comment_alloc = a); - } - uint pos = comment_size; - comment_size += (e - s + 1); - comment[pos + e - s] = '\n'; // comment newline - memcpy(comment + pos, s, e - s); // copy comment contents - continue; - } - - /* it's a group? */ - if (s[0] == '[') { - if (e[-1] != ']') { - ShowInfoF("ini: invalid group name '%s'", buffer); - } else { - e--; - } - s++; // skip [ - group = new IniGroup(this, s, e - s); - if (comment_size != 0) { - group->comment = strndup(comment, comment_size); - comment_size = 0; - } - } else if (group != NULL) { - char *t; - /* find end of keyname */ - if (*s == '\"') { - s++; - for (t = s; *t != '\0' && *t != '\"'; t++) {} - if (*t == '\"') *t = ' '; - } else { - for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {} - } - - /* it's an item in an existing group */ - IniItem *item = new IniItem(group, s, t - s); - if (comment_size != 0) { - item->comment = strndup(comment, comment_size); - comment_size = 0; - } - - /* find start of parameter */ - while (*t == '=' || *t == ' ' || *t == '\t') t++; - - bool quoted = (*t == '\"'); - /* remove starting quotation marks */ - if (*t == '\"') t++; - /* remove ending quotation marks */ - e = t + strlen(t); - if (e > t && e[-1] == '\"') e--; - *e = '\0'; - - /* If the value was not quoted and empty, it must be NULL */ - item->value = (!quoted && e == t) ? NULL : strndup(t, e - t); - } else { - /* it's an orphan item */ - ShowInfoF("ini: '%s' outside of group", buffer); - } - } - - if (comment_size > 0) { - this->comment = strndup(comment, comment_size); - comment_size = 0; - } - - free(comment); - fclose(in); } /** diff --git a/src/ini_load.cpp b/src/ini_load.cpp new file mode 100644 index 000000000..65c8b865c --- /dev/null +++ b/src/ini_load.cpp @@ -0,0 +1,311 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file ini_load.cpp Definition of the #IniLoadFile class, related to reading and storing '*.ini' files. */ + +#include "stdafx.h" +#include "core/alloc_func.hpp" +#include "core/mem_func.hpp" +#include "debug.h" +#include "ini_type.h" +#include "string_func.h" +#include "fileio_func.h" + +/** + * Construct a new in-memory item of an Ini file. + * @param parent the group we belong to + * @param name the name of the item + * @param len the length of the name of the item + */ +IniItem::IniItem(IniGroup *parent, const char *name, size_t len) : next(NULL), value(NULL), comment(NULL) +{ + if (len == 0) len = strlen(name); + + this->name = strndup(name, len); + *parent->last_item = this; + parent->last_item = &this->next; +} + +/** Free everything we loaded. */ +IniItem::~IniItem() +{ + free(this->name); + free(this->value); + free(this->comment); + + delete this->next; +} + +/** + * Replace the current value with another value. + * @param value the value to replace with. + */ +void IniItem::SetValue(const char *value) +{ + free(this->value); + this->value = strdup(value); +} + +/** + * Construct a new in-memory group of an Ini file. + * @param parent the file we belong to + * @param name the name of the group + * @param len the length of the name of the group + */ +IniGroup::IniGroup(IniLoadFile *parent, const char *name, size_t len) : next(NULL), type(IGT_VARIABLES), item(NULL), comment(NULL) +{ + if (len == 0) len = strlen(name); + + this->name = strndup(name, len); + this->last_item = &this->item; + *parent->last_group = this; + parent->last_group = &this->next; + + if (parent->list_group_names == NULL) return; + + for (uint i = 0; parent->list_group_names[i] != NULL; i++) { + if (strcmp(this->name, parent->list_group_names[i]) == 0) { + this->type = IGT_LIST; + return; + } + } +} + +/** Free everything we loaded. */ +IniGroup::~IniGroup() +{ + free(this->name); + free(this->comment); + + delete this->item; + delete this->next; +} + +/** + * Get the item with the given name, and if it doesn't exist + * and create is true it creates a new item. + * @param name name of the item to find. + * @param create whether to create an item when not found or not. + * @return the requested item or NULL if not found. + */ +IniItem *IniGroup::GetItem(const char *name, bool create) +{ + for (IniItem *item = this->item; item != NULL; item = item->next) { + if (strcmp(item->name, name) == 0) return item; + } + + if (!create) return NULL; + + /* otherwise make a new one */ + return new IniItem(this, name, strlen(name)); +} + +/** + * Clear all items in the group + */ +void IniGroup::Clear() +{ + delete this->item; + this->item = NULL; + this->last_item = &this->item; +} + +/** + * Construct a new in-memory Ini file representation. + * @param list_group_names A NULL terminated list with groups that should be + * loaded as lists instead of variables. + */ +IniLoadFile::IniLoadFile(const char * const *list_group_names) : group(NULL), comment(NULL), list_group_names(list_group_names) +{ + this->last_group = &this->group; +} + +/** Free everything we loaded. */ +IniLoadFile::~IniLoadFile() +{ + free(this->comment); + delete this->group; +} + +/** + * Get the group with the given name, and if it doesn't exist + * create a new group. + * @param name name of the group to find. + * @param len the maximum length of said name. + * @return the requested group. + */ +IniGroup *IniLoadFile::GetGroup(const char *name, size_t len) +{ + if (len == 0) len = strlen(name); + + /* does it exist already? */ + for (IniGroup *group = this->group; group != NULL; group = group->next) { + if (!strncmp(group->name, name, len) && group->name[len] == 0) { + return group; + } + } + + /* otherwise make a new one */ + IniGroup *group = new IniGroup(this, name, len); + group->comment = strdup("\n"); + return group; +} + +/** + * Remove the group with the given name. + * @param name name of the group to remove. + */ +void IniLoadFile::RemoveGroup(const char *name) +{ + size_t len = strlen(name); + IniGroup *prev = NULL; + IniGroup *group; + + /* does it exist already? */ + for (group = this->group; group != NULL; prev = group, group = group->next) { + if (strncmp(group->name, name, len) == 0) { + break; + } + } + + if (group == NULL) return; + + if (prev != NULL) { + prev->next = prev->next->next; + if (this->last_group == &group->next) this->last_group = &prev->next; + } else { + this->group = this->group->next; + if (this->last_group == &group->next) this->last_group = &this->group; + } + + group->next = NULL; + delete group; +} + +/** + * Load the Ini file's data from the disk. + * @param filename the file to load. + * @pre nothing has been loaded yet. + */ +void IniLoadFile::LoadFromDisk(const char *filename) +{ + assert(this->last_group == &this->group); + + char buffer[1024]; + IniGroup *group = NULL; + + char *comment = NULL; + uint comment_size = 0; + uint comment_alloc = 0; + + size_t end; + /* + * Now we are going to open a file that contains no more than simple + * plain text. That would raise the question: "why open the file as + * if it is a binary file?". That's simple... Microsoft, in all + * their greatness and wisdom decided it would be useful if ftell + * is aware of '\r\n' and "sees" that as a single character. The + * easiest way to test for that situation is by searching for '\n' + * and decrease the value every time you encounter a '\n'. This will + * thus also make ftell "see" the '\r' when it is not there, so the + * result of ftell will be highly unreliable. So to work around this + * marvel of wisdom we have to open in as a binary file. + */ + FILE *in = FioFOpenFile(filename, "rb", DATA_DIR, &end); + if (in == NULL) return; + + end += ftell(in); + + /* for each line in the file */ + while ((size_t)ftell(in) < end && fgets(buffer, sizeof(buffer), in)) { + char c, *s; + /* trim whitespace from the left side */ + for (s = buffer; *s == ' ' || *s == '\t'; s++) {} + + /* trim whitespace from right side. */ + char *e = s + strlen(s); + while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--; + *e = '\0'; + + /* skip comments and empty lines */ + if (*s == '#' || *s == ';' || *s == '\0') { + uint ns = comment_size + (e - s + 1); + uint a = comment_alloc; + /* add to comment */ + if (ns > a) { + a = max(a, 128U); + do a *= 2; while (a < ns); + comment = ReallocT(comment, comment_alloc = a); + } + uint pos = comment_size; + comment_size += (e - s + 1); + comment[pos + e - s] = '\n'; // comment newline + memcpy(comment + pos, s, e - s); // copy comment contents + continue; + } + + /* it's a group? */ + if (s[0] == '[') { + if (e[-1] != ']') { + ShowInfoF("ini: invalid group name '%s'", buffer); + } else { + e--; + } + s++; // skip [ + group = new IniGroup(this, s, e - s); + if (comment_size != 0) { + group->comment = strndup(comment, comment_size); + comment_size = 0; + } + } else if (group != NULL) { + char *t; + /* find end of keyname */ + if (*s == '\"') { + s++; + for (t = s; *t != '\0' && *t != '\"'; t++) {} + if (*t == '\"') *t = ' '; + } else { + for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {} + } + + /* it's an item in an existing group */ + IniItem *item = new IniItem(group, s, t - s); + if (comment_size != 0) { + item->comment = strndup(comment, comment_size); + comment_size = 0; + } + + /* find start of parameter */ + while (*t == '=' || *t == ' ' || *t == '\t') t++; + + bool quoted = (*t == '\"'); + /* remove starting quotation marks */ + if (*t == '\"') t++; + /* remove ending quotation marks */ + e = t + strlen(t); + if (e > t && e[-1] == '\"') e--; + *e = '\0'; + + /* If the value was not quoted and empty, it must be NULL */ + item->value = (!quoted && e == t) ? NULL : strndup(t, e - t); + } else { + /* it's an orphan item */ + ShowInfoF("ini: '%s' outside of group", buffer); + } + } + + if (comment_size > 0) { + this->comment = strndup(comment, comment_size); + comment_size = 0; + } + + free(comment); + fclose(in); +} + diff --git a/src/ini_type.h b/src/ini_type.h index b89a77b56..2bb4fea27 100644 --- a/src/ini_type.h +++ b/src/ini_type.h @@ -40,27 +40,33 @@ struct IniGroup { char *name; ///< name of group char *comment; ///< comment for group - IniGroup(struct IniFile *parent, const char *name, size_t len = 0); + IniGroup(struct IniLoadFile *parent, const char *name, size_t len = 0); ~IniGroup(); IniItem *GetItem(const char *name, bool create); void Clear(); }; -/** The complete ini file. */ -struct IniFile { +/** Ini file that only supports loading. */ +struct IniLoadFile { IniGroup *group; ///< the first group in the ini IniGroup **last_group; ///< the last group in the ini char *comment; ///< last comment in file const char * const *list_group_names; ///< NULL terminated list with group names that are lists - IniFile(const char * const *list_group_names = NULL); - ~IniFile(); + IniLoadFile(const char * const *list_group_names = NULL); + virtual ~IniLoadFile(); IniGroup *GetGroup(const char *name, size_t len = 0); void RemoveGroup(const char *name); void LoadFromDisk(const char *filename); +}; + +/** Ini file that supports both loading and saving. */ +struct IniFile : IniLoadFile { + IniFile(const char * const *list_group_names = NULL); + bool SaveToDisk(const char *filename); }; -- cgit v1.2.3-54-g00ecf