]> git.saurik.com Git - apple/cf.git/blob - CFFileUtilities.c
CF-744.12.tar.gz
[apple/cf.git] / CFFileUtilities.c
1 /*
2 * Copyright (c) 2012 Apple 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
24 /* CFFileUtilities.c
25 Copyright (c) 1999-2012, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
27 */
28
29 #include "CFInternal.h"
30 #include <CoreFoundation/CFPriv.h>
31
32 #include <sys/stat.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <stdio.h>
36
37 #if DEPLOYMENT_TARGET_WINDOWS
38 #include <io.h>
39 #include <fcntl.h>
40
41 #define close _close
42 #define write _write
43 #define read _read
44 #define open _NS_open
45 #define stat _NS_stat
46 #define fstat _fstat
47 #define mkdir(a,b) _NS_mkdir(a)
48 #define rmdir _NS_rmdir
49 #define unlink _NS_unlink
50
51 #define statinfo _stat
52
53 #else
54
55 #include <unistd.h>
56 #include <dirent.h>
57 #include <sys/types.h>
58 #include <pwd.h>
59 #include <fcntl.h>
60
61 #define statinfo stat
62
63 #endif
64
65 CF_INLINE int openAutoFSNoWait() {
66 #if DEPLOYMENT_TARGET_WINDOWS
67 return -1;
68 #else
69 return (__CFProphylacticAutofsAccess ? open("/dev/autofs_nowait", 0) : -1);
70 #endif
71 }
72
73 CF_INLINE void closeAutoFSNoWait(int fd) {
74 if (-1 != fd) close(fd);
75 }
76
77 __private_extern__ CFStringRef _CFCopyExtensionForAbstractType(CFStringRef abstractType) {
78 return (abstractType ? (CFStringRef)CFRetain(abstractType) : NULL);
79 }
80
81
82 __private_extern__ Boolean _CFCreateDirectory(const char *path) {
83 int no_hang_fd = openAutoFSNoWait();
84 int ret = ((mkdir(path, 0777) == 0) ? true : false);
85 closeAutoFSNoWait(no_hang_fd);
86 return ret;
87 }
88
89 __private_extern__ Boolean _CFRemoveDirectory(const char *path) {
90 int no_hang_fd = openAutoFSNoWait();
91 int ret = ((rmdir(path) == 0) ? true : false);
92 closeAutoFSNoWait(no_hang_fd);
93 return ret;
94 }
95
96 __private_extern__ Boolean _CFDeleteFile(const char *path) {
97 int no_hang_fd = openAutoFSNoWait();
98 int ret = unlink(path) == 0;
99 closeAutoFSNoWait(no_hang_fd);
100 return ret;
101 }
102
103 __private_extern__ Boolean _CFReadBytesFromPathAndGetFD(CFAllocatorRef alloc, const char *path, void **bytes, CFIndex *length, CFIndex maxLength, int extraOpenFlags, int *fd) { // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length.
104 struct statinfo statBuf;
105
106 *bytes = NULL;
107
108
109 int no_hang_fd = openAutoFSNoWait();
110 *fd = open(path, O_RDONLY|extraOpenFlags|CF_OPENFLGS, 0666);
111
112 if (*fd < 0) {
113 closeAutoFSNoWait(no_hang_fd);
114 return false;
115 }
116 if (fstat(*fd, &statBuf) < 0) {
117 int saveerr = thread_errno();
118 close(*fd);
119 *fd = -1;
120 closeAutoFSNoWait(no_hang_fd);
121 thread_set_errno(saveerr);
122 return false;
123 }
124 if ((statBuf.st_mode & S_IFMT) != S_IFREG) {
125 close(*fd);
126 *fd = -1;
127 closeAutoFSNoWait(no_hang_fd);
128 thread_set_errno(EACCES);
129 return false;
130 }
131 if (statBuf.st_size == 0) {
132 *bytes = CFAllocatorAllocate(alloc, 4, 0); // don't return constant string -- it's freed!
133 if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)");
134 *length = 0;
135 } else {
136 CFIndex desiredLength;
137 if ((maxLength >= statBuf.st_size) || (maxLength == 0)) {
138 desiredLength = statBuf.st_size;
139 } else {
140 desiredLength = maxLength;
141 }
142 *bytes = CFAllocatorAllocate(alloc, desiredLength, 0);
143 if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)");
144 // fcntl(fd, F_NOCACHE, 1);
145 if (read(*fd, *bytes, desiredLength) < 0) {
146 CFAllocatorDeallocate(alloc, *bytes);
147 close(*fd);
148 *fd = -1;
149 closeAutoFSNoWait(no_hang_fd);
150 return false;
151 }
152 *length = desiredLength;
153 }
154 closeAutoFSNoWait(no_hang_fd);
155 return true;
156 }
157
158 __private_extern__ Boolean _CFReadBytesFromPath(CFAllocatorRef alloc, const char *path, void **bytes, CFIndex *length, CFIndex maxLength, int extraOpenFlags) {
159 int fd = -1;
160 Boolean result = _CFReadBytesFromPathAndGetFD(alloc, path, bytes, length, maxLength, extraOpenFlags, &fd);
161 if (fd >= 0) {
162 close(fd);
163 }
164 return result;
165 }
166 __private_extern__ Boolean _CFReadBytesFromFile(CFAllocatorRef alloc, CFURLRef url, void **bytes, CFIndex *length, CFIndex maxLength, int extraOpenFlags) {
167 // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length.
168
169 char path[CFMaxPathSize];
170 if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathSize)) {
171 return false;
172 }
173 return _CFReadBytesFromPath(alloc, (const char *)path, bytes, length, maxLength, extraOpenFlags);
174 }
175
176 __private_extern__ Boolean _CFWriteBytesToFile(CFURLRef url, const void *bytes, CFIndex length) {
177 int fd = -1;
178 int mode;
179 struct statinfo statBuf;
180 char path[CFMaxPathSize];
181 if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathSize)) {
182 return false;
183 }
184
185 int no_hang_fd = openAutoFSNoWait();
186 mode = 0666;
187 if (0 == stat(path, &statBuf)) {
188 mode = statBuf.st_mode;
189 } else if (thread_errno() != ENOENT) {
190 closeAutoFSNoWait(no_hang_fd);
191 return false;
192 }
193 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666);
194 if (fd < 0) {
195 closeAutoFSNoWait(no_hang_fd);
196 return false;
197 }
198 if (length && write(fd, bytes, length) != length) {
199 int saveerr = thread_errno();
200 close(fd);
201 closeAutoFSNoWait(no_hang_fd);
202 thread_set_errno(saveerr);
203 return false;
204 }
205 #if DEPLOYMENT_TARGET_WINDOWS
206 FlushFileBuffers((HANDLE)_get_osfhandle(fd));
207 #else
208 fsync(fd);
209 #endif
210 close(fd);
211 closeAutoFSNoWait(no_hang_fd);
212 return true;
213 }
214
215
216 /* On Mac OS 8/9, one of dirSpec and dirURL must be non-NULL. On all other platforms, one of path and dirURL must be non-NULL
217 If both are present, they are assumed to be in-synch; that is, they both refer to the same directory. */
218 /* Lately, dirSpec appears to be (rightfully) unused. */
219 __private_extern__ CFMutableArrayRef _CFContentsOfDirectory(CFAllocatorRef alloc, char *dirPath, void *dirSpec, CFURLRef dirURL, CFStringRef matchingAbstractType) {
220 CFMutableArrayRef files = NULL;
221 Boolean releaseBase = false;
222 CFIndex pathLength = dirPath ? strlen(dirPath) : 0;
223 // MF:!!! Need to use four-letter type codes where appropriate.
224 CFStringRef extension = (matchingAbstractType ? _CFCopyExtensionForAbstractType(matchingAbstractType) : NULL);
225 CFIndex targetExtLen = (extension ? CFStringGetLength(extension) : 0);
226
227 #if DEPLOYMENT_TARGET_WINDOWS
228 // This is a replacement for 'dirent' below, and also uses wchar_t to support unicode paths
229 wchar_t extBuff[CFMaxPathSize];
230 int extBuffInteriorDotCount = 0; //people insist on using extensions like ".trace.plist", so we need to know how many dots back to look :(
231
232 if (targetExtLen > 0) {
233 CFIndex usedBytes = 0;
234 CFStringGetBytes(extension, CFRangeMake(0, targetExtLen), kCFStringEncodingUTF16, 0, false, (uint8_t *)extBuff, CFMaxPathLength, &usedBytes);
235 targetExtLen = usedBytes / sizeof(wchar_t);
236 extBuff[targetExtLen] = '\0';
237 wchar_t *extBuffStr = (wchar_t *)extBuff;
238 if (extBuffStr[0] == '.')
239 extBuffStr++; //skip the first dot, it's legitimate to have ".plist" for example
240
241 wchar_t *extBuffDotPtr = extBuffStr;
242 while ((extBuffDotPtr = wcschr(extBuffStr, '.'))) { //find the next . in the extension...
243 extBuffInteriorDotCount++;
244 extBuffStr = extBuffDotPtr + 1;
245 }
246 }
247
248 wchar_t pathBuf[CFMaxPathSize];
249
250 if (!dirPath) {
251 if (!_CFURLGetWideFileSystemRepresentation(dirURL, true, pathBuf, CFMaxPathLength)) {
252 if (extension) CFRelease(extension);
253 return NULL;
254 }
255
256 pathLength = wcslen(pathBuf);
257
258 } else {
259 // Convert dirPath to a wide representation and put it into our pathBuf
260 // Get the real length of the string in UTF16 characters
261 CFStringRef dirPathStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, dirPath, kCFStringEncodingUTF8);
262 CFIndex strLen = CFStringGetLength(dirPathStr);
263
264 // Copy the string into the buffer and terminate
265 CFStringGetCharacters(dirPathStr, CFRangeMake(0, strLen), (UniChar *)pathBuf);
266 pathBuf[strLen] = 0;
267
268 CFRelease(dirPathStr);
269 }
270
271 WIN32_FIND_DATAW file;
272 HANDLE handle;
273
274 if (pathLength + 2 >= CFMaxPathLength) {
275 if (extension) {
276 CFRelease(extension);
277 }
278 return NULL;
279 }
280
281 pathBuf[pathLength] = '\\';
282 pathBuf[pathLength + 1] = '*';
283 pathBuf[pathLength + 2] = '\0';
284 handle = FindFirstFileW(pathBuf, (LPWIN32_FIND_DATAW)&file);
285 if (INVALID_HANDLE_VALUE == handle) {
286 pathBuf[pathLength] = '\0';
287 if (extension) {
288 CFRelease(extension);
289 }
290 return NULL;
291 }
292
293 files = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
294
295 do {
296 CFURLRef fileURL;
297 CFIndex namelen = wcslen(file.cFileName);
298 if (file.cFileName[0] == '.' && (namelen == 1 || (namelen == 2 && file.cFileName[1] == '.'))) {
299 continue;
300 }
301
302 if (targetExtLen > namelen) continue; // if the extension is the same length or longer than the name, it can't possibly match.
303
304 if (targetExtLen > 0) {
305 if (file.cFileName[namelen - 1] == '.') continue; //filename ends with a dot, no extension
306
307 wchar_t *fileExt = NULL;
308
309 if (extBuffInteriorDotCount == 0) {
310 fileExt = wcsrchr(file.cFileName, '.');
311 } else { //find the Nth occurrence of . from the end of the string, to handle ".foo.bar"
312 wchar_t *save = file.cFileName;
313 while ((save = wcschr(save, '.')) && !fileExt) {
314 wchar_t *temp = save;
315 int moreDots = 0;
316 while ((temp = wcschr(temp, '.'))) {
317 if (++moreDots == extBuffInteriorDotCount) break;
318 }
319 if (moreDots == extBuffInteriorDotCount) {
320 fileExt = save;
321 }
322 }
323 }
324
325 if (!fileExt) continue; //no extension
326
327 if (((const wchar_t *)extBuff)[0] != '.')
328 fileExt++; //omit the dot if the target file extension omits the dot
329
330 CFIndex fileExtLen = wcslen(fileExt);
331
332 //if the extensions are different lengths, they can't possibly match
333 if (fileExtLen != targetExtLen) continue;
334
335 // Check to see if it matches the extension we're looking for.
336 if (_wcsicmp(fileExt, (const wchar_t *)extBuff) != 0) {
337 continue;
338 }
339 }
340 if (dirURL == NULL) {
341 CFStringRef dirURLStr = CFStringCreateWithBytes(alloc, (const uint8_t *)pathBuf, pathLength * sizeof(wchar_t), kCFStringEncodingUTF16, NO);
342 dirURL = CFURLCreateWithFileSystemPath(alloc, dirURLStr, kCFURLWindowsPathStyle, true);
343 CFRelease(dirURLStr);
344 releaseBase = true;
345 }
346 // MF:!!! What about the trailing slash?
347 CFStringRef fileURLStr = CFStringCreateWithBytes(alloc, (const uint8_t *)file.cFileName, namelen * sizeof(wchar_t), kCFStringEncodingUTF16, NO);
348 fileURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, fileURLStr, kCFURLWindowsPathStyle, (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false, dirURL);
349 CFArrayAppendValue(files, fileURL);
350 CFRelease(fileURL);
351 CFRelease(fileURLStr);
352 } while (FindNextFileW(handle, &file));
353 FindClose(handle);
354 pathBuf[pathLength] = '\0';
355
356 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
357 uint8_t extBuff[CFMaxPathSize];
358 int extBuffInteriorDotCount = 0; //people insist on using extensions like ".trace.plist", so we need to know how many dots back to look :(
359
360 if (targetExtLen > 0) {
361 CFStringGetBytes(extension, CFRangeMake(0, targetExtLen), CFStringFileSystemEncoding(), 0, false, extBuff, CFMaxPathLength, &targetExtLen);
362 extBuff[targetExtLen] = '\0';
363 char *extBuffStr = (char *)extBuff;
364 if (extBuffStr[0] == '.')
365 extBuffStr++; //skip the first dot, it's legitimate to have ".plist" for example
366
367 char *extBuffDotPtr = extBuffStr;
368 while ((extBuffDotPtr = strchr(extBuffStr, '.'))) { //find the next . in the extension...
369 extBuffInteriorDotCount++;
370 extBuffStr = extBuffDotPtr + 1;
371 }
372 }
373
374 uint8_t pathBuf[CFMaxPathSize];
375
376 if (!dirPath) {
377 if (!CFURLGetFileSystemRepresentation(dirURL, true, pathBuf, CFMaxPathLength)) {
378 if (extension) CFRelease(extension);
379 return NULL;
380 } else {
381 dirPath = (char *)pathBuf;
382 pathLength = strlen(dirPath);
383 }
384 }
385
386 struct dirent buffer;
387 struct dirent *dp;
388 int err;
389
390 int no_hang_fd = __CFProphylacticAutofsAccess ? open("/dev/autofs_nowait", 0) : -1;
391
392 DIR *dirp = opendir(dirPath);
393 if (!dirp) {
394 if (extension) {
395 CFRelease(extension);
396 }
397 if (-1 != no_hang_fd) close(no_hang_fd);
398 return NULL;
399 // raiseErrno("opendir", path);
400 }
401 files = CFArrayCreateMutable(alloc, 0, & kCFTypeArrayCallBacks);
402
403 while((0 == readdir_r(dirp, &buffer, &dp)) && dp) {
404 CFURLRef fileURL;
405 unsigned namelen = strlen(dp->d_name);
406
407 // skip . & ..; they cause descenders to go berserk
408 if (dp->d_name[0] == '.' && (namelen == 1 || (namelen == 2 && dp->d_name[1] == '.'))) {
409 continue;
410 }
411
412 if (targetExtLen > namelen) continue; // if the extension is the same length or longer than the name, it can't possibly match.
413
414 if (targetExtLen > 0) {
415 if (dp->d_name[namelen - 1] == '.') continue; //filename ends with a dot, no extension
416
417 char *fileExt = NULL;
418 if (extBuffInteriorDotCount == 0) {
419 fileExt = strrchr(dp->d_name, '.');
420 } else { //find the Nth occurrence of . from the end of the string, to handle ".foo.bar"
421 char *save = dp->d_name;
422 while ((save = strchr(save, '.')) && !fileExt) {
423 char *temp = save;
424 int moreDots = 0;
425 while ((temp = strchr(temp, '.'))) {
426 if (++moreDots == extBuffInteriorDotCount) break;
427 }
428 if (moreDots == extBuffInteriorDotCount) {
429 fileExt = save;
430 }
431 }
432 }
433
434 if (!fileExt) continue; //no extension
435
436 if (((char *)extBuff)[0] != '.')
437 fileExt++; //omit the dot if the target extension omits the dot; safe, because we checked to make sure it isn't the last character just before
438
439 size_t fileExtLen = strlen(fileExt);
440
441 //if the extensions are different lengths, they can't possibly match
442 if (fileExtLen != targetExtLen) continue;
443
444 // Check to see if it matches the extension we're looking for.
445 if (strncmp(fileExt, (char *)extBuff, fileExtLen) != 0) {
446 continue;
447 }
448 }
449 if (dirURL == NULL) {
450 dirURL = CFURLCreateFromFileSystemRepresentation(alloc, (uint8_t *)dirPath, pathLength, true);
451 releaseBase = true;
452 }
453 if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN || dp->d_type == DT_LNK || dp->d_type == DT_WHT) {
454 Boolean isDir = (dp->d_type == DT_DIR);
455 if (!isDir) {
456 // Ugh; must stat.
457 char subdirPath[CFMaxPathLength];
458 struct statinfo statBuf;
459 strlcpy(subdirPath, dirPath, sizeof(subdirPath));
460 strlcat(subdirPath, "/", sizeof(subdirPath));
461 strlcat(subdirPath, dp->d_name, sizeof(subdirPath));
462 if (stat(subdirPath, &statBuf) == 0) {
463 isDir = ((statBuf.st_mode & S_IFMT) == S_IFDIR);
464 }
465 }
466 #if DEPLOYMENT_TARGET_LINUX
467 fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, (uint8_t *)dp->d_name, namelen, isDir, dirURL);
468 #else
469 fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, (uint8_t *)dp->d_name, dp->d_namlen, isDir, dirURL);
470 #endif
471 } else {
472 #if DEPLOYMENT_TARGET_LINUX
473 fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc, (uint8_t *)dp->d_name, namelen, false, dirURL);
474 #else
475 fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc, (uint8_t *)dp->d_name, dp->d_namlen, false, dirURL);
476 #endif
477 }
478 CFArrayAppendValue(files, fileURL);
479 CFRelease(fileURL);
480 }
481 err = closedir(dirp);
482 if (-1 != no_hang_fd) close(no_hang_fd);
483 if (err != 0) {
484 CFRelease(files);
485 if (releaseBase) {
486 CFRelease(dirURL);
487 }
488 if (extension) {
489 CFRelease(extension);
490 }
491 return NULL;
492 }
493
494 #else
495
496 #error _CFContentsOfDirectory() unknown architecture, not implemented
497
498 #endif
499
500 if (extension) {
501 CFRelease(extension);
502 }
503 if (releaseBase) {
504 CFRelease(dirURL);
505 }
506 return files;
507 }
508
509 __private_extern__ SInt32 _CFGetPathProperties(CFAllocatorRef alloc, char *path, Boolean *exists, SInt32 *posixMode, int64_t *size, CFDateRef *modTime, SInt32 *ownerID, CFArrayRef *dirContents) {
510 Boolean fileExists;
511 Boolean isDirectory = false;
512
513 if ((exists == NULL) && (posixMode == NULL) && (size == NULL) && (modTime == NULL) && (ownerID == NULL) && (dirContents == NULL)) {
514 // Nothing to do.
515 return 0;
516 }
517
518 struct statinfo statBuf;
519
520 if (stat(path, &statBuf) != 0) {
521 // stat failed, but why?
522 if (thread_errno() == ENOENT) {
523 fileExists = false;
524 } else {
525 return thread_errno();
526 }
527 } else {
528 fileExists = true;
529 isDirectory = ((statBuf.st_mode & S_IFMT) == S_IFDIR);
530 }
531
532
533 if (exists != NULL) {
534 *exists = fileExists;
535 }
536
537 if (posixMode != NULL) {
538 if (fileExists) {
539
540 *posixMode = statBuf.st_mode;
541
542 } else {
543 *posixMode = 0;
544 }
545 }
546
547 if (size != NULL) {
548 if (fileExists) {
549
550 *size = statBuf.st_size;
551
552 } else {
553 *size = 0;
554 }
555 }
556
557 if (modTime != NULL) {
558 if (fileExists) {
559 #if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
560 struct timespec ts = {statBuf.st_mtime, 0};
561 #else
562 struct timespec ts = statBuf.st_mtimespec;
563 #endif
564 *modTime = CFDateCreate(alloc, _CFAbsoluteTimeFromFileTimeSpec(ts));
565 } else {
566 *modTime = NULL;
567 }
568 }
569
570 if (ownerID != NULL) {
571 if (fileExists) {
572
573 *ownerID = statBuf.st_uid;
574
575 } else {
576 *ownerID = -1;
577 }
578 }
579
580 if (dirContents != NULL) {
581 if (fileExists && isDirectory) {
582
583 CFMutableArrayRef contents = _CFContentsOfDirectory(alloc, (char *)path, NULL, NULL, NULL);
584
585 if (contents) {
586 *dirContents = contents;
587 } else {
588 *dirContents = NULL;
589 }
590 } else {
591 *dirContents = NULL;
592 }
593 }
594 return 0;
595 }
596
597 __private_extern__ SInt32 _CFGetFileProperties(CFAllocatorRef alloc, CFURLRef pathURL, Boolean *exists, SInt32 *posixMode, int64_t *size, CFDateRef *modTime, SInt32 *ownerID, CFArrayRef *dirContents) {
598
599 char path[CFMaxPathSize];
600
601 if (!CFURLGetFileSystemRepresentation(pathURL, true, (uint8_t *)path, CFMaxPathLength)) {
602 return -1;
603 }
604
605 return _CFGetPathProperties(alloc, path, exists, posixMode, size, modTime, ownerID, dirContents);
606 }
607
608
609 #if DEPLOYMENT_TARGET_WINDOWS
610 #define WINDOWS_PATH_SEMANTICS
611 #else
612 #define UNIX_PATH_SEMANTICS
613 #endif
614
615 #if defined(WINDOWS_PATH_SEMANTICS)
616 #define CFPreferredSlash ((UniChar)'\\')
617 #elif defined(UNIX_PATH_SEMANTICS)
618 #define CFPreferredSlash ((UniChar)'/')
619 #elif defined(HFS_PATH_SEMANTICS)
620 #define CFPreferredSlash ((UniChar)':')
621 #else
622 #error Cannot define NSPreferredSlash on this platform
623 #endif
624
625 #if defined(HFS_PATH_SEMANTICS)
626 #define HAS_DRIVE(S) (false)
627 #define HAS_NET(S) (false)
628 #else
629 #define HAS_DRIVE(S) ((S)[1] == ':' && (('A' <= (S)[0] && (S)[0] <= 'Z') || ('a' <= (S)[0] && (S)[0] <= 'z')))
630 #define HAS_NET(S) ((S)[0] == '\\' && (S)[1] == '\\')
631 #endif
632
633 #if defined(WINDOWS_PATH_SEMANTICS)
634 #define IS_SLASH(C) ((C) == '\\' || (C) == '/')
635 #elif defined(UNIX_PATH_SEMANTICS)
636 #define IS_SLASH(C) ((C) == '/')
637 #elif defined(HFS_PATH_SEMANTICS)
638 #define IS_SLASH(C) ((C) == ':')
639 #endif
640
641 __private_extern__ UniChar _CFGetSlash(){
642 return CFPreferredSlash;
643 }
644
645 __private_extern__ Boolean _CFIsAbsolutePath(UniChar *unichars, CFIndex length) {
646 if (length < 1) {
647 return false;
648 }
649 #if defined(WINDOWS_PATH_SEMANTICS)
650 if (unichars[0] == '~') {
651 return true;
652 }
653 if (length < 2) {
654 return false;
655 }
656 if (HAS_NET(unichars)) {
657 return true;
658 }
659 if (length < 3) {
660 return false;
661 }
662 if (IS_SLASH(unichars[2]) && HAS_DRIVE(unichars)) {
663 return true;
664 }
665 #elif defined(HFS_PATH_SEMANTICS)
666 return !IS_SLASH(unichars[0]);
667 #else
668 if (unichars[0] == '~') {
669 return true;
670 }
671 if (IS_SLASH(unichars[0])) {
672 return true;
673 }
674 #endif
675 return false;
676 }
677
678 __private_extern__ Boolean _CFStripTrailingPathSlashes(UniChar *unichars, CFIndex *length) {
679 Boolean destHasDrive = (1 < *length) && HAS_DRIVE(unichars);
680 CFIndex oldLength = *length;
681 while (((destHasDrive && 3 < *length) || (!destHasDrive && 1 < *length)) && IS_SLASH(unichars[*length - 1])) {
682 (*length)--;
683 }
684 return (oldLength != *length);
685 }
686
687 __private_extern__ Boolean _CFAppendTrailingPathSlash(UniChar *unichars, CFIndex *length, CFIndex maxLength) {
688 if (maxLength < *length + 1) {
689 return false;
690 }
691 switch (*length) {
692 case 0:
693 break;
694 case 1:
695 if (!IS_SLASH(unichars[0])) {
696 unichars[(*length)++] = CFPreferredSlash;
697 }
698 break;
699 case 2:
700 if (!HAS_DRIVE(unichars) && !HAS_NET(unichars)) {
701 unichars[(*length)++] = CFPreferredSlash;
702 }
703 break;
704 default:
705 unichars[(*length)++] = CFPreferredSlash;
706 break;
707 }
708 return true;
709 }
710
711 __private_extern__ Boolean _CFAppendPathComponent(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *component, CFIndex componentLength) {
712 if (0 == componentLength) {
713 return true;
714 }
715 if (maxLength < *length + 1 + componentLength) {
716 return false;
717 }
718 _CFAppendTrailingPathSlash(unichars, length, maxLength);
719 memmove(unichars + *length, component, componentLength * sizeof(UniChar));
720 *length += componentLength;
721 return true;
722 }
723
724 __private_extern__ Boolean _CFAppendPathExtension(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *extension, CFIndex extensionLength) {
725 if (maxLength < *length + 1 + extensionLength) {
726 return false;
727 }
728 if ((0 < extensionLength && IS_SLASH(extension[0])) || (1 < extensionLength && HAS_DRIVE(extension))) {
729 return false;
730 }
731 _CFStripTrailingPathSlashes(unichars, length);
732 switch (*length) {
733 case 0:
734 return false;
735 case 1:
736 if (IS_SLASH(unichars[0]) || unichars[0] == '~') {
737 return false;
738 }
739 break;
740 case 2:
741 if (HAS_DRIVE(unichars) || HAS_NET(unichars)) {
742 return false;
743 }
744 break;
745 case 3:
746 if (IS_SLASH(unichars[2]) && HAS_DRIVE(unichars)) {
747 return false;
748 }
749 break;
750 }
751 if (0 < *length && unichars[0] == '~') {
752 CFIndex idx;
753 Boolean hasSlash = false;
754 for (idx = 1; idx < *length; idx++) {
755 if (IS_SLASH(unichars[idx])) {
756 hasSlash = true;
757 break;
758 }
759 }
760 if (!hasSlash) {
761 return false;
762 }
763 }
764 unichars[(*length)++] = '.';
765 memmove(unichars + *length, extension, extensionLength * sizeof(UniChar));
766 *length += extensionLength;
767 return true;
768 }
769
770 __private_extern__ Boolean _CFTransmutePathSlashes(UniChar *unichars, CFIndex *length, UniChar replSlash) {
771 CFIndex didx, sidx, scnt = *length;
772 sidx = (1 < *length && HAS_NET(unichars)) ? 2 : 0;
773 didx = sidx;
774 while (sidx < scnt) {
775 if (IS_SLASH(unichars[sidx])) {
776 unichars[didx++] = replSlash;
777 for (sidx++; sidx < scnt && IS_SLASH(unichars[sidx]); sidx++);
778 } else {
779 unichars[didx++] = unichars[sidx++];
780 }
781 }
782 *length = didx;
783 return (scnt != didx);
784 }
785
786 __private_extern__ CFIndex _CFStartOfLastPathComponent(UniChar *unichars, CFIndex length) {
787 CFIndex idx;
788 if (length < 2) {
789 return 0;
790 }
791 for (idx = length - 1; idx; idx--) {
792 if (IS_SLASH(unichars[idx - 1])) {
793 return idx;
794 }
795 }
796 if ((2 < length) && HAS_DRIVE(unichars)) {
797 return 2;
798 }
799 return 0;
800 }
801
802 __private_extern__ CFIndex _CFLengthAfterDeletingLastPathComponent(UniChar *unichars, CFIndex length) {
803 CFIndex idx;
804 if (length < 2) {
805 return 0;
806 }
807 for (idx = length - 1; idx; idx--) {
808 if (IS_SLASH(unichars[idx - 1])) {
809 if ((idx != 1) && (!HAS_DRIVE(unichars) || idx != 3)) {
810 return idx - 1;
811 }
812 return idx;
813 }
814 }
815 if ((2 < length) && HAS_DRIVE(unichars)) {
816 return 2;
817 }
818 return 0;
819 }
820
821 __private_extern__ CFIndex _CFStartOfPathExtension(UniChar *unichars, CFIndex length) {
822 CFIndex idx;
823 if (length < 2) {
824 return 0;
825 }
826 for (idx = length - 1; idx; idx--) {
827 if (IS_SLASH(unichars[idx - 1])) {
828 return 0;
829 }
830 if (unichars[idx] != '.') {
831 continue;
832 }
833 if (idx == 2 && HAS_DRIVE(unichars)) {
834 return 0;
835 }
836 return idx;
837 }
838 return 0;
839 }
840
841 __private_extern__ CFIndex _CFLengthAfterDeletingPathExtension(UniChar *unichars, CFIndex length) {
842 CFIndex start = _CFStartOfPathExtension(unichars, length);
843 return ((0 < start) ? start : length);
844 }
845
846