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 
00209 void IniLoadFile::LoadFromDisk(const char *filename)
00210 {
00211   assert(this->last_group == &this->group);
00212 
00213   char buffer[1024];
00214   IniGroup *group = NULL;
00215 
00216   char *comment = NULL;
00217   uint comment_size = 0;
00218   uint comment_alloc = 0;
00219 
00220   size_t end;
00221   FILE *in = this->OpenFile(filename, &end);
00222   if (in == NULL) return;
00223 
00224   end += ftell(in);
00225 
00226   /* for each line in the file */
00227   while ((size_t)ftell(in) < end && fgets(buffer, sizeof(buffer), in)) {
00228     char c, *s;
00229     /* trim whitespace from the left side */
00230     for (s = buffer; *s == ' ' || *s == '\t'; s++) {}
00231 
00232     /* trim whitespace from right side. */
00233     char *e = s + strlen(s);
00234     while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--;
00235     *e = '\0';
00236 
00237     /* Skip comments and empty lines outside IGT_SEQUENCE groups. */
00238     if ((group == NULL || group->type != IGT_SEQUENCE) && (*s == '#' || *s == ';' || *s == '\0')) {
00239       uint ns = comment_size + (e - s + 1);
00240       uint a = comment_alloc;
00241       /* add to comment */
00242       if (ns > a) {
00243         a = max(a, 128U);
00244         do a *= 2; while (a < ns);
00245         comment = ReallocT(comment, comment_alloc = a);
00246       }
00247       uint pos = comment_size;
00248       comment_size += (e - s + 1);
00249       comment[pos + e - s] = '\n'; // comment newline
00250       memcpy(comment + pos, s, e - s); // copy comment contents
00251       continue;
00252     }
00253 
00254     /* it's a group? */
00255     if (s[0] == '[') {
00256       if (e[-1] != ']') {
00257         this->ReportFileError("ini: invalid group name '", buffer, "'");
00258       } else {
00259         e--;
00260       }
00261       s++; // skip [
00262       group = new IniGroup(this, s, e - s);
00263       if (comment_size != 0) {
00264         group->comment = strndup(comment, comment_size);
00265         comment_size = 0;
00266       }
00267     } else if (group != NULL) {
00268       if (group->type == IGT_SEQUENCE) {
00269         /* A sequence group, use the line as item name without further interpretation. */
00270         IniItem *item = new IniItem(group, buffer, e - buffer);
00271         if (comment_size) {
00272           item->comment = strndup(comment, comment_size);
00273           comment_size = 0;
00274         }
00275         continue;
00276       }
00277       char *t;
00278       /* find end of keyname */
00279       if (*s == '\"') {
00280         s++;
00281         for (t = s; *t != '\0' && *t != '\"'; t++) {}
00282         if (*t == '\"') *t = ' ';
00283       } else {
00284         for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {}
00285       }
00286 
00287       /* it's an item in an existing group */
00288       IniItem *item = new IniItem(group, s, t - s);
00289       if (comment_size != 0) {
00290         item->comment = strndup(comment, comment_size);
00291         comment_size = 0;
00292       }
00293 
00294       /* find start of parameter */
00295       while (*t == '=' || *t == ' ' || *t == '\t') t++;
00296 
00297       bool quoted = (*t == '\"');
00298       /* remove starting quotation marks */
00299       if (*t == '\"') t++;
00300       /* remove ending quotation marks */
00301       e = t + strlen(t);
00302       if (e > t && e[-1] == '\"') e--;
00303       *e = '\0';
00304 
00305       /* If the value was not quoted and empty, it must be NULL */
00306       item->value = (!quoted && e == t) ? NULL : strndup(t, e - t);
00307     } else {
00308       /* it's an orphan item */
00309       this->ReportFileError("ini: '", buffer, "' outside of group");
00310     }
00311   }
00312 
00313   if (comment_size > 0) {
00314     this->comment = strndup(comment, comment_size);
00315     comment_size = 0;
00316   }
00317 
00318   free(comment);
00319   fclose(in);
00320 }
00321 

Generated on Mon May 9 05:18:54 2011 for OpenTTD by  doxygen 1.6.1