4 #define WIN32_LEAN_AND_MEAN
16 public enum GlobalSettingType
23 public enum SettingsIOResult { error, success, fileNotFound, fileNotCompatibleWithDriver };
25 public class GlobalSettingsDriver
27 class_data char * name;
29 class_property char * name
31 set { class_data(name) = value; }
32 get { return class_data(name); }
36 virtual SettingsIOResult ::Load(File f, GlobalSettings globalSettings);
37 virtual SettingsIOResult ::Save(File f, GlobalSettings globalSettings);
41 static subclass(GlobalSettingsDriver) GetGlobalSettingsDriver(char * driverName)
44 for(link = class(GlobalSettingsDriver).derivatives.first; link; link = link.next)
46 subclass(GlobalSettingsDriver) driver = link.data;
47 if(driver.name && !strcmp(driver.name, driverName))
53 char moduleName[MAX_LOCATION];
54 sprintf(moduleName, "ecere%s", driverName);
55 if(module = eModule_Load(__thisModule.application, moduleName, publicAccess))
57 //Class dsdClass = class(GlobalSettingsDriver);
58 Class dsdClass = eSystem_FindClass(module /-__thisModule.application-/, "GlobalSettingsDriver");
60 for(link = dsdClass.derivatives.first; link; link = link.next)
62 subclass(GlobalSettingsDriver) driver = link.data;
63 if(driver.name && !strcmp(driver.name, driverName))
71 public class GlobalSettingsData
75 public class GlobalSettings
78 property char * settingsName
80 set { delete settingsName; if(value && value[0]) settingsName = CopyString(value); }
81 get { return settingsName; }
83 property char * settingsExtension
85 set { delete settingsExtension; if(value && value[0]) settingsExtension = CopyString(value); }
86 get { return settingsExtension; }
88 property char * settingsLocation
90 set { delete settingsLocation; if(value && value[0]) settingsLocation = CopyString(value); }
91 get { return settingsLocation; }
93 property char * settingsFilePath
95 get { return settingsFilePath; }
97 property bool allowDefaultLocations
99 set { allowDefaultLocations = value; }
100 get { return allowDefaultLocations; }
103 property String driver
109 driverClass = GetGlobalSettingsDriver(value);
111 get { return driverClass ? (driverClass.name : null; }
113 GlobalSettingsData data;
114 GlobalSettingsData * dataOwner;
115 subclass(GlobalSettingsData) dataClass;
120 char * settingsExtension;
121 char * settingsLocation;
122 char * settingsFilePath;
123 bool allowDefaultLocations;
125 FileMonitor settingsMonitor
127 this, fileChange = { modified = true };
129 bool OnFileNotify(FileChange action, char * param)
131 OnAskReloadSettings();
137 subclass(GlobalSettingsDriver) driverClass;
142 f.Unlock(0, 0, true);
145 delete settingsExtension;
146 delete settingsLocation;
147 delete settingsFilePath;
150 char * PrepareSpecifiedLocationPath()
153 if(settingsLocation && FileExists(settingsLocation).isDirectory)
155 char * extension = GetExtension();
156 path = new char[strlen(settingsLocation) + strlen(settingsName) + strlen(extension) + 16];
157 strcpy(path, settingsLocation);
158 PathCat(path, settingsName);
160 strcat(path, extension);
165 char * PreparePortablePath()
169 char location[MAX_LOCATION];
170 LocateModule(null, location);
171 StripLastDirectory(location, location);
172 if(location[0] && FileExists(location).isDirectory)
174 char * extension = GetExtension();
175 char * name = new char[strlen(settingsName) + 16];
176 path = new char[strlen(location) + strlen(settingsName) + strlen(extension) + 16];
177 strcpy(name, settingsName);
179 strcat(name, extension);
180 strcpy(path, location);
187 char * PrepareHomePath(bool useIni)
191 char * home = getenv("HOME");
192 if(home && home[0] && FileExists(home).isDirectory)
194 char * name = new char[strlen(settingsName) + 16];
195 path = new char[strlen(home) + strlen(settingsName) + 16];
198 strcpy(name, settingsName);
200 strcat(name, GetExtension());
205 strcat(name, settingsName);
215 #if defined(__WIN32__)
216 char * PrepareUserProfilePath()
218 // Windows attempts: $USERPROFILE/app.ini
220 char * profile = getenv("USERPROFILE");
221 if(profile && profile[0] && FileExists(profile).isDirectory)
223 char * extension = GetExtension();
224 path = new char[strlen(profile) + strlen(settingsName) + strlen(extension) + 16];
225 strcpy(path, profile);
226 PathCat(path, settingsName);
228 strcat(path, extension);
233 char * PrepareHomeDrivePath()
236 char * homedrive = getenv("HOMEDRIVE");
237 if(homedrive && homedrive[0])
239 char * homepath = getenv("HOMEPATH");
240 if(homepath && homepath[0])
242 char * extension = GetExtension();
243 path = new char[strlen(homedrive) + strlen(homepath) + strlen(settingsName) + strlen(extension) + 32];
244 strcpy(path, homedrive);
245 PathCat(path, homepath);
246 if(FileExists(path).isDirectory)
248 PathCat(path, settingsName);
250 strcat(path, extension);
259 char * PrepareSystemPath()
261 char * path = new char[MAX_LOCATION];
262 char * extension = GetExtension();
263 uint16 _wfilePath[MAX_LOCATION];
264 GetSystemDirectory(_wfilePath, MAX_LOCATION);
265 UTF16toUTF8Buffer(_wfilePath, path, MAX_LOCATION);
266 PathCat(path, settingsName);
268 strcat(path, extension);
272 char * PrepareEtcPath()
275 char * etc = "/etc/";
276 char * extension = GetExtension();
277 path = new char[strlen(etc) + strlen(settingsName) + strlen(extension) + 16];
279 PathCat(path, settingsName);
281 strcat(path, extension);
286 char * GetExtension()
289 if(settingsExtension)
290 extension = settingsExtension;
292 #if defined(__WIN32__)
300 void FileOpenTryRead()
302 f = FileOpen(settingsFilePath, read);
303 if(!f) // This delete will cover both trying the next possible config location and
304 delete settingsFilePath; // the case when we're doing a load when the config file is no longer available
305 } // and we want to re-try all possible config locations.
307 bool FileOpenTryWrite()
310 f = FileOpen(settingsFilePath, write);
313 if(!(f = FileOpen(settingsFilePath, readWrite)))
315 f = FileOpen(settingsFilePath, writeRead);
317 f = FileOpen(settingsFilePath, readWrite);
318 if(!f) // This delete will cover both trying the next possible config location and
319 delete settingsFilePath; // allow trying to save to a location where user has permission.
326 virtual void OnAskReloadSettings();
328 virtual SettingsIOResult Load()
330 SettingsIOResult result = fileNotFound;
333 settingsMonitor.StopMonitoring();
338 if(!settingsFilePath && settingsName && settingsName[0])
340 if(!f && (settingsFilePath = PrepareSpecifiedLocationPath()))
342 if(!settingsLocation || allowDefaultLocations)
344 if(!f && (settingsFilePath = PreparePortablePath()))
348 #if defined(__WIN32__)
349 if(!f && (settingsFilePath = PrepareHomePath(true)))
352 if(!f && (settingsFilePath = PrepareHomePath(false)))
354 #if defined(__WIN32__)
355 if(!f && (settingsFilePath = PrepareUserProfilePath()))
357 if(!f && (settingsFilePath = PrepareHomeDrivePath()))
359 if(!f && (settingsFilePath = PrepareSystemPath()))
362 if(!f && (settingsFilePath = PrepareEtcPath()))
372 // At some point wait was true, it was changed to false and now we do retries.
373 // Could it be because the wait to true was causing blocking behaviors?
374 //if(f && f.Lock(shared, 0, 0, true)) <- I think the wait to true is bad, it wrote blanked out global settings.
375 for(c = 0; c < 10 && !(locked = f.Lock(shared, 0, 0, false)); c++)
377 ecere::sys::Sleep(0.01);
382 result = driverClass.Load(f, this);
392 virtual SettingsIOResult Save()
394 SettingsIOResult result = error;
397 char filePath[MAX_LOCATION];
399 settingsMonitor.StopMonitoring();
404 if(!settingsFilePath && settingsName && settingsName[0])
406 if(!f && (settingsFilePath = PrepareSpecifiedLocationPath()))
408 if(!settingsLocation || allowDefaultLocations)
410 // never try to write a new portable configuration file?
411 //if(!f && (settingsFilePath = PreparePortablePath()))
412 // FileOpenTryWrite();
413 #if !defined(__WIN32__)
414 if(!f && (settingsFilePath = PrepareEtcPath()))
417 if(!f && (settingsFilePath = PrepareHomePath(
418 #if defined(__WIN32__)
425 #if defined(__WIN32__)
426 if(!f && (settingsFilePath = PrepareUserProfilePath()))
428 if(!f && (settingsFilePath = PrepareHomeDrivePath()))
430 if(!f && (settingsFilePath = PrepareSystemPath()))
435 // Don't wait for a lock, first one to lock gets to write, other will likely loose changes on a reload.
436 if(f && f.Lock(exclusive, 0, 0, false))
439 result = driverClass.Save(f, this);
451 settingsMonitor.StopMonitoring();
453 delete f; // WTH... this used to be: delete(f); f = null; // why?
457 void CloseAndMonitor()
460 if(settingsFilePath && OnAskReloadSettings != GlobalAppSettings::OnAskReloadSettings)
462 settingsMonitor.fileName = settingsFilePath;
463 settingsMonitor.StartMonitoring();
468 public class GlobalAppSettings : GlobalSettings
471 bool GetGlobalValue(char * section, char * name, GlobalSettingType type, void * value)
477 int lenSection = strlen(section);
478 int lenName = strlen(name);
480 while(f.GetLine(line, sizeof(line)))
482 if(line[0] == '[' && !strncmp(line+1, section, lenSection) && line[lenSection+1] == ']')
487 while(f.GetLine(line, sizeof(line)))
489 if(!strncmp(line, name, lenName) && line[lenName] == '=')
491 char * string = line + lenName + 1;
497 Container<String> list = value;
499 int numTokens = TokenizeWith(string,
500 sizeof(tokens) / sizeof(byte *), tokens, " ,", false);
502 for(c = 0; c<numTokens; c++)
504 list.Add(CopyString(tokens[c]));
510 char ** thisString = value;
511 *thisString = CopyString(string);
516 int * integer = value;
517 *integer = (int)strtol(string, null, 0);
530 bool PutGlobalValue(char * section, char * name, GlobalSettingType type, void * value)
535 char line[92048], outputLine[92048] = "";
536 int lenSection = strlen(section);
537 int lenName = strlen(name);
539 byte * remainingBuffer = null;
540 int remainingLen = 0;
541 uint currentSize = 0, newSize = 0;
545 // Prepare the output line
546 strcpy(outputLine, name);
547 strcat(outputLine, "=");
552 Container<String> list = value;
553 Iterator<String> item { list };
557 strcat(outputLine, "\"");
559 strcat(outputLine, item.data);
560 strcat(outputLine, "\"");
563 strcat(outputLine, ",");
568 // strcat(outputLine, "\"");
570 strcat(outputLine, value);
571 // strcat(outputLine, "\"");
576 sprintf(integer, "%d", value);
577 strcat(outputLine, integer);
581 #if defined(__WIN32__)
582 strcat(outputLine, "\r\n");
584 strcat(outputLine, "\n");
586 lenOutput = strlen(outputLine);
588 while(f.GetLine(line, sizeof(line)))
590 if(line[0] == '[' && !strncmp(line+1, section, lenSection) && line[lenSection+1] == ']')
595 int posBeforeSection = -1;
599 if(!f.GetLine(line, sizeof(line)))
601 if(posBeforeSection == -1 || !line[0])
602 posBeforeSection = pos;
606 // We've already reached next section
607 f.Seek(posBeforeSection, start);
609 // Remember the rest of the file
612 remainingBuffer = renew remainingBuffer byte[remainingLen + 65536];
613 remainingLen += f.Read(remainingBuffer + remainingLen, 1, 65536);
615 // Don't need to truncate. leave currentSize and newSize at 0
616 f.Seek(posBeforeSection, start);
619 else if(!strncmp(line, name, lenName) && line[lenName] == '=')
621 uint endLinePos = f.Tell();
623 // Remember the rest of the file
626 remainingBuffer = renew remainingBuffer byte[remainingLen + 65536];
627 remainingLen += f.Read(remainingBuffer + remainingLen, 1, 65536);
629 currentSize = endLinePos + remainingLen;
630 newSize = pos + lenOutput + remainingLen;
632 // Go back where we will overwrite this line
641 #if defined(__WIN32__)
642 f.Printf("\r\n[%s]\r\n", section);
644 f.Printf("\n[%s]\n", section);
649 f.Write(outputLine, 1, lenOutput);
651 // Write back the rest of the file if necessary
654 f.Write(remainingBuffer, 1, remainingLen);
655 delete remainingBuffer;
656 if(currentSize != newSize)
657 FileTruncate(settingsFilePath, newSize);