1
/* Implementation of filesystem & path-related functions for GNUstep
2
Copyright (C) 1996-2004 Free Software Foundation, Inc.
4
Written by: Andrew Kachites McCallum <address@hidden>
6
Rewrite by: Sheldon Gill
8
Rewrites by: Richard Frith-Macdonald
11
This file is part of the GNUstep Base Library.
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.
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.
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.
27
<title>NSPathUtilities function reference</title>
28
$Date: 2005/12/07 08:02:48 $ $Revision: 1.59 $
33
<heading>Path Utility Functions</heading>
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
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...)
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.
51
See <REF "filesystem.pdf">GNUstep File System Hierarchy</REF> document
52
for more information and detailed descriptions.</p>
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"
74
#include "GSPrivate.h"
77
#include <unistd.h> // for getuid()
80
#include <pwd.h> // for getpwnam()
82
#include <sys/types.h>
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
90
static NSString *gnustep_target_cpu =
91
#ifdef GNUSTEP_TARGET_CPU
96
static NSString *gnustep_target_os =
97
#ifdef GNUSTEP_TARGET_OS
102
static NSString *library_combo =
108
static NSString *gnustep_flattened =
109
#ifdef GNUSTEP_FLATTENED
115
#if defined(__WIN32__)
117
* FIXME ... should check access properly if the file is on an NTFS volume.
119
#define ATTRMASK 0700
121
#define ATTRMASK 0777
124
#define MGR() [NSFileManager defaultManager]
126
/* ------------------ */
127
/* Internal variables */
128
/* ------------------ */
130
static NSString *gnustepConfigPath = nil;
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 */
138
static NSString *gnustepUserDir = nil;
139
static NSString *gnustepUserHome = nil;
140
static NSString *gnustepUserDefaultsDir = nil;
142
static NSString *theUserName = nil; /* The user's login name */
143
static NSString *tempDir = nil; /* user's temporary directory */
145
static NSString *osSysApps = nil;
146
static NSString *osSysLibs = nil;
147
static NSString *osSysAdmin = nil;
149
static NSString *platformResources = nil;
150
static NSString *platformApps = nil;
151
static NSString *platformLibs = nil;
152
static NSString *platformAdmin = nil;
154
static NSString *localResources = nil;
155
static NSString *localApps = nil;
156
static NSString *localLibs = nil;
158
/* ============================= */
159
/* Internal function prototypes. */
160
/* ============================= */
162
static NSMutableDictionary* GNUstepConfig(NSDictionary *newConfig);
164
static void UserConfig(NSMutableDictionary *config, NSString *userName);
166
static BOOL ParseConfigurationFile(NSString *name, NSMutableDictionary *dict);
168
static void InitialisePathUtilities(void);
169
static void ShutdownPathUtilities(void);
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.
176
#define ASSIGN_IF_SET(var, dictionary, key) ({\
177
id val = [dictionary objectForKey: key];\
182
[dictionary removeObjectForKey: key];\
186
#define ASSIGN_PATH(var, dictionary, key) ({\
187
id val = getPathConfig(dictionary, key);\
192
[dictionary removeObjectForKey: key];\
196
/* Conditionally assign lval to var only if var is nil */
197
#define TEST_ASSIGN(var, lval) ({\
198
if ((var == nil)&&(lval != nil))\
204
/* Get a full path string */
205
static inline NSString *
206
getPath(NSString *path)
208
if ([path hasPrefix: @"./"] == YES)
210
path = [gnustepConfigPath stringByAppendingPathComponent:
211
[path substringFromIndex: 2]];
212
path = [path stringByStandardizingPath];
217
/* Get a full path string from a dictionary */
218
static inline NSString *
219
getPathConfig(NSDictionary *dict, NSString *key)
223
path = [dict objectForKey: key];
226
path = getPath(path);
227
if ([path isAbsolutePath] == NO)
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)
237
[path getCharacters: buf range: NSMakeRange(0, 3)];
238
if ((buf[0] == '/' || bug[0] == '\\') && isalpha(buf[1])
239
&& (buf[2] == '/' || bug[2] == '\\'))
241
path = [NSString stringWithFormat: @"%c:%@", (char)buf[1],
242
[path substringFromindex: 2]];
243
path = [path stringByReplacingString: @"/"
245
NSLog(@"I am guessing that you meant '%@'", path);
254
static void ExtractValuesFromConfig(NSDictionary *config)
256
NSMutableDictionary *c = [config mutableCopy];
259
* Move values out of the dictionary and into variables for rapid reference.
261
ASSIGN_PATH(gnustepSystemRoot, c, @"GNUSTEP_SYSTEM_ROOT");
262
ASSIGN_PATH(gnustepNetworkRoot, c, @"GNUSTEP_NETWORK_ROOT");
263
ASSIGN_PATH(gnustepLocalRoot, c, @"GNUSTEP_LOCAL_ROOT");
265
ASSIGN_IF_SET(gnustepUserDir, c, @"GNUSTEP_USER_DIR");
266
ASSIGN_IF_SET(gnustepUserDefaultsDir, c, @"GNUSTEP_USER_DEFAULTS_DIR");
268
ASSIGN_PATH(osSysApps, c, @"GNUSTEP_SYS_APPS");
269
ASSIGN_PATH(osSysLibs, c, @"GNUSTEP_SYS_LIBS");
270
ASSIGN_PATH(osSysAdmin, c, @"GNUSTEP_SYS_ADMIN");
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");
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");
282
* Remove any other dictionary entries we have used.
284
[c removeObjectForKey: @"GNUSTEP_USER_CONFIG_FILE"];
289
* The dictionary should be empty ... report problems
291
fprintf(stderr, "Configuration contains unknown keys - %s\n",
292
[[[c allKeys] description] UTF8String]);
297
* Set default locations for user files if necessary.
299
if (gnustepUserDir == nil)
301
ASSIGN(gnustepUserDir, @GNUSTEP_TARGET_USER_DIR);
303
if (gnustepUserDefaultsDir == nil)
305
ASSIGN(gnustepUserDefaultsDir, @GNUSTEP_TARGET_USER_DEFAULTS_DIR);
308
* Set the user root from the user home and the user dir
310
ASSIGN(gnustepUserRoot,
311
[gnustepUserHome stringByAppendingPathComponent: gnustepUserDir]);
314
* Try to ensure that essential user directories exist.
315
* FIXME ... Check/creation should perhaps be configurable.
319
NSFileManager *manager;
324
manager = [NSFileManager defaultManager];
325
attr = [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: 0750]
326
forKey: NSFilePosixPermissions];
328
// make sure user root exists.
329
path = gnustepUserRoot;
330
if ([manager fileExistsAtPath: path isDirectory: &flag] == NO
333
[manager createDirectoryAtPath: path attributes: attr];
336
// make sure library directory exists (to store resources).
337
path = [path stringByAppendingPathComponent: @"Library"];
338
if ([manager fileExistsAtPath: path isDirectory: &flag] == NO
341
[manager createDirectoryAtPath: path attributes: attr];
346
* Finally set default locations for the essential paths if required.
348
if (gnustepSystemRoot == nil)
350
gnustepSystemRoot = @GNUSTEP_TARGET_SYSTEM_ROOT;
351
gnustepSystemRoot = RETAIN(getPath(gnustepSystemRoot));
353
if (gnustepNetworkRoot == nil)
355
gnustepNetworkRoot = @GNUSTEP_TARGET_NETWORK_ROOT;
356
gnustepNetworkRoot = RETAIN(getPath(gnustepNetworkRoot));
358
if (gnustepLocalRoot == nil)
360
gnustepLocalRoot = @GNUSTEP_TARGET_LOCAL_ROOT;
361
gnustepLocalRoot = RETAIN(getPath(gnustepLocalRoot));
365
static NSMutableDictionary*
366
GNUstepConfig(NSDictionary *newConfig)
368
static NSDictionary *config = nil;
369
NSMutableDictionary *conf = nil;
370
BOOL changedSystemConfig = NO;
372
[gnustep_global_lock lock];
373
if (config == nil || (newConfig != nil && [config isEqual: newConfig] == NO))
377
if (newConfig == nil)
379
NSString *file = nil;
380
BOOL fromEnvironment = YES;
381
BOOL bareDirectory = NO;
383
conf = [[NSMutableDictionary alloc] initWithCapacity: 32];
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"];
392
fromEnvironment = NO;
393
file = [NSString stringWithCString:
394
STRINGIFY(GNUSTEP_CONFIG_FILE)];
398
* Is the file missing from the path ... if so we won't
401
if ([file hasSuffix: @"/"] || [file hasSuffix: @"\\"])
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.
411
if ([file hasPrefix: @"./"] == YES)
413
Class c = [NSProcessInfo class];
414
NSString *path = objc_get_symbol_path (c, 0);
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];
423
file = [file stringByStandardizingPath];
425
if ([file isAbsolutePath] == NO)
427
if (fromEnvironment == YES)
429
NSLog(@"GNUSTEP_CONFIG_FILE value ('%@') is not "
430
@"an absolute path. Please fix the environment "
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);
439
#if defined(__MINGW32_)
440
if ([file length] > 2)
444
[file getCharacters: buf range: NSMakeRange(0, 3)];
445
if ((buf[0] == '/' || bug[0] == '\\') && isalpha(buf[1])
446
&& (buf[2] == '/' || bug[2] == '\\'))
448
file = [NSString stringWithFormat: @"%c:%@",
449
(char)buf[1], [file substringFromindex: 2]];
450
file = [file stringByReplacingString: @"/"
452
NSLog(@"I am guessing that you meant '%@'", file);
457
if (bareDirectory == YES)
459
gnustepConfigPath = RETAIN(file);
464
= RETAIN([file stringByDeletingLastPathComponent]);
465
ParseConfigurationFile(file, conf);
470
conf = [newConfig mutableCopy];
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.
476
if ([conf objectForKey: @"GNUSTEP_USER_CONFIG_FILE"] == nil)
480
tmp = [NSString stringWithCString:\
481
STRINGIFY(GNUSTEP_USER_CONFIG_FILE)];
482
[conf setObject: tmp forKey: @"GNUSTEP_USER_CONFIG_FILE"];
486
changedSystemConfig = YES;
488
config = [conf copy];
493
[gnustep_global_lock unlock];
496
[localException raise];
500
[gnustep_global_lock unlock];
502
if (changedSystemConfig == YES)
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.
509
ShutdownPathUtilities();
510
InitialisePathUtilities();
513
return AUTORELEASE([config mutableCopy]);
517
UserConfig(NSMutableDictionary *config, NSString *userName)
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.
528
if (getuid() != geteuid())
541
file = RETAIN([config objectForKey: @"GNUSTEP_USER_CONFIG_FILE"]);
542
if ([file length] > 0)
544
home = NSHomeDirectoryForUser(userName);
545
path = [home stringByAppendingPathComponent: file];
546
ParseConfigurationFile(path, config);
549
* We don't let the user config file override the GNUSTEP_USER_CONFIG_FILE
550
* variable ... that would be silly/pointless.
552
[config setObject: file forKey: @"GNUSTEP_USER_CONFIG_FILE"];
557
/* Initialise all things required by this module */
558
static void InitialisePathUtilities(void)
560
if (gnustepSystemRoot != nil)
562
return; // Protect from multiple calls
565
/* Set up our root paths */
569
NSMutableDictionary *config;
571
[gnustep_global_lock lock];
572
userName = NSUserName();
573
config = GNUstepConfig(nil);
574
UserConfig(config, userName);
575
ASSIGNCOPY(gnustepUserHome, NSHomeDirectoryForUser(userName));
576
ExtractValuesFromConfig(config);
578
[gnustep_global_lock unlock];
582
/* unlock then re-raise the exception */
583
[gnustep_global_lock unlock];
584
[localException raise];
590
* Close down and release all things allocated.
592
static void ShutdownPathUtilities(void)
594
DESTROY(gnustepSystemRoot);
595
DESTROY(gnustepNetworkRoot);
596
DESTROY(gnustepLocalRoot);
597
DESTROY(gnustepUserRoot);
599
DESTROY(gnustepUserHome);
600
DESTROY(gnustepUserDefaultsDir);
606
DESTROY(platformResources);
607
DESTROY(platformApps);
608
DESTROY(platformLibs);
609
DESTROY(platformAdmin);
611
DESTROY(localResources);
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.
644
ParseConfigurationFile(NSString *fileName, NSMutableDictionary *dict)
646
NSDictionary *attributes;
659
if ([MGR() isReadableFileAtPath: fileName] == NO)
664
attributes = [MGR() fileAttributesAtPath: fileName traverseLink: YES];
665
if (([attributes filePosixPermissions] & (0022 & ATTRMASK)) != 0)
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]);
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]);
683
[NSException raise: NSInvalidArgumentException
684
format: @"No destination dictionary supplied"];
687
file = [NSString stringWithContentsOfFile: fileName];
689
src = (unichar*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(unichar) * l);
692
dst = (unichar*)NSZoneMalloc(NSDefaultMallocZone(), sizeof(unichar) * l);
694
[file getCharacters: src];
699
* Step past any whitespace ... including blank lines
708
break; // At end of file ... odd but not fatal
713
break; // OK ... found a non space character.
715
if (*spos == '\r' || *spos == '\n')
723
* Handle any comments .. hash on a new line.
730
* On a newline ...so the last key had no value set.
731
* Put an empty value in the dictionary.
733
[dict setObject: @"" forKey: key];
737
if (spos < end && *spos == '#')
739
// Got a comment ... ignore remainder of line.
740
while (spos < end && *spos != '\n' && *spos != '\r')
744
continue; // restart loop ... skip space at start of line
758
else if (*spos == '\'')
772
NSString *val = [NSString alloc];
774
val = [val initWithCharacters: dst length: dpos - dst];
777
[dict setObject: val forKey: key];
783
dpos = dst; // reset output buffer
785
else if (*spos == '"')
797
break; // Unexpected end of file
802
continue; // escaped newline is removed.
807
if (spos < end && *spos == '\n')
811
continue; // escaped newline is removed.
815
if (*spos == '"' && escaped == NO)
824
NSString *val = [NSString alloc];
826
val = [val initWithCharacters: dst length: dpos - dst];
829
[dict setObject: val forKey: key];
835
dpos = dst; // reset output buffer
846
break; // Unexpected end of file
851
continue; // escaped newline is removed.
856
if (spos < end && *spos == '\n')
860
continue; // escaped newline is removed.
863
if (isspace(*spos) || *spos == '=')
872
key = [NSString alloc];
873
key = [key initWithCharacters: dst length: dpos - dst];
879
else if (wantVal == YES)
881
NSString *val = [NSString alloc];
883
val = [val initWithCharacters: dst length: dpos - dst];
886
[dict setObject: val forKey: key];
892
dpos = dst; // reset output buffer
897
[dict setObject: @"" forKey: key];
900
NSZoneFree(NSDefaultMallocZone(), src);
901
NSZoneFree(NSDefaultMallocZone(), dst);
907
/* See NSPathUtilities.h for description */
909
GSSetUserName(NSString *aName)
911
NSCParameterAssert([aName length] > 0);
914
* Do nothing if it's not a different user.
916
if ([theUserName isEqualToString: aName])
924
[gnustep_global_lock lock];
925
ShutdownPathUtilities();
928
* Reset things as new user
930
ASSIGN(theUserName, aName);
931
InitialisePathUtilities();
932
[NSUserDefaults resetStandardUserDefaults];
934
[gnustep_global_lock unlock];
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.
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.
951
#if defined(__WIN32__)
952
if (theUserName == nil)
954
/* Use the LOGNAME environment variable if set. */
955
theUserName = [[[NSProcessInfo processInfo] environment]
956
objectForKey: @"LOGNAME"];
957
if ([theUserName length] > 0)
963
/* The GetUserName function returns the current user name */
967
if (GetUserNameW(buf, &n) != 0 && buf[0] != '\0')
969
theUserName = [[NSString alloc] initWithCharacters: buf
970
length: wcslen(buf)];
975
[NSException raise: NSInternalInconsistencyException
976
format: @"Unable to determine current user name"];
981
/* Set olduid to some invalid uid that we could never start off running
983
static int olduid = -1;
988
#endif /* HAVE_GETEUID */
990
if (theUserName == nil || uid != olduid)
992
const char *loginName = 0;
994
struct passwd *pwent = getpwuid (uid);
995
loginName = pwent->pw_name;
996
#endif /* HAVE_GETPWUID */
999
theUserName = [[NSString alloc] initWithCString: loginName];
1001
[NSException raise: NSInternalInconsistencyException
1002
format: @"Unable to determine current user name"];
1010
* Return the caller's home directory as an NSString object.
1011
* Calls NSHomeDirectoryForUser() to do this.
1014
NSHomeDirectory(void)
1016
return NSHomeDirectoryForUser (NSUserName ());
1020
* Returns loginName's home directory as an NSString object.
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.
1027
NSHomeDirectoryForUser(NSString *loginName)
1031
#if !defined(__MINGW32__)
1034
[gnustep_global_lock lock];
1035
pw = getpwnam ([loginName cString]);
1036
if (pw != 0 && pw->pw_dir != NULL)
1038
s = [NSString stringWithCString: pw->pw_dir];
1040
[gnustep_global_lock unlock];
1042
if ([loginName isEqual: NSUserName()] == YES)
1044
NSDictionary *e = [[NSProcessInfo processInfo] environment];
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.
1053
s = [e objectForKey: @"HOMEPATH"];
1054
if (s != nil && ([s length] < 2 || [s characterAtIndex: 1] != ':'))
1056
s = [[e objectForKey: @"HOMEDRIVE"] stringByAppendingString: s];
1060
s = [e objectForKey: @"USERPROFILE"];
1064
; // FIXME: Talk to the NET API and get the profile path
1070
NSLog(@"Trying to get home for '%@' when user is '%@'",
1071
loginName, NSUserName());
1072
NSLog(@"Can't determine other user home directories in Win32.");
1075
if ([s length] == 0 && [loginName length] != 1)
1078
NSLog(@"NSHomeDirectoryForUser(%@) failed", loginName);
1085
* Returns the full username of the current user.
1086
* If unable to determine this, returns the standard user name.
1089
NSFullUserName(void)
1091
#if defined(__WIN32__)
1092
/* FIXME: Win32 way to get full user name via Net API */
1093
return NSUserName();
1098
pw = getpwnam([NSUserName() cString]);
1099
return [NSString stringWithCString: pw->pw_gecos];
1101
NSLog(@"Warning: NSFullUserName not implemented\n");
1102
return NSUserName();
1103
#endif /* HAVE_PWD_H */
1104
#endif /* defined(__Win32__) else */
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.
1114
GSDefaultsRootForUser(NSString *userName)
1117
NSString *defaultsDir;
1119
InitialisePathUtilities();
1120
if ([userName length] == 0)
1122
userName = NSUserName();
1124
home = NSHomeDirectoryForUser(userName);
1125
if ([userName isEqual: NSUserName()])
1127
defaultsDir = gnustepUserDefaultsDir;
1131
NSMutableDictionary *config;
1133
config = GNUstepConfig(nil);
1134
UserConfig(config, userName);
1135
defaultsDir = [config objectForKey: @"GNUSTEP_USER_DEFAULTS_DIR"];
1136
if (defaultsDir == nil)
1138
defaultsDir = @GNUSTEP_TARGET_USER_DEFAULTS_DIR;
1141
#if defined(__MINGW32__)
1142
if ([defaultsDir rangeOfString: @":REGISTRY:"].length > 0)
1144
return defaultsDir; // Just use windows registry.
1147
home = [home stringByAppendingPathComponent: defaultsDir];
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.
1158
NSStandardApplicationPaths(void)
1160
return NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory,
1161
NSAllDomainsMask, YES);
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.
1170
NSStandardLibraryPaths(void)
1172
return NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory,
1173
NSAllDomainsMask, YES);
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.
1180
* If a suitable directory can't be found or created, this function raises an
1181
* NSGenericException.
1184
NSTemporaryDirectory(void)
1186
NSFileManager *manager;
1187
NSString *tempDirName;
1188
NSString *baseTempDirName = nil;
1193
#if !defined(__WIN32__)
1196
unichar buffer[1024];
1198
if (GetTempPathW(1024, buffer))
1200
baseTempDirName = [NSString stringWithCharacters: buffer
1201
length: wcslen(buffer)];
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.
1210
if (baseTempDirName == nil)
1212
NSDictionary *env = [[NSProcessInfo processInfo] environment];
1214
baseTempDirName = [env objectForKey: @"TEMP"];
1215
if (baseTempDirName == nil)
1217
baseTempDirName = [env objectForKey: @"TMP"];
1218
if (baseTempDirName == nil)
1220
#if defined(__MINGW32__)
1222
baseTempDirName = @"/cygdrive/c/";
1224
baseTempDirName = @"/c/";
1227
baseTempDirName = @"/tmp";
1234
* Check that the base directory exists ... if it doesn't we can't
1237
tempDirName = baseTempDirName;
1238
manager = [NSFileManager defaultManager];
1239
if ([manager fileExistsAtPath: tempDirName isDirectory: &flag] == NO
1242
[NSException raise: NSGenericException
1243
format: @"Temporary directory (%@) does not exist",
1245
return nil; /* Not reached. */
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
1253
attr = [manager fileAttributesAtPath: tempDirName traverseLink: YES];
1254
owner = [[attr objectForKey: NSFileOwnerAccountID] intValue];
1255
perm = [[attr objectForKey: NSFilePosixPermissions] intValue];
1258
// Mateu Batle: secure temporary directories don't work in MinGW
1261
#if defined(__MINGW32__)
1268
#endif /* HAVE_GETEUID */
1270
if ((perm != 0700 && perm != 0600) || owner != uid)
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.
1281
secure = [NSString stringWithFormat: @"GNUstepSecure%d", uid];
1283
= [baseTempDirName stringByAppendingPathComponent: secure];
1285
NSLog(@"Temporary directory (%@) may be insecure ... attempting to "
1286
@"add secure subdirectory", tempDirName);
1288
if ([manager fileExistsAtPath: tempDirName] == NO)
1290
NSNumber *p = [NSNumber numberWithInt: 0700];
1292
attr = [NSDictionary dictionaryWithObject: p
1293
forKey: NSFilePosixPermissions];
1294
if ([manager createDirectoryAtPath: tempDirName
1295
attributes: attr] == NO)
1297
[NSException raise: NSGenericException
1299
@"Attempt to create a secure temporary directory (%@) failed.",
1301
return nil; /* Not reached. */
1306
* Check that the new directory is really secure.
1308
attr = [manager fileAttributesAtPath: tempDirName traverseLink: YES];
1309
owner = [[attr objectForKey: NSFileOwnerAccountID] intValue];
1310
perm = [[attr objectForKey: NSFilePosixPermissions] intValue];
1312
if ((perm != 0700 && perm != 0600) || owner != uid)
1314
[NSException raise: NSGenericException
1316
@"Attempt to create a secure temporary directory (%@) failed.",
1318
return nil; /* Not reached. */
1323
if ([manager isWritableFileAtPath: tempDirName] == NO)
1325
[NSException raise: NSGenericException
1326
format: @"Temporary directory (%@) is not writable",
1328
return nil; /* Not reached. */
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.
1345
NSOpenStepRootDirectory(void)
1349
#if defined(__CYGWIN__)
1350
root = @"/cygdrive/c/";
1351
#elif defined(__MINGW32__)
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.
1364
NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directoryKey,
1365
NSSearchPathDomainMask domainMask, BOOL expandTilde)
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];
1383
InitialisePathUtilities();
1385
NSCAssert(gnustepSystemRoot!=nil,@"Path utilities without initialisation!");
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.
1395
#define ADD_PATH(mask, base_dir, add_dir) \
1396
if (domainMask & mask) \
1398
path = [base_dir stringByAppendingPathComponent: add_dir]; \
1399
if (path != nil && [paths containsObject: path] == NO) \
1400
[paths addObject: path]; \
1402
#define ADD_PLATFORM_PATH(mask, add_dir) \
1403
if (domainMask & mask) \
1405
if (add_dir != nil && [paths containsObject: add_dir] == NO) \
1406
[paths addObject: add_dir]; \
1409
switch (directoryKey)
1411
case NSAllApplicationsDirectory:
1413
NSString *devDemosDir;
1414
NSString *devAppsDir;
1415
NSString *devAdminDir;
1417
devDemosDir = [devDir stringByAppendingPathComponent: demosDir];
1418
devAppsDir = [devDir stringByAppendingPathComponent: appsDir];
1419
devAdminDir = [devDir stringByAppendingPathComponent: adminDir];
1421
ADD_PATH(NSUserDomainMask, gnustepUserRoot, appsDir);
1422
ADD_PATH(NSUserDomainMask, gnustepUserRoot, devAppsDir);
1424
ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, appsDir);
1425
ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, devAppsDir);
1426
ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, devAdminDir);
1428
ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, appsDir);
1429
ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, devAppsDir);
1430
ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, devAdminDir);
1432
ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, appsDir);
1433
ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, devAppsDir);
1434
ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, devAdminDir);
1436
ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, devDemosDir);
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);
1446
case NSApplicationDirectory:
1448
ADD_PATH(NSUserDomainMask, gnustepUserRoot, appsDir);
1449
ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, appsDir);
1450
ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, appsDir);
1451
ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, appsDir);
1453
ADD_PLATFORM_PATH(NSLocalDomainMask, localApps);
1454
ADD_PLATFORM_PATH(NSSystemDomainMask, platformApps);
1455
ADD_PLATFORM_PATH(NSSystemDomainMask, osSysApps);
1459
case NSDemoApplicationDirectory:
1461
NSString *devDemosDir;
1463
devDemosDir = [devDir stringByAppendingPathComponent: demosDir];
1464
ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, devDemosDir);
1468
case NSDeveloperApplicationDirectory:
1470
NSString *devAppsDir;
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);
1480
case NSAdminApplicationDirectory:
1482
NSString *devAdminDir;
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);
1490
ADD_PLATFORM_PATH(NSSystemDomainMask, osSysAdmin);
1491
ADD_PLATFORM_PATH(NSSystemDomainMask, platformAdmin);
1495
case NSAllLibrariesDirectory:
1497
ADD_PATH(NSUserDomainMask, gnustepUserRoot, libraryDir);
1498
ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, libraryDir);
1499
ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, libraryDir);
1500
ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, libraryDir);
1502
ADD_PLATFORM_PATH(NSLocalDomainMask, localResources);
1503
ADD_PLATFORM_PATH(NSSystemDomainMask, platformResources);
1507
case NSLibraryDirectory:
1509
ADD_PATH(NSUserDomainMask, gnustepUserRoot, libraryDir);
1510
ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, libraryDir);
1511
ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, libraryDir);
1512
ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, libraryDir);
1514
ADD_PLATFORM_PATH(NSLocalDomainMask, localResources);
1515
ADD_PLATFORM_PATH(NSSystemDomainMask, platformResources);
1519
case NSDeveloperDirectory:
1521
ADD_PATH(NSUserDomainMask, gnustepUserRoot, devDir);
1522
ADD_PATH(NSLocalDomainMask, gnustepLocalRoot, devDir);
1523
ADD_PATH(NSNetworkDomainMask, gnustepNetworkRoot, devDir);
1524
ADD_PATH(NSSystemDomainMask, gnustepSystemRoot, devDir);
1528
case NSUserDirectory:
1530
if (domainMask & NSUserDomainMask)
1532
[paths addObject: gnustepUserRoot];
1537
case NSDocumentationDirectory:
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);
1549
/* Now the GNUstep additions */
1550
case GSApplicationSupportDirectory:
1552
NSString *appSupDir;
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);
1562
case GSFrameworksDirectory:
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);
1574
case GSFontsDirectory:
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);
1586
case GSLibrariesDirectory:
1588
NSString *gslibsDir;
1589
NSString *full = nil;
1590
NSString *part = nil;
1592
gslibsDir = [libraryDir stringByAppendingPathComponent: libsDir];
1593
if ([gnustep_flattened boolValue] == NO
1594
&& gnustep_target_cpu != nil && gnustep_target_os != nil)
1596
part = [gnustep_target_cpu stringByAppendingPathComponent:
1598
if (library_combo != nil)
1600
full = [part stringByAppendingPathComponent: library_combo];
1601
full = [gslibsDir stringByAppendingPathComponent: full];
1603
part = [gslibsDir stringByAppendingPathComponent: part];
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);
1619
ADD_PLATFORM_PATH(NSLocalDomainMask, localLibs);
1620
ADD_PLATFORM_PATH(NSSystemDomainMask, platformLibs);
1621
ADD_PLATFORM_PATH(NSSystemDomainMask, osSysLibs);
1625
case GSToolsDirectory:
1627
NSString *full = nil;
1628
NSString *part = nil;
1630
if ([gnustep_flattened boolValue] == NO
1631
&& gnustep_target_cpu != nil && gnustep_target_os != nil)
1633
part = [gnustep_target_cpu stringByAppendingPathComponent:
1635
if (library_combo != nil)
1637
full = [part stringByAppendingPathComponent: library_combo];
1638
full = [toolsDir stringByAppendingPathComponent: full];
1640
part = [toolsDir stringByAppendingPathComponent: part];
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);
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);
1664
case GSPreferencesDirectory:
1672
#undef ADD_PLATFORM_PATH
1674
count = [paths count];
1675
for (i = 0; i < count; i++)
1677
path = [paths objectAtIndex: i];
1679
/* remove paths which don't exist on this system */
1680
if ([MGR() fileExistsAtPath: path] == NO)
1682
[paths removeObjectAtIndex: i];
1686
else if (expandTilde == YES)
1688
[paths replaceObjectAtIndex: i
1689
withObject: [path stringByExpandingTildeInPath]];
1693
[paths replaceObjectAtIndex: i
1694
withObject: [path stringByAbbreviatingWithTildeInPath]];
1698
AUTORELEASE (paths);