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 #if !defined(__EMSCRIPTEN__)
150 FileMonitor settingsMonitor
152 this, fileChange = { modified = true };
154 bool OnFileNotify(FileChange action, const char * param)
156 OnAskReloadSettings();
164 subclass(GlobalSettingsDriver) driverClass;
169 f.Unlock(0, 0, true);
172 delete settingsExtension;
173 delete settingsLocation;
174 delete settingsFilePath;
175 delete settingsDirectory;
178 char * PreparePath(SettingsLocationType type, const char * extension, bool create, bool unixStyle)
181 char * buffer = new char[MAX_LOCATION];
188 strcpy(buffer, settingsLocation);
189 path = GetFilePath(buffer, extension, create, false, false);
194 LocateModule(null, buffer);
195 StripLastDirectory(buffer, buffer);
196 path = GetFilePath(buffer, extension, create, false, false);
201 char * home = getenv("HOME");
204 strcpy(buffer, home);
205 path = GetFilePath(buffer, extension, create, true, unixStyle);
209 #if defined(__WIN32__)
212 // Windows attempts: $USERPROFILE/app.ini
213 char * profile = getenv("USERPROFILE");
214 if(profile && profile[0])
216 strcpy(buffer, profile);
217 path = GetFilePath(buffer, extension, create, false, false);
223 const char * homedrive = getenv("HOMEDRIVE");
224 const char * homepath = getenv("HOMEPATH");
225 if(homedrive && homedrive[0] && homepath && homepath[0])
227 strcpy(buffer, homedrive);
228 PathCatSlash(buffer, homepath);
229 path = GetFilePath(buffer, extension, create, false, false);
235 uint16 _wfilePath[MAX_LOCATION];
237 GetSystemDirectory(_wfilePath, MAX_LOCATION);
238 UTF16toUTF8Buffer(_wfilePath, buffer, MAX_LOCATION);
239 path = GetFilePath(buffer, extension, create, false, false);
244 char * allUsers = getenv("ALLUSERSPROFILE");
245 if(allUsers && allUsers[0])
247 strcpy(buffer, allUsers);
248 path = GetFilePath(buffer, extension, create, false, false);
255 strcpy(buffer, "/etc/");
256 path = GetFilePath(buffer, extension, create, false, false);
261 if(!path) delete buffer;
265 char * GetFilePath(char * location, const char * extension, bool create, bool dotPrefix, bool runCommandsStyle)
270 MakeSlashPath(location);
271 if(location[0] && (attribs = FileExists(location)) && (attribs.isDirectory || attribs.isDrive))
273 if(settingsDirectory)
275 if(dotPrefix && settingsDirectory[0] != '.')
277 int len = strlen(settingsDirectory);
278 String s = new char[len + 2];
280 memcpy(s + 1, settingsDirectory, len + 1);
281 PathCatSlash(location, s);
285 PathCatSlash(location, settingsDirectory);
288 attribs = FileExists(location);
290 if(attribs.isDirectory || attribs.isDrive)
292 char * name = new char[strlen(settingsName) + strlen(extension) + 4];
293 if(dotPrefix && !settingsDirectory && settingsName[0] != '.')
296 strcat(name, settingsName);
299 strcpy(name, settingsName);
300 if(!settingsExtension && runCommandsStyle)
305 strcat(name, extension);
307 PathCatSlash(location, name);
315 const char * GetExtension()
317 const char * extension;
318 if(settingsExtension)
319 extension = settingsExtension;
321 #if defined(__WIN32__)
329 void FileOpenTryRead(SettingsLocationType type)
331 f = FileOpen(settingsFilePath, read);
332 //PrintLn("GlobalSettings::FileOpenTryRead(", type, ") (", settingsFilePath, ") -- ", f ? "SUCCESS" : "FAIL");
337 // This delete will cover both trying the next possible config location and
338 delete settingsFilePath; // the case when we're doing a load when the config file is no longer available
339 } // and we want to re-try all possible config locations.
342 bool FileOpenTryWrite(SettingsLocationType type, bool shouldDelete, bool * locked)
345 f = FileOpen(settingsFilePath, readWrite);
348 f = FileOpen(settingsFilePath, writeRead);
352 f = FileOpen(settingsFilePath, readWrite);
355 //PrintLn("GlobalSettings::FileOpenTryWrite(", type, ") (", settingsFilePath, ") -- ", f ? "SUCCESS" : "FAIL");
359 // Don't wait for a lock, first one to lock gets to write, other will likely loose changes on a reload.
360 if(f.Lock(exclusive, 0, 0, false))
370 else if(shouldDelete)
372 delete settingsFilePath; // This delete will cover both trying the next possible config location and
373 } // allow trying to save to a location where user has permission.
378 virtual void OnAskReloadSettings();
380 property bool isGlobalPath { get { return globalPath; } }
382 bool OpenAndLock(FileSize * fileSize)
384 SettingsLocationType type = readType;
387 #if !defined(__EMSCRIPTEN__)
388 settingsMonitor.StopMonitoring();
392 FileOpenTryRead(type);
394 if(!settingsFilePath && settingsName && settingsName[0])
396 const char * extension = GetExtension();
398 if(!f && (settingsFilePath = PreparePath((type = specified), extension, false, false)))
399 FileOpenTryRead(type);
400 if(!f && (!settingsLocation || allowDefaultLocations))
403 if(!f && (settingsFilePath = PreparePath((type = portable), extension, false, false)))
404 FileOpenTryRead(type);
409 #if defined(__WIN32__)
410 if(!f && (settingsFilePath = PreparePath((type = home), extension, false, false)))
411 FileOpenTryRead(type);
413 if(!f && (settingsFilePath = PreparePath((type = home), extension, false, true)))
414 FileOpenTryRead(type);
416 #if defined(__WIN32__)
419 if(!f && (settingsFilePath = PreparePath((type = winUserProfile), extension, false, false)))
420 FileOpenTryRead(type);
421 if(!f && (settingsFilePath = PreparePath((type = winHomeDrive), extension, false, false)))
422 FileOpenTryRead(type);
426 if(!f && (settingsFilePath = PreparePath((type = winAllUsers), extension, false, false)))
427 FileOpenTryRead(type);
429 if(!f && (settingsFilePath = PreparePath((type = winSystemPath), extension, false, false)))
430 FileOpenTryRead(type);
434 if(!f && (settingsFilePath = PreparePath((type = nixEtc), extension, false, false)))
435 FileOpenTryRead(type);
445 // At some point wait was true, it was changed to false and now we do retries.
446 // Could it be because the wait to true was causing blocking behaviors?
447 //if(f && f.Lock(shared, 0, 0, true)) <- I think the wait to true is bad, it wrote blanked out global settings.
448 for(c = 0; c < 10 && !(locked = f.Lock(shared, 0, 0, false)); c++)
450 ecere::sys::Sleep(0.01);
454 if(locked && fileSize)
455 *fileSize = f.GetSize();
460 virtual SettingsIOResult Load()
462 SettingsIOResult result = fileNotFound;
469 result = driverClass.Load(f, this);
476 virtual SettingsIOResult Save()
478 SettingsIOResult result = error;
479 SettingsLocationType type = writeType;
484 #if !defined(__EMSCRIPTEN__)
485 settingsMonitor.StopMonitoring();
489 // Don't auto delete settingsFilePath because only want to try another path if we were using a global path
490 FileOpenTryWrite(type, false, &locked);
492 if((!settingsFilePath || (!f && globalPath)) && settingsName && settingsName[0])
494 const char * extension = GetExtension();
495 delete settingsFilePath;
497 if(!f && (settingsFilePath = PreparePath((type = specified), extension, true, false)))
498 FileOpenTryWrite(type, true, &locked);
499 if(!f && (!settingsLocation || allowDefaultLocations))
502 if(!f && portable && (settingsFilePath = PreparePath((type = portable), extension, true, false)))
503 FileOpenTryWrite(type, true, &locked);
504 #if defined(__WIN32__)
505 if(!f && allUsers && (settingsFilePath = PreparePath((type = winAllUsers), extension, true, false)))
506 FileOpenTryWrite(type, true, &locked);
508 if(!f && allUsers && (settingsFilePath = PreparePath((type = nixEtc), extension, true, false)))
509 FileOpenTryWrite(type, true, &locked);
514 if(!f && (settingsFilePath = PreparePath((type = home), extension, true,
515 #if defined(__WIN32__)
521 FileOpenTryWrite(type, true, &locked);
523 #if defined(__WIN32__)
527 if(!f && (settingsFilePath = PreparePath((type = winUserProfile), extension, true, false)))
528 FileOpenTryWrite(type, true, &locked);
529 if(!f && (settingsFilePath = PreparePath((type = winHomeDrive), extension, true, false)))
530 FileOpenTryWrite(type, true, &locked);
532 if(!f && (settingsFilePath = PreparePath((type = winSystemPath), extension, true, false)))
535 FileOpenTryWrite(type, true, &locked);
543 result = driverClass.Save(f, this);
555 #if !defined(__EMSCRIPTEN__)
556 settingsMonitor.StopMonitoring();
564 void CloseAndMonitor()
567 if(settingsFilePath && OnAskReloadSettings != GlobalSettings::OnAskReloadSettings)
569 #if !defined(__EMSCRIPTEN__)
570 settingsMonitor.fileName = settingsFilePath;
571 settingsMonitor.StartMonitoring();
577 public class GlobalAppSettings : GlobalSettings
580 bool GetGlobalValue(const char * section, const char * name, GlobalSettingType type, void * value)
586 int lenSection = strlen(section);
587 int lenName = strlen(name);
589 while(f.GetLine(line, sizeof(line)))
591 if(line[0] == '[' && !strncmp(line+1, section, lenSection) && line[lenSection+1] == ']')
596 while(f.GetLine(line, sizeof(line)))
598 if(!strncmp(line, name, lenName) && line[lenName] == '=')
600 char * string = line + lenName + 1;
606 Container<String> list = value;
608 int numTokens = TokenizeWith(string,
609 sizeof(tokens) / sizeof(byte *), tokens, " ,", false);
611 for(c = 0; c<numTokens; c++)
613 list.Add(CopyString(tokens[c]));
619 char ** thisString = value;
620 *thisString = CopyString(string);
625 int * integer = value;
626 *integer = (int)strtol(string, null, 0);
639 bool PutGlobalValue(const char * section, const char * name, GlobalSettingType type, const void * value)
644 char line[92048], outputLine[92048] = "";
645 int lenSection = strlen(section);
646 int lenName = strlen(name);
648 byte * remainingBuffer = null;
649 int remainingLen = 0;
650 uint currentSize = 0, newSize = 0;
654 // Prepare the output line
655 strcpy(outputLine, name);
656 strcat(outputLine, "=");
661 Container<String> list = (void *)value;
662 Iterator<String> item { list };
666 strcat(outputLine, "\"");
668 strcat(outputLine, item.data);
669 strcat(outputLine, "\"");
672 strcat(outputLine, ",");
677 // strcat(outputLine, "\"");
679 strcat(outputLine, value);
680 // strcat(outputLine, "\"");
685 sprintf(integer, "%d", (int)(intptr)value);
686 strcat(outputLine, integer);
690 #if defined(__WIN32__)
691 strcat(outputLine, "\r\n");
693 strcat(outputLine, "\n");
695 lenOutput = strlen(outputLine);
697 while(f.GetLine(line, sizeof(line)))
699 if(line[0] == '[' && !strncmp(line+1, section, lenSection) && line[lenSection+1] == ']')
704 int posBeforeSection = -1;
708 if(!f.GetLine(line, sizeof(line)))
710 if(posBeforeSection == -1 || !line[0])
711 posBeforeSection = pos;
715 // We've already reached next section
716 f.Seek(posBeforeSection, start);
718 // Remember the rest of the file
721 remainingBuffer = renew remainingBuffer byte[remainingLen + 65536];
722 remainingLen += f.Read(remainingBuffer + remainingLen, 1, 65536);
724 // Don't need to truncate. leave currentSize and newSize at 0
725 f.Seek(posBeforeSection, start);
728 else if(!strncmp(line, name, lenName) && line[lenName] == '=')
730 uint endLinePos = f.Tell();
732 // Remember the rest of the file
735 remainingBuffer = renew remainingBuffer byte[remainingLen + 65536];
736 remainingLen += f.Read(remainingBuffer + remainingLen, 1, 65536);
738 currentSize = endLinePos + remainingLen;
739 newSize = pos + lenOutput + remainingLen;
741 // Go back where we will overwrite this line
750 #if defined(__WIN32__)
751 f.Printf("\r\n[%s]\r\n", section);
753 f.Printf("\n[%s]\n", section);
758 f.Write(outputLine, 1, lenOutput);
760 // Write back the rest of the file if necessary
763 f.Write(remainingBuffer, 1, remainingLen);
764 delete remainingBuffer;
765 if(currentSize != newSize)
766 FileTruncate(settingsFilePath, newSize);