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