1 /********************************************************************************
2 * *
3 * R e g i s t r y C l a s s *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1998,2020 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as published by *
10 * the Free Software Foundation; either version 3 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This library is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public License *
19 * along with this program. If not, see <http://www.gnu.org/licenses/> *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "FXArray.h"
26 #include "FXHash.h"
27 #include "FXStream.h"
28 #include "FXObject.h"
29 #include "FXString.h"
30 #include "FXSystem.h"
31 #include "FXProcess.h"
32 #include "FXPath.h"
33 #include "FXStat.h"
34 #include "FXFile.h"
35 #include "FXDir.h"
36 #include "FXStringDictionary.h"
37 #include "FXRegistry.h"
38
39 /*
40 Notes:
41
42 - The FOX settings tree stores configuration data for FOX-based applications; it
43 is organized as follows:
44
45 SettingsRoot/foxrc The "foxrc" file stores common configuration
46 settings for all FOX-based applications,
47 such as colors and file bindings.
48
49 SettingsRoot/Vendor/Vendorrc The Vendorrc file is common to all applications
50 from a particular organization, so that common
51 configurable attributes may be given consistent
52 values easily.
53
54 SettingsRoot/Vendor/Applicationrc The Applicationrc file stores configuration data
55 unique to a specific application only.
56
57 SettingsRoot/Applicationrc If no Vendor name is specified, the Applicationrc
58 file is located at the toplevel of the SettingsRoot.
59
60 - A System-Wide SettingsRoot may be located in a read-only, system adminstrator designated
61 place, to be shared by all users on a particular installation. For example, this may store
62 license-keys, references to online documentation, and other installation-time parameters.
63
64 - A Per-User SettingsRoot is typically located in a user's home directory.
65 Configurations stored in these files will be merged with the System-Wide configurations.
66 Parameters given different values in the Per-User files will override those in the
67 System-Wide ones.
68
69 - Rationale:
70
71 1) When installing an application, simply copy "seed" registry files to
72 the System-Wide SettingsRoot; having a subdirectory Vendor prevents
73 clobbering other people's registry files, even if their application
74 has the same name.
75
76 2) System-Wide registry files are, as a matter of principle, read-only.
77
78 3) System-Wide registry files are loaded first, and Per-User registry
79 files are loaded on top of that.
80
81 4) Registry files loaded later will take precedence over those loaded
82 earlier; i.e. key/value pairs in a later file will override a key/value
83 pair with the same key loaded earlier.
84
85 5) The exception to the rule is that a key/value pair will not be overridden
86 if the value of the key was changed since it had been loaded.
87
88 6) An application reads files in the order:
89
90 System-Wide:
91 fox.rc,
92 Vendor/Vendor.rc,
93 Vendor/Application.rc
94
95 Per-User:
96 fox.rc,
97 Vendor/Vendor.rc,
98 Vendor/Application.rc
99
100 7) When System-Wide settings files are loaded, entries are not marked as
101 changed, and thus not written into the Per-User settings file. Only
102 values changed by the Application will be written into the Per-User file.
103
104 8) Settings loaded from the Per-User settings file *will* however be written
105 back to the Per-User settings file, regarless whether they are changed or
106 not (exception is when no single entry was changed at all!).
107
108 9) ONLY the Per-User, Application-specific settings files ARE WRITTEN!
109
110 10) Special applications, such as ControlPanel, may change other settings files,
111 however.
112
113 - Locations of registry settings files:
114
115 o System-Wide registry files are loaded from the directory defined by user-
116 specified environment variable $FOXDIR, if this variable was set.
117
118 o System-Wide registry files are searched in SystemDirectories path-list.
119 The path-list may be changed by setSystemDirectories().
120
121 o On UNIX systems, the XDG standard is followed; this means the location
122 of System-Wide settings should controlled by $XDG_CONFIG_DIRS.
123
124 o If this environment variable isn't set, then SystemDirectories with only have
125 one single directory, the default value: "/etc/xdg".
126
127 o Directories in SystemDirectories will be searched for a subdirectory "foxrc"
128 which is the root of the System-Wide settings tree for all FOX applications;
129 for example: "/etc/xdg/foxrc/Desktop".
130
131 o Per-User registry files are located from a single toplevel directory in variable
132 UserDirectory. The value of this variable may be changed by setUserDirectory().
133
134 o On UNIX systems, the XDG standard is followed; this means the location of the
135 Per-User settings should controlled by $XDG_CONFIG_HOME. If this environment
136 variable set, the root for the Per-User settings tree for all FOX applications
137 is $XDG_CONFIG_HOME/foxrc.
138
139 o Otherwise, it will have the default value: "~/.config".
140
141
142 - The Freedesktop.org XDG standard is found at:
143
144 http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
145
146 Important for FOX is:
147
148 $XDG_DATA_HOME Per-user data files, defaults to "$HOME/.local/share".
149
150 $XDG_CONFIG_HOME Per-user configuration files, defaults to "$HOME/.config".
151
152 $XDG_DATA_DIRS Colon separated search path for per-user data files; default
153 paths are "/usr/local/share/:/usr/share/"
154
155 $XDG_CONFIG_DIRS Colon separated search path for per-user configuration files;
156 defaults to "/etc/xdg".
157 */
158
159 #define MAXNAME 200
160 #define MAXVALUE 2000
161
162 // Default locations and names
163 #if defined(WIN32)
164 #define FOXRC "fox.ini"
165 #define SYSTEMDIRS "\\Program Files;\\Windows"
166 #define USERDIR "%USERPROFILE%\\fox"
167 #define FILEEXT ".ini"
168 #else
169 #define FOXRC "fox.rc"
170 #define SYSTEMDIRS "/etc/xdg"
171 #define USERDIR "~/.config"
172 #define FILEEXT ".rc"
173 #endif
174
175 using namespace FX;
176
177 /*******************************************************************************/
178
179 namespace FX {
180
181
182 // File extension for settings files
183 const FXchar FXRegistry::ext[]=FILEEXT;
184
185
186 // File name of common settings file
187 const FXchar FXRegistry::foxrc[]=FOXRC;
188
189
190 // Make registry object
FXRegistry(const FXString & akey,const FXString & vkey)191 FXRegistry::FXRegistry(const FXString& akey,const FXString& vkey):applicationkey(akey),vendorkey(vkey),systemdirs(SYSTEMDIRS),userdir(USERDIR){
192 #if defined(WIN32)
193 ascii=false;
194 #else
195 ascii=true;
196 #endif
197 }
198
199
200 #if defined(WIN32)
201
202 // Read from Windows Registry
readFromRegistry(FXptr hroot,FXbool mrk)203 FXbool FXRegistry::readFromRegistry(FXptr hroot,FXbool mrk){
204 HKEY hsoftware;
205 FXbool ok=false;
206
207 FXTRACE((100,"FXRegistry::readFromRegistry(%p,%d)\n",hroot,mrk));
208
209 // Open Software registry section
210 if(RegOpenKeyExA((HKEY)hroot,"Software",0,KEY_READ,&hsoftware)==ERROR_SUCCESS){
211
212 // Read Software\FOX
213 if(readFromRegistryGroup("FOX",hsoftware,false)) ok=true;
214
215 // Have vendor key
216 if(!vendorkey.empty()){
217 HKEY hvendor;
218
219 // Open vendor registry sub-section
220 if(RegOpenKeyExA(hsoftware,vendorkey.text(),0,KEY_READ,&hvendor)==ERROR_SUCCESS){
221
222 // Read under "Software\Vendor\Vendor"
223 if(readFromRegistryGroup(vendorkey,hvendor,false)) ok=true;
224
225 // Have application key
226 if(!applicationkey.empty()){
227
228 // Read under "Software\Vendor\Application"
229 if(readFromRegistryGroup(applicationkey,hvendor,mrk)) ok=true;
230 }
231 RegCloseKey(hvendor);
232 }
233 }
234
235 // No vendor key
236 else{
237
238 // Have application key
239 if(!applicationkey.empty()){
240
241 // Read under "Software\Application"
242 if(readFromRegistryGroup(applicationkey,hsoftware,mrk)) ok=true;
243 }
244 }
245 RegCloseKey(hsoftware);
246 }
247 return ok;
248 }
249
250
251 // Read from given group
readFromRegistryGroup(const FXString & group,FXptr hbase,FXbool mrk)252 FXbool FXRegistry::readFromRegistryGroup(const FXString& group,FXptr hbase,FXbool mrk){
253 FXchar section[MAXNAME],name[MAXNAME],value[MAXVALUE];
254 HKEY hgroup;
255
256 // Open registry group
257 if(RegOpenKeyExA((HKEY)hbase,group.text(),0,KEY_READ,&hgroup)==ERROR_SUCCESS){
258 DWORD sectionsize=MAXNAME;
259 DWORD sectionindex=0;
260 FILETIME writetime;
261
262 // Read sections
263 while(RegEnumKeyExA(hgroup,sectionindex,section,§ionsize,NULL,NULL,NULL,&writetime)==ERROR_SUCCESS){
264
265 // Open section
266 HKEY hsection;
267 if(RegOpenKeyExA(hgroup,section,0,KEY_READ,&hsection)==ERROR_SUCCESS){
268 DWORD namesize=MAXNAME;
269 DWORD valuesize=MAXVALUE;
270 DWORD index=0;
271 DWORD type;
272
273 // Read key-value pairs
274 while(RegEnumValueA(hsection,index,name,&namesize,NULL,&type,(BYTE*)value,&valuesize)!=ERROR_NO_MORE_ITEMS){
275 FXASSERT(type==REG_SZ);
276 at(section).at(name,mrk)=value;
277 namesize=MAXNAME;
278 valuesize=MAXVALUE;
279 index++;
280 }
281
282 // Close section
283 RegCloseKey(hsection);
284 }
285 sectionsize=MAXNAME;
286 sectionindex++;
287 }
288
289 // Close group
290 RegCloseKey(hgroup);
291 return true;
292 }
293 return false;
294 }
295
296
297 // Update current user's settings
writeToRegistry(FXptr hroot)298 FXbool FXRegistry::writeToRegistry(FXptr hroot){
299 FXbool ok=false;
300
301 FXTRACE((100,"FXRegistry::writeToRegistry(%p)\n",hroot));
302
303 // Have application key
304 if(!applicationkey.empty()){
305 HKEY hsoftware;
306
307 // Open software registry section
308 if(RegOpenKeyExA((HKEY)hroot,"Software",0,KEY_WRITE,&hsoftware)==ERROR_SUCCESS){
309
310 // Have vendor key
311 if(!vendorkey.empty()){
312 HKEY hvendor;
313 DWORD disp;
314
315 // Open vendor registry sub-section
316 if(RegCreateKeyExA(hsoftware,vendorkey.text(),0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE|KEY_READ,NULL,&hvendor,&disp)==ERROR_SUCCESS){
317
318 // Have application key
319 if(!applicationkey.empty()){
320
321 // Write under "Software\Vendor\Application"
322 if(writeToRegistryGroup(applicationkey,hvendor)) ok=true;
323 }
324
325 // Done with vendor key
326 RegCloseKey(hvendor);
327 }
328 }
329
330 // No vendor key
331 else{
332
333 // Have application key
334 if(!applicationkey.empty()){
335
336 // Write under "Software\Application"
337 if(writeToRegistryGroup(applicationkey,hsoftware)) ok=true;
338 }
339 }
340
341 // Done with software key
342 RegCloseKey(hsoftware);
343 }
344 }
345 return ok;
346 }
347
348
349 // Write to registry group
writeToRegistryGroup(const FXString & group,FXptr hbase)350 FXbool FXRegistry::writeToRegistryGroup(const FXString& group,FXptr hbase){
351 FXchar section[MAXNAME];
352 DWORD sectionsize,sectionindex,disp;
353 HKEY hgroup,hsection;
354 FXint s,e;
355 FILETIME writetime;
356
357 // Open registry group
358 if(RegCreateKeyExA((HKEY)hbase,group.text(),0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE|KEY_READ,NULL,&hgroup,&disp)==ERROR_SUCCESS){
359
360 // First, purge all existing sections
361 while(1){
362 sectionindex=0;
363 sectionsize=MAXNAME;
364 if(RegEnumKeyExA(hgroup,sectionindex,section,§ionsize,NULL,NULL,NULL,&writetime)!=ERROR_SUCCESS) break;
365 if(RegDeleteKeyA(hgroup,section)!=ERROR_SUCCESS) break;
366 }
367
368 // Write sections
369 for(s=0; s<no(); ++s){
370
371 // Section is non-empty
372 if(!empty(s)){
373 hsection=NULL;
374
375 // Write keys in this section
376 for(e=0; e<data(s).no(); ++e){
377
378 // Key is non-empty and marked
379 if(!data(s).empty(e) && data(s).mark(e)){
380
381 // Create section in registry upon finding first key in it
382 if(hsection==NULL){
383 if(RegCreateKeyExA(hgroup,key(s).text(),0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE|KEY_READ,NULL,&hsection,&disp)!=ERROR_SUCCESS) goto x;
384 }
385
386 // Write key-value pair
387 if(RegSetValueExA(hsection,data(s).key(e).text(),0,REG_SZ,(BYTE*)data(s).data(e).text(),data(s).data(e).length()+1)!=ERROR_SUCCESS) break;
388 }
389 }
390
391 // Close section
392 if(hsection) RegCloseKey(hsection);
393 }
394
395 // Process next registry section
396 x: continue;
397 }
398
399 // Close group
400 RegCloseKey(hgroup);
401 return true;
402 }
403 return false;
404 }
405
406 #endif
407
408
409 /*******************************************************************************/
410
411 // Read registry
read()412 FXbool FXRegistry::read(){
413 FXbool ok=false;
414 FXString path;
415 if(ascii){
416
417 // Read system-wide settings from systemdirs
418 if(!systemdirs.empty()){
419
420 // Find common settings
421 path=FXPath::search(systemdirs,FOXRC);
422 if(!path.empty()){
423 if(parseFile(path,false)) ok=true;
424 }
425
426 // Have vendor subdirectory
427 if(!vendorkey.empty()){
428
429 // Find vendor subdirectory
430 path=FXPath::search(systemdirs,vendorkey);
431 if(!path.empty()){
432
433 // Try read vendor settings
434 if(parseFile(path+PATHSEPSTRING+vendorkey+ext,false)) ok=true;
435
436 // Try read application settings
437 if(!applicationkey.empty()){
438 if(parseFile(path+PATHSEPSTRING+applicationkey+ext,false)) ok=true;
439 }
440 }
441 }
442
443 // Have application settings only
444 else if(!applicationkey.empty()){
445
446 // Find applications settings
447 path=FXPath::search(systemdirs,applicationkey+ext);
448
449 // Try read application settings
450 if(!path.empty()){
451 if(parseFile(path,false)) ok=true;
452 }
453 }
454 }
455
456 // Read per-user settings from userdir
457 if(!userdir.empty()){
458
459 // Path to settings data
460 path=FXPath::absolute(FXPath::expand(userdir));
461
462 // Try read common settings
463 if(parseFile(path+PATHSEPSTRING FOXRC,false)) ok=true;
464
465 // Try read vendor settings
466 if(!vendorkey.empty()){
467 path.append(PATHSEPSTRING+vendorkey);
468 if(parseFile(path+PATHSEPSTRING+vendorkey+ext,false)) ok=true;
469 }
470
471 // Try read application settings
472 if(!applicationkey.empty()){
473 if(parseFile(path+PATHSEPSTRING+applicationkey+ext,true)) ok=true;
474 }
475 }
476 }
477 #if defined(WIN32)
478 else{
479 // Try read system-wide registry settings from HKEY_LOCAL_MACHINE
480 if(readFromRegistry(HKEY_LOCAL_MACHINE,false)) ok=true;
481
482 // Try read per-user registry settings from HKEY_CURRENT_USER
483 if(readFromRegistry(HKEY_CURRENT_USER,true)) ok=true;
484 }
485 #endif
486 return ok;
487 }
488
489 /*******************************************************************************/
490
491 // Write registry
write()492 FXbool FXRegistry::write(){
493 FXbool ok=false;
494 FXString path;
495 if(isModified()){
496 if(ascii){
497
498 // Write per-user settings to userdir
499 if(!userdir.empty()){
500
501 // Have application key
502 if(!applicationkey.empty()){
503
504 // Path to settings data
505 path=FXPath::absolute(FXPath::expand(userdir));
506
507 // Have vendor key
508 if(!vendorkey.empty()){
509 path.append(PATHSEPSTRING+vendorkey);
510 }
511
512 // Ensure parent directories exist
513 if(FXDir::createDirectories(path)){
514 FXString realfile;
515 FXString tempfile;
516
517 // Final registry filename
518 realfile=path+PATHSEPSTRING+applicationkey+ext;
519
520 // Temporary registry filename
521 tempfile=path+PATHSEPSTRING+applicationkey+"_"+FXString::value(FXProcess::current())+ext;
522
523 // Unparse settings into temp file first
524 if(unparseFile(tempfile)){
525
526 // Rename ATOMICALLY to proper name
527 if(FXFile::move(tempfile,realfile,true)){
528 setModified(false);
529 ok=true;
530 }
531 }
532 }
533 }
534 }
535 }
536 #if defined(WIN32)
537 else{
538 // Write per-user registry settings to HKEY_CURRENT_USER
539 if(writeToRegistry(HKEY_CURRENT_USER)) ok=true;
540 }
541 #endif
542 }
543 return ok;
544 }
545
546
547 // Destructor
~FXRegistry()548 FXRegistry::~FXRegistry(){
549 }
550
551 }
552