4 #define WIN32_LEAN_AND_MEAN
16 public enum GlobalSettingType
23 // note: missing application data location support on windows
24 enum SettingsLocationType { none, specified, portable, home, winUserProfile, winHomeDrive, winSystemPath, winAllUsers, nixEtc };
26 public enum SettingsIOResult { error, success, fileNotFound, fileNotCompatibleWithDriver };
28 public class GlobalSettingsDriver
30 class_data const char * name;
32 class_property const char * name
34 set { class_data(name) = value; }
35 get { return class_data(name); }
39 virtual SettingsIOResult ::Load(File f, GlobalSettings globalSettings);
40 virtual SettingsIOResult ::Save(File f, GlobalSettings globalSettings);
44 static subclass(GlobalSettingsDriver) GetGlobalSettingsDriver(const char * driverName)
47 for(link = class(GlobalSettingsDriver).derivatives.first; link; link = link.next)
49 subclass(GlobalSettingsDriver) driver = link.data;
50 if(driver.name && !strcmp(driver.name, driverName))
56 char moduleName[MAX_LOCATION];
57 sprintf(moduleName, "ecere%s", driverName);
58 if(module = eModule_Load(__thisModule.application, moduleName, publicAccess))
60 //Class dsdClass = class(GlobalSettingsDriver);
61 Class dsdClass = eSystem_FindClass(module /-__thisModule.application-/, "GlobalSettingsDriver");
63 for(link = dsdClass.derivatives.first; link; link = link.next)
65 subclass(GlobalSettingsDriver) driver = link.data;
66 if(driver.name && !strcmp(driver.name, driverName))
74 public class GlobalSettingsData
78 public class GlobalSettings
81 property const char * settingsName
83 set { delete settingsName; if(value && value[0]) settingsName = CopyString(value); }
84 get { return settingsName; }
86 property const char * settingsExtension
88 set { delete settingsExtension; if(value && value[0]) settingsExtension = CopyString(value); }
89 get { return settingsExtension; }
91 property const char * settingsDirectory
93 set { delete settingsDirectory; if(value && value[0]) settingsDirectory = CopyUnixPath(value); }
94 get { return settingsDirectory; }
96 property const char * settingsLocation
98 set { delete settingsLocation; if(value && value[0]) settingsLocation = CopyUnixPath(value); }
99 get { return settingsLocation; }
101 property const char * settingsFilePath
103 set { delete settingsFilePath; if(value && value[0]) settingsFilePath = CopyUnixPath(value); }
104 get { return settingsFilePath; }
106 property bool allowDefaultLocations
108 set { allowDefaultLocations = value; }
109 get { return allowDefaultLocations; }
111 property bool allUsers
113 set { allUsers = value; }
114 get { return allUsers; }
116 property bool portable
118 set { portable = value; }
119 get { return portable; }
122 property const String driver
128 driverClass = GetGlobalSettingsDriver(value);
130 get { return driverClass ? driverClass.name : null; }
132 GlobalSettingsData data;
133 GlobalSettingsData * dataOwner;
134 subclass(GlobalSettingsData) dataClass;
138 char * settingsExtension;
139 char * settingsLocation;
140 char * settingsFilePath;
141 bool allowDefaultLocations;
145 char * settingsDirectory;
146 SettingsLocationType readType;
147 SettingsLocationType writeType;
149 FileMonitor settingsMonitor
151 this, fileChange = { modified = true };
153 bool OnFileNotify(FileChange action, const char * param)
155 OnAskReloadSettings();
162 subclass(GlobalSettingsDriver) driverClass;
167 f.Unlock(0, 0, true);
170 delete settingsExtension;
171 delete settingsLocation;
172 delete settingsFilePath;
173 delete settingsDirectory;
176 char * PreparePath(SettingsLocationType type, const char * extension, bool create, bool unixStyle)
179 char * buffer = new char[MAX_LOCATION];
186 strcpy(buffer, settingsLocation);
187 path = GetFilePath(buffer, extension, create, false, false);
192 LocateModule(null, buffer);
193 StripLastDirectory(buffer, buffer);
194 path = GetFilePath(buffer, extension, create, false, false);
199 char * home = getenv("HOME");
202 strcpy(buffer, home);
203 path = GetFilePath(buffer, extension, create, true, unixStyle);
207 #if defined(__WIN32__)
210 // Windows attempts: $USERPROFILE/app.ini
211 char * profile = getenv("USERPROFILE");
212 if(profile && profile[0])
214 strcpy(buffer, profile);
215 path = GetFilePath(buffer, extension, create, false, false);
221 const char * homedrive = getenv("HOMEDRIVE");
222 const char * homepath = getenv("HOMEPATH");
223 if(homedrive && homedrive[0] && homepath && homepath[0])
225 strcpy(buffer, homedrive);
226 PathCatSlash(buffer, homepath);
227 path = GetFilePath(buffer, extension, create, false, false);
233 uint16 _wfilePath[MAX_LOCATION];
235 GetSystemDirectory(_wfilePath, MAX_LOCATION);
236 UTF16toUTF8Buffer(_wfilePath, buffer, MAX_LOCATION);
237 path = GetFilePath(buffer, extension, create, false, false);
242 char * allUsers = getenv("ALLUSERSPROFILE");
243 if(allUsers && allUsers[0])
245 strcpy(buffer, allUsers);
246 path = GetFilePath(buffer, extension, create, false, false);
253 strcpy(buffer, "/etc/");
254 path = GetFilePath(buffer, extension, create, false, false);
259 if(!path) delete buffer;
263 char * GetFilePath(char * location, const char * extension, bool create, bool dotPrefix, bool runCommandsStyle)
268 MakeSlashPath(location);
269 if(location[0] && (attribs = FileExists(location)) && (attribs.isDirectory || attribs.isDrive))
271 if(settingsDirectory)
275 int len = strlen(settingsDirectory);
276 String s = new char[len + 2];
278 memcpy(s + 1, settingsDirectory, len + 1);
279 PathCatSlash(location, s);
283 PathCatSlash(location, settingsDirectory);
286 attribs = FileExists(location);
288 if(attribs.isDirectory || attribs.isDrive)
290 char * name = new char[strlen(settingsName) + strlen(extension) + 4];
291 if(dotPrefix && !settingsDirectory)
294 strcat(name, settingsName);
297 strcpy(name, settingsName);
303 strcat(name, extension);
305 PathCatSlash(location, name);
313 const char * GetExtension()
315 const char * extension;
316 if(settingsExtension)
317 extension = settingsExtension;
319 #if defined(__WIN32__)
327 void FileOpenTryRead(SettingsLocationType type)
329 f = FileOpen(settingsFilePath, read);
330 //PrintLn("GlobalSettings::FileOpenTryRead(", type, ") (", settingsFilePath, ") -- ", f ? "SUCCESS" : "FAIL");
335 // This delete will cover both trying the next possible config location and
336 delete settingsFilePath; // the case when we're doing a load when the config file is no longer available
337 } // and we want to re-try all possible config locations.
340 bool FileOpenTryWrite(SettingsLocationType type, bool shouldDelete, bool * locked)
343 f = FileOpen(settingsFilePath, readWrite);
346 f = FileOpen(settingsFilePath, writeRead);
350 f = FileOpen(settingsFilePath, readWrite);
353 //PrintLn("GlobalSettings::FileOpenTryWrite(", type, ") (", settingsFilePath, ") -- ", f ? "SUCCESS" : "FAIL");
357 // Don't wait for a lock, first one to lock gets to write, other will likely loose changes on a reload.
358 if(f.Lock(exclusive, 0, 0, false))
368 else if(shouldDelete)
370 delete settingsFilePath; // This delete will cover both trying the next possible config location and
371 } // allow trying to save to a location where user has permission.
376 virtual void OnAskReloadSettings();
378 bool OpenAndLock(FileSize * fileSize)
380 SettingsLocationType type = readType;
383 settingsMonitor.StopMonitoring();
386 FileOpenTryRead(type);
388 if(!settingsFilePath && settingsName && settingsName[0])
390 const char * extension = GetExtension();
392 if(!f && (settingsFilePath = PreparePath((type = specified), extension, false, false)))
393 FileOpenTryRead(type);
394 if(!f && (!settingsLocation || allowDefaultLocations))
397 if(!f && (settingsFilePath = PreparePath((type = portable), extension, false, false)))
398 FileOpenTryRead(type);
403 #if defined(__WIN32__)
404 if(!f && (settingsFilePath = PreparePath((type = home), extension, false, false)))
405 FileOpenTryRead(type);
407 if(!f && (settingsFilePath = PreparePath((type = home), extension, false, true)))
408 FileOpenTryRead(type);
410 #if defined(__WIN32__)
413 if(!f && (settingsFilePath = PreparePath((type = winUserProfile), extension, false, false)))
414 FileOpenTryRead(type);
415 if(!f && (settingsFilePath = PreparePath((type = winHomeDrive), extension, false, false)))
416 FileOpenTryRead(type);
420 if(!f && (settingsFilePath = PreparePath((type = winAllUsers), extension, false, false)))
421 FileOpenTryRead(type);
423 if(!f && (settingsFilePath = PreparePath((type = winSystemPath), extension, false, false)))
424 FileOpenTryRead(type);
428 if(!f && (settingsFilePath = PreparePath((type = nixEtc), extension, false, false)))
429 FileOpenTryRead(type);
439 // At some point wait was true, it was changed to false and now we do retries.
440 // Could it be because the wait to true was causing blocking behaviors?
441 //if(f && f.Lock(shared, 0, 0, true)) <- I think the wait to true is bad, it wrote blanked out global settings.
442 for(c = 0; c < 10 && !(locked = f.Lock(shared, 0, 0, false)); c++)
444 ecere::sys::Sleep(0.01);
448 if(locked && fileSize)
449 *fileSize = f.GetSize();
454 virtual SettingsIOResult Load()
456 SettingsIOResult result = fileNotFound;
463 result = driverClass.Load(f, this);
470 virtual SettingsIOResult Save()
472 SettingsIOResult result = error;
473 SettingsLocationType type = writeType;
478 settingsMonitor.StopMonitoring();
481 // Don't auto delete settingsFilePath because only want to try another path if we were using a global path
482 FileOpenTryWrite(type, false, &locked);
484 if((!settingsFilePath || (!f && globalPath)) && settingsName && settingsName[0])
486 const char * extension = GetExtension();
487 delete settingsFilePath;
489 if(!f && (settingsFilePath = PreparePath((type = specified), extension, true, false)))
490 FileOpenTryWrite(type, true, &locked);
491 if(!f && (!settingsLocation || allowDefaultLocations))
494 if(!f && portable && (settingsFilePath = PreparePath((type = portable), extension, true, false)))
495 FileOpenTryWrite(type, true, &locked);
496 #if defined(__WIN32__)
497 if(!f && allUsers && (settingsFilePath = PreparePath((type = winAllUsers), extension, true, false)))
498 FileOpenTryWrite(type, true, &locked);
500 if(!f && allUsers && (settingsFilePath = PreparePath((type = nixEtc), extension, true, false)))
501 FileOpenTryWrite(type, true, &locked);
506 if(!f && (settingsFilePath = PreparePath((type = home), extension, true,
507 #if defined(__WIN32__)
513 FileOpenTryWrite(type, true, &locked);
515 #if defined(__WIN32__)
519 if(!f && (settingsFilePath = PreparePath((type = winUserProfile), extension, true, false)))
520 FileOpenTryWrite(type, true, &locked);
521 if(!f && (settingsFilePath = PreparePath((type = winHomeDrive), extension, true, false)))
522 FileOpenTryWrite(type, true, &locked);
524 if(!f && (settingsFilePath = PreparePath((type = winSystemPath), extension, true, false)))
527 FileOpenTryWrite(type, true, &locked);
535 result = driverClass.Save(f, this);
547 settingsMonitor.StopMonitoring();
554 void CloseAndMonitor()
557 if(settingsFilePath && OnAskReloadSettings != GlobalSettings::OnAskReloadSettings)
559 settingsMonitor.fileName = settingsFilePath;
560 settingsMonitor.StartMonitoring();
565 public class GlobalAppSettings : GlobalSettings
568 bool GetGlobalValue(const char * section, const char * name, GlobalSettingType type, void * value)
574 int lenSection = strlen(section);
575 int lenName = strlen(name);
577 while(f.GetLine(line, sizeof(line)))
579 if(line[0] == '[' && !strncmp(line+1, section, lenSection) && line[lenSection+1] == ']')
584 while(f.GetLine(line, sizeof(line)))
586 if(!strncmp(line, name, lenName) && line[lenName] == '=')
588 char * string = line + lenName + 1;
594 Container<String> list = value;
596 int numTokens = TokenizeWith(string,
597 sizeof(tokens) / sizeof(byte *), tokens, " ,", false);
599 for(c = 0; c<numTokens; c++)
601 list.Add(CopyString(tokens[c]));
607 char ** thisString = value;
608 *thisString = CopyString(string);
613 int * integer = value;
614 *integer = (int)strtol(string, null, 0);
627 bool PutGlobalValue(const char * section, const char * name, GlobalSettingType type, const void * value)
632 char line[92048], outputLine[92048] = "";
633 int lenSection = strlen(section);
634 int lenName = strlen(name);
636 byte * remainingBuffer = null;
637 int remainingLen = 0;
638 uint currentSize = 0, newSize = 0;
642 // Prepare the output line
643 strcpy(outputLine, name);
644 strcat(outputLine, "=");
649 Container<String> list = (void *)value;
650 Iterator<String> item { list };
654 strcat(outputLine, "\"");
656 strcat(outputLine, item.data);
657 strcat(outputLine, "\"");
660 strcat(outputLine, ",");
665 // strcat(outputLine, "\"");
667 strcat(outputLine, value);
668 // strcat(outputLine, "\"");
673 sprintf(integer, "%d", (int)(intptr)value);
674 strcat(outputLine, integer);
678 #if defined(__WIN32__)
679 strcat(outputLine, "\r\n");
681 strcat(outputLine, "\n");
683 lenOutput = strlen(outputLine);
685 while(f.GetLine(line, sizeof(line)))
687 if(line[0] == '[' && !strncmp(line+1, section, lenSection) && line[lenSection+1] == ']')
692 int posBeforeSection = -1;
696 if(!f.GetLine(line, sizeof(line)))
698 if(posBeforeSection == -1 || !line[0])
699 posBeforeSection = pos;
703 // We've already reached next section
704 f.Seek(posBeforeSection, start);
706 // Remember the rest of the file
709 remainingBuffer = renew remainingBuffer byte[remainingLen + 65536];
710 remainingLen += f.Read(remainingBuffer + remainingLen, 1, 65536);
712 // Don't need to truncate. leave currentSize and newSize at 0
713 f.Seek(posBeforeSection, start);
716 else if(!strncmp(line, name, lenName) && line[lenName] == '=')
718 uint endLinePos = f.Tell();
720 // Remember the rest of the file
723 remainingBuffer = renew remainingBuffer byte[remainingLen + 65536];
724 remainingLen += f.Read(remainingBuffer + remainingLen, 1, 65536);
726 currentSize = endLinePos + remainingLen;
727 newSize = pos + lenOutput + remainingLen;
729 // Go back where we will overwrite this line
738 #if defined(__WIN32__)
739 f.Printf("\r\n[%s]\r\n", section);
741 f.Printf("\n[%s]\n", section);
746 f.Write(outputLine, 1, lenOutput);
748 // Write back the rest of the file if necessary
751 f.Write(remainingBuffer, 1, remainingLen);
752 delete remainingBuffer;
753 if(currentSize != newSize)
754 FileTruncate(settingsFilePath, newSize);