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 const char * name;
29 class_property const 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(const 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 const char * settingsName
80 set { delete settingsName; if(value && value[0]) settingsName = CopyString(value); }
81 get { return settingsName; }
83 property const char * settingsExtension
85 set { delete settingsExtension; if(value && value[0]) settingsExtension = CopyString(value); }
86 get { return settingsExtension; }
88 property const char * settingsDirectory
90 set { delete settingsDirectory; if(value && value[0]) settingsDirectory = CopyUnixPath(value); }
91 get { return settingsDirectory; }
93 property const char * settingsLocation
95 set { delete settingsLocation; if(value && value[0]) settingsLocation = CopyUnixPath(value); }
96 get { return settingsLocation; }
98 property const char * settingsFilePath
100 set { delete settingsFilePath; if(value && value[0]) settingsFilePath = CopyUnixPath(value); }
101 get { return settingsFilePath; }
103 property bool allowDefaultLocations
105 set { allowDefaultLocations = value; }
106 get { return allowDefaultLocations; }
108 property bool allUsers
110 set { allUsers = value; }
111 get { return allUsers; }
114 property const String driver
120 driverClass = GetGlobalSettingsDriver(value);
122 get { return driverClass ? driverClass.name : null; }
124 GlobalSettingsData data;
125 GlobalSettingsData * dataOwner;
126 subclass(GlobalSettingsData) dataClass;
131 char * settingsExtension;
132 char * settingsLocation;
133 char * settingsFilePath;
134 bool allowDefaultLocations;
137 char * settingsDirectory;
139 FileMonitor settingsMonitor
141 this, fileChange = { modified = true };
143 bool OnFileNotify(FileChange action, const char * param)
145 OnAskReloadSettings();
152 subclass(GlobalSettingsDriver) driverClass;
157 f.Unlock(0, 0, true);
160 delete settingsExtension;
161 delete settingsLocation;
162 delete settingsFilePath;
163 delete settingsDirectory;
166 char * PrepareSpecifiedLocationPath(const char * extension, bool create)
169 char location[MAX_LOCATION] = "";
171 strcpy(location, settingsLocation);
172 path = GetFilePath(location, extension, create, false, false);
176 char * PreparePortablePath(const char * extension, bool create)
180 char location[MAX_LOCATION];
181 LocateModule(null, location);
182 StripLastDirectory(location, location);
183 path = GetFilePath(location, extension, create, false, false);
187 char * PrepareHomePath(const char * extension, bool create, bool unixStyle)
191 char * home = getenv("HOME");
194 char location[MAX_LOCATION];
195 strcpy(location, home);
196 path = GetFilePath(location, extension, create, true, unixStyle);
201 #if defined(__WIN32__)
202 char * PrepareUserProfilePath(const char * extension, bool create)
204 // Windows attempts: $USERPROFILE/app.ini
206 char * profile = getenv("USERPROFILE");
207 if(profile && profile[0])
209 char location[MAX_LOCATION];
210 strcpy(location, profile);
211 path = GetFilePath(location, extension, create, false, false);
216 char * PrepareHomeDrivePath(const char * extension, bool create)
219 const char * homedrive = getenv("HOMEDRIVE");
220 const char * homepath = getenv("HOMEPATH");
221 if(homedrive && homedrive[0] && homepath && homepath[0])
223 char location[MAX_LOCATION];
224 strcpy(location, homedrive);
225 PathCatSlash(location, homepath);
226 path = GetFilePath(location, extension, create, false, false);
231 char * PrepareSystemPath(const char * extension, bool create)
234 uint16 _wfilePath[MAX_LOCATION];
235 char location[MAX_LOCATION];
236 GetSystemDirectory(_wfilePath, MAX_LOCATION);
237 UTF16toUTF8Buffer(_wfilePath, location, MAX_LOCATION);
238 path = GetFilePath(location, extension, create, false, false);
242 char * PrepareAllUsersPath(const char * extension, bool create)
245 char * allUsers = getenv("ALLUSERSPROFILE");
246 if(allUsers && allUsers[0])
248 char location[MAX_LOCATION];
249 strcpy(location, allUsers);
250 path = GetFilePath(location, extension, create, false, false);
255 char * PrepareEtcPath(const char * extension, bool create)
258 char location[MAX_LOCATION] = "/etc/";
259 path = GetFilePath(location, extension, create, false, false);
264 char * GetFilePath(char * location, const char * extension, bool create, bool dotPrefix, bool runCommandsStyle)
269 MakeSlashPath(location);
270 if(location[0] && (attribs = FileExists(location)).isDirectory)
272 if(settingsDirectory)
276 PathCatSlash(location, ".");
277 strcat(location, settingsDirectory);
280 PathCatSlash(location, settingsDirectory);
283 attribs = FileExists(location);
285 if(attribs.isDirectory)
287 char * name = new char[strlen(settingsName) + strlen(extension) + 2];
288 if(dotPrefix && !settingsDirectory)
291 strcat(name, settingsName);
294 strcpy(name, settingsName);
300 strcat(name, extension);
302 path = new char[strlen(location) + strlen(name) + 16];
303 strcpy(path, location);
304 PathCatSlash(path, name);
311 const char * GetExtension()
313 const char * extension;
314 if(settingsExtension)
315 extension = settingsExtension;
317 #if defined(__WIN32__)
325 void FileOpenTryRead()
327 f = FileOpen(settingsFilePath, read);
328 //PrintLn("GlobalSettings::FileOpenTryRead -- ", settingsFilePath, " -- ", f != null);
329 if(!f) // This delete will cover both trying the next possible config location and
330 delete settingsFilePath; // the case when we're doing a load when the config file is no longer available
331 } // and we want to re-try all possible config locations.
333 bool FileOpenTryWrite(bool shouldDelete, bool * locked)
336 f = FileOpen(settingsFilePath, readWrite);
339 f = FileOpen(settingsFilePath, writeRead);
343 f = FileOpen(settingsFilePath, readWrite);
348 // Don't wait for a lock, first one to lock gets to write, other will likely loose changes on a reload.
349 if(f.Lock(exclusive, 0, 0, false))
359 else if(shouldDelete) // This delete will cover both trying the next possible config location and
360 delete settingsFilePath; // allow trying to save to a location where user has permission.
361 //PrintLn("GlobalSettings::FileOpenTryWrite -- ", settingsFilePath, " -- ", f != null);
366 virtual void OnAskReloadSettings();
368 bool OpenAndLock(FileSize * fileSize)
372 settingsMonitor.StopMonitoring();
377 if(!settingsFilePath && settingsName && settingsName[0])
379 const char * extension = GetExtension();
381 if(!f && (settingsFilePath = PrepareSpecifiedLocationPath(extension, false)))
383 if(!f && (!settingsLocation || allowDefaultLocations))
386 if(!f && (settingsFilePath = PreparePortablePath(extension, false)))
392 #if defined(__WIN32__)
393 if(!f && (settingsFilePath = PrepareHomePath(extension, false, false)))
396 if(!f && (settingsFilePath = PrepareHomePath(extension, false, true)))
399 #if defined(__WIN32__)
402 if(!f && (settingsFilePath = PrepareUserProfilePath(extension, false)))
404 if(!f && (settingsFilePath = PrepareHomeDrivePath(extension, false)))
409 if(!f && (settingsFilePath = PrepareAllUsersPath(extension, false)))
412 if(!f && (settingsFilePath = PrepareSystemPath(extension, false)))
417 if(!f && (settingsFilePath = PrepareEtcPath(extension, false)))
428 // At some point wait was true, it was changed to false and now we do retries.
429 // Could it be because the wait to true was causing blocking behaviors?
430 //if(f && f.Lock(shared, 0, 0, true)) <- I think the wait to true is bad, it wrote blanked out global settings.
431 for(c = 0; c < 10 && !(locked = f.Lock(shared, 0, 0, false)); c++)
433 ecere::sys::Sleep(0.01);
437 if(locked && fileSize)
438 *fileSize = f.GetSize();
443 virtual SettingsIOResult Load()
445 SettingsIOResult result = fileNotFound;
452 result = driverClass.Load(f, this);
459 virtual SettingsIOResult Save()
461 SettingsIOResult result = error;
466 settingsMonitor.StopMonitoring();
469 // Don't auto delete settingsFilePath because only want to try another path if we were using a global path
470 FileOpenTryWrite(false, &locked);
472 if((!settingsFilePath || (!f && globalPath)) && settingsName && settingsName[0])
474 const char * extension = GetExtension();
475 delete settingsFilePath;
477 if(!f && (settingsFilePath = PrepareSpecifiedLocationPath(extension, true)))
478 FileOpenTryWrite(true, &locked);
479 if(!f && (!settingsLocation || allowDefaultLocations))
482 // never try to write a new portable configuration file?
483 // -- Probably always want to write back to the same file, the first FileOpenTryWrite(true) should succeed
484 //if(!f && (settingsFilePath = PreparePortablePath(extension, true)))
485 // FileOpenTryWrite(true);
486 #if defined(__WIN32__)
487 if(!f && (settingsFilePath = PrepareAllUsersPath(extension, true)))
488 FileOpenTryWrite(true, &locked);
490 if(!f && (settingsFilePath = PrepareEtcPath(extension, true)))
491 FileOpenTryWrite(true, &locked);
496 if(!f && (settingsFilePath = PrepareHomePath(extension, true,
497 #if defined(__WIN32__)
503 FileOpenTryWrite(true, &locked);
505 #if defined(__WIN32__)
509 if(!f && (settingsFilePath = PrepareUserProfilePath(extension, true)))
510 FileOpenTryWrite(true, &locked);
511 if(!f && (settingsFilePath = PrepareHomeDrivePath(extension, true)))
512 FileOpenTryWrite(true, &locked);
514 if(!f && (settingsFilePath = PrepareSystemPath(extension, true)))
517 FileOpenTryWrite(true, &locked);
525 result = driverClass.Save(f, this);
537 settingsMonitor.StopMonitoring();
544 void CloseAndMonitor()
547 if(settingsFilePath && OnAskReloadSettings != GlobalSettings::OnAskReloadSettings)
549 settingsMonitor.fileName = settingsFilePath;
550 settingsMonitor.StartMonitoring();
555 public class GlobalAppSettings : GlobalSettings
558 bool GetGlobalValue(const char * section, const char * name, GlobalSettingType type, void * value)
564 int lenSection = strlen(section);
565 int lenName = strlen(name);
567 while(f.GetLine(line, sizeof(line)))
569 if(line[0] == '[' && !strncmp(line+1, section, lenSection) && line[lenSection+1] == ']')
574 while(f.GetLine(line, sizeof(line)))
576 if(!strncmp(line, name, lenName) && line[lenName] == '=')
578 char * string = line + lenName + 1;
584 Container<String> list = value;
586 int numTokens = TokenizeWith(string,
587 sizeof(tokens) / sizeof(byte *), tokens, " ,", false);
589 for(c = 0; c<numTokens; c++)
591 list.Add(CopyString(tokens[c]));
597 char ** thisString = value;
598 *thisString = CopyString(string);
603 int * integer = value;
604 *integer = (int)strtol(string, null, 0);
617 bool PutGlobalValue(const char * section, const char * name, GlobalSettingType type, const void * value)
622 char line[92048], outputLine[92048] = "";
623 int lenSection = strlen(section);
624 int lenName = strlen(name);
626 byte * remainingBuffer = null;
627 int remainingLen = 0;
628 uint currentSize = 0, newSize = 0;
632 // Prepare the output line
633 strcpy(outputLine, name);
634 strcat(outputLine, "=");
639 Container<String> list = (void *)value;
640 Iterator<String> item { list };
644 strcat(outputLine, "\"");
646 strcat(outputLine, item.data);
647 strcat(outputLine, "\"");
650 strcat(outputLine, ",");
655 // strcat(outputLine, "\"");
657 strcat(outputLine, value);
658 // strcat(outputLine, "\"");
663 sprintf(integer, "%d", (int)(intptr)value);
664 strcat(outputLine, integer);
668 #if defined(__WIN32__)
669 strcat(outputLine, "\r\n");
671 strcat(outputLine, "\n");
673 lenOutput = strlen(outputLine);
675 while(f.GetLine(line, sizeof(line)))
677 if(line[0] == '[' && !strncmp(line+1, section, lenSection) && line[lenSection+1] == ']')
682 int posBeforeSection = -1;
686 if(!f.GetLine(line, sizeof(line)))
688 if(posBeforeSection == -1 || !line[0])
689 posBeforeSection = pos;
693 // We've already reached next section
694 f.Seek(posBeforeSection, start);
696 // Remember the rest of the file
699 remainingBuffer = renew remainingBuffer byte[remainingLen + 65536];
700 remainingLen += f.Read(remainingBuffer + remainingLen, 1, 65536);
702 // Don't need to truncate. leave currentSize and newSize at 0
703 f.Seek(posBeforeSection, start);
706 else if(!strncmp(line, name, lenName) && line[lenName] == '=')
708 uint endLinePos = f.Tell();
710 // Remember the rest of the file
713 remainingBuffer = renew remainingBuffer byte[remainingLen + 65536];
714 remainingLen += f.Read(remainingBuffer + remainingLen, 1, 65536);
716 currentSize = endLinePos + remainingLen;
717 newSize = pos + lenOutput + remainingLen;
719 // Go back where we will overwrite this line
728 #if defined(__WIN32__)
729 f.Printf("\r\n[%s]\r\n", section);
731 f.Printf("\n[%s]\n", section);
736 f.Write(outputLine, 1, lenOutput);
738 // Write back the rest of the file if necessary
741 f.Write(remainingBuffer, 1, remainingLen);
742 delete remainingBuffer;
743 if(currentSize != newSize)
744 FileTruncate(settingsFilePath, newSize);