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