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