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