00001
00002
00005 #include "stdafx.h"
00006 #include "core/alloc_func.hpp"
00007 #include "core/math_func.hpp"
00008 #include "core/mem_func.hpp"
00009 #include "debug.h"
00010 #include "ini_type.h"
00011 #include "string_func.h"
00012 #include "fileio_func.h"
00013
00014 #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 500)
00015 # define WITH_FDATASYNC
00016 # include <unistd.h>
00017 #endif
00018
00019 #ifdef WIN32
00020 # include <shellapi.h>
00021 #endif
00022
00023 IniItem::IniItem(IniGroup *parent, const char *name, size_t len) : next(NULL), value(NULL), comment(NULL)
00024 {
00025 if (len == 0) len = strlen(name);
00026
00027 this->name = strndup(name, len);
00028 *parent->last_item = this;
00029 parent->last_item = &this->next;
00030 }
00031
00032 IniItem::~IniItem()
00033 {
00034 free(this->name);
00035 free(this->value);
00036 free(this->comment);
00037
00038 delete this->next;
00039 }
00040
00041 void IniItem::SetValue(const char *value)
00042 {
00043 free(this->value);
00044 this->value = strdup(value);
00045 }
00046
00047 IniGroup::IniGroup(IniFile *parent, const char *name, size_t len) : next(NULL), type(IGT_VARIABLES), item(NULL), comment(NULL)
00048 {
00049 if (len == 0) len = strlen(name);
00050
00051 this->name = strndup(name, len);
00052 this->last_item = &this->item;
00053 *parent->last_group = this;
00054 parent->last_group = &this->next;
00055
00056 if (parent->list_group_names == NULL) return;
00057
00058 for (uint i = 0; parent->list_group_names[i] != NULL; i++) {
00059 if (strcmp(this->name, parent->list_group_names[i]) == 0) {
00060 this->type = IGT_LIST;
00061 return;
00062 }
00063 }
00064 }
00065
00066 IniGroup::~IniGroup()
00067 {
00068 free(this->name);
00069 free(this->comment);
00070
00071 delete this->item;
00072 delete this->next;
00073 }
00074
00075 IniItem *IniGroup::GetItem(const char *name, bool create)
00076 {
00077 for (IniItem *item = this->item; item != NULL; item = item->next) {
00078 if (strcmp(item->name, name) == 0) return item;
00079 }
00080
00081 if (!create) return NULL;
00082
00083
00084 return new IniItem(this, name, strlen(name));
00085 }
00086
00087 void IniGroup::Clear()
00088 {
00089 delete this->item;
00090 this->item = NULL;
00091 this->last_item = &this->item;
00092 }
00093
00094 IniFile::IniFile(const char **list_group_names) : group(NULL), comment(NULL), list_group_names(list_group_names)
00095 {
00096 this->last_group = &this->group;
00097 }
00098
00099 IniFile::~IniFile()
00100 {
00101 free(this->comment);
00102 delete this->group;
00103 }
00104
00105 IniGroup *IniFile::GetGroup(const char *name, size_t len)
00106 {
00107 if (len == 0) len = strlen(name);
00108
00109
00110 for (IniGroup *group = this->group; group != NULL; group = group->next) {
00111 if (!memcmp(group->name, name, len) && group->name[len] == 0) {
00112 return group;
00113 }
00114 }
00115
00116
00117 IniGroup *group = new IniGroup(this, name, len);
00118 group->comment = strdup("\n");
00119 return group;
00120 }
00121
00122 void IniFile::RemoveGroup(const char *name)
00123 {
00124 size_t len = strlen(name);
00125 IniGroup *prev = NULL;
00126 IniGroup *group;
00127
00128
00129 for (group = this->group; group != NULL; prev = group, group = group->next) {
00130 if (memcmp(group->name, name, len) == 0) {
00131 break;
00132 }
00133 }
00134
00135 if (group == NULL) return;
00136
00137 if (prev != NULL) {
00138 prev->next = prev->next->next;
00139 if (this->last_group == &group->next) this->last_group = &prev->next;
00140 } else {
00141 this->group = this->group->next;
00142 if (this->last_group == &group->next) this->last_group = &this->group;
00143 }
00144
00145 group->next = NULL;
00146 delete group;
00147 }
00148
00149 void IniFile::LoadFromDisk(const char *filename)
00150 {
00151 assert(this->last_group == &this->group);
00152
00153 char buffer[1024];
00154 IniGroup *group = NULL;
00155
00156 char *comment = NULL;
00157 uint comment_size = 0;
00158 uint comment_alloc = 0;
00159
00160 size_t end;
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173 FILE *in = FioFOpenFile(filename, "rb", DATA_DIR, &end);
00174 if (in == NULL) return;
00175
00176 end += ftell(in);
00177
00178
00179 while ((size_t)ftell(in) < end && fgets(buffer, sizeof(buffer), in)) {
00180 char c, *s;
00181
00182 for (s = buffer; *s == ' ' || *s == '\t'; s++) {}
00183
00184
00185 char *e = s + strlen(s);
00186 while (e > s && ((c = e[-1]) == '\n' || c == '\r' || c == ' ' || c == '\t')) e--;
00187 *e = '\0';
00188
00189
00190 if (*s == '#' || *s == ';' || *s == '\0') {
00191 uint ns = comment_size + (e - s + 1);
00192 uint a = comment_alloc;
00193
00194 if (ns > a) {
00195 a = max(a, 128U);
00196 do a *= 2; while (a < ns);
00197 comment = ReallocT(comment, comment_alloc = a);
00198 }
00199 uint pos = comment_size;
00200 comment_size += (e - s + 1);
00201 comment[pos + e - s] = '\n';
00202 memcpy(comment + pos, s, e - s);
00203 continue;
00204 }
00205
00206
00207 if (s[0] == '[') {
00208 if (e[-1] != ']') {
00209 ShowInfoF("ini: invalid group name '%s'", buffer);
00210 } else {
00211 e--;
00212 }
00213 s++;
00214 group = new IniGroup(this, s, e - s);
00215 if (comment_size) {
00216 group->comment = strndup(comment, comment_size);
00217 comment_size = 0;
00218 }
00219 } else if (group) {
00220 char *t;
00221
00222 if (*s == '\"') {
00223 s++;
00224 for (t = s; *t != '\0' && *t != '\"'; t++) {}
00225 if (*t == '\"') *t = ' ';
00226 } else {
00227 for (t = s; *t != '\0' && *t != '=' && *t != '\t' && *t != ' '; t++) {}
00228 }
00229
00230
00231 IniItem *item = new IniItem(group, s, t - s);
00232 if (comment_size) {
00233 item->comment = strndup(comment, comment_size);
00234 comment_size = 0;
00235 }
00236
00237
00238 while (*t == '=' || *t == ' ' || *t == '\t') t++;
00239
00240
00241
00242 if (*t == '\"') t++;
00243
00244 e = t + strlen(t);
00245 if (e > t && e[-1] == '\"') e--;
00246 *e = '\0';
00247
00248 item->value = strndup(t, e - t);
00249 } else {
00250
00251 ShowInfoF("ini: '%s' outside of group", buffer);
00252 }
00253 }
00254
00255 if (comment_size > 0) {
00256 this->comment = strndup(comment, comment_size);
00257 comment_size = 0;
00258 }
00259
00260 free(comment);
00261 fclose(in);
00262 }
00263
00264 bool IniFile::SaveToDisk(const char *filename)
00265 {
00266
00267
00268
00269
00270
00271 char file_new[MAX_PATH];
00272
00273 strecpy(file_new, filename, lastof(file_new));
00274 strecat(file_new, ".new", lastof(file_new));
00275 FILE *f = fopen(file_new, "w");
00276 if (f == NULL) return false;
00277
00278 for (const IniGroup *group = this->group; group != NULL; group = group->next) {
00279 if (group->comment) fputs(group->comment, f);
00280 fprintf(f, "[%s]\n", group->name);
00281 for (const IniItem *item = group->item; item != NULL; item = item->next) {
00282 assert(item->value != NULL);
00283 if (item->comment != NULL) fputs(item->comment, f);
00284
00285
00286 if (strchr(item->name, ' ') != NULL) {
00287 fprintf(f, "\"%s\"", item->name);
00288 } else {
00289 fprintf(f, "%s", item->name);
00290 }
00291
00292 fprintf(f, " = %s\n", item->value);
00293 }
00294 }
00295 if (this->comment) fputs(this->comment, f);
00296
00297
00298
00299
00300
00301
00302
00303 #ifdef WITH_FDATASYNC
00304 int ret = fdatasync(fileno(f));
00305 fclose(f);
00306 if (ret != 0) return false;
00307 #else
00308 fclose(f);
00309 #endif
00310
00311 #if defined(WIN32) || defined(WIN64)
00312
00313 TCHAR tfilename[MAX_PATH + 1], tfile_new[MAX_PATH + 1];
00314 _tcsncpy(tfilename, OTTD2FS(filename), MAX_PATH);
00315 _tcsncpy(tfile_new, OTTD2FS(file_new), MAX_PATH);
00316
00317 tfilename[MAX_PATH - 1] = '\0';
00318 tfile_new[MAX_PATH - 1] = '\0';
00319 tfilename[_tcslen(tfilename) + 1] = '\0';
00320 tfile_new[_tcslen(tfile_new) + 1] = '\0';
00321
00322
00323 SHFILEOPSTRUCT shfopt;
00324 MemSetT(&shfopt, 0);
00325 shfopt.wFunc = FO_MOVE;
00326 shfopt.fFlags = FOF_NOCONFIRMATION | FOF_NOCONFIRMMKDIR | FOF_NOERRORUI | FOF_SILENT;
00327 shfopt.pFrom = tfile_new;
00328 shfopt.pTo = tfilename;
00329 SHFileOperation(&shfopt);
00330 #else
00331 rename(file_new, filename);
00332 #endif
00333
00334 return true;
00335 }