1 /********************************************************************************
2 * *
3 * R e g i s t r y C l a s s *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1998,2006 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU Lesser General Public *
10 * License as published by the Free Software Foundation; either *
11 * version 2.1 of the License, or (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 GNU *
16 * Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public *
19 * License along with this library; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
21 *********************************************************************************
22 * $Id: FXRegistry.cpp,v 1.61 2006/01/22 17:58:39 fox Exp $ *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "FXHash.h"
28 #include "FXStream.h"
29 #include "FXObject.h"
30 #include "FXString.h"
31 #include "FXSystem.h"
32 #include "FXPath.h"
33 #include "FXStat.h"
34 #include "FXFile.h"
35 #include "FXDir.h"
36 #include "FXStringDict.h"
37 #include "FXRegistry.h"
38
39 /*
40 Notes:
41
42 - Default directories for registry files where search for the FOX settings
43 database is tried:
44
45 1 $FOXDIR
46
47 2 /etc/foxrc /usr/lib/foxrc /usr/local/lib/foxrc
48
49 3 $PATH/foxrc
50
51 4 ~/.foxrc
52
53 - The latter one is writable, and also the one that overrides all other
54 values.
55
56 - Search $PATH for registry directory [$PATH/VendorKey/AppKey] or
57 [$PATH/AppKey]
58
59 - Compile-time #define for registry directories [for global settings].
60
61 - Registry is organized as follows:
62
63 DESKTOP Registry file common to all FOX applications, from all vendors
64 Vendor/Vendor Registry file common to all applications from Vendor
65 Vendor/Application Registry file for Application from Vendor
66 Application Registry file for Application, if no Vendor specified
67
68 Rationale:
69
70 1) When installing an application, simply copy ``seed'' registry files
71 in the appropriate places; having a subdirectory Vendor prevents clobbering
72 other people's registry files, even if their application has the same name.
73
74 2) System-wide registry files are, as a matter of principle, read-only.
75
76 3) System registry files are loaded first, then per-user registry files are
77 loaded on top of that.
78
79 4) Registry files loaded later will take precedence over those loaded earlier;
80 i.e. key/value pairs in a later file will override a key/value pair with the
81 same key loaded earlier.
82
83 5) The exception to the rule is that a key/value pair will not be overridden if
84 the value of the key has changed since loading it. In other words, changes
85 will persist.
86
87 6) An application reads files in the order of system: (DESKTOP, Vendor/Vendor,
88 Vendor/App), then user: (DESKTOP, Vendor/Vendor, Vendor/App).
89
90 7) All changed entries are written to the per-user directory. An application
91 starts by reading the registry files, runs for a while, then at the end writes
92 the settings back out to the per-user registry files.
93 It will ONLY write those entries which (a) have been changed, or (b) were
94 previously read from the same per-user registry files.
95
96 - Need ability to read/write DESKTOP, Vendor/Vendor, Vendor/Application registry,
97 for both user as well as system-wide settings. This is primarily important for
98 installation programs.
99
100 */
101
102 #define MAXNAME 200
103 #define MAXVALUE 2000
104
105 #ifndef REGISTRYPATH
106 #ifndef WIN32
107 #define REGISTRYPATH "/etc:/usr/lib:/usr/local/lib"
108 #else
109 #define REGISTRYPATH "\\WINDOWS\\foxrc"
110 #endif
111 #endif
112
113 #define DESKTOP "Desktop"
114
115 using namespace FX;
116
117 /*******************************************************************************/
118
119 namespace FX {
120
121 // Object implementation
122 FXIMPLEMENT(FXRegistry,FXSettings,NULL,0)
123
124 // Make registry object
FXRegistry(const FXString & akey,const FXString & vkey)125 FXRegistry::FXRegistry(const FXString& akey,const FXString& vkey):applicationkey(akey),vendorkey(vkey){
126 #ifndef WIN32
127 ascii=true;
128 #else
129 ascii=false;
130 #endif
131 }
132
133
134 // Read registry
read()135 bool FXRegistry::read(){
136 FXString dirname;
137 register bool ok=false;
138
139 #ifdef WIN32 // Either file based or system registry for WIN32
140
141 if(ascii){
142
143 FXTRACE((100,"Reading from file based settings database.\n"));
144
145 // Try get location from FOXDIR environment variable
146 dirname=getenv("FOXDIR");
147 if(!dirname.empty()){
148 FXTRACE((100,"Found registry %s in $FOXDIR.\n",dirname.text()));
149 ok=readFromDir(dirname,false);
150 }
151
152 // Try search along REGISTRYPATH if not specified explicitly
153 if(!ok){
154 dirname=FXPath::search(REGISTRYPATH,"foxrc");
155 if(!dirname.empty()){
156 FXTRACE((100,"Found registry %s in REGISTRYPATH.\n",dirname.text()));
157 ok=readFromDir(dirname,false);
158 }
159 }
160
161 // Try search along PATH if still not found
162 if(!ok){
163 dirname=FXPath::search(FXSystem::getExecPath(),"foxrc");
164 if(!dirname.empty()){
165 FXTRACE((100,"Found registry %s in $PATH.\n",dirname.text()));
166 ok=readFromDir(dirname,false);
167 }
168 }
169
170 // Get path to per-user settings directory
171 dirname=FXSystem::getEnvironment("USERPROFILE")+PATHSEPSTRING "foxrc";
172
173 // Then read per-user settings; overriding system-wide ones
174 if(readFromDir(dirname,true)) ok=true;
175 }
176
177 else{
178
179 FXTRACE((100,"Reading from registry HKEY_LOCAL_MACHINE.\n"));
180
181 // Load system-wide resources first
182 if(readFromRegistry(HKEY_LOCAL_MACHINE,false)) ok=true;
183
184 FXTRACE((100,"Reading from registry HKEY_CURRENT_USER.\n"));
185
186 // Now any modified resources for current user
187 if(readFromRegistry(HKEY_CURRENT_USER,true)) ok=true;
188 }
189
190 #else // File based registry for UNIX
191
192 // Try get location from FOXDIR environment variable
193 dirname=getenv("FOXDIR");
194 if(!dirname.empty()){
195 FXTRACE((100,"Found registry %s in $FOXDIR.\n",dirname.text()));
196 ok=readFromDir(dirname,false);
197 }
198
199 // Try search along REGISTRYPATH if not specified explicitly
200 if(!ok){
201 dirname=FXPath::search(REGISTRYPATH,"foxrc");
202 if(!dirname.empty()){
203 FXTRACE((100,"Found registry %s in REGISTRYPATH.\n",dirname.text()));
204 ok=readFromDir(dirname,false);
205 }
206 }
207
208 // Try search along PATH if still not found
209 if(!ok){
210 dirname=FXPath::search(FXSystem::getExecPath(),"foxrc");
211 if(!dirname.empty()){
212 FXTRACE((100,"Found registry %s in $PATH.\n",dirname.text()));
213 ok=readFromDir(dirname,false);
214 }
215 }
216
217 // Get path to per-user settings directory
218 dirname=FXSystem::getHomeDirectory()+PATHSEPSTRING ".foxrc";
219
220 // Then read per-user settings; overriding system-wide ones
221 if(readFromDir(dirname,true)) ok=true;
222
223 #endif
224
225 return ok;
226 }
227
228
229 // Try read registry from directory
readFromDir(const FXString & dirname,bool mark)230 bool FXRegistry::readFromDir(const FXString& dirname,bool mark){
231 bool ok=false;
232
233 // Directory is empty?
234 if(!dirname.empty()){
235
236 // First try to load desktop registry
237 #ifndef WIN32
238 if(parseFile(dirname+PATHSEPSTRING DESKTOP,false)) ok=true;
239 #else
240 if(parseFile(dirname+PATHSEPSTRING DESKTOP ".ini",false)) ok=true;
241 #endif
242
243 // Have vendor key
244 if(!vendorkey.empty()){
245 #ifndef WIN32
246 if(parseFile(dirname+PATHSEPSTRING+vendorkey+PATHSEPSTRING+vendorkey,false)) ok=true;
247 #else
248 if(parseFile(dirname+PATHSEPSTRING+vendorkey+PATHSEPSTRING+vendorkey+".ini",false)) ok=true;
249 #endif
250 // Have application key
251 if(!applicationkey.empty()){
252 #ifndef WIN32
253 if(parseFile(dirname+PATHSEPSTRING+vendorkey+PATHSEPSTRING+applicationkey,mark)) ok=true;
254 #else
255 if(parseFile(dirname+PATHSEPSTRING+vendorkey+PATHSEPSTRING+applicationkey+".ini",mark)) ok=true;
256 #endif
257 }
258 }
259
260 // No vendor key
261 else{
262
263 // Have application key
264 if(!applicationkey.empty()){
265 #ifndef WIN32
266 if(parseFile(dirname+PATHSEPSTRING+applicationkey,mark)) ok=true;
267 #else
268 if(parseFile(dirname+PATHSEPSTRING+applicationkey+".ini",mark)) ok=true;
269 #endif
270 }
271 }
272 }
273 return ok;
274 }
275
276
277 #ifdef WIN32
278
279 // Read from Windows Registry
readFromRegistry(void * hRootKey,bool mark)280 bool FXRegistry::readFromRegistry(void* hRootKey,bool mark){
281 HKEY hSoftKey,hOrgKey;
282 bool ok=false;
283
284 // Open Software registry section
285 if(RegOpenKeyExA((HKEY)hRootKey,"Software",0,KEY_READ,&hSoftKey)==ERROR_SUCCESS){
286
287 // Read Software\Desktop
288 if(readFromRegistryGroup(hSoftKey,DESKTOP)) ok=true;
289
290 // Have vendor key
291 if(!vendorkey.empty()){
292
293 // Open Vendor registry sub-section
294 if(RegOpenKeyExA(hSoftKey,vendorkey.text(),0,KEY_READ,&hOrgKey)==ERROR_SUCCESS){
295
296 // Read Software\Vendor\Vendor
297 if(readFromRegistryGroup(hOrgKey,vendorkey.text())) ok=true;
298
299 // Have application key
300 if(!applicationkey.empty()){
301
302 // Read Software\Vendor\Application
303 if(readFromRegistryGroup(hOrgKey,applicationkey.text(),mark)) ok=true;
304 }
305 RegCloseKey(hOrgKey);
306 }
307 }
308
309 // No vendor key
310 else{
311
312 // Have application key
313 if(!applicationkey.empty()){
314
315 // Read Software\Application
316 if(readFromRegistryGroup(hSoftKey,applicationkey.text(),mark)) ok=true;
317 }
318 }
319 RegCloseKey(hSoftKey);
320 }
321 return ok;
322 }
323
324
325 // Read from given group
readFromRegistryGroup(void * org,const char * groupname,bool mark)326 bool FXRegistry::readFromRegistryGroup(void* org,const char* groupname,bool mark){
327 FXchar section[MAXNAME],name[MAXNAME],value[MAXVALUE];
328 DWORD sectionsize,sectionindex,namesize,valuesize,index,type;
329 HKEY groupkey,sectionkey;
330 FILETIME writetime;
331 FXStringDict *group;
332 if(RegOpenKeyExA((HKEY)org,groupname,0,KEY_READ,&groupkey)==ERROR_SUCCESS){
333 sectionindex=0;
334 sectionsize=MAXNAME;
335 FXTRACE((100,"Reading registry group %s\n",groupname));
336 while(RegEnumKeyExA(groupkey,sectionindex,section,§ionsize,NULL,NULL,NULL,&writetime)==ERROR_SUCCESS){
337 group=insert(section);
338 FXTRACE((100,"[%s]\n",section));
339 if(RegOpenKeyExA(groupkey,section,0,KEY_READ,§ionkey)==ERROR_SUCCESS){
340 index=0;
341 namesize=MAXNAME;
342 valuesize=MAXVALUE;
343 while(RegEnumValueA(sectionkey,index,name,&namesize,NULL,&type,(BYTE*)value,&valuesize)!=ERROR_NO_MORE_ITEMS){
344 FXASSERT(type==REG_SZ);
345 FXTRACE((100,"%s=%s\n",name,value));
346 group->replace(name,value,mark);
347 namesize=MAXNAME;
348 valuesize=MAXVALUE;
349 index++;
350 }
351 RegCloseKey(sectionkey);
352 }
353 sectionsize=MAXNAME;
354 sectionindex++;
355 }
356 RegCloseKey(groupkey);
357 return true;
358 }
359 return false;
360 }
361
362
363 #endif
364
365
366
367 // Write registry
write()368 bool FXRegistry::write(){
369 FXString pathname,tempname;
370
371 // Settings have not changed
372 if(!isModified()) return true;
373
374 // We can not save if no application key given
375 if(!applicationkey.empty()){
376
377 #ifdef WIN32 // Either file based or system registry for WIN32
378
379 if(ascii){
380
381 FXTRACE((100,"Writing to file based settings database.\n"));
382
383 // Changes written only in the per-user registry
384 pathname=FXSystem::getEnvironment("USERPROFILE")+PATHSEPSTRING "foxrc";
385
386 // If this directory does not exist, make it
387 if(!FXStat::exists(pathname)){
388 if(!FXDir::create(pathname)){
389 fxwarning("%s: unable to create directory.\n",pathname.text());
390 return false;
391 }
392 }
393 else{
394 if(!FXStat::isDirectory(pathname)){
395 fxwarning("%s: is not a directory.\n",pathname.text());
396 return false;
397 }
398 }
399
400 // Add vendor subdirectory
401 if(!vendorkey.empty()){
402 pathname.append(PATHSEPSTRING+vendorkey);
403 if(!FXStat::exists(pathname)){
404 if(!FXDir::create(pathname)){
405 fxwarning("%s: unable to create directory.\n",pathname.text());
406 return false;
407 }
408 }
409 else{
410 if(!FXStat::isDirectory(pathname)){
411 fxwarning("%s: is not a directory.\n",pathname.text());
412 return false;
413 }
414 }
415 }
416
417 // Add application key
418 pathname.append(PATHSEPSTRING+applicationkey+".ini");
419
420 // Construct temp name
421 tempname.format("%s_%d",pathname.text(),fxgetpid());
422
423 // Unparse settings into temp file first
424 if(unparseFile(tempname)){
425
426 // Rename ATOMICALLY to proper name
427 if(!FXFile::rename(tempname,pathname)){
428 fxwarning("Unable to save registry.\n");
429 return false;
430 }
431
432 modified=false;
433 return true;
434 }
435 }
436
437 else{
438
439 FXTRACE((100,"Writing to registry HKEY_CURRENT_USER.\n"));
440
441 // Write back modified resources for current user
442 if(writeToRegistry(HKEY_CURRENT_USER)) return true;
443 }
444
445 #else // File based registry for X11
446
447 // Changes written only in the per-user registry
448 pathname=FXSystem::getHomeDirectory()+PATHSEPSTRING ".foxrc";
449
450 // If this directory does not exist, make it
451 if(!FXStat::exists(pathname)){
452 if(!FXDir::create(pathname)){
453 fxwarning("%s: unable to create directory.\n",pathname.text());
454 return false;
455 }
456 }
457 else{
458 if(!FXStat::isDirectory(pathname)){
459 fxwarning("%s: is not a directory.\n",pathname.text());
460 return false;
461 }
462 }
463
464 // Add vendor subdirectory
465 if(!vendorkey.empty()){
466 pathname.append(PATHSEPSTRING+vendorkey);
467 if(!FXStat::exists(pathname)){
468 if(!FXDir::create(pathname)){
469 fxwarning("%s: unable to create directory.\n",pathname.text());
470 return false;
471 }
472 }
473 else{
474 if(!FXStat::isDirectory(pathname)){
475 fxwarning("%s: is not a directory.\n",pathname.text());
476 return false;
477 }
478 }
479 }
480
481 // Add application key
482 pathname.append(PATHSEPSTRING+applicationkey);
483
484 // Construct temp name
485 tempname.format("%s_%d",pathname.text(),fxgetpid());
486
487 // Unparse settings into temp file first
488 if(unparseFile(tempname)){
489
490 // Rename ATOMICALLY to proper name
491 if(!FXFile::rename(tempname,pathname)){
492 fxwarning("Unable to save registry.\n");
493 return false;
494 }
495 setModified(false);
496 return true;
497 }
498
499 #endif
500
501 }
502 return false;
503 }
504
505
506
507 #ifdef WIN32
508
509 // Update current user's settings
writeToRegistry(void * hRootKey)510 bool FXRegistry::writeToRegistry(void* hRootKey){
511 HKEY hSoftKey,hOrgKey;
512 DWORD disp;
513 bool ok=false;
514
515 // Open Software registry section
516 if(RegOpenKeyExA((HKEY)hRootKey,"Software",0,KEY_WRITE,&hSoftKey)==ERROR_SUCCESS){
517
518 // Have vendor key
519 if(!vendorkey.empty()){
520
521 // Open Vendor registry sub-section
522 if(RegCreateKeyExA(hSoftKey,vendorkey.text(),0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE|KEY_READ,NULL,&hOrgKey,&disp)==ERROR_SUCCESS){
523
524 // Have application key
525 if(!applicationkey.empty()){
526
527 // Write Software\Vendor\Application
528 if(writeToRegistryGroup(hOrgKey,applicationkey.text())) ok=true;
529 }
530 RegCloseKey(hOrgKey);
531 }
532 }
533
534 // No vendor key
535 else{
536
537 // Have application key
538 if(!applicationkey.empty()){
539
540 // Write Software\Application
541 if(writeToRegistryGroup(hSoftKey,applicationkey.text())) ok=true;
542 }
543 }
544
545 // Done with Software key
546 RegCloseKey(hSoftKey);
547 }
548 return ok;
549 }
550
551
552 // Write to registry group
writeToRegistryGroup(void * org,const char * groupname)553 bool FXRegistry::writeToRegistryGroup(void* org,const char* groupname){
554 FXchar section[MAXNAME];
555 DWORD sectionsize,sectionindex,disp;
556 HKEY groupkey,sectionkey;
557 FXint s,e;
558 FILETIME writetime;
559 FXStringDict *group;
560 if(RegCreateKeyExA((HKEY)org,groupname,0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE|KEY_READ,NULL,&groupkey,&disp)==ERROR_SUCCESS){
561
562 // First, purge all existing sections
563 while(1){
564 sectionindex=0;
565 sectionsize=MAXNAME;
566 if(RegEnumKeyExA(groupkey,sectionindex,section,§ionsize,NULL,NULL,NULL,&writetime)!=ERROR_SUCCESS) break;
567 if(RegDeleteKeyA(groupkey,section)!=ERROR_SUCCESS) break;
568 }
569
570 // Dump the registry, writing only marked entries
571 s=first();
572 while(s<size()){
573 sectionkey=NULL;
574 group=data(s);
575 FXASSERT(group);
576 for(e=group->first(); e<group->size(); e=group->next(e)){
577 if(group->mark(e)){
578 if(sectionkey==NULL){
579 FXASSERT(key(s));
580 if(RegCreateKeyExA(groupkey,key(s),0,REG_NONE,REG_OPTION_NON_VOLATILE,KEY_WRITE|KEY_READ,NULL,§ionkey,&disp)!=ERROR_SUCCESS) goto x;
581 }
582 FXASSERT(group->key(e));
583 FXASSERT(group->data(e));
584 if(RegSetValueExA(sectionkey,group->key(e),0,REG_SZ,(BYTE*)group->data(e),strlen(group->data(e))+1)!=ERROR_SUCCESS) break;
585 }
586 }
587
588 // Close this section's key (if it exists)
589 if(sectionkey) RegCloseKey(sectionkey);
590
591 // Process next registry section
592 x: s=next(s);
593 }
594 RegCloseKey(groupkey);
595 return true;
596 }
597 return false;
598 }
599
600
601 #endif
602
603 }
604