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