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