00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "core/alloc_func.hpp"
00014 #include "core/mem_func.hpp"
00015 #include "debug.h"
00016 #include "ini_type.h"
00017 #include "string_func.h"
00018 #include "fileio_func.h"
00019
00020 #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500)
00021 # define WITH_FDATASYNC
00022 # include <unistd.h>
00023 #endif
00024
00025 #ifdef WIN32
00026 # include <shellapi.h>
00027 #endif
00028
00035 IniItem::IniItem(IniGroup *parent, const char *name, size_t len) : next(NULL), value(NULL), comment(NULL)
00036 {
00037 if (len == 0) len = strlen(name);
00038
00039 this->name = strndup(name, len);
00040 *parent->last_item = this;
00041 parent->last_item = &this->next;
00042 }
00043
00045 IniItem::~IniItem()
00046 {
00047 free(this->name);
00048 free(this->value);
00049 free(this->comment);
00050
00051 delete this->next;
00052 }
00053
00058 void IniItem::SetValue(const char *value)
00059 {
00060 free(this->value);
00061 this->value = strdup(value);
00062 }
00063
00070 IniGroup::IniGroup(IniFile *parent, const char *name, size_t len) : next(NULL), type(IGT_VARIABLES), item(NULL), comment(NULL)
00071 {
00072 if (len == 0) len = strlen(name);
00073
00074 this->name = strndup(name, len);
00075 this->last_item = &this->item;
00076 *parent->last_group = this;
00077 parent->last_group = &this->next;
00078
00079 if (parent->list_group_names == NULL) return;
00080
00081 for (uint i = 0; parent->list_group_names[i] != NULL; i++) {
00082 if (strcmp(this->name, parent->list_group_names[i]) == 0) {
00083 this->type = IGT_LIST;
00084 return;
00085 }
00086 }
00087 }
00088
00090 IniGroup::~IniGroup()
00091 {
00092 free(this->name);
00093 free(this->comment);
00094
00095 delete this->item;
00096 delete this->next;
00097 }
00098
00106 IniItem *IniGroup::GetItem(const char *name, bool create)
00107 {
00108 for (IniItem *item = this->item; item != NULL; item = item->next) {
00109 if (strcmp(item->name, name) == 0) return item;
00110 }
00111
00112 if (!create) return NULL;
00113
00114
00115 return new IniItem(this, name, strlen(name));
00116 }
00117
00121 void IniGroup::Clear()
00122 {
00123 delete this->item;
00124 this->item = NULL;
00125 this->last_item = &this->item;
00126 }
00127
00133 IniFile::IniFile(const char * const *list_group_names) : group(NULL), comment(NULL), list_group_names(list_group_names)
00134 {
00135 this->last_group = &this->group;
00136 }
00137
00139 IniFile::~IniFile()
00140 {
00141 free(this->comment);
00142 delete this->group;
00143 }
00144
00152 IniGroup *IniFile::GetGroup(const char *name, size_t len)
00153 {
00154 if (len == 0) len = strlen(name);
00155
00156
00157 for (IniGroup *group = this->group; group != NULL; group = group->next) {
00158 if (!strncmp(group->name, name, len) && group->name[len] == 0) {
00159 return group;
00160 }
00161 }
00162
00163
00164 IniGroup *group = new IniGroup(this, name, len);
00165 group->comment = strdup("\n");
00166 return group;
00167 }
00168
00173 void IniFile::RemoveGroup(const char *name)
00174 {
00175 size_t len = strlen(name);
00176 IniGroup *prev = NULL;
00177 IniGroup *group;
00178
00179 for (group = this->group; group != NULL; prev = group, group = group->next) {
00180 if (strncmp(group->name, name, len) == 0) {
00181 break;
00182 }
00183 }
00184
00185 if (group == NULL) return;
00186
00187 if (prev != NULL) {
00188 prev->next = prev->next->next;
00189 if (this->last_group == &group->next) this->last_group = &prev->next;
00190 } else {
00191 this->group = this->group->next;
00192 if (this->last_group == &group->next) this->last_group = &this->group;
00193 }
00194
00195 group->next = NULL;
00196 delete group;
00197 }
00198
00204 void IniFile::LoadFromDisk(const char *filename)
00205 {
00206 assert(this->last_group == &this->group);
00207
00208 char buffer[1024];
00209 IniGroup *group = NULL;
00210
00211 char *comment = NULL;
00212 uint comment_size = 0;
00213 uint comment_alloc = 0;
00214
00215 size_t end;
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228 FILE *in = FioFOpenFile(filename, "rb", DATA_DIR, &end);
00229 if (in == NULL) return;
00230
00231 end += ftell(in);
00232
00233
00234 while ((size_t)ftell(in) < end && fgets(buffer, sizeof(buffer), in)) {
00235 char c, *s;
00236
00237 for (s = buffer; *s == ' ' || *s == '\t'; s++) {}
00238
00239
00240 char *e = s + strlen(s);
00241 while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--;
00242 *e = '\0';
00243
00244
00245 if (*s == '#' || *s == ';' || *s == '\0') {
00246 uint ns = comment_size + (e - s + 1);
00247 uint a = comment_alloc;
00248
00249 if (ns > a) {
00250 a = max(a, 128U);
00251 do a *= 2; while (a < ns);
00252 comment = ReallocT(comment, comment_alloc = a);
00253 }
00254 uint pos = comment_size;
00255 comment_size += (e - s + 1);
00256 comment[pos + e - s] = '\n';
00257 memcpy(comment + pos, s, e - s);
00258 continue;
00259 }
00260
00261
00262 if (s[0] == '[') {
00263 if (e[-1] != ']') {
00264 ShowInfoF("ini: invalid group name '%s'", buffer);
00265 } else {
00266 e--;
00267 }
00268 s++;
00269 group = new IniGroup(this, s, e - s);
00270 if (comment_size != 0) {
00271 group->comment = strndup(comment, comment_size);
00272 comment_size = 0;
00273 }
00274 } else if (group != NULL) {
00275 char *t;
00276
00277 if (*s == '\"') {
00278 s++;
00279 for (t = s; *t != '\0' && *t != '\"'; t++) {}
00280 if (*t == '\"') *t = ' ';
00281 } else {
00282 for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {}
00283 }
00284
00285
00286 IniItem *item = new IniItem(group, s, t - s);
00287 if (comment_size != 0) {
00288 item->comment = strndup(comment, comment_size);
00289 comment_size = 0;
00290 }
00291
00292
00293 while (*t == '=' || *t == ' ' || *t == '\t') t++;
00294
00295 bool quoted = (*t == '\"');
00296
00297 if (*t == '\"') t++;
00298
00299 e = t + strlen(t);
00300 if (e > t && e[-1] == '\"') e--;
00301 *e = '\0';
00302
00303
00304 item->value = (!quoted && e == t) ? NULL : strndup(t, e - t);
00305 } else {
00306
00307 ShowInfoF("ini: '%s' outside of group", buffer);
00308 }
00309 }
00310
00311 if (comment_size > 0) {
00312 this->comment = strndup(comment, comment_size);
00313 comment_size = 0;
00314 }
00315
00316 free(comment);
00317 fclose(in);
00318 }
00319
00325 bool IniFile::SaveToDisk(const char *filename)
00326 {
00327
00328
00329
00330
00331
00332 char file_new[MAX_PATH];
00333
00334 strecpy(file_new, filename, lastof(file_new));
00335 strecat(file_new, ".new", lastof(file_new));
00336 FILE *f = fopen(file_new, "w");
00337 if (f == NULL) return false;
00338
00339 for (const IniGroup *group = this->group; group != NULL; group = group->next) {
00340 if (group->comment) fputs(group->comment, f);
00341 fprintf(f, "[%s]\n", group->name);
00342 for (const IniItem *item = group->item; item != NULL; item = item->next) {
00343 if (item->comment != NULL) fputs(item->comment, f);
00344
00345
00346 if (strchr(item->name, ' ') != NULL ||
00347 item->name[0] == '[') {
00348 fprintf(f, "\"%s\"", item->name);
00349 } else {
00350 fprintf(f, "%s", item->name);
00351 }
00352
00353 fprintf(f, " = %s\n", item->value == NULL ? "" : item->value);
00354 }
00355 }
00356 if (this->comment) fputs(this->comment, f);
00357
00358
00359
00360
00361
00362
00363
00364 #ifdef WITH_FDATASYNC
00365 int ret = fdatasync(fileno(f));
00366 fclose(f);
00367 if (ret != 0) return false;
00368 #else
00369 fclose(f);
00370 #endif
00371
00372 #if defined(WIN32) || defined(WIN64)
00373
00374 TCHAR tfilename[MAX_PATH + 1], tfile_new[MAX_PATH + 1];
00375 _tcsncpy(tfilename, OTTD2FS(filename), MAX_PATH);
00376 _tcsncpy(tfile_new, OTTD2FS(file_new), MAX_PATH);
00377
00378 tfilename[MAX_PATH - 1] = '\0';
00379 tfile_new[MAX_PATH - 1] = '\0';
00380 tfilename[_tcslen(tfilename) + 1] = '\0';
00381 tfile_new[_tcslen(tfile_new) + 1] = '\0';
00382
00383
00384 SHFILEOPSTRUCT shfopt;
00385 MemSetT(&shfopt, 0);
00386 shfopt.wFunc = FO_MOVE;
00387 shfopt.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_SILENT;
00388 shfopt.pFrom = tfile_new;
00389 shfopt.pTo = tfilename;
00390 SHFileOperation(&shfopt);
00391 #else
00392 rename(file_new, filename);
00393 #endif
00394
00395 return true;
00396 }