~ubuntu-branches/ubuntu/trusty/gnustep-base/trusty

« back to all changes in this revision

Viewing changes to Source/NSPathUtilities.m

Tags: upstream-1.11.2
ImportĀ upstreamĀ versionĀ 1.11.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Implementation of filesystem & path-related functions for GNUstep
 
2
   Copyright (C) 1996-2004 Free Software Foundation, Inc.
 
3
 
 
4
   Written by:  Andrew Kachites McCallum <address@hidden>
 
5
   Created: May 1996
 
6
   Rewrite by:  Sheldon Gill
 
7
   Date:    Jan 2004
 
8
   Rewrites by:  Richard Frith-Macdonald
 
9
   Date:    2004-2005
 
10
 
 
11
   This file is part of the GNUstep Base Library.
 
12
 
 
13
   This library is free software; you can redistribute it and/or
 
14
   modify it under the terms of the GNU Library General Public
 
15
   License as published by the Free Software Foundation; either
 
16
   version 2 of the License, or (at your option) any later version.
 
17
 
 
18
   This library is distributed in the hope that it will be useful,
 
19
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
20
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
21
   Library General Public License for more details.
 
22
 
 
23
   You should have received a copy of the GNU Library General Public
 
24
   License along with this library; if not, write to the Free
 
25
   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
 
26
 
 
27
   <title>NSPathUtilities function reference</title>
 
28
   $Date: 2005/12/07 08:02:48 $ $Revision: 1.59 $
 
29
   */
 
30
 
 
31
/**
 
32
   <unit>
 
33
   <heading>Path Utility Functions</heading>
 
34
   <p>
 
35
   Path utilities provides functions to dynamically discover paths
 
36
   for the platform the application is running on.
 
37
   This avoids the need for hard coding paths, making porting easier
 
38
   and also allowing for places to change without breaking
 
39
   applications.
 
40
   (why do this? Well imagine we're running GNUstep 1 and the new
 
41
   wonderful GNUstep 2 becomes available but we're not sure of it
 
42
   yet. You could install /GNUstep/System2/ and have applications
 
43
   use which ever System you wanted at the time...)
 
44
   </p>
 
45
   <p>
 
46
   On unix systems, the paths are initialised by reading a configuration
 
47
   file. Something like "/etc/GNUstep/GNUstep.conf". This provides the basic
 
48
   information required by the library to establish all locations required.
 
49
   </p>
 
50
   <p>
 
51
   See <REF "filesystem.pdf">GNUstep File System Hierarchy</REF> document
 
52
   for more information and detailed descriptions.</p>
 
53
   </unit>
 
54
*/
 
55
 
 
56
#include "config.h"
 
57
#include "GNUstepBase/preface.h"
 
58
#include "objc-load.h"
 
59
#include "Foundation/NSObjCRuntime.h"
 
60
#include "Foundation/NSString.h"
 
61
#include "Foundation/NSPathUtilities.h"
 
62
#include "Foundation/NSException.h"
 
63
#include "Foundation/NSArray.h"
 
64
#include "Foundation/NSDebug.h"
 
65
#include "Foundation/NSDictionary.h"
 
66
#include "Foundation/NSFileManager.h"
 
67
#include "Foundation/NSProcessInfo.h"
 
68
#include "Foundation/NSString.h"
 
69
#include "Foundation/NSValue.h"
 
70
#include "Foundation/NSLock.h"
 
71
#include "Foundation/NSUserDefaults.h"
 
72
#include "GNUstepBase/GSCategories.h"
 
73
 
 
74
#include "GSPrivate.h"
 
75
 
 
76
#ifdef HAVE_UNISTD_H
 
77
#include <unistd.h>             // for getuid()
 
78
#endif
 
79
#ifdef  HAVE_PWD_H
 
80
#include <pwd.h>                // for getpwnam()
 
81
#endif
 
82
#include <sys/types.h>
 
83
#include <stdio.h>
 
84
 
 
85
/* The global configuration file. The real value is read from config.h */
 
86
#ifndef GNUSTEP_CONFIG_FILE
 
87
# define   GNUSTEP_CONFIG_FILE  /etc/GNUstep/GNUstep.conf
 
88
#endif
 
89
 
 
90
static NSString *gnustep_target_cpu =
 
91
#ifdef GNUSTEP_TARGET_CPU
 
92
  @GNUSTEP_TARGET_CPU;
 
93
#else
 
94
  nil;
 
95
#endif
 
96
static NSString *gnustep_target_os =
 
97
#ifdef GNUSTEP_TARGET_OS
 
98
  @GNUSTEP_TARGET_OS;
 
99
#else
 
100
  nil;
 
101
#endif
 
102
static NSString *library_combo =
 
103
#ifdef LIBRARY_COMBO
 
104
  @LIBRARY_COMBO;
 
105
#else
 
106
  nil;
 
107
#endif
 
108
static NSString *gnustep_flattened =
 
109
#ifdef GNUSTEP_FLATTENED
 
110
  @GNUSTEP_FLATTENED;
 
111
#else
 
112
  nil;
 
113
#endif
 
114
 
 
115
#if     defined(__WIN32__)
 
116
/*
 
117
 * FIXME ... should check access properly if the file is on an NTFS volume.
 
118
 */
 
119
#define ATTRMASK        0700
 
120
#else
 
121
#define ATTRMASK        0777
 
122
#endif
 
123
 
 
124
#define MGR()   [NSFileManager defaultManager]
 
125
 
 
126
/* ------------------ */
 
127
/* Internal variables */
 
128
/* ------------------ */
 
129
 
 
130
static NSString *gnustepConfigPath = nil;
 
131
 
 
132
/* We read these four paths only once */
 
133
static NSString *gnustepUserRoot = nil;        /*    GNUSTEP_USER_ROOT path */
 
134
static NSString *gnustepLocalRoot = nil;       /*   GNUSTEP_LOCAL_ROOT path */
 
135
static NSString *gnustepNetworkRoot = nil;     /* GNUSTEP_NETWORK_ROOT path */
 
136
static NSString *gnustepSystemRoot = nil;      /*  GNUSTEP_SYSTEM_ROOT path */
 
137
 
 
138
static NSString *gnustepUserDir = nil;
 
139
static NSString *gnustepUserHome = nil;
 
140
static NSString *gnustepUserDefaultsDir = nil;
 
141
 
 
142
static NSString *theUserName = nil;             /*      The user's login name */
 
143
static NSString *tempDir = nil;                 /* user's temporary directory */
 
144
 
 
145
static NSString *osSysApps  = nil;
 
146
static NSString *osSysLibs  = nil;
 
147
static NSString *osSysAdmin = nil;
 
148
 
 
149
static NSString *platformResources = nil;
 
150
static NSString *platformApps  = nil;
 
151
static NSString *platformLibs  = nil;
 
152
static NSString *platformAdmin = nil;
 
153
 
 
154
static NSString *localResources = nil;
 
155
static NSString *localApps  = nil;
 
156
static NSString *localLibs  = nil;
 
157
 
 
158
/* ============================= */
 
159
/* Internal function prototypes. */
 
160
/* ============================= */
 
161
 
 
162
static NSMutableDictionary* GNUstepConfig(NSDictionary *newConfig);
 
163
 
 
164
static void UserConfig(NSMutableDictionary *config, NSString *userName);
 
165
 
 
166
static BOOL ParseConfigurationFile(NSString *name, NSMutableDictionary *dict);
 
167
 
 
168
static void InitialisePathUtilities(void);
 
169
static void ShutdownPathUtilities(void);
 
170
 
 
171
/* Conditionally assign an object from a dictionary to var
 
172
 * We don't need to retain val before releasing var, because we
 
173
 * can be sure that if var is val it is retained by the dictionary
 
174
 * as well as being retained when it was first placed in var.
 
175
 */
 
176
#define ASSIGN_IF_SET(var, dictionary, key) ({\
 
177
  id val = [dictionary objectForKey: key];\
 
178
  if (val != nil)\
 
179
    {\
 
180
      RELEASE(var);\
 
181
      var = RETAIN(val);\
 
182
      [dictionary removeObjectForKey: key];\
 
183
    }\
 
184
})
 
185
 
 
186
#define ASSIGN_PATH(var, dictionary, key) ({\
 
187
  id val = getPathConfig(dictionary, key);\
 
188
  if (val != nil)\
 
189
    {\
 
190
      RELEASE(var);\
 
191
      var = RETAIN(val);\
 
192
      [dictionary removeObjectForKey: key];\
 
193
    }\
 
194
})
 
195
 
 
196
/* Conditionally assign lval to var only if var is nil */
 
197
#define TEST_ASSIGN(var, lval) ({\
 
198
  if ((var == nil)&&(lval != nil))\
 
199
    {\
 
200
      var = RETAIN(lval);\
 
201
    }\
 
202
  })
 
203
 
 
204
/* Get a full path string */
 
205
static inline NSString *
 
206
getPath(NSString *path)
 
207
{
 
208
  if ([path hasPrefix: @"./"] == YES)
 
209
    {
 
210
      path = [gnustepConfigPath stringByAppendingPathComponent:
 
211
        [path substringFromIndex: 2]];
 
212
      path = [path stringByStandardizingPath];
 
213
    }
 
214
  return path;
 
215
}
 
216
 
 
217
/* Get a full path string from a dictionary */
 
218
static inline NSString *
 
219
getPathConfig(NSDictionary *dict, NSString *key)
 
220
{
 
221
  NSString      *path;
 
222
 
 
223
  path = [dict objectForKey: key];
 
224
  if (path != nil)
 
225
    {
 
226
      path = getPath(path);
 
227
      if ([path isAbsolutePath] == NO)
 
228
        {
 
229
          NSLog(@"GNUstep configuration file entry '%@' ('%@') is not "
 
230
            @"an absolute path.  Please fix your configuration file",
 
231
            key, [dict objectForKey: key]);
 
232
#if     defined(__MINGW32_)
 
233
          if ([path length] > 2)
 
234
            {
 
235
              unichar   buf[3];
 
236
 
 
237
              [path getCharacters: buf range: NSMakeRange(0, 3)];
 
238
              if ((buf[0] == '/' || bug[0] == '\\') && isalpha(buf[1])
 
239
                && (buf[2] == '/' || bug[2] == '\\'))
 
240
                {
 
241
                  path = [NSString stringWithFormat: @"%c:%@", (char)buf[1],
 
242
                    [path substringFromindex: 2]];
 
243
                  path = [path stringByReplacingString: @"/"
 
244
                                            withString: @"\\"];
 
245
                  NSLog(@"I am guessing that you meant '%@'", path);
 
246
                }
 
247
            }
 
248
#endif
 
249
        }
 
250
    }
 
251
  return path;
 
252
}
 
253
 
 
254
static void ExtractValuesFromConfig(NSDictionary *config)
 
255
{
 
256
  NSMutableDictionary   *c = [config mutableCopy];
 
257
 
 
258
  /*
 
259
   * Move values out of the dictionary and into variables for rapid reference.
 
260
   */
 
261
  ASSIGN_PATH(gnustepSystemRoot, c, @"GNUSTEP_SYSTEM_ROOT");
 
262
  ASSIGN_PATH(gnustepNetworkRoot, c, @"GNUSTEP_NETWORK_ROOT");
 
263
  ASSIGN_PATH(gnustepLocalRoot, c, @"GNUSTEP_LOCAL_ROOT");
 
264
 
 
265
  ASSIGN_IF_SET(gnustepUserDir, c, @"GNUSTEP_USER_DIR");
 
266
  ASSIGN_IF_SET(gnustepUserDefaultsDir, c, @"GNUSTEP_USER_DEFAULTS_DIR");
 
267
 
 
268
  ASSIGN_PATH(osSysApps, c, @"GNUSTEP_SYS_APPS");
 
269
  ASSIGN_PATH(osSysLibs, c, @"GNUSTEP_SYS_LIBS");
 
270
  ASSIGN_PATH(osSysAdmin, c, @"GNUSTEP_SYS_ADMIN");
 
271
 
 
272
  ASSIGN_PATH(platformResources, c, @"GNUSTEP_PLATFORM_RESOURCES");
 
273
  ASSIGN_PATH(platformApps, c, @"GNUSTEP_PLATFORM_APPS");
 
274
  ASSIGN_PATH(platformLibs, c, @"GNUSTEP_PLATFORM_LIBS");
 
275
  ASSIGN_PATH(platformAdmin, c, @"GNUSTEP_PLATFORM_ADMIN");
 
276
 
 
277
  ASSIGN_PATH(localResources, c, @"GNUSTEP_PLATFORM_LOCAL_RESOURCES");
 
278
  ASSIGN_PATH(localApps, c, @"GNUSTEP_PLATFORM_LOCAL_APPS");
 
279
  ASSIGN_PATH(localLibs, c, @"GNUSTEP_PLATFORM_LOCAL_LIBS");
 
280
 
 
281
  /*
 
282
   * Remove any other dictionary entries we have used.
 
283
   */
 
284
  [c removeObjectForKey: @"GNUSTEP_USER_CONFIG_FILE"];
 
285
 
 
286
  if ([c count] > 0)
 
287
    {
 
288
      /*
 
289
       * The dictionary should be empty ... report problems
 
290
       */
 
291
      fprintf(stderr, "Configuration contains unknown keys - %s\n",
 
292
        [[[c allKeys] description] UTF8String]);
 
293
    }
 
294
  DESTROY(c);
 
295
 
 
296
  /*
 
297
   * Set default locations for user files if necessary.
 
298
   */
 
299
  if (gnustepUserDir == nil)
 
300
    {
 
301
      ASSIGN(gnustepUserDir, @GNUSTEP_TARGET_USER_DIR);
 
302
    }
 
303
  if (gnustepUserDefaultsDir == nil)
 
304
    {
 
305
      ASSIGN(gnustepUserDefaultsDir, @GNUSTEP_TARGET_USER_DEFAULTS_DIR);
 
306
    }
 
307
  /*
 
308
   * Set the user root from the user home and the user dir
 
309
   */
 
310
  ASSIGN(gnustepUserRoot,
 
311
    [gnustepUserHome stringByAppendingPathComponent: gnustepUserDir]);
 
312
 
 
313
  /*
 
314
   * Try to ensure that essential user directories exist.
 
315
   * FIXME  ... Check/creation should perhaps be configurable.
 
316
   */
 
317
  if (1)
 
318
    {
 
319
      NSFileManager     *manager;
 
320
      NSString          *path;
 
321
      NSDictionary      *attr;
 
322
      BOOL              flag;
 
323
 
 
324
      manager = [NSFileManager defaultManager];
 
325
      attr = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: 0750]
 
326
                                         forKey: NSFilePosixPermissions];
 
327
 
 
328
      // make sure user root exists.
 
329
      path = gnustepUserRoot;
 
330
      if ([manager fileExistsAtPath: path isDirectory: &flag] == NO
 
331
        || flag == NO)
 
332
        {
 
333
          [manager createDirectoryAtPath: path attributes: attr];
 
334
        }
 
335
 
 
336
      // make sure library directory exists (to store resources).
 
337
      path = [path stringByAppendingPathComponent: @"Library"];
 
338
      if ([manager fileExistsAtPath: path isDirectory: &flag] == NO
 
339
        || flag == NO)
 
340
        {
 
341
          [manager createDirectoryAtPath: path attributes: attr];
 
342
        }
 
343
    }
 
344
 
 
345
  /*
 
346
   * Finally set default locations for the essential paths if required.
 
347
   */
 
348
  if (gnustepSystemRoot == nil)
 
349
    {
 
350
      gnustepSystemRoot = @GNUSTEP_TARGET_SYSTEM_ROOT;
 
351
      gnustepSystemRoot = RETAIN(getPath(gnustepSystemRoot));
 
352
    }
 
353
  if (gnustepNetworkRoot == nil)
 
354
    {
 
355
      gnustepNetworkRoot = @GNUSTEP_TARGET_NETWORK_ROOT;
 
356
      gnustepNetworkRoot = RETAIN(getPath(gnustepNetworkRoot));
 
357
    }
 
358
  if (gnustepLocalRoot == nil)
 
359
    {
 
360
      gnustepLocalRoot = @GNUSTEP_TARGET_LOCAL_ROOT;
 
361
      gnustepLocalRoot = RETAIN(getPath(gnustepLocalRoot));
 
362
    }
 
363
}
 
364
 
 
365
static NSMutableDictionary*
 
366
GNUstepConfig(NSDictionary *newConfig)
 
367
{
 
368
  static NSDictionary   *config = nil;
 
369
  NSMutableDictionary   *conf = nil;
 
370
  BOOL                  changedSystemConfig = NO;
 
371
 
 
372
  [gnustep_global_lock lock];
 
373
  if (config == nil || (newConfig != nil && [config isEqual: newConfig] == NO))
 
374
    {
 
375
      NS_DURING
 
376
        {
 
377
          if (newConfig == nil)
 
378
            {
 
379
              NSString  *file = nil;
 
380
              BOOL      fromEnvironment = YES;
 
381
              BOOL      bareDirectory = NO;
 
382
 
 
383
              conf = [[NSMutableDictionary alloc] initWithCapacity: 32];
 
384
 
 
385
              /* Now we source the configuration file if it exists */
 
386
#if     !OPTION_NO_ENVIRONMENT
 
387
              file = [[[NSProcessInfo processInfo] environment]
 
388
                objectForKey: @"GNUSTEP_CONFIG_FILE"];
 
389
#endif
 
390
              if (file == nil)
 
391
                {
 
392
                  fromEnvironment = NO;
 
393
                  file = [NSString stringWithCString:
 
394
                    STRINGIFY(GNUSTEP_CONFIG_FILE)];
 
395
                }
 
396
 
 
397
              /*
 
398
               * Is the file missing from the path ... if so we won't
 
399
               * be reading it.
 
400
               */
 
401
              if ([file hasSuffix: @"/"] || [file hasSuffix: @"\\"])
 
402
                {
 
403
                  bareDirectory = YES;
 
404
                }
 
405
 
 
406
              /*
 
407
               * Special case ... if the config file location begins './'
 
408
               * then we determine it's actual path by working relative
 
409
               * to the gnustep-base library.
 
410
               */
 
411
              if ([file hasPrefix: @"./"] == YES)
 
412
                {
 
413
                  Class         c = [NSProcessInfo class];
 
414
                  NSString      *path = objc_get_symbol_path (c, 0);
 
415
 
 
416
                  // Remove library name from path
 
417
                  path = [path stringByDeletingLastPathComponent];
 
418
                  // Remove ./ prefix from filename
 
419
                  file = [file substringFromIndex: 2];
 
420
                  // Join the two together
 
421
                  file = [path stringByAppendingPathComponent: file];
 
422
                }
 
423
              file = [file stringByStandardizingPath];
 
424
 
 
425
              if ([file isAbsolutePath] == NO)
 
426
                {
 
427
                  if (fromEnvironment ==  YES)
 
428
                    {
 
429
                      NSLog(@"GNUSTEP_CONFIG_FILE value ('%@') is not "
 
430
                        @"an absolute path.  Please fix the environment "
 
431
                        @"variable.", file);
 
432
                    }
 
433
                  else
 
434
                    {
 
435
                      NSLog(@"GNUSTEP_CONFIG_FILE value ('%@') is not "
 
436
                        @"an absolute path.  Please rebuild GNUstep-base "
 
437
                        @"specifying a valid path to the config file.", file);
 
438
                    }
 
439
#if     defined(__MINGW32_)
 
440
                  if ([file length] > 2)
 
441
                    {
 
442
                      unichar   buf[3];
 
443
 
 
444
                      [file getCharacters: buf range: NSMakeRange(0, 3)];
 
445
                      if ((buf[0] == '/' || bug[0] == '\\') && isalpha(buf[1])
 
446
                        && (buf[2] == '/' || bug[2] == '\\'))
 
447
                        {
 
448
                          file = [NSString stringWithFormat: @"%c:%@",
 
449
                            (char)buf[1], [file substringFromindex: 2]];
 
450
                          file = [file stringByReplacingString: @"/"
 
451
                                                    withString: @"\\"];
 
452
                          NSLog(@"I am guessing that you meant '%@'", file);
 
453
                        }
 
454
                    }
 
455
#endif
 
456
                }
 
457
              if (bareDirectory == YES)
 
458
                {
 
459
                  gnustepConfigPath = RETAIN(file);
 
460
                }
 
461
              else
 
462
                {
 
463
                  gnustepConfigPath
 
464
                    = RETAIN([file stringByDeletingLastPathComponent]);
 
465
                  ParseConfigurationFile(file, conf);
 
466
                }
 
467
            }
 
468
          else
 
469
            {
 
470
              conf = [newConfig mutableCopy];
 
471
            }
 
472
          /* System admins may force the user and defaults paths by
 
473
           * setting GNUSTEP_USER_CONFIG_FILE to be an empty string.
 
474
           * If they simply don't define it at all, we assign a default.
 
475
           */
 
476
          if ([conf objectForKey: @"GNUSTEP_USER_CONFIG_FILE"] == nil)
 
477
            {
 
478
              NSString  *tmp;
 
479
 
 
480
              tmp = [NSString stringWithCString:\
 
481
                STRINGIFY(GNUSTEP_USER_CONFIG_FILE)];
 
482
              [conf setObject: tmp forKey: @"GNUSTEP_USER_CONFIG_FILE"];
 
483
            }
 
484
          if (config != nil)
 
485
            {
 
486
              changedSystemConfig = YES;
 
487
            }
 
488
          config = [conf copy];
 
489
          DESTROY(conf);
 
490
        }
 
491
      NS_HANDLER
 
492
        {
 
493
          [gnustep_global_lock unlock];
 
494
          config = nil;
 
495
          DESTROY(conf);
 
496
          [localException raise];
 
497
        }
 
498
      NS_ENDHANDLER
 
499
    }
 
500
  [gnustep_global_lock unlock];
 
501
 
 
502
  if (changedSystemConfig == YES)
 
503
    {
 
504
      /*
 
505
       * The main configuration was changed by passing in a dictionary to
 
506
       * this function, so we need to reset the path utilities system to use
 
507
       * any new values from the config.
 
508
       */
 
509
      ShutdownPathUtilities();
 
510
      InitialisePathUtilities();
 
511
    }
 
512
 
 
513
  return AUTORELEASE([config mutableCopy]);
 
514
}
 
515
 
 
516
static void
 
517
UserConfig(NSMutableDictionary *config, NSString *userName)
 
518
{
 
519
#ifdef HAVE_GETEUID
 
520
  if (userName != nil)
 
521
    {
 
522
      /*
 
523
       * A program which is running setuid cannot be trusted
 
524
       * to pick up user specific config, so we clear the userName
 
525
       * to force the system configuration to be returned rather
 
526
       * than a per-user config.
 
527
       */
 
528
      if (getuid() != geteuid())
 
529
        {
 
530
          userName = nil;
 
531
        }
 
532
    }
 
533
#endif
 
534
 
 
535
  if (userName != nil)
 
536
    {
 
537
      NSString          *file;
 
538
      NSString          *home;
 
539
      NSString          *path;
 
540
 
 
541
      file = RETAIN([config objectForKey: @"GNUSTEP_USER_CONFIG_FILE"]);
 
542
      if ([file length] > 0)
 
543
        {
 
544
          home = NSHomeDirectoryForUser(userName);
 
545
          path = [home stringByAppendingPathComponent: file];
 
546
          ParseConfigurationFile(path, config);
 
547
        }
 
548
      /*
 
549
       * We don't let the user config file override the GNUSTEP_USER_CONFIG_FILE
 
550
       * variable ... that would be silly/pointless.
 
551
       */
 
552
      [config setObject: file forKey: @"GNUSTEP_USER_CONFIG_FILE"];
 
553
      RELEASE(file);
 
554
    }
 
555
}
 
556
 
 
557
/* Initialise all things required by this module */
 
558
static void InitialisePathUtilities(void)
 
559
{
 
560
  if (gnustepSystemRoot != nil)
 
561
    {
 
562
      return;   // Protect from multiple calls
 
563
    }
 
564
 
 
565
  /* Set up our root paths */
 
566
  NS_DURING
 
567
    {
 
568
      NSString                  *userName;
 
569
      NSMutableDictionary       *config;
 
570
 
 
571
      [gnustep_global_lock lock];
 
572
      userName = NSUserName();
 
573
      config = GNUstepConfig(nil);
 
574
      UserConfig(config, userName);
 
575
      ASSIGNCOPY(gnustepUserHome, NSHomeDirectoryForUser(userName));
 
576
      ExtractValuesFromConfig(config);
 
577
 
 
578
      [gnustep_global_lock unlock];
 
579
    }
 
580
  NS_HANDLER
 
581
    {
 
582
      /* unlock then re-raise the exception */
 
583
      [gnustep_global_lock unlock];
 
584
      [localException raise];
 
585
    }
 
586
  NS_ENDHANDLER
 
587
}
 
588
 
 
589
/*
 
590
 * Close down and release all things allocated.
 
591
 */
 
592
static void ShutdownPathUtilities(void)
 
593
{
 
594
  DESTROY(gnustepSystemRoot);
 
595
  DESTROY(gnustepNetworkRoot);
 
596
  DESTROY(gnustepLocalRoot);
 
597
  DESTROY(gnustepUserRoot);
 
598
 
 
599
  DESTROY(gnustepUserHome);
 
600
  DESTROY(gnustepUserDefaultsDir);
 
601
 
 
602
  DESTROY(osSysApps);
 
603
  DESTROY(osSysLibs);
 
604
  DESTROY(osSysAdmin);
 
605
 
 
606
  DESTROY(platformResources);
 
607
  DESTROY(platformApps);
 
608
  DESTROY(platformLibs);
 
609
  DESTROY(platformAdmin);
 
610
 
 
611
  DESTROY(localResources);
 
612
  DESTROY(localApps);
 
613
  DESTROY(localLibs);
 
614
 
 
615
  DESTROY(tempDir);
 
616
}
 
617
 
 
618
/**
 
619
 * Reads a file and expects it to be in basic unix "conf" style format with
 
620
 * one key = value per line (the format a unix shell can 'source' in order
 
621
 * to define shell variables).<br />
 
622
 * This attempts to mimic the escape sequence and quoting conventions of
 
623
 * the standard bourne shell, so that a config file sourced by the make
 
624
 * package will produce the same results as one parsed by this function.<br />
 
625
 * Keys, by convention, consiste of uppercase letters, digits,
 
626
 * and underscores, and must not begin with a digit.<br />
 
627
 * A value may be any quoted string (or an unquoted string containing no
 
628
 * white space).<br />
 
629
 * Lines beginning with a hash '#' are deemed comment lines and ignored.<br/ >
 
630
 * The backslash character may be used as an escape character anywhere
 
631
 * in the file  except within a singly quoted string
 
632
 * (where it is taken literally).<br />
 
633
 * A backslash followed immediately by a newline (except in a singly
 
634
 * quoted string) is removed completely along with the newline ... it
 
635
 * thus serves to join lines so that they are treated as a single line.<br />
 
636
 * NB. Since ms-windows uses backslash characters in paths, it is a good
 
637
 * idea to specify path values in the config file as singly quoted
 
638
 * strings to avoid having to double all occurrences of the backslash.<br />
 
639
 * Returns a dictionary of the (key,value) pairs.<br/ >
 
640
 * If the file does not exist,
 
641
 * the function makes no changes to dict and returns NO.
 
642
 */
 
643
static BOOL
 
644
ParseConfigurationFile(NSString *fileName, NSMutableDictionary *dict)
 
645
{
 
646
  NSDictionary  *attributes;
 
647
  NSString      *file;
 
648
  unsigned      l;
 
649
  unichar       *src;
 
650
  unichar       *dst;
 
651
  unichar       *end;
 
652
  unichar       *spos;
 
653
  unichar       *dpos;
 
654
  BOOL          newLine = YES;
 
655
  BOOL          wantKey = YES;
 
656
  BOOL          wantVal = NO;
 
657
  NSString      *key = nil;
 
658
 
 
659
  if ([MGR() isReadableFileAtPath: fileName] == NO)
 
660
    {
 
661
      return NO;
 
662
    }
 
663
 
 
664
  attributes = [MGR() fileAttributesAtPath: fileName traverseLink: YES];
 
665
  if (([attributes filePosixPermissions] & (0022 & ATTRMASK)) != 0)
 
666
    {
 
667
#if defined(__WIN32__)
 
668
      fprintf(stderr, "The file '%S' is writable by someone other than"
 
669
        " its owner (permissions 0%lo).\nIgnoring it.\n",
 
670
        (const unichar*)[fileName fileSystemRepresentation],
 
671
        [attributes filePosixPermissions]);
 
672
#else
 
673
      fprintf(stderr, "The file '%s' is writable by someone other than"
 
674
        " its owner (permissions 0%lo).\nIgnoring it.\n",
 
675
        [fileName fileSystemRepresentation],
 
676
        [attributes filePosixPermissions]);
 
677
#endif
 
678
      return NO;
 
679
    }
 
680
 
 
681
  if (dict == nil)
 
682
    {
 
683
      [NSException raise: NSInvalidArgumentException
 
684
                  format: @"No destination dictionary supplied"];
 
685
    }
 
686
 
 
687
  file = [NSString stringWithContentsOfFile: fileName];
 
688
  l = [file length];
 
689
  src = (unichar*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(unichar) * l);
 
690
  spos = src;
 
691
  end = src + l;
 
692
  dst = (unichar*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(unichar) * l);
 
693
  dpos = dst;
 
694
  [file getCharacters: src];
 
695
 
 
696
  while (spos < end)
 
697
    {
 
698
      /*
 
699
       * Step past any whitespace ... including blank lines
 
700
       */
 
701
      while (spos < end)
 
702
        {
 
703
          if (*spos == '\\')
 
704
            {
 
705
              spos++;
 
706
              if (spos >= end)
 
707
                {
 
708
                  break;        // At end of file ... odd but not fatal
 
709
                }
 
710
            }
 
711
          if (*spos > ' ')
 
712
            {
 
713
              break;            // OK ... found a non space character.
 
714
            }
 
715
          if (*spos == '\r' || *spos == '\n')
 
716
            {
 
717
              newLine = YES;
 
718
            }
 
719
          spos++;
 
720
        }
 
721
 
 
722
      /*
 
723
       * Handle any comments .. hash on a new line.
 
724
       */
 
725
      if (newLine == YES)
 
726
        {
 
727
          if (wantVal == YES)
 
728
            {
 
729
              /*
 
730
               * On a newline ...so the last key had no value set.
 
731
               * Put an empty value in the dictionary.
 
732
               */
 
733
              [dict setObject: @"" forKey: key];
 
734
              DESTROY(key);
 
735
              wantVal = NO;
 
736
            }
 
737
          if (spos < end && *spos == '#')
 
738
            {
 
739
              // Got a comment ... ignore remainder of line.
 
740
              while (spos < end && *spos != '\n' && *spos != '\r')
 
741
                {
 
742
                  spos++;
 
743
                }
 
744
              continue; // restart loop ... skip space at start of line
 
745
            }
 
746
          newLine = NO;
 
747
          wantKey = YES;
 
748
        }
 
749
 
 
750
      if (*spos == '=')
 
751
        {
 
752
          if (wantKey == NO)
 
753
            {
 
754
              wantVal = YES;
 
755
            }
 
756
          spos++;
 
757
        }
 
758
      else if (*spos == '\'')
 
759
        {
 
760
          spos++;
 
761
          while (spos < end)
 
762
            {
 
763
              if (*spos == '\'')
 
764
                {
 
765
                  spos++;
 
766
                  break;
 
767
                }
 
768
              *dpos++ = *spos++;
 
769
            }
 
770
          if (wantVal == YES)
 
771
            {
 
772
              NSString  *val = [NSString alloc];
 
773
 
 
774
              val = [val initWithCharacters: dst length: dpos - dst];
 
775
              if (val != nil)
 
776
                {
 
777
                  [dict setObject: val forKey: key];
 
778
                  DESTROY(key);
 
779
                  DESTROY(val);
 
780
                  wantVal = NO;
 
781
                }
 
782
            }
 
783
          dpos = dst;   // reset output buffer
 
784
        }
 
785
      else if (*spos == '"')
 
786
        {
 
787
          spos++;
 
788
          while (spos < end)
 
789
            {
 
790
              BOOL      escaped = NO;
 
791
 
 
792
              if (*spos == '\\')
 
793
                {
 
794
                  spos++;
 
795
                  if (spos >= end)
 
796
                    {
 
797
                      break;    // Unexpected end of file
 
798
                    }
 
799
                  if (*spos == '\n')
 
800
                    {
 
801
                      spos++;
 
802
                      continue; // escaped newline is removed.
 
803
                    }
 
804
                  if (*spos == '\r')
 
805
                    {
 
806
                      spos++;
 
807
                      if (spos < end && *spos == '\n')
 
808
                        {
 
809
                          spos++;
 
810
                        }
 
811
                      continue; // escaped newline is removed.
 
812
                    }
 
813
                  escaped = YES;
 
814
                }
 
815
              if (*spos == '"' && escaped == NO)
 
816
                {
 
817
                  spos++;
 
818
                  break;
 
819
                }
 
820
              *dpos++ = *spos++;
 
821
            }
 
822
          if (wantVal == YES)
 
823
            {
 
824
              NSString  *val = [NSString alloc];
 
825
 
 
826
              val = [val initWithCharacters: dst length: dpos - dst];
 
827
              if (val != nil)
 
828
                {
 
829
                  [dict setObject: val forKey: key];
 
830
                  DESTROY(key);
 
831
                  DESTROY(val);
 
832
                  wantVal = NO;
 
833
                }
 
834
            }
 
835
          dpos = dst;   // reset output buffer
 
836
        }
 
837
      else
 
838
        {
 
839
          while (spos < end)
 
840
            {
 
841
              if (*spos == '\\')
 
842
                {
 
843
                  spos++;
 
844
                  if (spos >= end)
 
845
                    {
 
846
                      break;    // Unexpected end of file
 
847
                    }
 
848
                  if (*spos == '\n')
 
849
                    {
 
850
                      spos++;
 
851
                      continue; // escaped newline is removed.
 
852
                    }
 
853
                  if (*spos == '\r')
 
854
                    {
 
855
                      spos++;
 
856
                      if (spos < end && *spos == '\n')
 
857
                        {
 
858
                          spos++;
 
859
                        }
 
860
                      continue; // escaped newline is removed.
 
861
                    }
 
862
                }
 
863
              if (isspace(*spos) || *spos == '=')
 
864
                {
 
865
                  break;
 
866
                }
 
867
              *dpos++ = *spos++;
 
868
            }
 
869
 
 
870
          if (wantKey == YES)
 
871
            {
 
872
              key = [NSString alloc];
 
873
              key = [key initWithCharacters: dst length: dpos - dst];
 
874
              if (key != nil)
 
875
                {
 
876
                  wantKey = NO;
 
877
                }
 
878
            }
 
879
          else if (wantVal == YES)
 
880
            {
 
881
              NSString  *val = [NSString alloc];
 
882
 
 
883
              val = [val initWithCharacters: dst length: dpos - dst];
 
884
              if (val != nil)
 
885
                {
 
886
                  [dict setObject: val forKey: key];
 
887
                  DESTROY(key);
 
888
                  DESTROY(val);
 
889
                  wantVal = NO;
 
890
                }
 
891
            }
 
892
          dpos = dst;   // reset output buffer
 
893
        }
 
894
    }
 
895
  if (wantVal == YES)
 
896
    {
 
897
      [dict setObject: @"" forKey: key];
 
898
      DESTROY(key);
 
899
    }
 
900
  NSZoneFree(NSDefaultMallocZone(), src);
 
901
  NSZoneFree(NSDefaultMallocZone(), dst);
 
902
 
 
903
  return YES;
 
904
}
 
905
 
 
906
 
 
907
/* See NSPathUtilities.h for description */
 
908
void
 
909
GSSetUserName(NSString *aName)
 
910
{
 
911
  NSCParameterAssert([aName length] > 0);
 
912
 
 
913
  /*
 
914
   * Do nothing if it's not a different user.
 
915
   */
 
916
  if ([theUserName isEqualToString: aName])
 
917
    {
 
918
      return;
 
919
    }
 
920
 
 
921
  /*
 
922
   * Release the memory
 
923
   */
 
924
  [gnustep_global_lock lock];
 
925
  ShutdownPathUtilities();
 
926
 
 
927
  /*
 
928
   * Reset things as new user
 
929
   */
 
930
  ASSIGN(theUserName, aName);
 
931
  InitialisePathUtilities();
 
932
  [NSUserDefaults resetStandardUserDefaults];
 
933
 
 
934
  [gnustep_global_lock unlock];
 
935
}
 
936
 
 
937
/**
 
938
 * Return the caller's login name as an NSString object.<br/ >
 
939
 * Under unix-like systems, the name associated with the current
 
940
 * effective user ID is used.<br/ >
 
941
 * Under ms-windows, the 'LOGNAME' environment is used, or if that fails, the
 
942
 * GetUserName() call is used to find the user name.
 
943
 */
 
944
/* NOTE FOR DEVELOPERS.
 
945
 * If you change the behavior of this method you must also change
 
946
 * user_home.c in the makefiles package to match.
 
947
 */
 
948
NSString *
 
949
NSUserName(void)
 
950
{
 
951
#if defined(__WIN32__)
 
952
  if (theUserName == nil)
 
953
    {
 
954
      /* Use the LOGNAME environment variable if set. */
 
955
      theUserName = [[[NSProcessInfo processInfo] environment]
 
956
        objectForKey: @"LOGNAME"];
 
957
      if ([theUserName length] > 0)
 
958
        {
 
959
          RETAIN(theUserName);
 
960
        }
 
961
      else
 
962
        {
 
963
          /* The GetUserName function returns the current user name */
 
964
          unichar buf[1024];
 
965
          DWORD n = 1024;
 
966
 
 
967
          if (GetUserNameW(buf, &n) != 0 && buf[0] != '\0')
 
968
            {
 
969
              theUserName = [[NSString alloc] initWithCharacters: buf
 
970
                                                          length: wcslen(buf)];
 
971
            }
 
972
          else
 
973
            {
 
974
              theUserName = nil;
 
975
              [NSException raise: NSInternalInconsistencyException
 
976
                          format: @"Unable to determine current user name"];
 
977
            }
 
978
        }
 
979
    }
 
980
#else
 
981
  /* Set olduid to some invalid uid that we could never start off running
 
982
     as.  */
 
983
  static int    olduid = -1;
 
984
#ifdef HAVE_GETEUID
 
985
  int uid = geteuid();
 
986
#else
 
987
  int uid = getuid();
 
988
#endif /* HAVE_GETEUID */
 
989
 
 
990
  if (theUserName == nil || uid != olduid)
 
991
    {
 
992
      const char *loginName = 0;
 
993
#ifdef HAVE_GETPWUID
 
994
      struct passwd *pwent = getpwuid (uid);
 
995
      loginName = pwent->pw_name;
 
996
#endif /* HAVE_GETPWUID */
 
997
      olduid = uid;
 
998
      if (loginName)
 
999
        theUserName = [[NSString alloc] initWithCString: loginName];
 
1000
      else
 
1001
        [NSException raise: NSInternalInconsistencyException
 
1002
                    format: @"Unable to determine current user name"];
 
1003
    }
 
1004
#endif
 
1005
  return theUserName;
 
1006
}
 
1007
 
 
1008
 
 
1009
/**
 
1010
 * Return the caller's home directory as an NSString object.
 
1011
 * Calls NSHomeDirectoryForUser() to do this.
 
1012
 */
 
1013
NSString *
 
1014
NSHomeDirectory(void)
 
1015
{
 
1016
  return NSHomeDirectoryForUser (NSUserName ());
 
1017
}
 
1018
 
 
1019
/**
 
1020
 * Returns loginName's home directory as an NSString object.
 
1021
 */
 
1022
/* NOTE FOR DEVELOPERS.
 
1023
 * If you change the behavior of this method you must also change
 
1024
 * user_home.c in the makefiles package to match.
 
1025
 */
 
1026
NSString *
 
1027
NSHomeDirectoryForUser(NSString *loginName)
 
1028
{
 
1029
  NSString      *s = nil;
 
1030
 
 
1031
#if !defined(__MINGW32__)
 
1032
  struct passwd *pw;
 
1033
 
 
1034
  [gnustep_global_lock lock];
 
1035
  pw = getpwnam ([loginName cString]);
 
1036
  if (pw != 0  && pw->pw_dir != NULL)
 
1037
    {
 
1038
      s = [NSString stringWithCString: pw->pw_dir];
 
1039
    }
 
1040
  [gnustep_global_lock unlock];
 
1041
#else
 
1042
  if ([loginName isEqual: NSUserName()] == YES)
 
1043
    {
 
1044
      NSDictionary      *e = [[NSProcessInfo processInfo] environment];
 
1045
 
 
1046
      /*
 
1047
       * The environment variable HOMEPATH holds the home directory
 
1048
       * for the user on Windows NT;
 
1049
       * For OPENSTEP compatibility (and because USERPROFILE is usually
 
1050
       * unusable because it contains spaces), we use HOMEPATH in
 
1051
       * preference to USERPROFILE.
 
1052
       */
 
1053
      s = [e objectForKey: @"HOMEPATH"];
 
1054
      if (s != nil && ([s length] < 2 || [s characterAtIndex: 1] != ':'))
 
1055
        {
 
1056
          s = [[e objectForKey: @"HOMEDRIVE"] stringByAppendingString: s];
 
1057
        }
 
1058
      if (s == nil)
 
1059
        {
 
1060
          s = [e objectForKey: @"USERPROFILE"];
 
1061
        }
 
1062
      if (s == nil)
 
1063
        {
 
1064
          ; // FIXME: Talk to the NET API and get the profile path
 
1065
        }
 
1066
    }
 
1067
  else
 
1068
    {
 
1069
      s = nil;
 
1070
      NSLog(@"Trying to get home for '%@' when user is '%@'",
 
1071
        loginName, NSUserName());    
 
1072
      NSLog(@"Can't determine other user home directories in Win32.");    
 
1073
    }
 
1074
  
 
1075
  if ([s length] == 0 && [loginName length] != 1)
 
1076
    {
 
1077
      s = nil;
 
1078
      NSLog(@"NSHomeDirectoryForUser(%@) failed", loginName);
 
1079
    }
 
1080
#endif
 
1081
  return s;
 
1082
}
 
1083
 
 
1084
/**
 
1085
 * Returns the full username of the current user.
 
1086
 * If unable to determine this, returns the standard user name.
 
1087
 */
 
1088
NSString *
 
1089
NSFullUserName(void)
 
1090
{
 
1091
#if defined(__WIN32__)
 
1092
  /* FIXME: Win32 way to get full user name via Net API */
 
1093
  return NSUserName();
 
1094
#else
 
1095
#ifdef  HAVE_PWD_H
 
1096
  struct passwd *pw;
 
1097
 
 
1098
  pw = getpwnam([NSUserName() cString]);
 
1099
  return [NSString stringWithCString: pw->pw_gecos];
 
1100
#else
 
1101
  NSLog(@"Warning: NSFullUserName not implemented\n");
 
1102
  return NSUserName();
 
1103
#endif /* HAVE_PWD_H */
 
1104
#endif /* defined(__Win32__) else */
 
1105
}
 
1106
 
 
1107
/**
 
1108
 * Return the path of the defaults directory for userName.<br />
 
1109
 * This examines the .GNUsteprc file in the home directory of the
 
1110
 * user for the GNUSTEP_DEFAULTS_ROOT or the GNUSTEP_USER_ROOT
 
1111
 * directory definitions, over-riding those in GNUstep.conf.
 
1112
 */
 
1113
NSString *
 
1114
GSDefaultsRootForUser(NSString *userName)
 
1115
{
 
1116
  NSString *home;
 
1117
  NSString *defaultsDir;
 
1118
 
 
1119
  InitialisePathUtilities();
 
1120
  if ([userName length] == 0)
 
1121
    {
 
1122
      userName = NSUserName();
 
1123
    }
 
1124
  home = NSHomeDirectoryForUser(userName);
 
1125
  if ([userName isEqual: NSUserName()])
 
1126
    {
 
1127
      defaultsDir = gnustepUserDefaultsDir;
 
1128
    }
 
1129
  else
 
1130
    {
 
1131
      NSMutableDictionary       *config;
 
1132
 
 
1133
      config = GNUstepConfig(nil);
 
1134
      UserConfig(config, userName);
 
1135
      defaultsDir = [config objectForKey: @"GNUSTEP_USER_DEFAULTS_DIR"];
 
1136
      if (defaultsDir == nil)
 
1137
        {
 
1138
          defaultsDir = @GNUSTEP_TARGET_USER_DEFAULTS_DIR;
 
1139
        }
 
1140
    }
 
1141
#if     defined(__MINGW32__)
 
1142
  if ([defaultsDir rangeOfString: @":REGISTRY:"].length > 0)
 
1143
    {
 
1144
      return defaultsDir;       // Just use windows registry.
 
1145
    }
 
1146
#endif
 
1147
  home = [home stringByAppendingPathComponent: defaultsDir];
 
1148
 
 
1149
  return home;
 
1150
}
 
1151
 
 
1152
/**
 
1153
 * Returns the standard paths in which applications are stored and
 
1154
 * should be searched for.  Calls NSSearchPathForDirectoriesInDomains()<br/ >
 
1155
 * Refer to the GNUstep File System Hierarchy documentation for more info.
 
1156
 */
 
1157
NSArray *
 
1158
NSStandardApplicationPaths(void)
 
1159
{
 
1160
  return NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory,
 
1161
                                             NSAllDomainsMask, YES);
 
1162
}
 
1163
 
 
1164
/**
 
1165
 * Returns the standard paths in which resources are stored and
 
1166
 * should be searched for.  Calls NSSearchPathForDirectoriesInDomains()<br/ >
 
1167
 * Refer to the GNUstep File System Hierarchy documentation for more info.
 
1168
 */
 
1169
NSArray *
 
1170
NSStandardLibraryPaths(void)
 
1171
{
 
1172
  return NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory,
 
1173
                                             NSAllDomainsMask, YES);
 
1174
}
 
1175
 
 
1176
/**
 
1177
 * Returns the name of a directory in which temporary files can be stored.
 
1178
 * Under GNUstep this is a location which is not readable by other users.
 
1179
 * <br />
 
1180
 * If a suitable directory can't be found or created, this function raises an
 
1181
 * NSGenericException.
 
1182
 */
 
1183
NSString *
 
1184
NSTemporaryDirectory(void)
 
1185
{
 
1186
  NSFileManager *manager;
 
1187
  NSString      *tempDirName;
 
1188
  NSString      *baseTempDirName = nil;
 
1189
  NSDictionary  *attr;
 
1190
  int           perm;
 
1191
  int           owner;
 
1192
  BOOL          flag;
 
1193
#if     !defined(__WIN32__)
 
1194
  int           uid;
 
1195
#else
 
1196
  unichar buffer[1024];
 
1197
 
 
1198
  if (GetTempPathW(1024, buffer))
 
1199
    {
 
1200
      baseTempDirName = [NSString stringWithCharacters: buffer
 
1201
                                                length: wcslen(buffer)];
 
1202
    }
 
1203
#endif
 
1204
 
 
1205
  /*
 
1206
   * If the user has supplied a directory name in the TEMP or TMP
 
1207
   * environment variable, attempt to use that unless we already
 
1208
   * have a temporary directory specified.
 
1209
   */
 
1210
  if (baseTempDirName == nil)
 
1211
    {
 
1212
      NSDictionary      *env = [[NSProcessInfo processInfo] environment];
 
1213
 
 
1214
      baseTempDirName = [env objectForKey: @"TEMP"];
 
1215
      if (baseTempDirName == nil)
 
1216
        {
 
1217
          baseTempDirName = [env objectForKey: @"TMP"];
 
1218
          if (baseTempDirName == nil)
 
1219
            {
 
1220
#if     defined(__MINGW32__)
 
1221
#ifdef  __CYGWIN__
 
1222
              baseTempDirName = @"/cygdrive/c/";
 
1223
#else
 
1224
              baseTempDirName = @"/c/";
 
1225
#endif
 
1226
#else
 
1227
              baseTempDirName = @"/tmp";
 
1228
#endif
 
1229
            }
 
1230
        }
 
1231
    }
 
1232
 
 
1233
  /*
 
1234
   * Check that the base directory exists ... if it doesn't we can't
 
1235
   * go any further.
 
1236
   */
 
1237
  tempDirName = baseTempDirName;
 
1238
  manager = [NSFileManager defaultManager];
 
1239
  if ([manager fileExistsAtPath: tempDirName isDirectory: &flag] == NO
 
1240
    || flag == NO)
 
1241
    {
 
1242
      [NSException raise: NSGenericException
 
1243
                  format: @"Temporary directory (%@) does not exist",
 
1244
                          tempDirName];
 
1245
      return nil; /* Not reached. */
 
1246
    }
 
1247
 
 
1248
  /*
 
1249
   * Check that we are the directory owner, and that we, and nobody else,
 
1250
   * have access to it. If other people have access, try to create a secure
 
1251
   * subdirectory.
 
1252
   */
 
1253
  attr = [manager fileAttributesAtPath: tempDirName traverseLink: YES];
 
1254
  owner = [[attr objectForKey: NSFileOwnerAccountID] intValue];
 
1255
  perm = [[attr objectForKey: NSFilePosixPermissions] intValue];
 
1256
  perm = perm & 0777;
 
1257
 
 
1258
// Mateu Batle: secure temporary directories don't work in MinGW
 
1259
#ifndef __MINGW32__
 
1260
 
 
1261
#if     defined(__MINGW32__)
 
1262
  uid = owner;
 
1263
#else
 
1264
#ifdef HAVE_GETEUID
 
1265
  uid = geteuid();
 
1266
#else
 
1267
  uid = getuid();
 
1268
#endif /* HAVE_GETEUID */
 
1269
#endif
 
1270
  if ((perm != 0700 && perm != 0600) || owner != uid)
 
1271
    {
 
1272
      NSString  *secure;
 
1273
 
 
1274
      /*
 
1275
       * The name of the secure subdirectory reflects the user ID rather
 
1276
       * than the user name, since it is possible to have an account with
 
1277
       * lots of names on a unix system (ie multiple entries in the password
 
1278
       * file but a single userid).  The private directory is secure within
 
1279
       * the account, not to a particular user name.
 
1280
       */
 
1281
      secure = [NSString stringWithFormat: @"GNUstepSecure%d", uid];
 
1282
      tempDirName
 
1283
        = [baseTempDirName stringByAppendingPathComponent: secure];
 
1284
      /*
 
1285
      NSLog(@"Temporary directory (%@) may be insecure ... attempting to "
 
1286
        @"add secure subdirectory", tempDirName);
 
1287
      */
 
1288
      if ([manager fileExistsAtPath: tempDirName] == NO)
 
1289
        {
 
1290
          NSNumber      *p = [NSNumber numberWithInt: 0700];
 
1291
 
 
1292
          attr = [NSDictionary dictionaryWithObject: p
 
1293
                                             forKey: NSFilePosixPermissions];
 
1294
          if ([manager createDirectoryAtPath: tempDirName
 
1295
                                  attributes: attr] == NO)
 
1296
            {
 
1297
              [NSException raise: NSGenericException
 
1298
                          format:
 
1299
                @"Attempt to create a secure temporary directory (%@) failed.",
 
1300
                                  tempDirName];
 
1301
              return nil; /* Not reached. */
 
1302
            }
 
1303
        }
 
1304
 
 
1305
      /*
 
1306
       * Check that the new directory is really secure.
 
1307
       */
 
1308
      attr = [manager fileAttributesAtPath: tempDirName traverseLink: YES];
 
1309
      owner = [[attr objectForKey: NSFileOwnerAccountID] intValue];
 
1310
      perm = [[attr objectForKey: NSFilePosixPermissions] intValue];
 
1311
      perm = perm & 0777;
 
1312
      if ((perm != 0700 && perm != 0600) || owner != uid)
 
1313
        {
 
1314
          [NSException raise: NSGenericException
 
1315
                      format:
 
1316
            @"Attempt to create a secure temporary directory (%@) failed.",
 
1317
                              tempDirName];
 
1318
          return nil; /* Not reached. */
 
1319
        }
 
1320
    }
 
1321
#endif
 
1322
 
 
1323
  if ([manager isWritableFileAtPath: tempDirName] == NO)
 
1324
    {
 
1325
      [NSException raise: NSGenericException
 
1326
                  format: @"Temporary directory (%@) is not writable",
 
1327
                          tempDirName];
 
1328
      return nil; /* Not reached. */
 
1329
    }
 
1330
  return tempDirName;
 
1331
}
 
1332
 
 
1333
/**
 
1334
 * Returns the location of the <em>root</em> directory of the file
 
1335
 * hierarchy. This lets you build paths in a system independent manner
 
1336
 * (for instance the root on unix is '/' but on windows it is 'C:\')
 
1337
 * by appending path components to the root.<br />
 
1338
 * Don't assume that /System, /Network etc exist in this path (generally
 
1339
 * they don't)! Use other path utility functions such as
 
1340
 * NSSearchPathForDirectoriesInDomains() to find standard locations
 
1341
 * for libraries, applications etc.<br />
 
1342
 * Refer to the GNUstep File System Hierarchy documentation for more info.
 
1343
 */
 
1344
NSString *
 
1345
NSOpenStepRootDirectory(void)
 
1346
{
 
1347
  NSString      *root;
 
1348
 
 
1349
#if     defined(__CYGWIN__)
 
1350
  root = @"/cygdrive/c/";
 
1351
#elif   defined(__MINGW32__)
 
1352
  root = @"C:\\";
 
1353
#else
 
1354
  root = @"/";
 
1355
#endif
 
1356
  return root;
 
1357
}
 
1358
 
 
1359
/**
 
1360
 * Returns an array of search paths to look at for resources.<br/ >
 
1361
 * The paths are returned in domain order: USER, LOCAL, NETWORK then SYSTEM.
 
1362
 */
 
1363
NSArray *
 
1364
NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directoryKey,
 
1365
  NSSearchPathDomainMask domainMask, BOOL expandTilde)
 
1366
{
 
1367
  static NSString *adminDir     = @"Administrator";
 
1368
  static NSString *appsDir      = @"Applications";
 
1369
  static NSString *devDir       = @"Developer";
 
1370
  static NSString *demosDir     = @"Demos";
 
1371
  static NSString *libraryDir   = @"Library";
 
1372
  static NSString *supportDir   = @"ApplicationSupport";
 
1373
  static NSString *docDir       = @"Documentation";
 
1374
  static NSString *fontsDir     = @"Fonts";
 
1375
  static NSString *frameworkDir = @"Frameworks";
 
1376
  static NSString *libsDir      = @"Libraries";
 
1377
  static NSString *toolsDir     = @"Tools";
 
1378
  NSMutableArray  *paths = [NSMutableArray new];
 
1379
  NSString        *path;
 
1380
  unsigned        i;
 
1381
  unsigned        count;
 
1382
 
 
1383
  InitialisePathUtilities();
 
1384
 
 
1385
  NSCAssert(gnustepSystemRoot!=nil,@"Path utilities without initialisation!");
 
1386
 
 
1387
  /*
 
1388
   * The order in which we return paths is important - user must come
 
1389
   * first, followed by local, followed by network, followed by system.
 
1390
   * The calling code can then loop on the returned paths, and stop as
 
1391
   * soon as it finds something.  So things in user automatically
 
1392
   * override things in system etc.
 
1393
   */
 
1394
 
 
1395
#define ADD_PATH(mask, base_dir, add_dir) \
 
1396
if (domainMask & mask) \
 
1397
{ \
 
1398
  path = [base_dir stringByAppendingPathComponent: add_dir]; \
 
1399
  if (path != nil && [paths containsObject: path] == NO) \
 
1400
    [paths addObject: path]; \
 
1401
}
 
1402
#define ADD_PLATFORM_PATH(mask, add_dir) \
 
1403
if (domainMask & mask) \
 
1404
{ \
 
1405
  if (add_dir != nil && [paths containsObject: add_dir] == NO) \
 
1406
    [paths addObject: add_dir]; \
 
1407
}
 
1408
 
 
1409
  switch (directoryKey)
 
1410
    {
 
1411
      case NSAllApplicationsDirectory:
 
1412
        {
 
1413
          NSString *devDemosDir;
 
1414
          NSString *devAppsDir;
 
1415
          NSString *devAdminDir;
 
1416
 
 
1417
          devDemosDir = [devDir stringByAppendingPathComponent: demosDir];
 
1418
          devAppsDir = [devDir stringByAppendingPathComponent: appsDir];
 
1419
          devAdminDir = [devDir stringByAppendingPathComponent: adminDir];
 
1420
 
 
1421
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, appsDir);
 
1422
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, devAppsDir);
 
1423
 
 
1424
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, appsDir);
 
1425
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, devAppsDir);
 
1426
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, devAdminDir);
 
1427
 
 
1428
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, appsDir);
 
1429
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, devAppsDir);
 
1430
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, devAdminDir);
 
1431
 
 
1432
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, appsDir);
 
1433
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, devAppsDir);
 
1434
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, devAdminDir);
 
1435
 
 
1436
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, devDemosDir);
 
1437
 
 
1438
          ADD_PLATFORM_PATH(NSLocalDomainMask, localApps);
 
1439
          ADD_PLATFORM_PATH(NSSystemDomainMask, platformApps);
 
1440
          ADD_PLATFORM_PATH(NSSystemDomainMask, osSysApps);
 
1441
          ADD_PLATFORM_PATH(NSSystemDomainMask, osSysAdmin);
 
1442
          ADD_PLATFORM_PATH(NSSystemDomainMask, platformAdmin);
 
1443
        }
 
1444
        break;
 
1445
 
 
1446
      case NSApplicationDirectory:
 
1447
        {
 
1448
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, appsDir);
 
1449
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, appsDir);
 
1450
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, appsDir);
 
1451
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, appsDir);
 
1452
 
 
1453
          ADD_PLATFORM_PATH(NSLocalDomainMask, localApps);
 
1454
          ADD_PLATFORM_PATH(NSSystemDomainMask, platformApps);
 
1455
          ADD_PLATFORM_PATH(NSSystemDomainMask, osSysApps);
 
1456
        }
 
1457
        break;
 
1458
 
 
1459
      case NSDemoApplicationDirectory:
 
1460
        {
 
1461
          NSString *devDemosDir;
 
1462
 
 
1463
          devDemosDir = [devDir stringByAppendingPathComponent: demosDir];
 
1464
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, devDemosDir);
 
1465
        }
 
1466
        break;
 
1467
 
 
1468
      case NSDeveloperApplicationDirectory:
 
1469
        {
 
1470
          NSString *devAppsDir;
 
1471
 
 
1472
          devAppsDir = [devDir stringByAppendingPathComponent: appsDir];
 
1473
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, devAppsDir);
 
1474
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, devAppsDir);
 
1475
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, devAppsDir);
 
1476
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, devAppsDir);
 
1477
        }
 
1478
        break;
 
1479
 
 
1480
      case NSAdminApplicationDirectory:
 
1481
        {
 
1482
          NSString *devAdminDir;
 
1483
 
 
1484
          devAdminDir = [devDir stringByAppendingPathComponent: adminDir];
 
1485
          /* NSUserDomainMask - users have no Administrator directory */
 
1486
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, devAdminDir);
 
1487
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, devAdminDir);
 
1488
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, devAdminDir);
 
1489
 
 
1490
          ADD_PLATFORM_PATH(NSSystemDomainMask, osSysAdmin);
 
1491
          ADD_PLATFORM_PATH(NSSystemDomainMask, platformAdmin);
 
1492
        }
 
1493
        break;
 
1494
 
 
1495
      case NSAllLibrariesDirectory:
 
1496
        {
 
1497
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, libraryDir);
 
1498
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, libraryDir);
 
1499
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, libraryDir);
 
1500
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, libraryDir);
 
1501
 
 
1502
          ADD_PLATFORM_PATH(NSLocalDomainMask,  localResources);
 
1503
          ADD_PLATFORM_PATH(NSSystemDomainMask, platformResources);
 
1504
        }
 
1505
        break;
 
1506
 
 
1507
      case NSLibraryDirectory:
 
1508
        {
 
1509
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, libraryDir);
 
1510
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, libraryDir);
 
1511
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, libraryDir);
 
1512
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, libraryDir);
 
1513
 
 
1514
          ADD_PLATFORM_PATH(NSLocalDomainMask,  localResources);
 
1515
          ADD_PLATFORM_PATH(NSSystemDomainMask, platformResources);
 
1516
        }
 
1517
        break;
 
1518
 
 
1519
      case NSDeveloperDirectory:
 
1520
        {
 
1521
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, devDir);
 
1522
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, devDir);
 
1523
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, devDir);
 
1524
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, devDir);
 
1525
        }
 
1526
        break;
 
1527
 
 
1528
      case NSUserDirectory:
 
1529
        {
 
1530
          if (domainMask & NSUserDomainMask)
 
1531
            {
 
1532
              [paths addObject: gnustepUserRoot];
 
1533
            }
 
1534
        }
 
1535
        break;
 
1536
 
 
1537
      case NSDocumentationDirectory:
 
1538
        {
 
1539
          NSString *gsdocDir;
 
1540
 
 
1541
          gsdocDir = [libraryDir stringByAppendingPathComponent: docDir];
 
1542
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, gsdocDir);
 
1543
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, gsdocDir);
 
1544
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, gsdocDir);
 
1545
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, gsdocDir);
 
1546
        }
 
1547
        break;
 
1548
 
 
1549
      /* Now the GNUstep additions */
 
1550
      case GSApplicationSupportDirectory:
 
1551
        {
 
1552
          NSString *appSupDir;
 
1553
 
 
1554
          appSupDir = [libraryDir stringByAppendingPathComponent: supportDir];
 
1555
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, appSupDir);
 
1556
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, appSupDir);
 
1557
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, appSupDir);
 
1558
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, appSupDir);
 
1559
        }
 
1560
        break;
 
1561
 
 
1562
      case GSFrameworksDirectory:
 
1563
        {
 
1564
          NSString *frameDir;
 
1565
 
 
1566
          frameDir = [libraryDir stringByAppendingPathComponent: frameworkDir];
 
1567
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, frameDir);
 
1568
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, frameDir);
 
1569
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, frameDir);
 
1570
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, frameDir);
 
1571
        }
 
1572
        break;
 
1573
 
 
1574
      case GSFontsDirectory:
 
1575
        {
 
1576
          NSString *fontDir;
 
1577
 
 
1578
          fontDir = [libraryDir stringByAppendingPathComponent: fontsDir];
 
1579
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, fontDir);
 
1580
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, fontDir);
 
1581
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, fontDir);
 
1582
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, fontDir);
 
1583
        }
 
1584
        break;
 
1585
 
 
1586
      case GSLibrariesDirectory:
 
1587
        {
 
1588
          NSString *gslibsDir;
 
1589
          NSString *full = nil;
 
1590
          NSString *part = nil;
 
1591
 
 
1592
          gslibsDir = [libraryDir stringByAppendingPathComponent: libsDir];
 
1593
          if ([gnustep_flattened boolValue] == NO
 
1594
            && gnustep_target_cpu != nil && gnustep_target_os != nil)
 
1595
            {
 
1596
              part = [gnustep_target_cpu stringByAppendingPathComponent:
 
1597
                gnustep_target_os];
 
1598
              if (library_combo != nil)
 
1599
                {
 
1600
                  full = [part stringByAppendingPathComponent: library_combo];
 
1601
                  full = [gslibsDir stringByAppendingPathComponent: full];
 
1602
                }
 
1603
              part = [gslibsDir stringByAppendingPathComponent: part];
 
1604
            }
 
1605
 
 
1606
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, gslibsDir);
 
1607
          if (full) ADD_PATH(NSUserDomainMask, gnustepUserRoot, full);
 
1608
          if (part) ADD_PATH(NSUserDomainMask, gnustepUserRoot, part);
 
1609
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, gslibsDir);
 
1610
          if (full) ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, full);
 
1611
          if (part) ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, part);
 
1612
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, gslibsDir);
 
1613
          if (full) ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, full);
 
1614
          if (part) ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, part);
 
1615
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, gslibsDir);
 
1616
          if (full) ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, full);
 
1617
          if (part) ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, part);
 
1618
 
 
1619
          ADD_PLATFORM_PATH(NSLocalDomainMask, localLibs);
 
1620
          ADD_PLATFORM_PATH(NSSystemDomainMask, platformLibs);
 
1621
          ADD_PLATFORM_PATH(NSSystemDomainMask, osSysLibs);
 
1622
        }
 
1623
        break;
 
1624
 
 
1625
      case GSToolsDirectory:
 
1626
        {
 
1627
          NSString      *full = nil;
 
1628
          NSString      *part = nil;
 
1629
 
 
1630
          if ([gnustep_flattened boolValue] == NO
 
1631
            && gnustep_target_cpu != nil && gnustep_target_os != nil)
 
1632
            {
 
1633
              part = [gnustep_target_cpu stringByAppendingPathComponent:
 
1634
                gnustep_target_os];
 
1635
              if (library_combo != nil)
 
1636
                {
 
1637
                  full = [part stringByAppendingPathComponent: library_combo];
 
1638
                  full = [toolsDir stringByAppendingPathComponent: full];
 
1639
                }
 
1640
              part = [toolsDir stringByAppendingPathComponent: part];
 
1641
            }
 
1642
 
 
1643
          ADD_PATH(NSUserDomainMask, gnustepUserRoot, toolsDir);
 
1644
          if (full) ADD_PATH(NSUserDomainMask, gnustepUserRoot, full);
 
1645
          if (part) ADD_PATH(NSUserDomainMask, gnustepUserRoot, part);
 
1646
          ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, toolsDir);
 
1647
          if (full) ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, full);
 
1648
          if (part) ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, part);
 
1649
          ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, toolsDir);
 
1650
          if (full) ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, full);
 
1651
          if (part) ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, part);
 
1652
          ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, toolsDir);
 
1653
          if (full) ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, full);
 
1654
          if (part) ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, part);
 
1655
 
 
1656
          ADD_PLATFORM_PATH(NSLocalDomainMask, localApps);
 
1657
          ADD_PLATFORM_PATH(NSSystemDomainMask, platformApps);
 
1658
          ADD_PLATFORM_PATH(NSSystemDomainMask, osSysApps);
 
1659
          ADD_PLATFORM_PATH(NSSystemDomainMask, platformAdmin);
 
1660
          ADD_PLATFORM_PATH(NSSystemDomainMask, osSysAdmin);
 
1661
        }
 
1662
        break;
 
1663
 
 
1664
      case GSPreferencesDirectory:
 
1665
        {
 
1666
          // Not used
 
1667
        }
 
1668
        break;
 
1669
    }
 
1670
 
 
1671
#undef ADD_PATH
 
1672
#undef ADD_PLATFORM_PATH
 
1673
 
 
1674
  count = [paths count];
 
1675
  for (i = 0; i < count; i++)
 
1676
    {
 
1677
      path = [paths objectAtIndex: i];
 
1678
 
 
1679
      /* remove paths which don't exist on this system */
 
1680
      if ([MGR() fileExistsAtPath: path] == NO)
 
1681
        {
 
1682
          [paths removeObjectAtIndex: i];
 
1683
          i--;
 
1684
          count--;
 
1685
        }
 
1686
      else if (expandTilde == YES)
 
1687
        {
 
1688
          [paths replaceObjectAtIndex: i
 
1689
                          withObject: [path stringByExpandingTildeInPath]];
 
1690
        }
 
1691
      else
 
1692
        {
 
1693
          [paths replaceObjectAtIndex: i
 
1694
            withObject: [path stringByAbbreviatingWithTildeInPath]];
 
1695
        }
 
1696
    }
 
1697
 
 
1698
  AUTORELEASE (paths);
 
1699
  return paths;
 
1700
}