# ini_load.cpp

Go to the documentation of this file.

    00001 /* $Id: ini_load.cpp 26541 2014-04-29 18:18:52Z frosch $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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 <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "core/alloc_func.hpp"
    00014 #include "core/mem_func.hpp"
    00015 #include "ini_type.h"
    00016 #include "string_func.h"
    00017
    00024 IniItem::IniItem(IniGroup *parent, const char *name, size_t len) : next(NULL), value(NULL), comment(NULL)
    00025 {
    00026   if (len == 0) len = strlen(name);
    00027
    00028   this->name = strndup(name, len);
    00029   if (this->name != NULL) str_validate(this->name, this->name + len);
    00030
    00031   *parent->last_item = this;
    00032   parent->last_item = &this->next;
    00033 }
    00034
    00036 IniItem::~IniItem()
    00037 {
    00038   free(this->name);
    00039   free(this->value);
    00040   free(this->comment);
    00041
    00042   delete this->next;
    00043 }
    00044
    00049 void IniItem::SetValue(const char *value)
    00050 {
    00051   free(this->value);
    00052   this->value = strdup(value);
    00053 }
    00054
    00061 IniGroup::IniGroup(IniLoadFile *parent, const char *name, size_t len) : next(NULL), type(IGT_VARIABLES), item(NULL), comment(NULL)
    00062 {
    00063   if (len == 0) len = strlen(name);
    00064
    00065   this->name = strndup(name, len);
    00066   if (this->name == NULL) error("not enough memory to allocate group name");
    00067   str_validate(this->name, this->name + len);
    00068
    00069   this->last_item = &this->item;
    00070   *parent->last_group = this;
    00071   parent->last_group = &this->next;
    00072
    00073   if (parent->list_group_names != NULL) {
    00074     for (uint i = 0; parent->list_group_names[i] != NULL; i++) {
    00075       if (strcmp(this->name, parent->list_group_names[i]) == 0) {
    00076         this->type = IGT_LIST;
    00077         return;
    00078       }
    00079     }
    00080   }
    00081   if (parent->seq_group_names != NULL) {
    00082     for (uint i = 0; parent->seq_group_names[i] != NULL; i++) {
    00083       if (strcmp(this->name, parent->seq_group_names[i]) == 0) {
    00084         this->type = IGT_SEQUENCE;
    00085         return;
    00086       }
    00087     }
    00088   }
    00089 }
    00090
    00092 IniGroup::~IniGroup()
    00093 {
    00094   free(this->name);
    00095   free(this->comment);
    00096
    00097   delete this->item;
    00098   delete this->next;
    00099 }
    00100
    00108 IniItem *IniGroup::GetItem(const char *name, bool create)
    00109 {
    00110   for (IniItem *item = this->item; item != NULL; item = item->next) {
    00111     if (strcmp(item->name, name) == 0) return item;
    00112   }
    00113
    00114   if (!create) return NULL;
    00115
    00116   /* otherwise make a new one */
    00117   return new IniItem(this, name, strlen(name));
    00118 }
    00119
    00123 void IniGroup::Clear()
    00124 {
    00125   delete this->item;
    00126   this->item = NULL;
    00127   this->last_item = &this->item;
    00128 }
    00129
    00135 IniLoadFile::IniLoadFile(const char * const *list_group_names, const char * const *seq_group_names) :
    00136     group(NULL),
    00137     comment(NULL),
    00138     list_group_names(list_group_names),
    00139     seq_group_names(seq_group_names)
    00140 {
    00141   this->last_group = &this->group;
    00142 }
    00143
    00145 IniLoadFile::~IniLoadFile()
    00146 {
    00147   free(this->comment);
    00148   delete this->group;
    00149 }
    00150
    00159 IniGroup *IniLoadFile::GetGroup(const char *name, size_t len, bool create_new)
    00160 {
    00161   if (len == 0) len = strlen(name);
    00162
    00163   /* does it exist already? */
    00164   for (IniGroup *group = this->group; group != NULL; group = group->next) {
    00165     if (!strncmp(group->name, name, len) && group->name[len] == 0) {
    00166       return group;
    00167     }
    00168   }
    00169
    00170   if (!create_new) return NULL;
    00171
    00172   /* otherwise make a new one */
    00173   IniGroup *group = new IniGroup(this, name, len);
    00174   group->comment = strdup("\n");
    00175   return group;
    00176 }
    00177
    00182 void IniLoadFile::RemoveGroup(const char *name)
    00183 {
    00184   size_t len = strlen(name);
    00185   IniGroup *prev = NULL;
    00186   IniGroup *group;
    00187
    00188   /* does it exist already? */
    00189   for (group = this->group; group != NULL; prev = group, group = group->next) {
    00190     if (strncmp(group->name, name, len) == 0) {
    00191       break;
    00192     }
    00193   }
    00194
    00195   if (group == NULL) return;
    00196
    00197   if (prev != NULL) {
    00198     prev->next = prev->next->next;
    00199     if (this->last_group == &group->next) this->last_group = &prev->next;
    00200   } else {
    00201     this->group = this->group->next;
    00202     if (this->last_group == &group->next) this->last_group = &this->group;
    00203   }
    00204
    00205   group->next = NULL;
    00206   delete group;
    00207 }
    00208
    00215 void IniLoadFile::LoadFromDisk(const char *filename, Subdirectory subdir)
    00216 {
    00217   assert(this->last_group == &this->group);
    00218
    00219   char buffer[1024];
    00220   IniGroup *group = NULL;
    00221
    00222   char *comment = NULL;
    00223   uint comment_size = 0;
    00224   uint comment_alloc = 0;
    00225
    00226   size_t end;
    00227   FILE *in = this->OpenFile(filename, subdir, &end);
    00228   if (in == NULL) return;
    00229
    00230   end += ftell(in);
    00231
    00232   /* for each line in the file */
    00233   while ((size_t)ftell(in) < end && fgets(buffer, sizeof(buffer), in)) {
    00234     char c, *s;
    00235     /* trim whitespace from the left side */
    00236     for (s = buffer; *s == ' ' || *s == '\t'; s++) {}
    00237
    00238     /* trim whitespace from right side. */
    00239     char *e = s + strlen(s);
    00240     while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--;
    00241     *e = '\0';
    00242
    00243     /* Skip comments and empty lines outside IGT_SEQUENCE groups. */
    00244     if ((group == NULL || group->type != IGT_SEQUENCE) && (*s == '#' || *s == ';' || *s == '\0')) {
    00245       uint ns = comment_size + (e - s + 1);
    00246       uint a = comment_alloc;
    00247       /* add to comment */
    00248       if (ns > a) {
    00249         a = max(a, 128U);
    00250         do a *= 2; while (a < ns);
    00251         comment = ReallocT(comment, comment_alloc = a);
    00252       }
    00253       uint pos = comment_size;
    00254       comment_size += (e - s + 1);
    00255       comment[pos + e - s] = '\n'; // comment newline
    00256       memcpy(comment + pos, s, e - s); // copy comment contents
    00257       continue;
    00258     }
    00259
    00260     /* it's a group? */
    00261     if (s[0] == '[') {
    00262       if (e[-1] != ']') {
    00263         this->ReportFileError("ini: invalid group name '", buffer, "'");
    00264       } else {
    00265         e--;
    00266       }
    00267       s++; // skip [
    00268       group = new IniGroup(this, s, e - s);
    00269       if (comment_size != 0) {
    00270         group->comment = strndup(comment, comment_size);
    00271         comment_size = 0;
    00272       }
    00273     } else if (group != NULL) {
    00274       if (group->type == IGT_SEQUENCE) {
    00275         /* A sequence group, use the line as item name without further interpretation. */
    00276         IniItem *item = new IniItem(group, buffer, e - buffer);
    00277         if (comment_size) {
    00278           item->comment = strndup(comment, comment_size);
    00279           comment_size = 0;
    00280         }
    00281         continue;
    00282       }
    00283       char *t;
    00284       /* find end of keyname */
    00285       if (*s == '\"') {
    00286         s++;
    00287         for (t = s; *t != '\0' && *t != '\"'; t++) {}
    00288         if (*t == '\"') *t = ' ';
    00289       } else {
    00290         for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {}
    00291       }
    00292
    00293       /* it's an item in an existing group */
    00294       IniItem *item = new IniItem(group, s, t - s);
    00295       if (comment_size != 0) {
    00296         item->comment = strndup(comment, comment_size);
    00297         comment_size = 0;
    00298       }
    00299
    00300       /* find start of parameter */
    00301       while (*t == '=' || *t == ' ' || *t == '\t') t++;
    00302
    00303       bool quoted = (*t == '\"');
    00304       /* remove starting quotation marks */
    00305       if (*t == '\"') t++;
    00306       /* remove ending quotation marks */
    00307       e = t + strlen(t);
    00308       if (e > t && e[-1] == '\"') e--;
    00309       *e = '\0';
    00310
    00311       /* If the value was not quoted and empty, it must be NULL */
    00312       item->value = (!quoted && e == t) ? NULL : strndup(t, e - t);
    00313       if (item->value != NULL) str_validate(item->value, item->value + strlen(item->value));
    00314     } else {
    00315       /* it's an orphan item */
    00316       this->ReportFileError("ini: '", buffer, "' outside of group");
    00317     }
    00318   }
    00319
    00320   if (comment_size > 0) {
    00321     this->comment = strndup(comment, comment_size);
    00322     comment_size = 0;
    00323   }
    00324
    00325   free(comment);
    00326   fclose(in);
    00327 }
    00328