# ini_load.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    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   *parent->last_item = this;
    00030   parent->last_item = &this->next;
    00031 }
    00032
    00034 IniItem::~IniItem()
    00035 {
    00036   free(this->name);
    00037   free(this->value);
    00038   free(this->comment);
    00039
    00040   delete this->next;
    00041 }
    00042
    00047 void IniItem::SetValue(const char *value)
    00048 {
    00049   free(this->value);
    00050   this->value = strdup(value);
    00051 }
    00052
    00059 IniGroup::IniGroup(IniLoadFile *parent, const char *name, size_t len) : next(NULL), type(IGT_VARIABLES), item(NULL), comment(NULL)
    00060 {
    00061   if (len == 0) len = strlen(name);
    00062
    00063   this->name = strndup(name, len);
    00064   this->last_item = &this->item;
    00065   *parent->last_group = this;
    00066   parent->last_group = &this->next;
    00067
    00068   if (parent->list_group_names != NULL) {
    00069     for (uint i = 0; parent->list_group_names[i] != NULL; i++) {
    00070       if (strcmp(this->name, parent->list_group_names[i]) == 0) {
    00071         this->type = IGT_LIST;
    00072         return;
    00073       }
    00074     }
    00075   }
    00076   if (parent->seq_group_names != NULL) {
    00077     for (uint i = 0; parent->seq_group_names[i] != NULL; i++) {
    00078       if (strcmp(this->name, parent->seq_group_names[i]) == 0) {
    00079         this->type = IGT_SEQUENCE;
    00080         return;
    00081       }
    00082     }
    00083   }
    00084 }
    00085
    00087 IniGroup::~IniGroup()
    00088 {
    00089   free(this->name);
    00090   free(this->comment);
    00091
    00092   delete this->item;
    00093   delete this->next;
    00094 }
    00095
    00103 IniItem *IniGroup::GetItem(const char *name, bool create)
    00104 {
    00105   for (IniItem *item = this->item; item != NULL; item = item->next) {
    00106     if (strcmp(item->name, name) == 0) return item;
    00107   }
    00108
    00109   if (!create) return NULL;
    00110
    00111   /* otherwise make a new one */
    00112   return new IniItem(this, name, strlen(name));
    00113 }
    00114
    00118 void IniGroup::Clear()
    00119 {
    00120   delete this->item;
    00121   this->item = NULL;
    00122   this->last_item = &this->item;
    00123 }
    00124
    00130 IniLoadFile::IniLoadFile(const char * const *list_group_names, const char * const *seq_group_names) :
    00131     group(NULL),
    00132     comment(NULL),
    00133     list_group_names(list_group_names),
    00134     seq_group_names(seq_group_names)
    00135 {
    00136   this->last_group = &this->group;
    00137 }
    00138
    00140 IniLoadFile::~IniLoadFile()
    00141 {
    00142   free(this->comment);
    00143   delete this->group;
    00144 }
    00145
    00154 IniGroup *IniLoadFile::GetGroup(const char *name, size_t len, bool create_new)
    00155 {
    00156   if (len == 0) len = strlen(name);
    00157
    00158   /* does it exist already? */
    00159   for (IniGroup *group = this->group; group != NULL; group = group->next) {
    00160     if (!strncmp(group->name, name, len) && group->name[len] == 0) {
    00161       return group;
    00162     }
    00163   }
    00164
    00165   if (!create_new) return NULL;
    00166
    00167   /* otherwise make a new one */
    00168   IniGroup *group = new IniGroup(this, name, len);
    00169   group->comment = strdup("\n");
    00170   return group;
    00171 }
    00172
    00177 void IniLoadFile::RemoveGroup(const char *name)
    00178 {
    00179   size_t len = strlen(name);
    00180   IniGroup *prev = NULL;
    00181   IniGroup *group;
    00182
    00183   /* does it exist already? */
    00184   for (group = this->group; group != NULL; prev = group, group = group->next) {
    00185     if (strncmp(group->name, name, len) == 0) {
    00186       break;
    00187     }
    00188   }
    00189
    00190   if (group == NULL) return;
    00191
    00192   if (prev != NULL) {
    00193     prev->next = prev->next->next;
    00194     if (this->last_group == &group->next) this->last_group = &prev->next;
    00195   } else {
    00196     this->group = this->group->next;
    00197     if (this->last_group == &group->next) this->last_group = &this->group;
    00198   }
    00199
    00200   group->next = NULL;
    00201   delete group;
    00202 }
    00203
    00210 void IniLoadFile::LoadFromDisk(const char *filename, Subdirectory subdir)
    00211 {
    00212   assert(this->last_group == &this->group);
    00213
    00214   char buffer[1024];
    00215   IniGroup *group = NULL;
    00216
    00217   char *comment = NULL;
    00218   uint comment_size = 0;
    00219   uint comment_alloc = 0;
    00220
    00221   size_t end;
    00222   FILE *in = this->OpenFile(filename, subdir, &end);
    00223   if (in == NULL) return;
    00224
    00225   end += ftell(in);
    00226
    00227   /* for each line in the file */
    00228   while ((size_t)ftell(in) < end && fgets(buffer, sizeof(buffer), in)) {
    00229     char c, *s;
    00230     /* trim whitespace from the left side */
    00231     for (s = buffer; *s == ' ' || *s == '\t'; s++) {}
    00232
    00233     /* trim whitespace from right side. */
    00234     char *e = s + strlen(s);
    00235     while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--;
    00236     *e = '\0';
    00237
    00238     /* Skip comments and empty lines outside IGT_SEQUENCE groups. */
    00239     if ((group == NULL || group->type != IGT_SEQUENCE) && (*s == '#' || *s == ';' || *s == '\0')) {
    00240       uint ns = comment_size + (e - s + 1);
    00241       uint a = comment_alloc;
    00242       /* add to comment */
    00243       if (ns > a) {
    00244         a = max(a, 128U);
    00245         do a *= 2; while (a < ns);
    00246         comment = ReallocT(comment, comment_alloc = a);
    00247       }
    00248       uint pos = comment_size;
    00249       comment_size += (e - s + 1);
    00250       comment[pos + e - s] = '\n'; // comment newline
    00251       memcpy(comment + pos, s, e - s); // copy comment contents
    00252       continue;
    00253     }
    00254
    00255     /* it's a group? */
    00256     if (s[0] == '[') {
    00257       if (e[-1] != ']') {
    00258         this->ReportFileError("ini: invalid group name '", buffer, "'");
    00259       } else {
    00260         e--;
    00261       }
    00262       s++; // skip [
    00263       group = new IniGroup(this, s, e - s);
    00264       if (comment_size != 0) {
    00265         group->comment = strndup(comment, comment_size);
    00266         comment_size = 0;
    00267       }
    00268     } else if (group != NULL) {
    00269       if (group->type == IGT_SEQUENCE) {
    00270         /* A sequence group, use the line as item name without further interpretation. */
    00271         IniItem *item = new IniItem(group, buffer, e - buffer);
    00272         if (comment_size) {
    00273           item->comment = strndup(comment, comment_size);
    00274           comment_size = 0;
    00275         }
    00276         continue;
    00277       }
    00278       char *t;
    00279       /* find end of keyname */
    00280       if (*s == '\"') {
    00281         s++;
    00282         for (t = s; *t != '\0' && *t != '\"'; t++) {}
    00283         if (*t == '\"') *t = ' ';
    00284       } else {
    00285         for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {}
    00286       }
    00287
    00288       /* it's an item in an existing group */
    00289       IniItem *item = new IniItem(group, s, t - s);
    00290       if (comment_size != 0) {
    00291         item->comment = strndup(comment, comment_size);
    00292         comment_size = 0;
    00293       }
    00294
    00295       /* find start of parameter */
    00296       while (*t == '=' || *t == ' ' || *t == '\t') t++;
    00297
    00298       bool quoted = (*t == '\"');
    00299       /* remove starting quotation marks */
    00300       if (*t == '\"') t++;
    00301       /* remove ending quotation marks */
    00302       e = t + strlen(t);
    00303       if (e > t && e[-1] == '\"') e--;
    00304       *e = '\0';
    00305
    00306       /* If the value was not quoted and empty, it must be NULL */
    00307       item->value = (!quoted && e == t) ? NULL : strndup(t, e - t);
    00308     } else {
    00309       /* it's an orphan item */
    00310       this->ReportFileError("ini: '", buffer, "' outside of group");
    00311     }
    00312   }
    00313
    00314   if (comment_size > 0) {
    00315     this->comment = strndup(comment, comment_size);
    00316     comment_size = 0;
    00317   }
    00318
    00319   free(comment);
    00320   fclose(in);
    00321 }
    00322