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