]>
Commit | Line | Data |
---|---|---|
9ce05555 | 1 | /* |
bd5b749c | 2 | * Copyright (c) 2008 Apple Inc. All rights reserved. |
9ce05555 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
9ce05555 A |
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" | |
bd5b749c A |
30 | #include <string.h> |
31 | #include <unistd.h> | |
32 | #include <dirent.h> | |
33 | #include <sys/stat.h> | |
34 | #include <sys/types.h> | |
35 | #include <pwd.h> | |
36 | #include <fcntl.h> | |
37 | #include <errno.h> | |
38 | #include <stdio.h> | |
9ce05555 | 39 | |
bd5b749c | 40 | #define CF_OPENFLGS (0) |
9ce05555 A |
41 | |
42 | ||
43 | __private_extern__ CFStringRef _CFCopyExtensionForAbstractType(CFStringRef abstractType) { | |
bd5b749c | 44 | return (abstractType ? (CFStringRef)CFRetain(abstractType) : NULL); |
9ce05555 A |
45 | } |
46 | ||
47 | ||
48 | __private_extern__ Boolean _CFCreateDirectory(const char *path) { | |
bd5b749c | 49 | #if 0 || 0 |
9ce05555 A |
50 | return CreateDirectoryA(path, (LPSECURITY_ATTRIBUTES)NULL); |
51 | #else | |
bd5b749c A |
52 | int no_hang_fd = open("/dev/autofs_nowait", 0); |
53 | int ret = ((mkdir(path, 0777) == 0) ? true : false); | |
54 | close(no_hang_fd); | |
55 | return ret; | |
9ce05555 A |
56 | #endif |
57 | } | |
58 | ||
59 | __private_extern__ Boolean _CFRemoveDirectory(const char *path) { | |
bd5b749c | 60 | #if 0 || 0 |
9ce05555 A |
61 | return RemoveDirectoryA(path); |
62 | #else | |
bd5b749c A |
63 | int no_hang_fd = open("/dev/autofs_nowait", 0); |
64 | int ret = ((rmdir(path) == 0) ? true : false); | |
65 | close(no_hang_fd); | |
66 | return ret; | |
9ce05555 A |
67 | #endif |
68 | } | |
69 | ||
70 | __private_extern__ Boolean _CFDeleteFile(const char *path) { | |
bd5b749c | 71 | #if 0 || 0 |
9ce05555 A |
72 | return DeleteFileA(path); |
73 | #else | |
bd5b749c A |
74 | int no_hang_fd = open("/dev/autofs_nowait", 0); |
75 | int ret = unlink(path) == 0; | |
76 | close(no_hang_fd); | |
77 | return ret; | |
9ce05555 A |
78 | #endif |
79 | } | |
80 | ||
81 | __private_extern__ Boolean _CFReadBytesFromFile(CFAllocatorRef alloc, CFURLRef url, void **bytes, CFIndex *length, CFIndex maxLength) { | |
82 | // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length. | |
83 | struct stat statBuf; | |
84 | int fd = -1; | |
85 | char path[CFMaxPathSize]; | |
bd5b749c | 86 | if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathSize)) { |
9ce05555 A |
87 | return false; |
88 | } | |
89 | ||
90 | *bytes = NULL; | |
91 | ||
0ae65c4b | 92 | |
bd5b749c | 93 | #if 0 || 0 |
9ce05555 A |
94 | fd = open(path, O_RDONLY|CF_OPENFLGS, 0666|_S_IREAD); |
95 | #else | |
bd5b749c | 96 | int no_hang_fd = open("/dev/autofs_nowait", 0); |
9ce05555 A |
97 | fd = open(path, O_RDONLY|CF_OPENFLGS, 0666); |
98 | #endif | |
99 | if (fd < 0) { | |
bd5b749c | 100 | close(no_hang_fd); |
9ce05555 A |
101 | return false; |
102 | } | |
103 | if (fstat(fd, &statBuf) < 0) { | |
104 | int saveerr = thread_errno(); | |
105 | close(fd); | |
bd5b749c | 106 | close(no_hang_fd); |
9ce05555 A |
107 | thread_set_errno(saveerr); |
108 | return false; | |
109 | } | |
110 | if ((statBuf.st_mode & S_IFMT) != S_IFREG) { | |
111 | close(fd); | |
bd5b749c | 112 | close(no_hang_fd); |
9ce05555 A |
113 | thread_set_errno(EACCES); |
114 | return false; | |
115 | } | |
116 | if (statBuf.st_size == 0) { | |
117 | *bytes = CFAllocatorAllocate(alloc, 4, 0); // don't return constant string -- it's freed! | |
118 | if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)"); | |
119 | *length = 0; | |
120 | } else { | |
121 | CFIndex desiredLength; | |
122 | if ((maxLength >= statBuf.st_size) || (maxLength == 0)) { | |
123 | desiredLength = statBuf.st_size; | |
124 | } else { | |
125 | desiredLength = maxLength; | |
126 | } | |
127 | *bytes = CFAllocatorAllocate(alloc, desiredLength, 0); | |
128 | if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)"); | |
d8925383 | 129 | // fcntl(fd, F_NOCACHE, 1); |
9ce05555 A |
130 | if (read(fd, *bytes, desiredLength) < 0) { |
131 | CFAllocatorDeallocate(alloc, *bytes); | |
132 | close(fd); | |
bd5b749c | 133 | close(no_hang_fd); |
9ce05555 A |
134 | return false; |
135 | } | |
136 | *length = desiredLength; | |
137 | } | |
138 | close(fd); | |
bd5b749c | 139 | close(no_hang_fd); |
9ce05555 A |
140 | return true; |
141 | } | |
142 | ||
143 | __private_extern__ Boolean _CFWriteBytesToFile(CFURLRef url, const void *bytes, CFIndex length) { | |
144 | struct stat statBuf; | |
145 | int fd = -1; | |
bd5b749c | 146 | int mode; |
9ce05555 | 147 | char path[CFMaxPathSize]; |
bd5b749c | 148 | if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathSize)) { |
9ce05555 A |
149 | return false; |
150 | } | |
151 | ||
bd5b749c A |
152 | #if 0 || 0 |
153 | mode = 0666; | |
9ce05555 A |
154 | if (0 == stat(path, &statBuf)) { |
155 | mode = statBuf.st_mode; | |
156 | } else if (thread_errno() != ENOENT) { | |
157 | return false; | |
158 | } | |
9ce05555 | 159 | fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666|_S_IWRITE); |
9ce05555 A |
160 | if (fd < 0) { |
161 | return false; | |
162 | } | |
163 | if (length && write(fd, bytes, length) != length) { | |
164 | int saveerr = thread_errno(); | |
165 | close(fd); | |
166 | thread_set_errno(saveerr); | |
167 | return false; | |
168 | } | |
9ce05555 | 169 | FlushFileBuffers((HANDLE)_get_osfhandle(fd)); |
bd5b749c | 170 | close(fd); |
9ce05555 | 171 | #else |
bd5b749c A |
172 | int no_hang_fd = open("/dev/autofs_nowait", 0); |
173 | mode = 0666; | |
174 | if (0 == stat(path, &statBuf)) { | |
175 | mode = statBuf.st_mode; | |
176 | } else if (thread_errno() != ENOENT) { | |
177 | close(no_hang_fd); | |
178 | return false; | |
179 | } | |
180 | fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666); | |
181 | if (fd < 0) { | |
182 | close(no_hang_fd); | |
183 | return false; | |
184 | } | |
185 | if (length && write(fd, bytes, length) != length) { | |
186 | int saveerr = thread_errno(); | |
187 | close(fd); | |
188 | close(no_hang_fd); | |
189 | thread_set_errno(saveerr); | |
190 | return false; | |
191 | } | |
9ce05555 | 192 | fsync(fd); |
9ce05555 | 193 | close(fd); |
bd5b749c A |
194 | close(no_hang_fd); |
195 | #endif | |
9ce05555 A |
196 | return true; |
197 | } | |
198 | ||
199 | ||
200 | /* 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 | |
201 | If both are present, they are assumed to be in-synch; that is, they both refer to the same directory. */ | |
bd5b749c | 202 | /* Lately, dirSpec appears to be (rightfully) unused. */ |
9ce05555 A |
203 | __private_extern__ CFMutableArrayRef _CFContentsOfDirectory(CFAllocatorRef alloc, char *dirPath, void *dirSpec, CFURLRef dirURL, CFStringRef matchingAbstractType) { |
204 | CFMutableArrayRef files = NULL; | |
205 | Boolean releaseBase = false; | |
206 | CFIndex pathLength = dirPath ? strlen(dirPath) : 0; | |
207 | // MF:!!! Need to use four-letter type codes where appropriate. | |
208 | CFStringRef extension = (matchingAbstractType ? _CFCopyExtensionForAbstractType(matchingAbstractType) : NULL); | |
209 | CFIndex extLen = (extension ? CFStringGetLength(extension) : 0); | |
210 | uint8_t extBuff[CFMaxPathSize]; | |
9ce05555 A |
211 | |
212 | if (extLen > 0) { | |
bd5b749c | 213 | CFStringGetBytes(extension, CFRangeMake(0, extLen), CFStringFileSystemEncoding(), 0, false, extBuff, CFMaxPathLength, &extLen); |
9ce05555 A |
214 | extBuff[extLen] = '\0'; |
215 | } | |
d8925383 A |
216 | |
217 | uint8_t pathBuf[CFMaxPathSize]; | |
218 | ||
219 | if (!dirPath) { | |
220 | if (!CFURLGetFileSystemRepresentation(dirURL, true, pathBuf, CFMaxPathLength)) { | |
221 | if (extension) CFRelease(extension); | |
222 | return NULL; | |
223 | } else { | |
bd5b749c | 224 | dirPath = (char *)pathBuf; |
d8925383 A |
225 | pathLength = strlen(dirPath); |
226 | } | |
227 | } | |
9ce05555 | 228 | |
bd5b749c A |
229 | #if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD |
230 | struct dirent buffer; | |
d8925383 A |
231 | struct dirent *dp; |
232 | int err; | |
bd5b749c A |
233 | |
234 | int no_hang_fd = open("/dev/autofs_nowait", 0); | |
235 | ||
236 | DIR *dirp = opendir(dirPath); | |
9ce05555 A |
237 | if (!dirp) { |
238 | if (extension) { | |
239 | CFRelease(extension); | |
240 | } | |
bd5b749c | 241 | close(no_hang_fd); |
9ce05555 A |
242 | return NULL; |
243 | // raiseErrno("opendir", path); | |
244 | } | |
245 | files = CFArrayCreateMutable(alloc, 0, & kCFTypeArrayCallBacks); | |
246 | ||
bd5b749c | 247 | while((0 == readdir_r(dirp, &buffer, &dp)) && dp) { |
9ce05555 A |
248 | CFURLRef fileURL; |
249 | unsigned namelen = strlen(dp->d_name); | |
250 | ||
251 | // skip . & ..; they cause descenders to go berserk | |
252 | if (dp->d_name[0] == '.' && (namelen == 1 || (namelen == 2 && dp->d_name[1] == '.'))) { | |
253 | continue; | |
254 | } | |
bd5b749c A |
255 | |
256 | if (extLen > namelen) continue; // if the extension is the same length or longer than the name, it can't possibly match. | |
257 | ||
9ce05555 A |
258 | if (extLen > 0) { |
259 | // Check to see if it matches the extension we're looking for. | |
bd5b749c | 260 | if (strncmp(&(dp->d_name[namelen - extLen]), (char *)extBuff, extLen) != 0) { |
9ce05555 A |
261 | continue; |
262 | } | |
263 | } | |
264 | if (dirURL == NULL) { | |
bd5b749c | 265 | dirURL = CFURLCreateFromFileSystemRepresentation(alloc, (uint8_t *)dirPath, pathLength, true); |
9ce05555 A |
266 | releaseBase = true; |
267 | } | |
bd5b749c A |
268 | if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) { |
269 | Boolean isDir = (dp->d_type == DT_DIR); | |
270 | if (!isDir) { | |
271 | // Ugh; must stat. | |
272 | char subdirPath[CFMaxPathLength]; | |
273 | struct stat statBuf; | |
274 | strlcpy(subdirPath, dirPath, sizeof(subdirPath)); | |
275 | strlcat(subdirPath, "/", sizeof(subdirPath)); | |
276 | strlcat(subdirPath, dp->d_name, sizeof(subdirPath)); | |
277 | if (stat(subdirPath, &statBuf) == 0) { | |
278 | isDir = ((statBuf.st_mode & S_IFMT) == S_IFDIR); | |
279 | } | |
280 | } | |
281 | fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, (uint8_t *)dp->d_name, dp->d_namlen, isDir, dirURL); | |
282 | } else { | |
283 | fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc, (uint8_t *)dp->d_name, dp->d_namlen, false, dirURL); | |
284 | } | |
9ce05555 A |
285 | CFArrayAppendValue(files, fileURL); |
286 | CFRelease(fileURL); | |
287 | } | |
288 | err = closedir(dirp); | |
bd5b749c | 289 | close(no_hang_fd); |
9ce05555 A |
290 | if (err != 0) { |
291 | CFRelease(files); | |
292 | if (releaseBase) { | |
293 | CFRelease(dirURL); | |
294 | } | |
295 | if (extension) { | |
296 | CFRelease(extension); | |
297 | } | |
298 | return NULL; | |
9ce05555 A |
299 | } |
300 | ||
9ce05555 A |
301 | #else |
302 | ||
303 | #error _CFContentsOfDirectory() unknown architechture, not implemented | |
304 | ||
305 | #endif | |
306 | ||
307 | if (extension) { | |
308 | CFRelease(extension); | |
309 | } | |
310 | if (releaseBase) { | |
311 | CFRelease(dirURL); | |
312 | } | |
313 | return files; | |
314 | } | |
315 | ||
316 | __private_extern__ SInt32 _CFGetFileProperties(CFAllocatorRef alloc, CFURLRef pathURL, Boolean *exists, SInt32 *posixMode, int64_t *size, CFDateRef *modTime, SInt32 *ownerID, CFArrayRef *dirContents) { | |
317 | Boolean fileExists; | |
318 | Boolean isDirectory = false; | |
319 | ||
bd5b749c A |
320 | struct stat64 statBuf; |
321 | char path[CFMaxPathSize]; | |
9ce05555 A |
322 | |
323 | if ((exists == NULL) && (posixMode == NULL) && (size == NULL) && (modTime == NULL) && (ownerID == NULL) && (dirContents == NULL)) { | |
324 | // Nothing to do. | |
325 | return 0; | |
326 | } | |
327 | ||
bd5b749c | 328 | if (!CFURLGetFileSystemRepresentation(pathURL, true, (uint8_t *)path, CFMaxPathLength)) { |
9ce05555 A |
329 | return -1; |
330 | } | |
331 | ||
bd5b749c | 332 | if (stat64(path, &statBuf) != 0) { |
9ce05555 A |
333 | // stat failed, but why? |
334 | if (thread_errno() == ENOENT) { | |
335 | fileExists = false; | |
336 | } else { | |
337 | return thread_errno(); | |
338 | } | |
339 | } else { | |
340 | fileExists = true; | |
341 | isDirectory = ((statBuf.st_mode & S_IFMT) == S_IFDIR); | |
342 | } | |
343 | ||
344 | ||
345 | if (exists != NULL) { | |
346 | *exists = fileExists; | |
347 | } | |
348 | ||
349 | if (posixMode != NULL) { | |
350 | if (fileExists) { | |
351 | ||
352 | *posixMode = statBuf.st_mode; | |
353 | ||
354 | } else { | |
355 | *posixMode = 0; | |
356 | } | |
357 | } | |
358 | ||
359 | if (size != NULL) { | |
360 | if (fileExists) { | |
361 | ||
362 | *size = statBuf.st_size; | |
363 | ||
364 | } else { | |
365 | *size = 0; | |
366 | } | |
367 | } | |
368 | ||
369 | if (modTime != NULL) { | |
370 | if (fileExists) { | |
bd5b749c A |
371 | CFAbsoluteTime theTime = (CFAbsoluteTime)statBuf.st_mtimespec.tv_sec - kCFAbsoluteTimeIntervalSince1970; |
372 | theTime += (CFAbsoluteTime)statBuf.st_mtimespec.tv_nsec / 1000000000.0; | |
9ce05555 A |
373 | *modTime = CFDateCreate(alloc, theTime); |
374 | } else { | |
375 | *modTime = NULL; | |
376 | } | |
377 | } | |
378 | ||
379 | if (ownerID != NULL) { | |
380 | if (fileExists) { | |
381 | ||
382 | *ownerID = statBuf.st_uid; | |
383 | ||
384 | } else { | |
385 | *ownerID = -1; | |
386 | } | |
387 | } | |
388 | ||
389 | if (dirContents != NULL) { | |
390 | if (fileExists && isDirectory) { | |
391 | ||
392 | CFMutableArrayRef contents = _CFContentsOfDirectory(alloc, path, NULL, pathURL, NULL); | |
393 | ||
394 | if (contents) { | |
395 | *dirContents = contents; | |
396 | } else { | |
397 | *dirContents = NULL; | |
398 | } | |
399 | } else { | |
400 | *dirContents = NULL; | |
401 | } | |
402 | } | |
403 | return 0; | |
404 | } | |
405 | ||
406 | ||
407 | // MF:!!! Should pull in the rest of the UniChar based path utils from Foundation. | |
bd5b749c | 408 | #if (DEPLOYMENT_TARGET_MACOSX) || defined(__svr4__) || defined(__hpux__) || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD |
9ce05555 A |
409 | #define UNIX_PATH_SEMANTICS |
410 | #elif defined(__WIN32__) | |
411 | #define WINDOWS_PATH_SEMANTICS | |
412 | #else | |
413 | #error Unknown platform | |
414 | #endif | |
415 | ||
416 | #if defined(WINDOWS_PATH_SEMANTICS) | |
417 | #define CFPreferredSlash ((UniChar)'\\') | |
418 | #elif defined(UNIX_PATH_SEMANTICS) | |
419 | #define CFPreferredSlash ((UniChar)'/') | |
420 | #elif defined(HFS_PATH_SEMANTICS) | |
421 | #define CFPreferredSlash ((UniChar)':') | |
422 | #else | |
423 | #error Cannot define NSPreferredSlash on this platform | |
424 | #endif | |
425 | ||
426 | #if defined(HFS_PATH_SEMANTICS) | |
427 | #define HAS_DRIVE(S) (false) | |
428 | #define HAS_NET(S) (false) | |
429 | #else | |
430 | #define HAS_DRIVE(S) ((S)[1] == ':' && (('A' <= (S)[0] && (S)[0] <= 'Z') || ('a' <= (S)[0] && (S)[0] <= 'z'))) | |
431 | #define HAS_NET(S) ((S)[0] == '\\' && (S)[1] == '\\') | |
432 | #endif | |
433 | ||
434 | #if defined(WINDOWS_PATH_SEMANTICS) | |
435 | #define IS_SLASH(C) ((C) == '\\' || (C) == '/') | |
436 | #elif defined(UNIX_PATH_SEMANTICS) | |
437 | #define IS_SLASH(C) ((C) == '/') | |
438 | #elif defined(HFS_PATH_SEMANTICS) | |
439 | #define IS_SLASH(C) ((C) == ':') | |
440 | #endif | |
441 | ||
442 | __private_extern__ Boolean _CFIsAbsolutePath(UniChar *unichars, CFIndex length) { | |
443 | if (length < 1) { | |
444 | return false; | |
445 | } | |
446 | #if defined(WINDOWS_PATH_SEMANTICS) | |
447 | if (unichars[0] == '~') { | |
448 | return true; | |
449 | } | |
450 | if (length < 2) { | |
451 | return false; | |
452 | } | |
453 | if (HAS_NET(unichars)) { | |
454 | return true; | |
455 | } | |
456 | if (length < 3) { | |
457 | return false; | |
458 | } | |
459 | if (IS_SLASH(unichars[2]) && HAS_DRIVE(unichars)) { | |
460 | return true; | |
461 | } | |
462 | #elif defined(HFS_PATH_SEMANTICS) | |
463 | return !IS_SLASH(unichars[0]); | |
464 | #else | |
465 | if (unichars[0] == '~') { | |
466 | return true; | |
467 | } | |
468 | if (IS_SLASH(unichars[0])) { | |
469 | return true; | |
470 | } | |
471 | #endif | |
472 | return false; | |
473 | } | |
474 | ||
475 | __private_extern__ Boolean _CFStripTrailingPathSlashes(UniChar *unichars, CFIndex *length) { | |
476 | Boolean destHasDrive = (1 < *length) && HAS_DRIVE(unichars); | |
477 | CFIndex oldLength = *length; | |
478 | while (((destHasDrive && 3 < *length) || (!destHasDrive && 1 < *length)) && IS_SLASH(unichars[*length - 1])) { | |
479 | (*length)--; | |
480 | } | |
481 | return (oldLength != *length); | |
482 | } | |
483 | ||
484 | __private_extern__ Boolean _CFAppendPathComponent(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *component, CFIndex componentLength) { | |
485 | if (0 == componentLength) { | |
486 | return true; | |
487 | } | |
488 | if (maxLength < *length + 1 + componentLength) { | |
489 | return false; | |
490 | } | |
491 | switch (*length) { | |
492 | case 0: | |
493 | break; | |
494 | case 1: | |
495 | if (!IS_SLASH(unichars[0])) { | |
496 | unichars[(*length)++] = CFPreferredSlash; | |
497 | } | |
498 | break; | |
499 | case 2: | |
500 | if (!HAS_DRIVE(unichars) && !HAS_NET(unichars)) { | |
501 | unichars[(*length)++] = CFPreferredSlash; | |
502 | } | |
503 | break; | |
504 | default: | |
505 | unichars[(*length)++] = CFPreferredSlash; | |
506 | break; | |
507 | } | |
508 | memmove(unichars + *length, component, componentLength * sizeof(UniChar)); | |
509 | *length += componentLength; | |
510 | return true; | |
511 | } | |
512 | ||
513 | __private_extern__ Boolean _CFAppendPathExtension(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *extension, CFIndex extensionLength) { | |
514 | if (maxLength < *length + 1 + extensionLength) { | |
515 | return false; | |
516 | } | |
517 | if ((0 < extensionLength && IS_SLASH(extension[0])) || (1 < extensionLength && HAS_DRIVE(extension))) { | |
518 | return false; | |
519 | } | |
520 | _CFStripTrailingPathSlashes(unichars, length); | |
521 | switch (*length) { | |
522 | case 0: | |
523 | return false; | |
524 | case 1: | |
525 | if (IS_SLASH(unichars[0]) || unichars[0] == '~') { | |
526 | return false; | |
527 | } | |
528 | break; | |
529 | case 2: | |
530 | if (HAS_DRIVE(unichars) || HAS_NET(unichars)) { | |
531 | return false; | |
532 | } | |
533 | break; | |
534 | case 3: | |
535 | if (IS_SLASH(unichars[2]) && HAS_DRIVE(unichars)) { | |
536 | return false; | |
537 | } | |
538 | break; | |
539 | } | |
540 | if (0 < *length && unichars[0] == '~') { | |
541 | CFIndex idx; | |
542 | Boolean hasSlash = false; | |
543 | for (idx = 1; idx < *length; idx++) { | |
544 | if (IS_SLASH(unichars[idx])) { | |
545 | hasSlash = true; | |
546 | break; | |
547 | } | |
548 | } | |
549 | if (!hasSlash) { | |
550 | return false; | |
551 | } | |
552 | } | |
553 | unichars[(*length)++] = '.'; | |
554 | memmove(unichars + *length, extension, extensionLength * sizeof(UniChar)); | |
555 | *length += extensionLength; | |
556 | return true; | |
557 | } | |
558 | ||
559 | __private_extern__ Boolean _CFTransmutePathSlashes(UniChar *unichars, CFIndex *length, UniChar replSlash) { | |
560 | CFIndex didx, sidx, scnt = *length; | |
561 | sidx = (1 < *length && HAS_NET(unichars)) ? 2 : 0; | |
562 | didx = sidx; | |
563 | while (sidx < scnt) { | |
564 | if (IS_SLASH(unichars[sidx])) { | |
565 | unichars[didx++] = replSlash; | |
566 | for (sidx++; sidx < scnt && IS_SLASH(unichars[sidx]); sidx++); | |
567 | } else { | |
568 | unichars[didx++] = unichars[sidx++]; | |
569 | } | |
570 | } | |
571 | *length = didx; | |
572 | return (scnt != didx); | |
573 | } | |
574 | ||
575 | __private_extern__ CFIndex _CFStartOfLastPathComponent(UniChar *unichars, CFIndex length) { | |
576 | CFIndex idx; | |
577 | if (length < 2) { | |
578 | return 0; | |
579 | } | |
580 | for (idx = length - 1; idx; idx--) { | |
581 | if (IS_SLASH(unichars[idx - 1])) { | |
582 | return idx; | |
583 | } | |
584 | } | |
585 | if ((2 < length) && HAS_DRIVE(unichars)) { | |
586 | return 2; | |
587 | } | |
588 | return 0; | |
589 | } | |
590 | ||
591 | __private_extern__ CFIndex _CFLengthAfterDeletingLastPathComponent(UniChar *unichars, CFIndex length) { | |
592 | CFIndex idx; | |
593 | if (length < 2) { | |
594 | return 0; | |
595 | } | |
596 | for (idx = length - 1; idx; idx--) { | |
597 | if (IS_SLASH(unichars[idx - 1])) { | |
598 | if ((idx != 1) && (!HAS_DRIVE(unichars) || idx != 3)) { | |
599 | return idx - 1; | |
600 | } | |
601 | return idx; | |
602 | } | |
603 | } | |
604 | if ((2 < length) && HAS_DRIVE(unichars)) { | |
605 | return 2; | |
606 | } | |
607 | return 0; | |
608 | } | |
609 | ||
610 | __private_extern__ CFIndex _CFStartOfPathExtension(UniChar *unichars, CFIndex length) { | |
611 | CFIndex idx; | |
612 | if (length < 2) { | |
613 | return 0; | |
614 | } | |
615 | for (idx = length - 1; idx; idx--) { | |
616 | if (IS_SLASH(unichars[idx - 1])) { | |
617 | return 0; | |
618 | } | |
619 | if (unichars[idx] != '.') { | |
620 | continue; | |
621 | } | |
622 | if (idx == 2 && HAS_DRIVE(unichars)) { | |
623 | return 0; | |
624 | } | |
625 | return idx; | |
626 | } | |
627 | return 0; | |
628 | } | |
629 | ||
630 | __private_extern__ CFIndex _CFLengthAfterDeletingPathExtension(UniChar *unichars, CFIndex length) { | |
631 | CFIndex start = _CFStartOfPathExtension(unichars, length); | |
632 | return ((0 < start) ? start : length); | |
633 | } | |
634 | ||
635 | #undef CF_OPENFLGS | |
636 | #undef UNIX_PATH_SEMANTICS | |
637 | #undef WINDOWS_PATH_SEMANTICS | |
638 | #undef HFS_PATH_SEMANTICS | |
639 | #undef CFPreferredSlash | |
640 | #undef HAS_DRIVE | |
641 | #undef HAS_NET | |
642 | #undef IS_SLASH | |
643 |