~ubuntu-branches/ubuntu/karmic/gnustep-base/karmic

« back to all changes in this revision

Viewing changes to Source/NSUserDefaults.m

  • Committer: Bazaar Package Importer
  • Author(s): Eric Heintzmann
  • Date: 2005-04-17 00:14:38 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050417001438-enf0y07c9tku85z1
Tags: 1.10.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
24
24
 
25
25
   <title>NSUserDefaults class reference</title>
26
 
   $Date: 2002/03/13 09:58:43 $ $Revision: 1.77 $
 
26
   $Date: 2005/02/22 14:06:28 $ $Revision: 1.118 $
27
27
*/
28
28
 
29
 
#include <config.h>
30
 
#include <base/preface.h>
 
29
#include "config.h"
 
30
#include "GNUstepBase/preface.h"
31
31
#include <sys/stat.h>
32
32
#include <sys/types.h>
33
33
#include <errno.h>
34
34
 
35
 
#include <Foundation/NSUserDefaults.h>
36
 
#include <Foundation/NSFileManager.h>
37
 
#include <Foundation/NSPathUtilities.h>
38
 
#include <Foundation/NSDictionary.h>
39
 
#include <Foundation/NSArray.h>
40
 
#include <Foundation/NSDate.h>
41
 
#include <Foundation/NSUtilities.h>
42
 
#include <Foundation/NSArchiver.h>
43
 
#include <Foundation/NSException.h>
44
 
#include <Foundation/NSNotification.h>
45
 
#include <Foundation/NSTimer.h>
46
 
#include <Foundation/NSProcessInfo.h>
47
 
#include <Foundation/NSRunLoop.h>
48
 
#include <Foundation/NSBundle.h>
49
 
#include <Foundation/NSValue.h>
50
 
#include <Foundation/NSLock.h>
51
 
#include <base/GSLocale.h>
 
35
#include "Foundation/NSUserDefaults.h"
 
36
#include "Foundation/NSArchiver.h"
 
37
#include "Foundation/NSArray.h"
 
38
#include "Foundation/NSBundle.h"
 
39
#include "Foundation/NSDate.h"
 
40
#include "Foundation/NSDictionary.h"
 
41
#include "Foundation/NSDistributedLock.h"
 
42
#include "Foundation/NSException.h"
 
43
#include "Foundation/NSFileManager.h"
 
44
#include "Foundation/NSLock.h"
 
45
#include "Foundation/NSNotification.h"
 
46
#include "Foundation/NSPathUtilities.h"
 
47
#include "Foundation/NSProcessInfo.h"
 
48
#include "Foundation/NSRunLoop.h"
 
49
#include "Foundation/NSSet.h"
 
50
#include "Foundation/NSThread.h"
 
51
#include "Foundation/NSTimer.h"
 
52
#include "Foundation/NSUtilities.h"
 
53
#include "Foundation/NSValue.h"
 
54
#include "Foundation/NSDebug.h"
 
55
#include "GNUstepBase/GSLocale.h"
 
56
#include "GNUstepBase/GSLock.h"
 
57
 
 
58
#ifdef HAVE_LOCALE_H
 
59
#include <locale.h>
 
60
#endif
52
61
 
53
62
#include "GSPrivate.h"
54
63
 
64
73
 
65
74
static Class    NSArrayClass;
66
75
static Class    NSDataClass;
 
76
static Class    NSDateClass;
67
77
static Class    NSDictionaryClass;
 
78
static Class    NSNumberClass;
68
79
static Class    NSMutableDictionaryClass;
69
80
static Class    NSStringClass;
70
81
 
71
82
static NSUserDefaults   *sharedDefaults = nil;
72
83
static NSMutableString  *processName = nil;
73
84
static NSMutableArray   *userLanguages = nil;
 
85
static BOOL             invalidatedLanguages = NO;
74
86
static NSRecursiveLock  *classLock = nil;
75
87
 
76
88
/*
82
94
{
83
95
  if (self == sharedDefaults)
84
96
    {
 
97
      NSArray   *debug;
 
98
 
 
99
      /**
 
100
       * If there is an array NSUserDefault called GNU-Debug,
 
101
       * we add its contents to the set of active debug levels.
 
102
       */
 
103
      debug = [self arrayForKey: @"GNU-Debug"];
 
104
      if (debug != nil)
 
105
        {
 
106
          unsigned      c = [debug count];
 
107
          NSMutableSet  *s;
 
108
 
 
109
          s = [[NSProcessInfo processInfo] debugSet];
 
110
          while (c-- > 0)
 
111
            {
 
112
              NSString  *level = [debug objectAtIndex: c];
 
113
 
 
114
              [s addObject: level];
 
115
            }
 
116
        }
 
117
 
85
118
      flags[GSMacOSXCompatible]
86
119
        = [self boolForKey: @"GSMacOSXCompatible"];
87
120
      flags[GSOldStyleGeometry]
88
121
        = [self boolForKey: @"GSOldStyleGeometry"];
89
122
      flags[GSLogSyslog]
90
123
        = [self boolForKey: @"GSLogSyslog"];
 
124
      flags[GSLogThread]
 
125
        = [self boolForKey: @"GSLogThread"];
91
126
      flags[NSWriteOldStylePropertyLists]
92
127
        = [self boolForKey: @"NSWriteOldStylePropertyLists"];
93
128
    }
97
132
 *** Local method definitions
98
133
 *************************************************************************/
99
134
@interface NSUserDefaults (__local_NSUserDefaults)
100
 
- (void) __createStandardSearchList;
101
135
- (NSDictionary*) __createArgumentDictionary;
102
136
- (void) __changePersistentDomain: (NSString*)domainName;
103
 
- (void) __timerTicked: (NSTimer*)tim;
104
137
@end
105
138
 
106
139
/**
130
163
 *   <term><code>NSArgumentDomain</code> ... volatile</term>
131
164
 *   <desc>
132
165
 *     Contains defaults read from the arguments provided
133
 
 *     to the application at startup.
 
166
 *     to the application at startup.<br />
 
167
 *     Pairs of arguments are used for this, with the first argument in
 
168
 *     each pair being the name of a default (with a hyphen prepended)
 
169
 *     and the second argument of the pair being the value of the defalt.<br />
 
170
 *     NB. In GNUstep special arguments of the form <code>--GNU-Debug=...</code>
 
171
 *     are used to enable debugging.  Despite beginning with a hyphen, these
 
172
 *     are not treated as default keys.
134
173
 *   </desc>
135
174
 *   <term>Application (name of the current process) ... persistent</term>
136
175
 *   <desc>
177
216
 * </p>
178
217
 * <p>
179
218
 *   NB. The GNUstep implementation differs from the Apple one in
180
 
 *   that it is thread-safe while Apples (as of MacOS-X 10.1) is not.
 
219
 *   that it is thread-safe while Apple's (as of MacOS-X 10.1) is not.
181
220
 * </p>
182
221
 */
183
222
@implementation NSUserDefaults: NSObject
184
223
 
185
 
static BOOL setSharedDefaults = NO;     /* Flag to prevent infinite recursion */
 
224
static BOOL setSharedDefaults = NO;     /* Flag to prevent infinite recursion */
186
225
 
187
226
+ (void) initialize
188
227
{
196
235
       */
197
236
      NSArrayClass = [NSArray class];
198
237
      NSDataClass = [NSData class];
 
238
      NSDateClass = [NSDate class];
199
239
      NSDictionaryClass = [NSDictionary class];
 
240
      NSNumberClass = [NSNumber class];
200
241
      NSMutableDictionaryClass = [NSMutableDictionary class];
201
242
      NSStringClass = [NSString class];
202
 
      classLock = [NSRecursiveLock new];
 
243
      classLock = [GSLazyRecursiveLock new];
203
244
    }
204
245
}
205
246
 
217
258
    {
218
259
      NSDictionary      *regDefs;
219
260
 
 
261
      [sharedDefaults synchronize];     // Ensure changes are written.
220
262
      regDefs = RETAIN([sharedDefaults->_tempDomains
221
263
        objectForKey: NSRegistrationDomain]);
 
264
 
222
265
      setSharedDefaults = NO;
223
 
      AUTORELEASE(sharedDefaults);      // Let tother threads keep it.
224
 
      sharedDefaults = nil;
 
266
      DESTROY(sharedDefaults);
225
267
      if (regDefs != nil)
226
268
        {
227
269
          [self standardUserDefaults];
356
398
  NSArray *uL;
357
399
  NSEnumerator *enumerator;
358
400
 
 
401
  /*
 
402
   * Calling +standardUserDefaults and +userLanguages is horribly interrelated.
 
403
   * Messing with the order of assignments and calls within these methods can
 
404
   * break things very easily ... take care.
 
405
   *
 
406
   * If +standardUserDefaults is called first, it sets up initial information
 
407
   * in sharedDefaults then calls +userLanguages to get language information.
 
408
   * +userLanguages then calls +standardUserDefaults to read NSLanguages
 
409
   * and returns the language information.
 
410
   * +standardUserDefaults then sets up language domains and loads localisation
 
411
   * information before returning.
 
412
   *
 
413
   * If +userLanguages is called first, it calls +standardUserDefaults
 
414
   * to obtain the NSLanguages array.
 
415
   * +standardUserDefaults loads basic information initialising the
 
416
   * sharedDefaults variable, then calls back to +userLanguages to set up
 
417
   * its search list.
 
418
   * +userLanguages calls +standardUserDefaults again to get NSLanguages.
 
419
   * +standardUserDefaults returns the partially initialised sharedDefaults.
 
420
   * +userLanguages uses this to create and return the user languages.
 
421
   * +standardUserDefaults uses the languages to update the search list
 
422
   * and load in localisation information then returns sharedDefaults.
 
423
   * +userLanguages uses this to rebuild language information and return it.
 
424
   */
 
425
 
359
426
  [classLock lock];
360
 
  if (setSharedDefaults)
 
427
 
 
428
  /*
 
429
   * NB. The use of the setSharedDefaults flag ensures that a recursive
 
430
   * call to this method is quietly suppressed ... so we get a more
 
431
   * manageable problem.
 
432
   */
 
433
  if (setSharedDefaults == YES)
361
434
    {
362
435
      RETAIN(sharedDefaults);
363
436
      [classLock unlock];
364
437
      return AUTORELEASE(sharedDefaults);
365
438
    }
366
439
  setSharedDefaults = YES;
367
 
  /*
368
 
   * Get the user languages *before* setting up sharedDefaults, to avoid
369
 
   * the userLanguages method trying to look up languages in a partially
370
 
   * constructed user defaults object.
371
 
   */
372
 
  uL = [[self class] userLanguages];
 
440
 
373
441
  // Create new sharedDefaults (NOTE: Not added to the autorelease pool!)
374
442
  sharedDefaults = [[self alloc] init];
375
443
  if (sharedDefaults == nil)
379
447
      return nil;
380
448
    }
381
449
 
382
 
  [sharedDefaults __createStandardSearchList];
 
450
  /*
 
451
   * Set up search list (excluding language list, which we don't know yet)
 
452
   */
 
453
  [sharedDefaults->_searchList addObject: NSArgumentDomain];
 
454
  [sharedDefaults->_searchList addObject: processName];
 
455
  [sharedDefaults->_searchList addObject: NSGlobalDomain];
 
456
  [sharedDefaults->_searchList addObject: NSRegistrationDomain];
 
457
 
 
458
  /*
 
459
   * Look up user languages list and insert language specific domains
 
460
   * into search list before NSRegistrationDomain
 
461
   */
 
462
  uL = [self userLanguages];
 
463
  enumerator = [uL objectEnumerator];
 
464
  while ((lang = [enumerator nextObject]))
 
465
    {
 
466
      unsigned  index = [sharedDefaults->_searchList count] - 1;
 
467
 
 
468
      [sharedDefaults->_searchList insertObject: lang atIndex: index];
 
469
    }
383
470
 
384
471
  /* Set up language constants */
385
472
  added_locale = NO;
387
474
  enumerator = [uL objectEnumerator];
388
475
  while ((lang = [enumerator nextObject]))
389
476
    {
390
 
      NSString *path;
391
 
      NSDictionary *dict;
392
 
      path = [NSBundle pathForGNUstepResource: lang
393
 
                                       ofType: nil
394
 
                                  inDirectory: @"Resources/Languages"];
 
477
      NSString          *path;
 
478
      NSDictionary      *dict;
 
479
      NSBundle          *gbundle;
 
480
 
 
481
      gbundle = [NSBundle bundleForLibrary: @"gnustep-base"];
 
482
      path = [gbundle pathForResource: lang
 
483
                               ofType: nil
 
484
                          inDirectory: @"Languages"];
395
485
      dict = nil;
396
 
      if (path)
397
 
        dict = [NSDictionary dictionaryWithContentsOfFile: path];
 
486
      if (path != nil)
 
487
        {
 
488
          dict = [NSDictionary dictionaryWithContentsOfFile: path];
 
489
        }
398
490
      if (dict)
399
491
        {
400
492
          [sharedDefaults setVolatileDomain: dict forName: lang];
402
494
        }
403
495
      else if (added_locale == NO)
404
496
        {
405
 
          NSString *locale = GSSetLocale(nil);
 
497
          NSString      *locale = nil;
 
498
 
 
499
#ifdef HAVE_LOCALE_H
 
500
#ifdef LC_MESSAGES
 
501
          locale = GSSetLocale(LC_MESSAGES, nil);
 
502
#endif
 
503
#endif
406
504
          if (locale == nil)
407
 
            continue;
 
505
            {
 
506
              continue;
 
507
            }
408
508
          /* See if we can get the dictionary from i18n functions.
409
509
             Note that we get the dict from the current locale regardless
410
510
             of what 'lang' is, since it should match anyway. */
411
511
          /* Also, I don't think that the i18n routines can handle more than
412
512
             one locale, but tell me if I'm wrong... */
413
513
          if (GSLanguageFromLocale(locale))
414
 
            lang = GSLanguageFromLocale(locale);
 
514
            {
 
515
              lang = GSLanguageFromLocale(locale);
 
516
            }
415
517
          dict = GSDomainFromDefaultLocale();
416
 
          if (dict)
417
 
            [sharedDefaults setVolatileDomain: dict forName: lang];
 
518
          if (dict != nil)
 
519
            {
 
520
              [sharedDefaults setVolatileDomain: dict forName: lang];
 
521
            }
418
522
          added_locale = YES;
419
523
        }
420
524
    }
439
543
+ (NSArray*) userLanguages
440
544
{
441
545
  NSArray       *currLang = nil;
442
 
  NSString      *locale;
443
 
 
 
546
  NSArray       *result;
 
547
  NSString      *locale = nil;
 
548
 
 
549
  /*
 
550
   * Calling +standardUserDefaults and +userLanguages is horribly interrelated.
 
551
   * Messing with the order of assignments and calls within these methods can
 
552
   * break things very easily ... take care.
 
553
   *
 
554
   * If +standardUserDefaults is called first, it sets up initial information
 
555
   * in sharedDefaults then calls +userLanguages to get language information.
 
556
   * +userLanguages then calls +standardUserDefaults to read NSLanguages
 
557
   * and returns the language information.
 
558
   * +standardUserDefaults then sets up language domains and loads localisation
 
559
   * information before returning.
 
560
   *
 
561
   * If +userLanguages is called first, it calls +standardUserDefaults
 
562
   * to obtain the NSLanguages array.
 
563
   * +standardUserDefaults loads basic information initialising the
 
564
   * sharedDefaults variable, then calls back to +userLanguages to set up
 
565
   * its search list.
 
566
   * +userLanguages calls +standardUserDefaults again to get NSLanguages.
 
567
   * +standardUserDefaults returns the partially initialised sharedDefaults.
 
568
   * +userLanguages uses this to create and return the user languages.
 
569
   * +standardUserDefaults uses the languages to update the search list
 
570
   * and load in localisation information then returns sharedDefaults.
 
571
   * +userLanguages uses this to rebuild language information and return it.
 
572
   */
 
573
 
 
574
#ifdef HAVE_LOCALE_H
 
575
#ifdef LC_MESSAGES
 
576
  locale = GSSetLocale(LC_MESSAGES, nil);
 
577
#endif
 
578
#endif
444
579
  [classLock lock];
445
 
  if (userLanguages != nil)
 
580
  if (invalidatedLanguages == YES)
446
581
    {
447
 
      RETAIN(userLanguages);
448
 
      [classLock unlock];
449
 
      return AUTORELEASE(userLanguages);
 
582
      invalidatedLanguages = NO;
 
583
      DESTROY(userLanguages);
450
584
    }
451
 
  userLanguages = RETAIN([NSMutableArray arrayWithCapacity: 5]);
452
 
  locale = GSSetLocale(@"");
453
 
  if (sharedDefaults == nil)
 
585
  if (userLanguages == nil)
454
586
    {
455
 
      /* Create our own defaults to get "NSLanguages" since sharedDefaults
456
 
         depends on us */
457
 
      NSUserDefaults    *tempDefaults;
458
 
 
459
 
      tempDefaults = [[self alloc] init];
460
 
      if (tempDefaults != nil)
 
587
      currLang = [[NSUserDefaults standardUserDefaults]
 
588
        stringArrayForKey: @"NSLanguages"];
 
589
 
 
590
      userLanguages = [[NSMutableArray alloc] initWithCapacity: 5];
 
591
 
 
592
      if (currLang == nil && locale != nil && GSLanguageFromLocale(locale))
461
593
        {
462
 
          NSMutableArray        *sList;
463
 
 
464
 
          /*
465
 
           * Can't use the standard method to set up a search list,
466
 
           * it would cause mutual recursion as it includes languages.
467
 
           */
468
 
          sList = [[NSMutableArray alloc] initWithCapacity: 4];
469
 
          [sList addObject: NSArgumentDomain];
470
 
          [sList addObject: processName];
471
 
          [sList addObject: NSGlobalDomain];
472
 
          [sList addObject: NSRegistrationDomain];
473
 
          [tempDefaults setSearchList: sList];
474
 
          RELEASE(sList);
475
 
          currLang = [tempDefaults stringArrayForKey: @"NSLanguages"];
476
 
          AUTORELEASE(tempDefaults);
 
594
          currLang = [NSArray arrayWithObject: GSLanguageFromLocale(locale)];
477
595
        }
478
 
    }
479
 
  else
480
 
    {
481
 
      currLang
482
 
        = [[self standardUserDefaults] stringArrayForKey: @"NSLanguages"];
483
 
    }
484
 
  if (currLang == nil && locale != 0 && GSLanguageFromLocale(locale))
485
 
    {
486
 
      currLang = [NSArray arrayWithObject: GSLanguageFromLocale(locale)];
487
 
    }
488
596
#ifdef __MINGW__
489
 
  if (currLang == nil && locale != 0)
490
 
    {
491
 
      /* Check for language as the first part of the locale string */
492
 
      NSRange under = [locale rangeOfString: @"_"];
493
 
      if (under.location)
494
 
        currLang = [NSArray arrayWithObject:
495
 
                     [locale substringToIndex: under.location]];
496
 
    }
 
597
      if (currLang == nil && locale != nil)
 
598
        {
 
599
          /* Check for language as the first part of the locale string */
 
600
          NSRange under = [locale rangeOfString: @"_"];
 
601
          if (under.location)
 
602
            currLang = [NSArray arrayWithObject:
 
603
                         [locale substringToIndex: under.location]];
 
604
        }
497
605
#endif
498
 
  if (currLang == nil)
499
 
    {
500
 
      const char        *env_list;
501
 
      NSString          *env;
502
 
 
503
 
      env_list = getenv("LANGUAGES");
504
 
      if (env_list != 0)
505
 
        {
506
 
          env = [NSStringClass stringWithCString: env_list];
507
 
          currLang = [env componentsSeparatedByString: @";"];
508
 
        }
509
 
    }
510
 
 
511
 
  if (currLang != nil)
512
 
    {
513
 
      if ([currLang containsObject: @""] == YES)
514
 
        {
515
 
          NSMutableArray        *a = [currLang mutableCopy];
516
 
 
517
 
          [a removeObject: @""];
518
 
          currLang = (NSArray*)AUTORELEASE(a);
519
 
        }
520
 
      [userLanguages addObjectsFromArray: currLang];
521
 
    }
522
 
 
523
 
  /* Check if "English" is included. We do this to make sure all the
524
 
     required language constants are set somewhere if they aren't set
525
 
     in the default language */
526
 
  if ([userLanguages containsObject: @"English"] == NO)
527
 
    {
528
 
      [userLanguages addObject: @"English"];
529
 
    }
530
 
  RETAIN(userLanguages);
 
606
      if (currLang == nil)
 
607
        {
 
608
          const char    *env_list;
 
609
          NSString      *env;
 
610
 
 
611
          env_list = getenv("LANGUAGES");
 
612
          if (env_list != 0)
 
613
            {
 
614
              env = [NSStringClass stringWithCString: env_list];
 
615
              currLang = [env componentsSeparatedByString: @";"];
 
616
            }
 
617
        }
 
618
 
 
619
      if (currLang != nil)
 
620
        {
 
621
          if ([currLang containsObject: @""] == YES)
 
622
            {
 
623
              NSMutableArray    *a = [currLang mutableCopy];
 
624
 
 
625
              [a removeObject: @""];
 
626
              currLang = (NSArray*)AUTORELEASE(a);
 
627
            }
 
628
          [userLanguages addObjectsFromArray: currLang];
 
629
        }
 
630
 
 
631
      /* Check if "English" is included. We do this to make sure all the
 
632
         required language constants are set somewhere if they aren't set
 
633
         in the default language */
 
634
      if ([userLanguages containsObject: @"English"] == NO)
 
635
        {
 
636
          [userLanguages addObject: @"English"];
 
637
        }
 
638
    }
 
639
  result = RETAIN(userLanguages);
531
640
  [classLock unlock];
532
 
  return AUTORELEASE(userLanguages);
 
641
  return AUTORELEASE(result);
533
642
}
534
643
 
535
644
/**
549
658
  [[self standardUserDefaults]
550
659
    setPersistentDomain: globDict forName: NSGlobalDomain];
551
660
  RELEASE(globDict);
552
 
  return;
553
661
}
554
662
 
555
663
/*************************************************************************
592
700
#endif
593
701
  attr = [NSDictionary dictionaryWithObjectsAndKeys:
594
702
    NSUserName(), NSFileOwnerAccountName,
595
 
    [NSNumber numberWithUnsignedLong: desired], NSFilePosixPermissions,
 
703
    [NSNumberClass numberWithUnsignedLong: desired], NSFilePosixPermissions,
596
704
    nil];
597
705
 
598
706
  if ([mgr fileExistsAtPath: home isDirectory: &isDir] == NO)
679
787
{
680
788
  NSString      *path = pathForUser(userName);
681
789
 
682
 
  if (path == nil)
683
 
    {
684
 
      RELEASE(self);
685
 
      return nil;
686
 
    }
687
790
  return [self initWithContentsOfFile: path];
688
791
}
689
792
 
694
797
 */
695
798
- (id) initWithContentsOfFile: (NSString*)path
696
799
{
 
800
  NSFileManager *mgr = [NSFileManager defaultManager];
 
801
  BOOL          flag;
 
802
 
697
803
  self = [super init];
698
804
 
699
805
  /*
704
810
      processName = RETAIN([[NSProcessInfo processInfo] processName]);
705
811
    }
706
812
 
707
 
  if (path != nil && [path isEqual: @""] == NO)
708
 
    {
709
 
      _defaultsDatabase = [path copy];
 
813
  if (path == nil || [path isEqual: @""] == YES)
 
814
    {
 
815
      path = pathForUser(NSUserName());
 
816
    }
 
817
  path = [path stringByStandardizingPath];
 
818
  _defaultsDatabase = [path copy];
 
819
  path = [path stringByDeletingLastPathComponent];
 
820
  if ([mgr isWritableFileAtPath: path] == NO)
 
821
    {
 
822
      NSWarnMLog(@"Path '%@' is not writable - making user defaults for '%@' "
 
823
        @" read-only\n", path, _defaultsDatabase);
 
824
 
 
825
    }
 
826
  else if ([mgr fileExistsAtPath: path isDirectory: &flag] == NO && flag == NO)
 
827
    {
 
828
      NSWarnMLog(@"Path '%@' is not an accessible directory - making user "
 
829
        @"defaults for '%@' read-only\n", path, _defaultsDatabase);
 
830
    }
 
831
  else if ([mgr fileExistsAtPath: _defaultsDatabase] == YES
 
832
    && [mgr isReadableFileAtPath: _defaultsDatabase] == NO)
 
833
    {
 
834
      NSWarnMLog(@"Path '%@' is not readable - making user defaults blank\n",
 
835
        _defaultsDatabase);
710
836
    }
711
837
  else
712
838
    {
713
 
      _defaultsDatabase = [pathForUser(NSUserName()) copy];
 
839
      /*
 
840
       * Only create the file lock if we can update the file ...
 
841
       * if we can't the absence of the lock tells us we must be
 
842
       * in read-only mode.
 
843
       */
 
844
      _fileLock = [[NSDistributedLock alloc] initWithPath:
 
845
        [_defaultsDatabase stringByAppendingPathExtension: @"lck"]];
714
846
    }
 
847
  _lock = [GSLazyRecursiveLock new];
715
848
 
716
849
  // Create an empty search list
717
850
  _searchList = [[NSMutableArray alloc] initWithCapacity: 10];
720
853
  _persDomains = [[NSMutableDictionaryClass alloc] initWithCapacity: 10];
721
854
  if ([self synchronize] == NO)
722
855
    {
723
 
      NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
724
 
      BOOL      done = NO;
725
 
      int       attempts;
726
 
 
727
 
      // Retry for a couple of seconds in case we are locked out.
728
 
      for (attempts = 0; done == NO && attempts < 10; attempts++)
729
 
        {
730
 
          [runLoop runMode: NSDefaultRunLoopMode
731
 
                beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.2]];
732
 
          if ([self synchronize] == YES)
733
 
            {
734
 
              done = YES;
735
 
            }
736
 
        }
737
 
      if (done == NO)
738
 
        {
739
 
          DESTROY(self);
740
 
          return self;
741
 
        }
 
856
      DESTROY(self);
 
857
      return self;
742
858
    }
743
859
 
744
860
  // Check and if not existent add the Application and the Global domains
765
881
    setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10]
766
882
    forKey: NSRegistrationDomain];
767
883
 
768
 
  _lock = [NSRecursiveLock new];
 
884
  [[NSNotificationCenter defaultCenter] addObserver: self
 
885
           selector: @selector(synchronize)
 
886
               name: @"GSHousekeeping"
 
887
             object: nil];
 
888
 
769
889
  return self;
770
890
}
771
891
 
772
892
- (void) dealloc
773
893
{
774
 
  if (_tickingTimer)
775
 
    [_tickingTimer invalidate];
 
894
  [[NSNotificationCenter defaultCenter] removeObserver: self];
776
895
  RELEASE(_lastSync);
777
896
  RELEASE(_searchList);
778
897
  RELEASE(_persDomains);
779
898
  RELEASE(_tempDomains);
780
899
  RELEASE(_changedDomains);
781
900
  RELEASE(_dictionaryRep);
 
901
  RELEASE(_fileLock);
782
902
  RELEASE(_lock);
783
903
  [super dealloc];
784
904
}
797
917
}
798
918
 
799
919
/**
 
920
 * Adds the domain names aName to the search list of the receiver.<br />
 
921
 * The domain is added after the application domain.<br />
 
922
 * Suites may be removed using the -removeSuiteNamed: method.
 
923
 */
 
924
- (void) addSuiteNamed: (NSString*)aName
 
925
{
 
926
  unsigned      index;
 
927
 
 
928
  if (aName == nil)
 
929
    {
 
930
      [NSException raise: NSInvalidArgumentException
 
931
                  format: @"attempt to add suite with nil name"];
 
932
    }
 
933
  [_lock lock];
 
934
  DESTROY(_dictionaryRep);
 
935
  if (self == sharedDefaults) invalidatedLanguages = YES;
 
936
  [_searchList removeObject: aName];
 
937
  index = [_searchList indexOfObject: processName];
 
938
  index++;      // NSNotFound wraps to zero ... insert at start.
 
939
  aName = [aName copy];
 
940
  [_searchList insertObject: aName atIndex: index];
 
941
  [_lock unlock];
 
942
  RELEASE(aName);
 
943
}
 
944
 
 
945
/**
800
946
 * Looks up a value for a specified default using -objectForKey:
801
947
 * and checks that it is an NSArray object.  Returns nil if it is not.
802
948
 */
811
957
 
812
958
/**
813
959
 * Looks up a value for a specified default using -objectForKey:
814
 
 * and checks that it is a boolean.  Returns NO if it is not.
 
960
 * and returns its boolean representation.<br />
 
961
 * Returns NO if it is not a boolean.<br />
 
962
 * The text 'yes' or 'true' or any non zero numeric value is considered
 
963
 * to be a boolean YES.  Other string values are NO.<br />
 
964
 * NB. This differs slightly from the documented behavior for MacOS-X
 
965
 * (August 2002) in that the GNUstep version accepts the string 'TRUE'
 
966
 * as equivalent to 'YES'.
815
967
 */
816
968
- (BOOL) boolForKey: (NSString*)defaultName
817
969
{
818
 
  id    obj = [self stringForKey: defaultName];
 
970
  id    obj = [self objectForKey: defaultName];
819
971
 
820
 
  if (obj != nil)
821
 
    return [obj boolValue];
 
972
  if (obj != nil && ([obj isKindOfClass: NSStringClass]
 
973
    || [obj isKindOfClass: NSNumberClass]))
 
974
    {
 
975
      return [obj boolValue];
 
976
    }
822
977
  return NO;
823
978
}
824
979
 
844
999
  id    obj = [self objectForKey: defaultName];
845
1000
 
846
1001
  if (obj != nil && [obj isKindOfClass: NSDictionaryClass])
847
 
    return obj;
 
1002
    {
 
1003
      return obj;
 
1004
    }
848
1005
  return nil;
849
1006
}
850
1007
 
854
1011
 */
855
1012
- (float) floatForKey: (NSString*)defaultName
856
1013
{
857
 
  id    obj = [self stringForKey: defaultName];
 
1014
  id    obj = [self objectForKey: defaultName];
858
1015
 
859
 
  if (obj != nil)
860
 
    return [obj floatValue];
 
1016
  if (obj != nil && ([obj isKindOfClass: NSStringClass]
 
1017
    || [obj isKindOfClass: NSNumberClass]))
 
1018
    {
 
1019
      return [obj floatValue];
 
1020
    }
861
1021
  return 0.0;
862
1022
}
863
1023
 
867
1027
 */
868
1028
- (int) integerForKey: (NSString*)defaultName
869
1029
{
870
 
  id    obj = [self stringForKey: defaultName];
 
1030
  id    obj = [self objectForKey: defaultName];
871
1031
 
872
 
  if (obj != nil)
873
 
    return [obj intValue];
 
1032
  if (obj != nil && ([obj isKindOfClass: NSStringClass]
 
1033
    || [obj isKindOfClass: NSNumberClass]))
 
1034
    {
 
1035
      return [obj intValue];
 
1036
    }
874
1037
  return 0;
875
1038
}
876
1039
 
940
1103
      [self __changePersistentDomain: processName];
941
1104
    }
942
1105
  [_lock unlock];
943
 
  return;
944
1106
}
945
1107
 
946
1108
/**
947
 
 * Sets a boolean value for defaultName in the application domain.
948
 
 * <br />Calls -setObject:forKey: to make the change.
 
1109
 * Sets a boolean value for defaultName in the application domain.<br />
 
1110
 * The boolean value is stored as a string - either YES or NO.
 
1111
 * Calls -setObject:forKey: to make the change.
949
1112
 */
950
1113
- (void) setBool: (BOOL)value forKey: (NSString*)defaultName
951
1114
{
952
 
  id    obj = (value)?@"YES": @"NO";
 
1115
  NSNumber      *n = [NSNumberClass numberWithBool: value];
953
1116
 
954
 
  [self setObject: obj forKey: defaultName];
955
 
  return;
 
1117
  [self setObject: n forKey: defaultName];
956
1118
}
957
1119
 
958
1120
/**
961
1123
 */
962
1124
- (void) setFloat: (float)value forKey: (NSString*)defaultName
963
1125
{
964
 
  char  buf[32];
 
1126
  NSNumber      *n = [NSNumberClass numberWithFloat: value];
965
1127
 
966
 
  sprintf(buf,"%g",value);
967
 
  [self setObject: [NSStringClass stringWithCString: buf] forKey: defaultName];
968
 
  return;
 
1128
  [self setObject: n forKey: defaultName];
969
1129
}
970
1130
 
971
1131
/**
974
1134
 */
975
1135
- (void) setInteger: (int)value forKey: (NSString*)defaultName
976
1136
{
977
 
  char  buf[32];
978
 
 
979
 
  sprintf(buf,"%d",value);
980
 
  [self setObject: [NSStringClass stringWithCString: buf] forKey: defaultName];
981
 
  return;
 
1137
  NSNumber      *n = [NSNumberClass numberWithInt: value];
 
1138
 
 
1139
  [self setObject: n forKey: defaultName];
 
1140
}
 
1141
 
 
1142
static BOOL isPlistObject(id o)
 
1143
{
 
1144
  if ([o isKindOfClass: NSStringClass] == YES)
 
1145
    {
 
1146
      return YES;
 
1147
    }
 
1148
  if ([o isKindOfClass: NSDataClass] == YES)
 
1149
    {
 
1150
      return YES;
 
1151
    }
 
1152
  if ([o isKindOfClass: NSDateClass] == YES)
 
1153
    {
 
1154
      return YES;
 
1155
    }
 
1156
  if ([o isKindOfClass: NSNumberClass] == YES)
 
1157
    {
 
1158
      return YES;
 
1159
    }
 
1160
  if ([o isKindOfClass: NSArrayClass] == YES)
 
1161
    {
 
1162
      NSEnumerator      *e = [o objectEnumerator];
 
1163
      id                tmp;
 
1164
 
 
1165
      while ((tmp = [e nextObject]) != nil)
 
1166
        {
 
1167
          if (isPlistObject(tmp) == NO)
 
1168
            {
 
1169
              return NO;
 
1170
            }
 
1171
        }
 
1172
      return YES;
 
1173
    }
 
1174
  if ([o isKindOfClass: NSDictionaryClass] == YES)
 
1175
    {
 
1176
      NSEnumerator      *e = [o keyEnumerator];
 
1177
      id                tmp;
 
1178
 
 
1179
      while ((tmp = [e nextObject]) != nil)
 
1180
        {
 
1181
          if (isPlistObject(tmp) == NO)
 
1182
            {
 
1183
              return NO;
 
1184
            }
 
1185
          tmp = [o objectForKey: tmp];
 
1186
          if (isPlistObject(tmp) == NO)
 
1187
            {
 
1188
              return NO;
 
1189
            }
 
1190
        }
 
1191
      return YES;
 
1192
    }
 
1193
  return NO;
982
1194
}
983
1195
 
984
1196
/**
985
 
 * Sets an object value for defaultName in the application domain.
986
 
 * <br />Causes a NSUserDefaultsDidChangeNotification to be posted
 
1197
 * Sets an object value for defaultName in the application domain.<br />
 
1198
 * The defaultName must be a non-empty string.<br />
 
1199
 * The value must be an instance of one of the [NSString-propertyList]
 
1200
 * classes.<br />
 
1201
 * <p>Causes a NSUserDefaultsDidChangeNotification to be posted
987
1202
 * if this is the first change to a persistent-domain since the
988
1203
 * last -synchronize.
 
1204
 * </p>
 
1205
 * If value is nil, this is equivalent to the -removeObjectForKey: method.
989
1206
 */
990
1207
- (void) setObject: (id)value forKey: (NSString*)defaultName
991
1208
{
992
 
  if (value && defaultName && ([defaultName length] > 0))
993
 
    {
994
 
      NSMutableDictionary       *dict;
995
 
      id                        obj;
996
 
 
997
 
      [_lock lock];
998
 
      obj = [_persDomains objectForKey: processName];
999
 
      if ([obj isKindOfClass: NSMutableDictionaryClass] == YES)
1000
 
        {
1001
 
          dict = obj;
1002
 
        }
1003
 
      else
1004
 
        {
1005
 
          dict = [obj mutableCopy];
1006
 
          [_persDomains setObject: dict forKey: processName];
1007
 
          RELEASE(dict);
1008
 
        }
1009
 
      [dict setObject: value forKey: defaultName];
1010
 
      [self __changePersistentDomain: processName];
1011
 
      [_lock unlock];
1012
 
    }
1013
 
  return;
 
1209
  NSMutableDictionary   *dict;
 
1210
  id                    obj;
 
1211
 
 
1212
  if (value == nil)
 
1213
    {
 
1214
      [self removeObjectForKey: defaultName];
 
1215
    }
 
1216
  if ([defaultName isKindOfClass: [NSString class]] == NO
 
1217
    || [defaultName length] == 0)
 
1218
    {
 
1219
      [NSException raise: NSInvalidArgumentException
 
1220
        format: @"attempt to set object with bad key (%@)", defaultName];
 
1221
    }
 
1222
  if (isPlistObject(value) == NO)
 
1223
    {
 
1224
      [NSException raise: NSInvalidArgumentException
 
1225
        format: @"attempt to set non property list object for key (%@)",
 
1226
        defaultName];
 
1227
    }
 
1228
 
 
1229
  [_lock lock];
 
1230
  obj = [_persDomains objectForKey: processName];
 
1231
  if ([obj isKindOfClass: NSMutableDictionaryClass] == YES)
 
1232
    {
 
1233
      dict = obj;
 
1234
    }
 
1235
  else
 
1236
    {
 
1237
      dict = [obj mutableCopy];
 
1238
      [_persDomains setObject: dict forKey: processName];
 
1239
      RELEASE(dict);
 
1240
    }
 
1241
  [dict setObject: value forKey: defaultName];
 
1242
  [self __changePersistentDomain: processName];
 
1243
  [_lock unlock];
1014
1244
}
1015
1245
 
1016
1246
/**
1054
1284
/*************************************************************************
1055
1285
 *** Returning the Search List
1056
1286
 *************************************************************************/
 
1287
 
 
1288
/**
 
1289
 * Returns an array listing the domains searched in order to look up
 
1290
 * a value in the defaults system.  The order of the names in the
 
1291
 * array is the order in which the domains are searched.
 
1292
 */
1057
1293
- (NSArray*) searchList
1058
1294
{
1059
1295
  NSArray       *copy;
1064
1300
  return AUTORELEASE(copy);
1065
1301
}
1066
1302
 
 
1303
/**
 
1304
 * Sets the list of the domains searched in order to look up
 
1305
 * a value in the defaults system.  The order of the names in the
 
1306
 * array is the order in which the domains are searched.<br />
 
1307
 * On lookup, the first match is used.
 
1308
 */
1067
1309
- (void) setSearchList: (NSArray*)newList
1068
1310
{
1069
1311
  [_lock lock];
1070
1312
  DESTROY(_dictionaryRep);
 
1313
  if (self == sharedDefaults) invalidatedLanguages = YES;
1071
1314
  RELEASE(_searchList);
1072
1315
  _searchList = [newList mutableCopy];
1073
1316
  [_lock unlock];
1115
1358
      [self __changePersistentDomain: domainName];
1116
1359
    }
1117
1360
  [_lock unlock];
1118
 
  return;
1119
1361
}
1120
1362
 
1121
1363
/**
1122
 
 * Replaces the persistent-domain specified by domainname with
 
1364
 * Replaces the persistent-domain specified by domainName with
1123
1365
 * domain ... a dictionary containing keys and defaults values.
 
1366
 * <br />Raises an NSInvalidArgumentException if domainName already
 
1367
 * exists as a volatile-domain.
1124
1368
 * <br />Causes a NSUserDefaultsDidChangeNotification to be posted
1125
1369
 * if this is the first change to a persistent-domain since the
1126
1370
 * last -synchronize.
1128
1372
- (void) setPersistentDomain: (NSDictionary*)domain
1129
1373
                     forName: (NSString*)domainName
1130
1374
{
1131
 
  id    dict;
 
1375
  NSDictionary  *dict;
1132
1376
 
1133
1377
  [_lock lock];
1134
1378
  dict = [_tempDomains objectForKey: domainName];
1135
 
  if (dict)
 
1379
  if (dict != nil)
1136
1380
    {
1137
1381
      [_lock unlock];
1138
1382
      [NSException raise: NSInvalidArgumentException
1139
 
                   format: @"Persistant domain %@ already exists", domainName];
1140
 
      return;
 
1383
                  format: @"a volatile domain called %@ exists", domainName];
1141
1384
    }
1142
1385
  domain = [domain mutableCopy];
1143
1386
  [_persDomains setObject: domain forKey: domainName];
1144
1387
  RELEASE(domain);
1145
1388
  [self __changePersistentDomain: domainName];
1146
1389
  [_lock unlock];
1147
 
  return;
1148
1390
}
1149
1391
 
1150
1392
/**
1159
1401
  NSFileManager         *mgr = [NSFileManager defaultManager];
1160
1402
  NSMutableDictionary   *newDict;
1161
1403
  NSDictionary          *attr;
 
1404
  NSDate                *started = [NSDateClass date];
 
1405
  unsigned long         desired;
 
1406
  unsigned long         attributes;
 
1407
  static BOOL           isLocked = NO;
 
1408
  BOOL                  wasLocked;
1162
1409
 
1163
1410
  [_lock lock];
1164
1411
 
1165
 
  if (_tickingTimer == nil)
1166
 
    {
1167
 
      _tickingTimer = [NSTimer scheduledTimerWithTimeInterval: 30
1168
 
               target: self
1169
 
               selector: @selector(__timerTicked:)
1170
 
               userInfo: nil
1171
 
               repeats: NO];
1172
 
    }
1173
 
 
1174
1412
  /*
1175
1413
   *    If we haven't changed anything, we only need to synchronise if
1176
1414
   *    the on-disk database has been changed by someone else.
1177
1415
   */
1178
1416
  attr = [mgr fileAttributesAtPath: _defaultsDatabase
1179
1417
                      traverseLink: YES];
1180
 
  if (_changedDomains == NO)
 
1418
  if (_changedDomains == nil)
1181
1419
    {
1182
 
      BOOL              wantRead = NO;
 
1420
      BOOL      wantRead = NO;
1183
1421
 
1184
1422
      if (_lastSync == nil)
1185
1423
        {
1195
1433
            {
1196
1434
              NSDate    *mod;
1197
1435
 
 
1436
              /*
 
1437
               * If the database was modified since the last synchronisation
 
1438
               * we need to read it.
 
1439
               */
1198
1440
              mod = [attr objectForKey: NSFileModificationDate];
1199
 
              if ([_lastSync earlierDate: mod] != _lastSync)
 
1441
              if (mod != nil && [_lastSync laterDate: mod] != _lastSync)
1200
1442
                {
1201
1443
                  wantRead = YES;
1202
1444
                }
1209
1451
        }
1210
1452
    }
1211
1453
 
 
1454
  wasLocked = isLocked;
 
1455
  if (isLocked == NO && _fileLock != nil)
 
1456
    {
 
1457
      while ([_fileLock tryLock] == NO)
 
1458
        {
 
1459
          CREATE_AUTORELEASE_POOL(arp);
 
1460
          NSDate        *when;
 
1461
          NSDate        *lockDate;
 
1462
 
 
1463
          lockDate = [_fileLock lockDate];
 
1464
          when = [NSDateClass dateWithTimeIntervalSinceNow: 0.1];
 
1465
 
 
1466
          /*
 
1467
           * In case we have tried and failed to break the lock,
 
1468
           * we give up after a while ... 16 seconds should give
 
1469
           * us three lock breaks if we do them at 5 second
 
1470
           * intervals.
 
1471
           */
 
1472
          if ([when timeIntervalSinceDate: started] > 16.0)
 
1473
            {
 
1474
              NSLog(@"Failed to lock user defaults database even after "
 
1475
                @"breaking old locks!");
 
1476
              [_lock unlock];
 
1477
              return NO;
 
1478
            }
 
1479
 
 
1480
          /*
 
1481
           * If lockDate is nil, we should be able to lock again ... but we
 
1482
           * wait a little anyway ... so that in the case of a locking
 
1483
           * problem we do an idle wait rather than a busy one.
 
1484
           */
 
1485
          if (lockDate != nil && [when timeIntervalSinceDate: lockDate] > 5.0)
 
1486
            {
 
1487
              [_fileLock breakLock];
 
1488
            }
 
1489
          else
 
1490
            {
 
1491
              [NSThread sleepUntilDate: when];
 
1492
            }
 
1493
          RELEASE(arp);
 
1494
        }
 
1495
      isLocked = YES;
 
1496
    }
 
1497
 
 
1498
  /*
 
1499
   * Re-fetch database attributes in cased they changed while obtaining lock.
 
1500
   */
 
1501
  attr = [mgr fileAttributesAtPath: _defaultsDatabase
 
1502
                      traverseLink: YES];
 
1503
 
1212
1504
  DESTROY(_dictionaryRep);
 
1505
  if (self == sharedDefaults) invalidatedLanguages = YES;
1213
1506
 
1214
1507
  // Read the persistent data from the stored database
1215
 
  if (attr != nil)
 
1508
  if (attr == nil)
1216
1509
    {
1217
 
      unsigned long desired;
1218
 
      unsigned long attributes;
1219
 
 
1220
1510
      newDict = [[NSMutableDictionaryClass allocWithZone: [self zone]]
1221
 
        initWithContentsOfFile: _defaultsDatabase];
 
1511
        initWithCapacity: 1];
 
1512
      if (_fileLock != nil)
 
1513
        {
 
1514
          NSLog(@"Creating defaults database file %@", _defaultsDatabase);
 
1515
          [newDict writeToFile: _defaultsDatabase atomically: YES];
 
1516
          attr = [mgr fileAttributesAtPath: _defaultsDatabase
 
1517
                              traverseLink: YES];
 
1518
        }
 
1519
    }
 
1520
  else
 
1521
    {
 
1522
      if ([mgr isReadableFileAtPath: _defaultsDatabase] == NO)
 
1523
        {
 
1524
          newDict = nil;
 
1525
        }
 
1526
      else
 
1527
        {
 
1528
          newDict = [[NSMutableDictionaryClass allocWithZone: [self zone]]
 
1529
            initWithContentsOfFile: _defaultsDatabase];
 
1530
        }
1222
1531
      if (newDict == nil)
1223
1532
        {
1224
 
          NSLog(@"Unable to load defaults from '%@'", _defaultsDatabase);
1225
 
          [_lock unlock];
1226
 
          return NO;
 
1533
          if (_fileLock == nil)
 
1534
            {
 
1535
              /*
 
1536
               * Running with no readable user defaults ... but we were
 
1537
               * initialised that way (possibly on a read-only filesystem)
 
1538
               * so we just continue as best we can.
 
1539
               */
 
1540
              newDict = [[NSMutableDictionaryClass allocWithZone: [self zone]]
 
1541
                initWithCapacity: 4];
 
1542
            }
 
1543
          else
 
1544
            {
 
1545
              /*
 
1546
               * The defaults system has become unreadable singe we started...
 
1547
               * probably a severe error of some sort
 
1548
               */
 
1549
              NSLog(@"Unable to load defaults from '%@'", _defaultsDatabase);
 
1550
              if (wasLocked == NO)
 
1551
                {
 
1552
                  [_fileLock unlock];
 
1553
                  isLocked = NO;
 
1554
                }
 
1555
              [_lock unlock];
 
1556
              return NO;
 
1557
            }
1227
1558
        }
 
1559
    }
1228
1560
 
1229
 
      attributes = [attr filePosixPermissions];
1230
 
      // We enforce the permission mode 0600 on the defaults database
 
1561
  /*
 
1562
   * We enforce the permission mode 0600 on the defaults database
 
1563
   */
 
1564
  attributes = [attr filePosixPermissions];
1231
1565
#if     !(defined(S_IRUSR) && defined(S_IWUSR))
1232
 
      desired = 0600;
 
1566
  desired = 0600;
1233
1567
#else
1234
 
      desired = (S_IRUSR|S_IWUSR);
 
1568
  desired = (S_IRUSR|S_IWUSR);
1235
1569
#endif
1236
 
      if (attributes != desired)
1237
 
        {
1238
 
          NSMutableDictionary   *enforced_attributes;
1239
 
          NSNumber              *permissions;
1240
 
 
1241
 
          enforced_attributes = [NSMutableDictionary dictionaryWithDictionary:
1242
 
            [mgr fileAttributesAtPath: _defaultsDatabase traverseLink: YES]];
1243
 
 
1244
 
          permissions = [NSNumber numberWithUnsignedLong: desired];
1245
 
          [enforced_attributes setObject: permissions
1246
 
                                  forKey: NSFilePosixPermissions];
1247
 
 
1248
 
          [mgr changeFileAttributes: enforced_attributes
1249
 
                             atPath: _defaultsDatabase];
1250
 
        }
1251
 
    }
1252
 
  else
 
1570
  if (attributes != desired)
1253
1571
    {
1254
 
      unsigned long     desired;
1255
 
      NSNumber          *permissions;
1256
 
 
1257
 
      // We enforce the permission mode 0600 on the defaults database
1258
 
#if     !(defined(S_IRUSR) && defined(S_IWUSR))
1259
 
      desired = 0600;
1260
 
#else
1261
 
      desired = (S_IRUSR|S_IWUSR);
1262
 
#endif
1263
 
      permissions = [NSNumber numberWithUnsignedLong: desired];
1264
 
      attr = [NSDictionary dictionaryWithObjectsAndKeys:
1265
 
        NSUserName(), NSFileOwnerAccountName,
1266
 
        permissions, NSFilePosixPermissions,
1267
 
        nil];
1268
 
      NSLog(@"Creating defaults database file %@", _defaultsDatabase);
1269
 
      [mgr createFileAtPath: _defaultsDatabase
1270
 
                   contents: nil
1271
 
                 attributes: attr];
1272
 
      newDict = [[NSMutableDictionaryClass allocWithZone: [self zone]]
1273
 
                  initWithCapacity: 1];
1274
 
      [newDict writeToFile: _defaultsDatabase atomically: YES];
 
1572
      NSMutableDictionary       *enforced_attributes;
 
1573
      NSNumber                  *permissions;
 
1574
 
 
1575
      enforced_attributes = [NSMutableDictionary dictionaryWithDictionary:
 
1576
        [mgr fileAttributesAtPath: _defaultsDatabase traverseLink: YES]];
 
1577
 
 
1578
      permissions = [NSNumberClass numberWithUnsignedLong: desired];
 
1579
      [enforced_attributes setObject: permissions
 
1580
                              forKey: NSFilePosixPermissions];
 
1581
 
 
1582
      [mgr changeFileAttributes: enforced_attributes
 
1583
                         atPath: _defaultsDatabase];
1275
1584
    }
1276
1585
 
1277
 
  if (_changedDomains)
 
1586
  if (_changedDomains != nil)
1278
1587
    {           // Synchronize both dictionaries
1279
1588
      NSEnumerator      *enumerator = [_changedDomains objectEnumerator];
1280
 
      IMP               nextImp;
1281
 
      IMP               pImp;
1282
 
      id                obj, dict;
 
1589
      NSString          *domainName;
 
1590
      NSDictionary      *domain;
1283
1591
 
1284
 
      nextImp = [enumerator methodForSelector: nextObjectSel];
1285
 
      pImp = [_persDomains methodForSelector: objectForKeySel];
1286
 
      while ((obj = (*nextImp)(enumerator, nextObjectSel)) != nil)
 
1592
      DESTROY(_changedDomains); // Retained by enumerator.
 
1593
      while ((domainName = [enumerator nextObject]) != nil)
1287
1594
        {
1288
 
          dict = (*pImp)(_persDomains, objectForKeySel, obj);
1289
 
          if (dict)       // Domain was added or changed
 
1595
          domain = [_persDomains objectForKey: domainName];
 
1596
          if (domain != nil)    // Domain was added or changed
1290
1597
            {
1291
 
              [newDict setObject: dict forKey: obj];
 
1598
              [newDict setObject: domain forKey: domainName];
1292
1599
            }
1293
 
          else            // Domain was removed
 
1600
          else                  // Domain was removed
1294
1601
            {
1295
 
              [newDict removeObjectForKey: obj];
 
1602
              [newDict removeObjectForKey: domainName];
1296
1603
            }
1297
1604
        }
1298
1605
      RELEASE(_persDomains);
1299
1606
      _persDomains = newDict;
1300
 
      // Save the changes
1301
 
      if (![_persDomains writeToFile: _defaultsDatabase atomically: YES])
 
1607
      // Save the changes unless we are in read-only mode.
 
1608
      if (_fileLock != nil)
1302
1609
        {
1303
 
          [_lock unlock];
1304
 
          return NO;
 
1610
          if (![_persDomains writeToFile: _defaultsDatabase atomically: YES])
 
1611
            {
 
1612
              if (wasLocked == NO)
 
1613
                {
 
1614
                  [_fileLock unlock];
 
1615
                  isLocked = NO;
 
1616
                }
 
1617
              [_lock unlock];
 
1618
              return NO;
 
1619
            }
1305
1620
        }
1306
 
      ASSIGN(_lastSync, [NSDate date]);
 
1621
      ASSIGN(_lastSync, [NSDateClass date]);
1307
1622
    }
1308
1623
  else
1309
1624
    {
1310
 
      ASSIGN(_lastSync, [NSDate date]);
 
1625
      ASSIGN(_lastSync, [NSDateClass date]);
1311
1626
      if ([_persDomains isEqual: newDict] == NO)
1312
1627
        {
1313
1628
          RELEASE(_persDomains);
1323
1638
        }
1324
1639
    }
1325
1640
 
 
1641
  if (wasLocked == NO)
 
1642
    {
 
1643
      [_fileLock unlock];
 
1644
      isLocked = NO;
 
1645
    }
1326
1646
  [_lock unlock];
1327
1647
  return YES;
1328
1648
}
1329
1649
 
1330
1650
 
1331
 
/*************************************************************************
1332
 
 *** Maintaining Volatile Domains
1333
 
 *************************************************************************/
 
1651
/**
 
1652
 * Removes the volatile domain specified by domainName from the
 
1653
 * user defaults.
 
1654
 */
1334
1655
- (void) removeVolatileDomainForName: (NSString*)domainName
1335
1656
{
1336
1657
  [_lock lock];
1337
1658
  DESTROY(_dictionaryRep);
 
1659
  if (self == sharedDefaults) invalidatedLanguages = YES;
1338
1660
  [_tempDomains removeObjectForKey: domainName];
1339
1661
  [_lock unlock];
1340
1662
}
1341
1663
 
 
1664
/**
 
1665
 * Sets the volatile-domain specified by domainName to
 
1666
 * domain ... a dictionary containing keys and defaults values.<br />
 
1667
 * Raises an NSInvalidArgumentException if domainName already
 
1668
 * exists as either a volatile-domain or a persistent-domain.
 
1669
 */
1342
1670
- (void) setVolatileDomain: (NSDictionary*)domain
1343
1671
                   forName: (NSString*)domainName
1344
1672
{
1346
1674
 
1347
1675
  [_lock lock];
1348
1676
  dict = [_persDomains objectForKey: domainName];
1349
 
  if (dict)
1350
 
    {
1351
 
      [_lock unlock];
1352
 
      [NSException raise: NSInvalidArgumentException
1353
 
                  format: @"Volatile domain %@ already exists", domainName];
1354
 
      return;
1355
 
    }
 
1677
  if (dict != nil)
 
1678
    {
 
1679
      [_lock unlock];
 
1680
      [NSException raise: NSInvalidArgumentException
 
1681
                  format: @"a persistent domain called %@ exists", domainName];
 
1682
    }
 
1683
  dict = [_tempDomains objectForKey: domainName];
 
1684
  if (dict != nil)
 
1685
    {
 
1686
      [_lock unlock];
 
1687
      [NSException raise: NSInvalidArgumentException
 
1688
                  format: @"the volatile domain %@ already exists", domainName];
 
1689
    }
 
1690
 
1356
1691
  DESTROY(_dictionaryRep);
 
1692
  if (self == sharedDefaults) invalidatedLanguages = YES;
1357
1693
  domain = [domain mutableCopy];
1358
1694
  [_tempDomains setObject: domain forKey: domainName];
1359
1695
  RELEASE(domain);
1360
1696
  [_lock unlock];
1361
 
  return;
1362
1697
}
1363
1698
 
 
1699
/**
 
1700
 * Returns the volatile domain specified by domainName.
 
1701
 */
1364
1702
- (NSDictionary*) volatileDomainForName: (NSString*)domainName
1365
1703
{
1366
1704
  NSDictionary  *copy;
1417
1755
 
1418
1756
      while ((obj = (*nImp)(enumerator, nextObjectSel)) != nil)
1419
1757
        {
1420
 
          if ( (dict = (*pImp)(_persDomains, objectForKeySel, obj)) != nil
 
1758
          if ((dict = (*pImp)(_persDomains, objectForKeySel, obj)) != nil
1421
1759
            || (dict = (*tImp)(_tempDomains, objectForKeySel, obj)) != nil)
1422
1760
            (*addImp)(dictRep, addSel, dict);
1423
1761
        }
1449
1787
      [_tempDomains setObject: regDefs forKey: NSRegistrationDomain];
1450
1788
    }
1451
1789
  DESTROY(_dictionaryRep);
 
1790
  if (self == sharedDefaults) invalidatedLanguages = YES;
1452
1791
  [regDefs addEntriesFromDictionary: newVals];
1453
1792
  [_lock unlock];
1454
1793
}
1455
1794
 
 
1795
/**
 
1796
 * Removes the named domain from the serach list of the receiver.<br />
 
1797
 * Suites may be added using the -addSuiteNamed: method.
 
1798
 */
 
1799
- (void) removeSuiteNamed: (NSString*)aName
 
1800
{
 
1801
  if (aName == nil)
 
1802
    {
 
1803
      [NSException raise: NSInvalidArgumentException
 
1804
                  format: @"attempt to remove suite with nil name"];
 
1805
    }
 
1806
  [_lock lock];
 
1807
  DESTROY(_dictionaryRep);
 
1808
  if (self == sharedDefaults) invalidatedLanguages = YES;
 
1809
  [_searchList removeObject: aName];
 
1810
  [_lock unlock];
 
1811
}
 
1812
 
1456
1813
/*************************************************************************
1457
1814
 *** Accessing the User Defaults database
1458
1815
 *************************************************************************/
1459
 
- (void) __createStandardSearchList
1460
 
{
1461
 
  NSArray       *uL;
1462
 
  NSEnumerator  *enumerator;
1463
 
  id            object;
1464
 
 
1465
 
  [_lock lock];
1466
 
  // Note: The search list should exist!
1467
 
 
1468
 
  // 1. NSArgumentDomain
1469
 
  [_searchList addObject: NSArgumentDomain];
1470
 
 
1471
 
  // 2. Application
1472
 
  [_searchList addObject: processName];
1473
 
 
1474
 
  // 3. NSGlobalDomain
1475
 
  [_searchList addObject: NSGlobalDomain];
1476
 
 
1477
 
  // 4. User's preferred languages
1478
 
  uL = [[self class] userLanguages];
1479
 
  enumerator = [uL objectEnumerator];
1480
 
  while ((object = [enumerator nextObject]))
1481
 
    {
1482
 
      [_searchList addObject: object];
1483
 
    }
1484
 
 
1485
 
  // 5. NSRegistrationDomain
1486
 
  [_searchList addObject: NSRegistrationDomain];
1487
 
 
1488
 
  [_lock unlock];
1489
 
  return;
1490
 
}
1491
1816
 
1492
1817
- (NSDictionary*) __createArgumentDictionary
1493
1818
{
1506
1831
 
1507
1832
  while (!done)
1508
1833
    {
1509
 
      if ([key hasPrefix: @"-"])
 
1834
      if ([key hasPrefix: @"-"] == YES && [key isEqual: @"-"] == NO)
1510
1835
        {
1511
1836
          NSString      *old = nil;
1512
1837
 
1529
1854
              done = YES;
1530
1855
              continue;
1531
1856
            }
1532
 
          else if ([val hasPrefix: @"-"] == YES)
 
1857
          else if ([val hasPrefix: @"-"] == YES && [val isEqual: @"-"] == NO)
1533
1858
            {  // Yet another argument
1534
1859
              [argDict setObject: @"" forKey: key];             // arg is empty.
1535
1860
              if (old != nil)
1580
1905
 
1581
1906
- (void) __changePersistentDomain: (NSString*)domainName
1582
1907
{
1583
 
  NSEnumerator  *enumerator = nil;
1584
 
  IMP           nImp;
1585
 
  id            obj;
1586
 
 
1587
1908
  [_lock lock];
1588
1909
  DESTROY(_dictionaryRep);
1589
 
  if (!_changedDomains)
 
1910
  if (self == sharedDefaults) invalidatedLanguages = YES;
 
1911
  if (_changedDomains == nil)
1590
1912
    {
1591
 
      _changedDomains = [[NSMutableArray alloc] initWithCapacity: 5];
 
1913
      _changedDomains = [[NSMutableArray alloc] initWithObjects: &domainName
 
1914
                                                          count: 1];
1592
1915
      updateCache(self);
1593
1916
      [[NSNotificationCenter defaultCenter]
1594
1917
        postNotificationName: NSUserDefaultsDidChangeNotification
1595
1918
                      object: self];
1596
1919
    }
1597
 
 
1598
 
  enumerator = [_changedDomains objectEnumerator];
1599
 
  nImp = [enumerator methodForSelector: nextObjectSel];
1600
 
  while ((obj = (*nImp)(enumerator, nextObjectSel)) != nil)
 
1920
  else if ([_changedDomains containsObject: domainName] == NO)
1601
1921
    {
1602
 
      if ([obj isEqualToString: domainName])
1603
 
        {
1604
 
          [_lock unlock];
1605
 
          return;
1606
 
        }
 
1922
      [_changedDomains addObject: domainName];
1607
1923
    }
1608
 
  [_changedDomains addObject: domainName];
1609
1924
  [_lock unlock];
1610
 
  return;
1611
 
}
1612
 
 
1613
 
- (void) __timerTicked: (NSTimer*)tim
1614
 
{
1615
 
  if (tim == _tickingTimer)
1616
 
    _tickingTimer = nil;
1617
 
 
1618
 
  [self synchronize];
1619
1925
}
1620
1926
@end
1621
1927