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)
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)
296 strcat(name, settingsName);
299 strcpy(name, settingsName);
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 bool OpenAndLock(FileSize * fileSize)
382 SettingsLocationType type = readType;
385 #if !defined(__EMSCRIPTEN__)
386 settingsMonitor.StopMonitoring();
390 FileOpenTryRead(type);
392 if(!settingsFilePath && settingsName && settingsName[0])
394 const char * extension = GetExtension();
396 if(!f && (settingsFilePath = PreparePath((type = specified), extension, false, false)))
397 FileOpenTryRead(type);
398 if(!f && (!settingsLocation || allowDefaultLocations))
401 if(!f && (settingsFilePath = PreparePath((type = portable), extension, false, false)))
402 FileOpenTryRead(type);
407 #if defined(__WIN32__)
408 if(!f && (settingsFilePath = PreparePath((type = home), extension, false, false)))
409 FileOpenTryRead(type);
411 if(!f && (settingsFilePath = PreparePath((type = home), extension, false, true)))
412 FileOpenTryRead(type);
414 #if defined(__WIN32__)
417 if(!f && (settingsFilePath = PreparePath((type = winUserProfile), extension, false, false)))
418 FileOpenTryRead(type);
419 if(!f && (settingsFilePath = PreparePath((type = winHomeDrive), extension, false, false)))
420 FileOpenTryRead(type);
424 if(!f && (settingsFilePath = PreparePath((type = winAllUsers), extension, false, false)))
425 FileOpenTryRead(type);
427 if(!f && (settingsFilePath = PreparePath((type = winSystemPath), extension, false, false)))
428 FileOpenTryRead(type);
432 if(!f && (settingsFilePath = PreparePath((type = nixEtc), extension, false, false)))
433 FileOpenTryRead(type);
443 // At some point wait was true, it was changed to false and now we do retries.
444 // Could it be because the wait to true was causing blocking behaviors?
445 //if(f && f.Lock(shared, 0, 0, true)) <- I think the wait to true is bad, it wrote blanked out global settings.
446 for(c = 0; c < 10 && !(locked = f.Lock(shared, 0, 0, false)); c++)
448 ecere::sys::Sleep(0.01);
452 if(locked && fileSize)
453 *fileSize = f.GetSize();
458 virtual SettingsIOResult Load()
460 SettingsIOResult result = fileNotFound;
467 result = driverClass.Load(f, this);
474 virtual SettingsIOResult Save()
476 SettingsIOResult result = error;
477 SettingsLocationType type = writeType;
482 #if !defined(__EMSCRIPTEN__)
483 settingsMonitor.StopMonitoring();
487 // Don't auto delete settingsFilePath because only want to try another path if we were using a global path
488 FileOpenTryWrite(type, false, &locked);
490 if((!settingsFilePath || (!f && globalPath)) && settingsName && settingsName[0])
492 const char * extension = GetExtension();
493 delete settingsFilePath;
495 if(!f && (settingsFilePath = PreparePath((type = specified), extension, true, false)))
496 FileOpenTryWrite(type, true, &locked);
497 if(!f && (!settingsLocation || allowDefaultLocations))
500 if(!f && portable && (settingsFilePath = PreparePath((type = portable), extension, true, false)))
501 FileOpenTryWrite(type, true, &locked);
502 #if defined(__WIN32__)
503 if(!f && allUsers && (settingsFilePath = PreparePath((type = winAllUsers), extension, true, false)))
504 FileOpenTryWrite(type, true, &locked);
506 if(!f && allUsers && (settingsFilePath = PreparePath((type = nixEtc), extension, true, false)))
507 FileOpenTryWrite(type, true, &locked);
512 if(!f && (settingsFilePath = PreparePath((type = home), extension, true,
513 #if defined(__WIN32__)
519 FileOpenTryWrite(type, true, &locked);
521 #if defined(__WIN32__)
525 if(!f && (settingsFilePath = PreparePath((type = winUserProfile), extension, true, false)))
526 FileOpenTryWrite(type, true, &locked);
527 if(!f && (settingsFilePath = PreparePath((type = winHomeDrive), extension, true, false)))
528 FileOpenTryWrite(type, true, &locked);
530 if(!f && (settingsFilePath = PreparePath((type = winSystemPath), extension, true, false)))
533 FileOpenTryWrite(type, true, &locked);
541 result = driverClass.Save(f, this);
553 #if !defined(__EMSCRIPTEN__)
554 settingsMonitor.StopMonitoring();
562 void CloseAndMonitor()
565 if(settingsFilePath && OnAskReloadSettings != GlobalSettings::OnAskReloadSettings)
567 #if !defined(__EMSCRIPTEN__)
568 settingsMonitor.fileName = settingsFilePath;
569 settingsMonitor.StartMonitoring();
575 public class GlobalAppSettings : GlobalSettings
578 bool GetGlobalValue(const char * section, const char * name, GlobalSettingType type, void * value)
584 int lenSection = strlen(section);
585 int lenName = strlen(name);
587 while(f.GetLine(line, sizeof(line)))
589 if(line[0] == '[' && !strncmp(line+1, section, lenSection) && line[lenSection+1] == ']')
594 while(f.GetLine(line, sizeof(line)))
596 if(!strncmp(line, name, lenName) && line[lenName] == '=')
598 char * string = line + lenName + 1;
604 Container<String> list = value;
606 int numTokens = TokenizeWith(string,
607 sizeof(tokens) / sizeof(byte *), tokens, " ,", false);
609 for(c = 0; c<numTokens; c++)
611 list.Add(CopyString(tokens[c]));
617 char ** thisString = value;
618 *thisString = CopyString(string);
623 int * integer = value;
624 *integer = (int)strtol(string, null, 0);
637 bool PutGlobalValue(const char * section, const char * name, GlobalSettingType type, const void * value)
642 char line[92048], outputLine[92048] = "";
643 int lenSection = strlen(section);
644 int lenName = strlen(name);
646 byte * remainingBuffer = null;
647 int remainingLen = 0;
648 uint currentSize = 0, newSize = 0;
652 // Prepare the output line
653 strcpy(outputLine, name);
654 strcat(outputLine, "=");
659 Container<String> list = (void *)value;
660 Iterator<String> item { list };
664 strcat(outputLine, "\"");
666 strcat(outputLine, item.data);
667 strcat(outputLine, "\"");
670 strcat(outputLine, ",");
675 // strcat(outputLine, "\"");
677 strcat(outputLine, value);
678 // strcat(outputLine, "\"");
683 sprintf(integer, "%d", (int)(intptr)value);
684 strcat(outputLine, integer);
688 #if defined(__WIN32__)
689 strcat(outputLine, "\r\n");
691 strcat(outputLine, "\n");
693 lenOutput = strlen(outputLine);
695 while(f.GetLine(line, sizeof(line)))
697 if(line[0] == '[' && !strncmp(line+1, section, lenSection) && line[lenSection+1] == ']')
702 int posBeforeSection = -1;
706 if(!f.GetLine(line, sizeof(line)))
708 if(posBeforeSection == -1 || !line[0])
709 posBeforeSection = pos;
713 // We've already reached next section
714 f.Seek(posBeforeSection, start);
716 // Remember the rest of the file
719 remainingBuffer = renew remainingBuffer byte[remainingLen + 65536];
720 remainingLen += f.Read(remainingBuffer + remainingLen, 1, 65536);
722 // Don't need to truncate. leave currentSize and newSize at 0
723 f.Seek(posBeforeSection, start);
726 else if(!strncmp(line, name, lenName) && line[lenName] == '=')
728 uint endLinePos = f.Tell();
730 // Remember the rest of the file
733 remainingBuffer = renew remainingBuffer byte[remainingLen + 65536];
734 remainingLen += f.Read(remainingBuffer + remainingLen, 1, 65536);
736 currentSize = endLinePos + remainingLen;
737 newSize = pos + lenOutput + remainingLen;
739 // Go back where we will overwrite this line
748 #if defined(__WIN32__)
749 f.Printf("\r\n[%s]\r\n", section);
751 f.Printf("\n[%s]\n", section);
756 f.Write(outputLine, 1, lenOutput);
758 // Write back the rest of the file if necessary
761 f.Write(remainingBuffer, 1, remainingLen);
762 delete remainingBuffer;
763 if(currentSize != newSize)
764 FileTruncate(settingsFilePath, newSize);