]> git.saurik.com Git - apple/cf.git/blob - Base.subproj/CFPlatform.c
CF-368.18.tar.gz
[apple/cf.git] / Base.subproj / CFPlatform.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /* CFPlatform.c
24 Copyright 1999-2002, Apple, Inc. All rights reserved.
25 Responsibility: Christopher Kane
26 */
27
28 #include "CFInternal.h"
29 #include "CFPriv.h"
30 #if defined(__WIN32__)
31 #include <windows.h>
32 #include <stdlib.h>
33 #else
34 #include <sys/stat.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <pwd.h>
38 #endif
39 #if defined(__MACH__)
40 #include <crt_externs.h>
41 #endif
42
43 extern char *getenv(const char *name);
44
45 #if defined(__MACH__)
46 #define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8
47 #else
48 #define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding()
49 #endif
50
51 #if defined(__MACH__)
52 char **_CFArgv(void) {
53 return *_NSGetArgv();
54 }
55
56 int _CFArgc(void) {
57 return *_NSGetArgc();
58 }
59 #endif
60
61
62 __private_extern__ Boolean _CFGetCurrentDirectory(char *path, int maxlen) {
63 #if defined(__WIN32__)
64 DWORD len = GetCurrentDirectoryA(maxlen, path);
65 return (0 != len && len + 1 <= maxlen);
66 #else
67 return getcwd(path, maxlen) != NULL;
68 #endif
69 }
70
71 static Boolean __CFIsCFM = false;
72
73 // If called super early, we just return false
74 __private_extern__ Boolean _CFIsCFM(void) {
75 return __CFIsCFM;
76 }
77
78 #if defined(__WIN32__)
79 #define PATH_SEP '\\'
80 #else
81 #define PATH_SEP '/'
82 #endif
83
84 #if !defined(__WIN32__)
85 #define PATH_LIST_SEP ':'
86
87 static char *_CFSearchForNameInPath(CFAllocatorRef alloc, const char *name, char *path) {
88 struct stat statbuf;
89 char *nname = CFAllocatorAllocate(alloc, strlen(name) + strlen(path) + 2, 0);
90 if (__CFOASafe) __CFSetLastAllocationEventName(nname, "CFUtilities (temp)");
91 for (;;) {
92 char *p = (char *)strchr(path, PATH_LIST_SEP);
93 if (NULL != p) {
94 *p = '\0';
95 }
96 nname[0] = '\0';
97 strcat(nname, path);
98 strcat(nname, "/");
99 strcat(nname, name);
100 // Could also do access(us, X_OK) == 0 in next condition,
101 // for executable-only searching
102 if (0 == stat(nname, &statbuf) && (statbuf.st_mode & S_IFMT) == S_IFREG) {
103 if (p != NULL) {
104 *p = PATH_LIST_SEP;
105 }
106 return nname;
107 }
108 if (NULL == p) {
109 break;
110 }
111 *p = PATH_LIST_SEP;
112 path = p + 1;
113 }
114 CFAllocatorDeallocate(alloc, nname);
115 return NULL;
116 }
117
118 #endif
119
120
121 #if defined(__WIN32__)
122 // Returns the path to the CF DLL, which we can then use to find resources like char sets
123
124 __private_extern__ const char *_CFDLLPath(void) {
125 static TCHAR cachedPath[MAX_PATH+1] = "";
126
127 if ('\0' == cachedPath[0]) {
128 #if defined(DEBUG)
129 char *DLLFileName = "CoreFoundation_debug";
130 #elif defined(PROFILE)
131 char *DLLFileName = "CoreFoundation_profile";
132 #else
133 char *DLLFileName = "CoreFoundation";
134 #endif
135 HMODULE ourModule = GetModuleHandle(DLLFileName);
136 CFAssert(ourModule, __kCFLogAssertion, "GetModuleHandle failed");
137
138 DWORD wResult = GetModuleFileName(ourModule, cachedPath, MAX_PATH+1);
139 CFAssert1(wResult > 0, __kCFLogAssertion, "GetModuleFileName failed: %d", GetLastError());
140 CFAssert1(wResult < MAX_PATH+1, __kCFLogAssertion, "GetModuleFileName result truncated: %s", cachedPath);
141
142 // strip off last component, the DLL name
143 CFIndex idx;
144 for (idx = wResult - 1; idx; idx--) {
145 if ('\\' == cachedPath[idx]) {
146 cachedPath[idx] = '\0';
147 break;
148 }
149 }
150 }
151 return cachedPath;
152 }
153 #endif
154
155 static const char *__CFProcessPath = NULL;
156 static const char *__CFprogname = NULL;
157
158 const char **_CFGetProgname(void) {
159 if (!__CFprogname)
160 _CFProcessPath(); // sets up __CFprogname as a side-effect
161 return &__CFprogname;
162 }
163
164 const char **_CFGetProcessPath(void) {
165 if (!__CFProcessPath)
166 _CFProcessPath(); // sets up __CFProcessPath as a side-effect
167 return &__CFProcessPath;
168 }
169
170 const char *_CFProcessPath(void) {
171 CFAllocatorRef alloc = NULL;
172 char *thePath = NULL;
173
174 if (__CFProcessPath) return __CFProcessPath;
175 if (!__CFProcessPath) {
176 thePath = getenv("CFProcessPath");
177
178 alloc = CFRetain(__CFGetDefaultAllocator());
179
180 if (thePath) {
181 int len = strlen(thePath);
182 __CFProcessPath = CFAllocatorAllocate(alloc, len+1, 0);
183 if (__CFOASafe) __CFSetLastAllocationEventName((void *)__CFProcessPath, "CFUtilities (process-path)");
184 memmove((char *)__CFProcessPath, thePath, len + 1);
185 }
186 }
187
188 #if defined(__MACH__)
189 int execIndex = 0;
190 {
191 struct stat exec, lcfm;
192 uint32_t size = CFMaxPathSize;
193 char buffer[CFMaxPathSize];
194 if (0 == _NSGetExecutablePath(buffer, &size) &&
195 strcasestr(buffer, "LaunchCFMApp") != NULL &&
196 0 == stat("/System/Library/Frameworks/Carbon.framework/Versions/Current/Support/LaunchCFMApp", &lcfm) &&
197 0 == stat(buffer, &exec) &&
198 (lcfm.st_dev == exec.st_dev) &&
199 (lcfm.st_ino == exec.st_ino)) {
200 // Executable is LaunchCFMApp, take special action
201 execIndex = 1;
202 __CFIsCFM = true;
203 }
204 }
205 #endif
206
207 #if defined(__WIN32__)
208 if (!__CFProcessPath) {
209 char buf[CFMaxPathSize] = {0};
210 DWORD rlen = GetModuleFileName(NULL, buf, 1028);
211 thePath = rlen ? buf : NULL;
212 #else
213 if (!__CFProcessPath && NULL != (*_NSGetArgv())[execIndex]) {
214 char buf[CFMaxPathSize] = {0};
215 struct stat statbuf;
216 const char *arg0 = (*_NSGetArgv())[execIndex];
217 if (arg0[0] == '/') {
218 // We've got an absolute path; look no further;
219 thePath = (char *)arg0;
220 } else {
221 char *theList = getenv("PATH");
222 if (NULL != theList && NULL == strrchr(arg0, '/')) {
223 thePath = _CFSearchForNameInPath(alloc, arg0, theList);
224 if (thePath) {
225 // User could have "." or "../bin" or other relative path in $PATH
226 if (('/' != thePath[0]) && _CFGetCurrentDirectory(buf, CFMaxPathSize)) {
227 strlcat(buf, "/", CFMaxPathSize);
228 strlcat(buf, thePath, CFMaxPathSize);
229 if (0 == stat(buf, &statbuf)) {
230 CFAllocatorDeallocate(alloc, (void *)thePath);
231 thePath = buf;
232 }
233 }
234 if (thePath != buf) {
235 strlcpy(buf, thePath, CFMaxPathSize);
236 CFAllocatorDeallocate(alloc, (void *)thePath);
237 thePath = buf;
238 }
239 }
240 }
241 }
242
243 // After attempting a search through $PATH, if existant,
244 // try prepending the current directory to argv[0].
245 if (!thePath && _CFGetCurrentDirectory(buf, CFMaxPathSize)) {
246 if (buf[strlen(buf)-1] != '/') {
247 strlcat(buf, "/", CFMaxPathSize);
248 }
249 strlcat(buf, arg0, CFMaxPathSize);
250 if (0 == stat(buf, &statbuf)) {
251 thePath = buf;
252 }
253 }
254
255 if (thePath) {
256 // We are going to process the buffer replacing all "/./" and "//" with "/"
257 CFIndex srcIndex = 0, dstIndex = 0;
258 CFIndex len = strlen(thePath);
259 for (srcIndex=0; srcIndex<len; srcIndex++) {
260 thePath[dstIndex] = thePath[srcIndex];
261 dstIndex++;
262 while (srcIndex < len-1 && thePath[srcIndex] == '/' && (thePath[srcIndex+1] == '/' || (thePath[srcIndex+1] == '.' && srcIndex < len-2 && thePath[srcIndex+2] == '/'))) srcIndex += (thePath[srcIndex+1] == '/' ? 1 : 2);
263 }
264 thePath[dstIndex] = 0;
265 }
266 if (!thePath) {
267 thePath = (*_NSGetArgv())[execIndex];
268 }
269 #endif
270 if (thePath) {
271 int len = strlen(thePath);
272 __CFProcessPath = CFAllocatorAllocate(alloc, len + 1, 0);
273 if (__CFOASafe) __CFSetLastAllocationEventName((void *)__CFProcessPath, "CFUtilities (process-path)");
274 memmove((char *)__CFProcessPath, thePath, len + 1);
275 }
276 if (__CFProcessPath) {
277
278 const char *p = 0;
279 int i;
280 for (i = 0; __CFProcessPath[i] != 0; i++){
281 if (__CFProcessPath[i] == PATH_SEP)
282 p = __CFProcessPath + i;
283 }
284 if (p != 0)
285 __CFprogname = p + 1;
286 else
287 __CFprogname = __CFProcessPath;
288 }
289 }
290 if (!__CFProcessPath) {
291 __CFProcessPath = "";
292 __CFprogname = __CFProcessPath;
293 } else {
294 const char *p = 0;
295 int i;
296 for (i = 0; __CFProcessPath[i] != 0; i++){
297 if (__CFProcessPath[i] == PATH_SEP)
298 p = __CFProcessPath + i;
299 }
300 if (p != 0)
301 __CFprogname = p + 1;
302 else
303 __CFprogname = __CFProcessPath;
304 }
305 return __CFProcessPath;
306 }
307
308 __private_extern__ CFStringRef _CFProcessNameString(void) {
309 static CFStringRef __CFProcessNameString = NULL;
310 if (!__CFProcessNameString) {
311 const char *processName = *_CFGetProgname();
312 if (!processName) processName = "";
313 __CFProcessNameString = CFStringCreateWithCString(__CFGetDefaultAllocator(), processName, kCFPlatformInterfaceStringEncoding);
314 }
315 return __CFProcessNameString;
316 }
317
318 static CFStringRef __CFUserName = NULL;
319
320 #if defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__)
321 static CFURLRef __CFHomeDirectory = NULL;
322 static uint32_t __CFEUID = -1;
323 static uint32_t __CFUID = -1;
324
325 static CFURLRef _CFCopyHomeDirURLForUser(struct passwd *upwd) {
326 CFURLRef home = NULL;
327 if (upwd && upwd->pw_dir) {
328 home = CFURLCreateFromFileSystemRepresentation(NULL, upwd->pw_dir, strlen(upwd->pw_dir), true);
329 }
330 return home;
331 }
332
333 static void _CFUpdateUserInfo(void) {
334 struct passwd *upwd;
335
336 __CFEUID = geteuid();
337 __CFUID = getuid();
338 if (__CFHomeDirectory) CFRelease(__CFHomeDirectory);
339 __CFHomeDirectory = NULL;
340 if (__CFUserName) CFRelease(__CFUserName);
341 __CFUserName = NULL;
342
343 upwd = getpwuid(__CFEUID ? __CFEUID : __CFUID);
344 __CFHomeDirectory = _CFCopyHomeDirURLForUser(upwd);
345 if (!__CFHomeDirectory) {
346 const char *cpath = getenv("HOME");
347 if (cpath) {
348 __CFHomeDirectory = CFURLCreateFromFileSystemRepresentation(NULL, cpath, strlen(cpath), true);
349 }
350 }
351
352 // This implies that UserManager stores directory info in CString
353 // rather than FileSystemRep. Perhaps this is wrong & we should
354 // expect NeXTSTEP encodings. A great test of our localized system would
355 // be to have a user "O-umlat z e r". XXX
356 if (upwd && upwd->pw_name) {
357 __CFUserName = CFStringCreateWithCString(NULL, upwd->pw_name, kCFPlatformInterfaceStringEncoding);
358 } else {
359 const char *cuser = getenv("USER");
360 if (cuser)
361 __CFUserName = CFStringCreateWithCString(NULL, cuser, kCFPlatformInterfaceStringEncoding);
362 }
363 }
364 #endif
365
366 static CFURLRef _CFCreateHomeDirectoryURLForUser(CFStringRef uName) {
367 #if defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__)
368 if (!uName) {
369 if (geteuid() != __CFEUID || getuid() != __CFUID || !__CFHomeDirectory)
370 _CFUpdateUserInfo();
371 if (__CFHomeDirectory) CFRetain(__CFHomeDirectory);
372 return __CFHomeDirectory;
373 } else {
374 struct passwd *upwd = NULL;
375 char buf[128], *user;
376 SInt32 len = CFStringGetLength(uName), size = CFStringGetMaximumSizeForEncoding(len, kCFPlatformInterfaceStringEncoding);
377 CFIndex usedSize;
378 if (size < 127) {
379 user = buf;
380 } else {
381 user = CFAllocatorAllocate(kCFAllocatorDefault, size+1, 0);
382 if (__CFOASafe) __CFSetLastAllocationEventName(user, "CFUtilities (temp)");
383 }
384 if (CFStringGetBytes(uName, CFRangeMake(0, len), kCFPlatformInterfaceStringEncoding, 0, true, user, size, &usedSize) == len) {
385 user[usedSize] = '\0';
386 upwd = getpwnam(user);
387 }
388 if (buf != user) {
389 CFAllocatorDeallocate(kCFAllocatorDefault, user);
390 }
391 return _CFCopyHomeDirURLForUser(upwd);
392 }
393 #elif defined(__WIN32__)
394 #warning CF: Windows home directory goop disabled
395 return NULL;
396 #if 0
397 CFString *user = !uName ? CFUserName() : uName;
398
399 if (!uName || CFEqual(user, CFUserName())) {
400 const char *cpath = getenv("HOMEPATH");
401 const char *cdrive = getenv("HOMEDRIVE");
402 if (cdrive && cpath) {
403 char fullPath[CFMaxPathSize];
404 CFStringRef str;
405 strcpy(fullPath, cdrive);
406 strncat(fullPath, cpath, CFMaxPathSize-strlen(cdrive)-1);
407 str = CFStringCreateWithCString(NULL, fullPath, kCFPlatformInterfaceStringEncoding);
408 home = CFURLCreateWithFileSystemPath(NULL, str, kCFURLWindowsPathStyle, true);
409 CFRelease(str);
410 }
411 }
412 if (!home) {
413 struct _USER_INFO_2 *userInfo;
414 HINSTANCE hinstDll = GetModuleHandleA("NETAPI32");
415 if (!hinstDll)
416 hinstDll = LoadLibraryEx("NETAPI32", NULL, 0);
417 if (hinstDll) {
418 FARPROC lpfn = GetProcAddress(hinstDll, "NetUserGetInfo");
419 if (lpfn) {
420 unsigned namelen = CFStringGetLength(user);
421 UniChar *username;
422 username = CFAllocatorAllocate(kCFAllocatorDefault, sizeof(UniChar) * (namelen + 1), 0);
423 if (__CFOASafe) __CFSetLastAllocationEventName(username, "CFUtilities (temp)");
424 CFStringGetCharacters(user, CFRangeMake(0, namelen), username);
425 if (!(*lpfn)(NULL, (LPWSTR)username, 2, (LPBYTE *)&userInfo)) {
426 UInt32 len = 0;
427 CFMutableStringRef str;
428 while (userInfo->usri2_home_dir[len] != 0) len ++;
429 str = CFStringCreateMutable(NULL, len+1);
430 CFStringAppendCharacters(str, userInfo->usri2_home_dir, len);
431 home = CFURLCreateWithFileSystemPath(NULL, str, kCFURLWindowsPathStyle, true);
432 CFRelease(str);
433 }
434 CFAllocatorDeallocate(kCFAllocatorDefault, username);
435 }
436 } else {
437 }
438 }
439 // We could do more here (as in KB Article Q101507). If that article is to
440 // be believed, we should only run into this case on Win95, or through
441 // user error.
442 if (CFStringGetLength(CFURLGetPath(home)) == 0) {
443 CFRelease(home);
444 home=NULL;
445 }
446 #endif
447
448 #else
449 #error Dont know how to compute users home directories on this platform
450 #endif
451 }
452
453 static CFStringRef _CFUserName(void) {
454 #if defined(__MACH__) || defined(__svr4__) || defined(__hpux__) || defined(__LINUX__) || defined(__FREEBSD__)
455 if (geteuid() != __CFEUID || getuid() != __CFUID)
456 _CFUpdateUserInfo();
457 #elif defined(__WIN32__)
458 if (!__CFUserName) {
459 char username[1040];
460 DWORD size = 1040;
461 username[0] = 0;
462 if (GetUserNameA(username, &size)) {
463 __CFUserName = CFStringCreateWithCString(NULL, username, kCFPlatformInterfaceStringEncoding);
464 } else {
465 const char *cname = getenv("USERNAME");
466 if (cname)
467 __CFUserName = CFStringCreateWithCString(NULL, cname, kCFPlatformInterfaceStringEncoding);
468 }
469 }
470 #else
471 #error Dont know how to compute user name on this platform
472 #endif
473 if (!__CFUserName)
474 __CFUserName = CFRetain(CFSTR(""));
475 return __CFUserName;
476 }
477
478 __private_extern__ CFStringRef _CFGetUserName(void) {
479 return CFStringCreateCopy(NULL, _CFUserName());
480 }
481
482 #define CFMaxHostNameLength 256
483 #define CFMaxHostNameSize (CFMaxHostNameLength+1)
484
485 __private_extern__ CFStringRef _CFStringCreateHostName(void) {
486 char myName[CFMaxHostNameSize];
487
488 // return @"" instead of nil a la CFUserName() and Ali Ozer
489 if (0 != gethostname(myName, CFMaxHostNameSize)) myName[0] = '\0';
490 return CFStringCreateWithCString(NULL, myName, kCFPlatformInterfaceStringEncoding);
491 }
492
493 /* These are sanitized versions of the above functions. We might want to eliminate the above ones someday.
494 These can return NULL.
495 */
496 CF_EXPORT CFStringRef CFGetUserName(void) {
497 return _CFUserName();
498 }
499
500 CF_EXPORT CFURLRef CFCopyHomeDirectoryURLForUser(CFStringRef uName) {
501 return _CFCreateHomeDirectoryURLForUser(uName);
502 }
503
504 #undef CFMaxHostNameLength
505 #undef CFMaxHostNameSize
506