]>
Commit | Line | Data |
---|---|---|
9ce05555 | 1 | /* |
e29e285d | 2 | * Copyright (c) 2015 Apple Inc. All rights reserved. |
9ce05555 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
d7384798 | 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. | |
d7384798 | 12 | * |
9ce05555 A |
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. | |
d7384798 | 20 | * |
9ce05555 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
f64f9b69 | 23 | |
9ce05555 | 24 | /* CFFileUtilities.c |
d7384798 | 25 | Copyright (c) 1999-2014, Apple Inc. All rights reserved. |
8ca704e1 | 26 | Responsibility: Tony Parker |
9ce05555 A |
27 | */ |
28 | ||
29 | #include "CFInternal.h" | |
cf7d2af9 | 30 | #include <CoreFoundation/CFPriv.h> |
856091c5 A |
31 | |
32 | #include <sys/stat.h> | |
33 | #include <errno.h> | |
34 | #include <string.h> | |
35 | #include <stdio.h> | |
36 | ||
cf7d2af9 A |
37 | #if DEPLOYMENT_TARGET_WINDOWS |
38 | #include <io.h> | |
bd5b749c | 39 | #include <fcntl.h> |
9ce05555 | 40 | |
cf7d2af9 A |
41 | #define close _close |
42 | #define write _write | |
43 | #define read _read | |
44 | #define open _NS_open | |
45 | #define stat _NS_stat | |
46 | #define fstat _fstat | |
47 | #define mkdir(a,b) _NS_mkdir(a) | |
48 | #define rmdir _NS_rmdir | |
49 | #define unlink _NS_unlink | |
50 | ||
51 | #define statinfo _stat | |
52 | ||
53 | #else | |
856091c5 A |
54 | |
55 | #include <unistd.h> | |
56 | #include <dirent.h> | |
57 | #include <sys/types.h> | |
58 | #include <pwd.h> | |
59 | #include <fcntl.h> | |
cf7d2af9 A |
60 | |
61 | #define statinfo stat | |
62 | ||
63 | #endif | |
64 | ||
cf7d2af9 A |
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) { | |
cf7d2af9 | 74 | if (-1 != fd) close(fd); |
cf7d2af9 | 75 | } |
9ce05555 | 76 | |
a48904a4 | 77 | CF_PRIVATE CFStringRef _CFCopyExtensionForAbstractType(CFStringRef abstractType) { |
bd5b749c | 78 | return (abstractType ? (CFStringRef)CFRetain(abstractType) : NULL); |
9ce05555 A |
79 | } |
80 | ||
81 | ||
a48904a4 | 82 | CF_PRIVATE Boolean _CFCreateDirectory(const char *path) { |
cf7d2af9 | 83 | int no_hang_fd = openAutoFSNoWait(); |
bd5b749c | 84 | int ret = ((mkdir(path, 0777) == 0) ? true : false); |
cf7d2af9 | 85 | closeAutoFSNoWait(no_hang_fd); |
bd5b749c | 86 | return ret; |
9ce05555 A |
87 | } |
88 | ||
a48904a4 | 89 | CF_PRIVATE Boolean _CFRemoveDirectory(const char *path) { |
cf7d2af9 | 90 | int no_hang_fd = openAutoFSNoWait(); |
bd5b749c | 91 | int ret = ((rmdir(path) == 0) ? true : false); |
cf7d2af9 | 92 | closeAutoFSNoWait(no_hang_fd); |
bd5b749c | 93 | return ret; |
9ce05555 A |
94 | } |
95 | ||
a48904a4 | 96 | CF_PRIVATE Boolean _CFDeleteFile(const char *path) { |
cf7d2af9 | 97 | int no_hang_fd = openAutoFSNoWait(); |
bd5b749c | 98 | int ret = unlink(path) == 0; |
cf7d2af9 | 99 | closeAutoFSNoWait(no_hang_fd); |
bd5b749c | 100 | return ret; |
9ce05555 A |
101 | } |
102 | ||
d7384798 | 103 | static Boolean _CFReadBytesFromPathAndGetFD(CFAllocatorRef alloc, const char *path, void **bytes, CFIndex *length, CFIndex maxLength, int extraOpenFlags, int *fd) { // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length. |
cf7d2af9 | 104 | struct statinfo statBuf; |
856091c5 | 105 | |
9ce05555 | 106 | *bytes = NULL; |
856091c5 | 107 | |
0ae65c4b | 108 | |
cf7d2af9 | 109 | int no_hang_fd = openAutoFSNoWait(); |
856091c5 A |
110 | *fd = open(path, O_RDONLY|extraOpenFlags|CF_OPENFLGS, 0666); |
111 | ||
112 | if (*fd < 0) { | |
cf7d2af9 | 113 | closeAutoFSNoWait(no_hang_fd); |
9ce05555 A |
114 | return false; |
115 | } | |
856091c5 | 116 | if (fstat(*fd, &statBuf) < 0) { |
9ce05555 | 117 | int saveerr = thread_errno(); |
856091c5 A |
118 | close(*fd); |
119 | *fd = -1; | |
cf7d2af9 | 120 | closeAutoFSNoWait(no_hang_fd); |
9ce05555 A |
121 | thread_set_errno(saveerr); |
122 | return false; | |
123 | } | |
124 | if ((statBuf.st_mode & S_IFMT) != S_IFREG) { | |
856091c5 A |
125 | close(*fd); |
126 | *fd = -1; | |
cf7d2af9 | 127 | closeAutoFSNoWait(no_hang_fd); |
9ce05555 A |
128 | thread_set_errno(EACCES); |
129 | return false; | |
130 | } | |
131 | if (statBuf.st_size == 0) { | |
132 | *bytes = CFAllocatorAllocate(alloc, 4, 0); // don't return constant string -- it's freed! | |
133 | if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)"); | |
134 | *length = 0; | |
135 | } else { | |
136 | CFIndex desiredLength; | |
137 | if ((maxLength >= statBuf.st_size) || (maxLength == 0)) { | |
138 | desiredLength = statBuf.st_size; | |
139 | } else { | |
140 | desiredLength = maxLength; | |
141 | } | |
142 | *bytes = CFAllocatorAllocate(alloc, desiredLength, 0); | |
d7384798 A |
143 | if (!bytes) { |
144 | close(*fd); | |
145 | *fd = -1; | |
146 | closeAutoFSNoWait(no_hang_fd); | |
147 | return false; | |
148 | } | |
9ce05555 | 149 | if (__CFOASafe) __CFSetLastAllocationEventName(*bytes, "CFUtilities (file-bytes)"); |
856091c5 A |
150 | // fcntl(fd, F_NOCACHE, 1); |
151 | if (read(*fd, *bytes, desiredLength) < 0) { | |
9ce05555 | 152 | CFAllocatorDeallocate(alloc, *bytes); |
856091c5 A |
153 | close(*fd); |
154 | *fd = -1; | |
155 | closeAutoFSNoWait(no_hang_fd); | |
9ce05555 A |
156 | return false; |
157 | } | |
158 | *length = desiredLength; | |
159 | } | |
cf7d2af9 | 160 | closeAutoFSNoWait(no_hang_fd); |
9ce05555 A |
161 | return true; |
162 | } | |
163 | ||
d7384798 | 164 | static Boolean _CFReadBytesFromPath(CFAllocatorRef alloc, const char *path, void **bytes, CFIndex *length, CFIndex maxLength, int extraOpenFlags) { |
856091c5 A |
165 | int fd = -1; |
166 | Boolean result = _CFReadBytesFromPathAndGetFD(alloc, path, bytes, length, maxLength, extraOpenFlags, &fd); | |
167 | if (fd >= 0) { | |
168 | close(fd); | |
169 | } | |
170 | return result; | |
171 | } | |
a48904a4 | 172 | CF_PRIVATE Boolean _CFReadBytesFromFile(CFAllocatorRef alloc, CFURLRef url, void **bytes, CFIndex *length, CFIndex maxLength, int extraOpenFlags) { |
856091c5 A |
173 | // maxLength is the number of bytes desired, or 0 if the whole file is desired regardless of length. |
174 | ||
175 | char path[CFMaxPathSize]; | |
176 | if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathSize)) { | |
177 | return false; | |
178 | } | |
179 | return _CFReadBytesFromPath(alloc, (const char *)path, bytes, length, maxLength, extraOpenFlags); | |
180 | } | |
181 | ||
a48904a4 | 182 | CF_PRIVATE Boolean _CFWriteBytesToFile(CFURLRef url, const void *bytes, CFIndex length) { |
9ce05555 | 183 | int fd = -1; |
bd5b749c | 184 | int mode; |
cf7d2af9 | 185 | struct statinfo statBuf; |
9ce05555 | 186 | char path[CFMaxPathSize]; |
bd5b749c | 187 | if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathSize)) { |
9ce05555 A |
188 | return false; |
189 | } | |
190 | ||
cf7d2af9 | 191 | int no_hang_fd = openAutoFSNoWait(); |
bd5b749c | 192 | mode = 0666; |
9ce05555 A |
193 | if (0 == stat(path, &statBuf)) { |
194 | mode = statBuf.st_mode; | |
195 | } else if (thread_errno() != ENOENT) { | |
cf7d2af9 | 196 | closeAutoFSNoWait(no_hang_fd); |
9ce05555 A |
197 | return false; |
198 | } | |
cf7d2af9 | 199 | fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|CF_OPENFLGS, 0666); |
9ce05555 | 200 | if (fd < 0) { |
cf7d2af9 | 201 | closeAutoFSNoWait(no_hang_fd); |
9ce05555 A |
202 | return false; |
203 | } | |
204 | if (length && write(fd, bytes, length) != length) { | |
205 | int saveerr = thread_errno(); | |
206 | close(fd); | |
cf7d2af9 | 207 | closeAutoFSNoWait(no_hang_fd); |
9ce05555 A |
208 | thread_set_errno(saveerr); |
209 | return false; | |
210 | } | |
cf7d2af9 | 211 | #if DEPLOYMENT_TARGET_WINDOWS |
9ce05555 A |
212 | FlushFileBuffers((HANDLE)_get_osfhandle(fd)); |
213 | #else | |
214 | fsync(fd); | |
bd5b749c | 215 | #endif |
cf7d2af9 A |
216 | close(fd); |
217 | closeAutoFSNoWait(no_hang_fd); | |
9ce05555 A |
218 | return true; |
219 | } | |
220 | ||
221 | ||
222 | /* 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 | |
223 | If both are present, they are assumed to be in-synch; that is, they both refer to the same directory. */ | |
bd5b749c | 224 | /* Lately, dirSpec appears to be (rightfully) unused. */ |
a48904a4 | 225 | CF_PRIVATE CFMutableArrayRef _CFCreateContentsOfDirectory(CFAllocatorRef alloc, char *dirPath, void *dirSpec, CFURLRef dirURL, CFStringRef matchingAbstractType) { |
9ce05555 A |
226 | CFMutableArrayRef files = NULL; |
227 | Boolean releaseBase = false; | |
228 | CFIndex pathLength = dirPath ? strlen(dirPath) : 0; | |
229 | // MF:!!! Need to use four-letter type codes where appropriate. | |
230 | CFStringRef extension = (matchingAbstractType ? _CFCopyExtensionForAbstractType(matchingAbstractType) : NULL); | |
8ca704e1 | 231 | CFIndex targetExtLen = (extension ? CFStringGetLength(extension) : 0); |
cf7d2af9 A |
232 | |
233 | #if DEPLOYMENT_TARGET_WINDOWS | |
234 | // This is a replacement for 'dirent' below, and also uses wchar_t to support unicode paths | |
235 | wchar_t extBuff[CFMaxPathSize]; | |
8ca704e1 | 236 | int extBuffInteriorDotCount = 0; //people insist on using extensions like ".trace.plist", so we need to know how many dots back to look :( |
cf7d2af9 | 237 | |
8ca704e1 A |
238 | if (targetExtLen > 0) { |
239 | CFIndex usedBytes = 0; | |
240 | CFStringGetBytes(extension, CFRangeMake(0, targetExtLen), kCFStringEncodingUTF16, 0, false, (uint8_t *)extBuff, CFMaxPathLength, &usedBytes); | |
241 | targetExtLen = usedBytes / sizeof(wchar_t); | |
242 | extBuff[targetExtLen] = '\0'; | |
243 | wchar_t *extBuffStr = (wchar_t *)extBuff; | |
244 | if (extBuffStr[0] == '.') | |
245 | extBuffStr++; //skip the first dot, it's legitimate to have ".plist" for example | |
246 | ||
247 | wchar_t *extBuffDotPtr = extBuffStr; | |
248 | while ((extBuffDotPtr = wcschr(extBuffStr, '.'))) { //find the next . in the extension... | |
249 | extBuffInteriorDotCount++; | |
250 | extBuffStr = extBuffDotPtr + 1; | |
251 | } | |
cf7d2af9 A |
252 | } |
253 | ||
254 | wchar_t pathBuf[CFMaxPathSize]; | |
255 | ||
256 | if (!dirPath) { | |
257 | if (!_CFURLGetWideFileSystemRepresentation(dirURL, true, pathBuf, CFMaxPathLength)) { | |
258 | if (extension) CFRelease(extension); | |
259 | return NULL; | |
260 | } | |
261 | ||
262 | pathLength = wcslen(pathBuf); | |
263 | ||
264 | } else { | |
265 | // Convert dirPath to a wide representation and put it into our pathBuf | |
266 | // Get the real length of the string in UTF16 characters | |
267 | CFStringRef dirPathStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, dirPath, kCFStringEncodingUTF8); | |
268 | CFIndex strLen = CFStringGetLength(dirPathStr); | |
269 | ||
270 | // Copy the string into the buffer and terminate | |
271 | CFStringGetCharacters(dirPathStr, CFRangeMake(0, strLen), (UniChar *)pathBuf); | |
272 | pathBuf[strLen] = 0; | |
273 | ||
274 | CFRelease(dirPathStr); | |
275 | } | |
276 | ||
277 | WIN32_FIND_DATAW file; | |
278 | HANDLE handle; | |
279 | ||
280 | if (pathLength + 2 >= CFMaxPathLength) { | |
281 | if (extension) { | |
282 | CFRelease(extension); | |
283 | } | |
284 | return NULL; | |
285 | } | |
286 | ||
287 | pathBuf[pathLength] = '\\'; | |
288 | pathBuf[pathLength + 1] = '*'; | |
289 | pathBuf[pathLength + 2] = '\0'; | |
290 | handle = FindFirstFileW(pathBuf, (LPWIN32_FIND_DATAW)&file); | |
291 | if (INVALID_HANDLE_VALUE == handle) { | |
292 | pathBuf[pathLength] = '\0'; | |
293 | if (extension) { | |
294 | CFRelease(extension); | |
295 | } | |
296 | return NULL; | |
297 | } | |
298 | ||
299 | files = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); | |
300 | ||
301 | do { | |
302 | CFURLRef fileURL; | |
303 | CFIndex namelen = wcslen(file.cFileName); | |
304 | if (file.cFileName[0] == '.' && (namelen == 1 || (namelen == 2 && file.cFileName[1] == '.'))) { | |
305 | continue; | |
306 | } | |
307 | ||
8ca704e1 A |
308 | if (targetExtLen > namelen) continue; // if the extension is the same length or longer than the name, it can't possibly match. |
309 | ||
310 | if (targetExtLen > 0) { | |
311 | if (file.cFileName[namelen - 1] == '.') continue; //filename ends with a dot, no extension | |
312 | ||
313 | wchar_t *fileExt = NULL; | |
314 | ||
315 | if (extBuffInteriorDotCount == 0) { | |
316 | fileExt = wcsrchr(file.cFileName, '.'); | |
317 | } else { //find the Nth occurrence of . from the end of the string, to handle ".foo.bar" | |
318 | wchar_t *save = file.cFileName; | |
319 | while ((save = wcschr(save, '.')) && !fileExt) { | |
320 | wchar_t *temp = save; | |
321 | int moreDots = 0; | |
322 | while ((temp = wcschr(temp, '.'))) { | |
323 | if (++moreDots == extBuffInteriorDotCount) break; | |
324 | } | |
325 | if (moreDots == extBuffInteriorDotCount) { | |
326 | fileExt = save; | |
327 | } | |
328 | } | |
329 | } | |
330 | ||
331 | if (!fileExt) continue; //no extension | |
332 | ||
333 | if (((const wchar_t *)extBuff)[0] != '.') | |
334 | fileExt++; //omit the dot if the target file extension omits the dot | |
335 | ||
336 | CFIndex fileExtLen = wcslen(fileExt); | |
337 | ||
338 | //if the extensions are different lengths, they can't possibly match | |
339 | if (fileExtLen != targetExtLen) continue; | |
340 | ||
cf7d2af9 | 341 | // Check to see if it matches the extension we're looking for. |
8ca704e1 | 342 | if (_wcsicmp(fileExt, (const wchar_t *)extBuff) != 0) { |
cf7d2af9 A |
343 | continue; |
344 | } | |
345 | } | |
346 | if (dirURL == NULL) { | |
8ca704e1 | 347 | CFStringRef dirURLStr = CFStringCreateWithBytes(alloc, (const uint8_t *)pathBuf, pathLength * sizeof(wchar_t), kCFStringEncodingUTF16, NO); |
cf7d2af9 A |
348 | dirURL = CFURLCreateWithFileSystemPath(alloc, dirURLStr, kCFURLWindowsPathStyle, true); |
349 | CFRelease(dirURLStr); | |
350 | releaseBase = true; | |
351 | } | |
352 | // MF:!!! What about the trailing slash? | |
8ca704e1 | 353 | CFStringRef fileURLStr = CFStringCreateWithBytes(alloc, (const uint8_t *)file.cFileName, namelen * sizeof(wchar_t), kCFStringEncodingUTF16, NO); |
cf7d2af9 A |
354 | fileURL = CFURLCreateWithFileSystemPathRelativeToBase(alloc, fileURLStr, kCFURLWindowsPathStyle, (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false, dirURL); |
355 | CFArrayAppendValue(files, fileURL); | |
356 | CFRelease(fileURL); | |
357 | CFRelease(fileURLStr); | |
358 | } while (FindNextFileW(handle, &file)); | |
359 | FindClose(handle); | |
360 | pathBuf[pathLength] = '\0'; | |
361 | ||
856091c5 | 362 | #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD |
9ce05555 | 363 | uint8_t extBuff[CFMaxPathSize]; |
8ca704e1 | 364 | int extBuffInteriorDotCount = 0; //people insist on using extensions like ".trace.plist", so we need to know how many dots back to look :( |
9ce05555 | 365 | |
8ca704e1 A |
366 | if (targetExtLen > 0) { |
367 | CFStringGetBytes(extension, CFRangeMake(0, targetExtLen), CFStringFileSystemEncoding(), 0, false, extBuff, CFMaxPathLength, &targetExtLen); | |
368 | extBuff[targetExtLen] = '\0'; | |
369 | char *extBuffStr = (char *)extBuff; | |
370 | if (extBuffStr[0] == '.') | |
371 | extBuffStr++; //skip the first dot, it's legitimate to have ".plist" for example | |
372 | ||
373 | char *extBuffDotPtr = extBuffStr; | |
374 | while ((extBuffDotPtr = strchr(extBuffStr, '.'))) { //find the next . in the extension... | |
375 | extBuffInteriorDotCount++; | |
376 | extBuffStr = extBuffDotPtr + 1; | |
377 | } | |
9ce05555 | 378 | } |
cf7d2af9 | 379 | |
d8925383 | 380 | uint8_t pathBuf[CFMaxPathSize]; |
cf7d2af9 | 381 | |
d8925383 A |
382 | if (!dirPath) { |
383 | if (!CFURLGetFileSystemRepresentation(dirURL, true, pathBuf, CFMaxPathLength)) { | |
384 | if (extension) CFRelease(extension); | |
385 | return NULL; | |
386 | } else { | |
bd5b749c | 387 | dirPath = (char *)pathBuf; |
d8925383 A |
388 | pathLength = strlen(dirPath); |
389 | } | |
390 | } | |
9ce05555 | 391 | |
bd5b749c | 392 | struct dirent buffer; |
d8925383 A |
393 | struct dirent *dp; |
394 | int err; | |
bd5b749c | 395 | |
cf7d2af9 | 396 | int no_hang_fd = __CFProphylacticAutofsAccess ? open("/dev/autofs_nowait", 0) : -1; |
bd5b749c A |
397 | |
398 | DIR *dirp = opendir(dirPath); | |
9ce05555 A |
399 | if (!dirp) { |
400 | if (extension) { | |
401 | CFRelease(extension); | |
402 | } | |
cf7d2af9 | 403 | if (-1 != no_hang_fd) close(no_hang_fd); |
9ce05555 A |
404 | return NULL; |
405 | // raiseErrno("opendir", path); | |
406 | } | |
407 | files = CFArrayCreateMutable(alloc, 0, & kCFTypeArrayCallBacks); | |
408 | ||
bd5b749c | 409 | while((0 == readdir_r(dirp, &buffer, &dp)) && dp) { |
9ce05555 A |
410 | CFURLRef fileURL; |
411 | unsigned namelen = strlen(dp->d_name); | |
412 | ||
413 | // skip . & ..; they cause descenders to go berserk | |
414 | if (dp->d_name[0] == '.' && (namelen == 1 || (namelen == 2 && dp->d_name[1] == '.'))) { | |
415 | continue; | |
416 | } | |
bd5b749c | 417 | |
8ca704e1 A |
418 | if (targetExtLen > namelen) continue; // if the extension is the same length or longer than the name, it can't possibly match. |
419 | ||
420 | if (targetExtLen > 0) { | |
421 | if (dp->d_name[namelen - 1] == '.') continue; //filename ends with a dot, no extension | |
422 | ||
423 | char *fileExt = NULL; | |
424 | if (extBuffInteriorDotCount == 0) { | |
425 | fileExt = strrchr(dp->d_name, '.'); | |
426 | } else { //find the Nth occurrence of . from the end of the string, to handle ".foo.bar" | |
427 | char *save = dp->d_name; | |
428 | while ((save = strchr(save, '.')) && !fileExt) { | |
429 | char *temp = save; | |
430 | int moreDots = 0; | |
431 | while ((temp = strchr(temp, '.'))) { | |
432 | if (++moreDots == extBuffInteriorDotCount) break; | |
433 | } | |
434 | if (moreDots == extBuffInteriorDotCount) { | |
435 | fileExt = save; | |
436 | } | |
437 | } | |
438 | } | |
439 | ||
440 | if (!fileExt) continue; //no extension | |
441 | ||
442 | if (((char *)extBuff)[0] != '.') | |
443 | fileExt++; //omit the dot if the target extension omits the dot; safe, because we checked to make sure it isn't the last character just before | |
444 | ||
445 | size_t fileExtLen = strlen(fileExt); | |
446 | ||
447 | //if the extensions are different lengths, they can't possibly match | |
448 | if (fileExtLen != targetExtLen) continue; | |
449 | ||
450 | // Check to see if it matches the extension we're looking for. | |
451 | if (strncmp(fileExt, (char *)extBuff, fileExtLen) != 0) { | |
9ce05555 A |
452 | continue; |
453 | } | |
454 | } | |
455 | if (dirURL == NULL) { | |
bd5b749c | 456 | dirURL = CFURLCreateFromFileSystemRepresentation(alloc, (uint8_t *)dirPath, pathLength, true); |
9ce05555 A |
457 | releaseBase = true; |
458 | } | |
856091c5 | 459 | if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN || dp->d_type == DT_LNK || dp->d_type == DT_WHT) { |
bd5b749c A |
460 | Boolean isDir = (dp->d_type == DT_DIR); |
461 | if (!isDir) { | |
462 | // Ugh; must stat. | |
463 | char subdirPath[CFMaxPathLength]; | |
cf7d2af9 | 464 | struct statinfo statBuf; |
bd5b749c A |
465 | strlcpy(subdirPath, dirPath, sizeof(subdirPath)); |
466 | strlcat(subdirPath, "/", sizeof(subdirPath)); | |
467 | strlcat(subdirPath, dp->d_name, sizeof(subdirPath)); | |
468 | if (stat(subdirPath, &statBuf) == 0) { | |
469 | isDir = ((statBuf.st_mode & S_IFMT) == S_IFDIR); | |
470 | } | |
471 | } | |
8ca704e1 A |
472 | #if DEPLOYMENT_TARGET_LINUX |
473 | fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, (uint8_t *)dp->d_name, namelen, isDir, dirURL); | |
474 | #else | |
bd5b749c | 475 | fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(alloc, (uint8_t *)dp->d_name, dp->d_namlen, isDir, dirURL); |
8ca704e1 | 476 | #endif |
bd5b749c | 477 | } else { |
8ca704e1 A |
478 | #if DEPLOYMENT_TARGET_LINUX |
479 | fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc, (uint8_t *)dp->d_name, namelen, false, dirURL); | |
480 | #else | |
bd5b749c | 481 | fileURL = CFURLCreateFromFileSystemRepresentationRelativeToBase (alloc, (uint8_t *)dp->d_name, dp->d_namlen, false, dirURL); |
8ca704e1 | 482 | #endif |
bd5b749c | 483 | } |
9ce05555 A |
484 | CFArrayAppendValue(files, fileURL); |
485 | CFRelease(fileURL); | |
486 | } | |
487 | err = closedir(dirp); | |
cf7d2af9 | 488 | if (-1 != no_hang_fd) close(no_hang_fd); |
9ce05555 A |
489 | if (err != 0) { |
490 | CFRelease(files); | |
491 | if (releaseBase) { | |
492 | CFRelease(dirURL); | |
493 | } | |
494 | if (extension) { | |
495 | CFRelease(extension); | |
496 | } | |
497 | return NULL; | |
9ce05555 A |
498 | } |
499 | ||
9ce05555 A |
500 | #else |
501 | ||
a48904a4 | 502 | #error _CFCreateContentsOfDirectory() unknown architecture, not implemented |
9ce05555 A |
503 | |
504 | #endif | |
505 | ||
506 | if (extension) { | |
507 | CFRelease(extension); | |
508 | } | |
509 | if (releaseBase) { | |
510 | CFRelease(dirURL); | |
511 | } | |
512 | return files; | |
513 | } | |
514 | ||
a48904a4 | 515 | CF_PRIVATE SInt32 _CFGetPathProperties(CFAllocatorRef alloc, char *path, Boolean *exists, SInt32 *posixMode, int64_t *size, CFDateRef *modTime, SInt32 *ownerID, CFArrayRef *dirContents) { |
9ce05555 A |
516 | Boolean fileExists; |
517 | Boolean isDirectory = false; | |
8ca704e1 | 518 | |
9ce05555 A |
519 | if ((exists == NULL) && (posixMode == NULL) && (size == NULL) && (modTime == NULL) && (ownerID == NULL) && (dirContents == NULL)) { |
520 | // Nothing to do. | |
521 | return 0; | |
522 | } | |
8ca704e1 | 523 | |
cf7d2af9 | 524 | struct statinfo statBuf; |
8ca704e1 | 525 | |
cf7d2af9 | 526 | if (stat(path, &statBuf) != 0) { |
9ce05555 A |
527 | // stat failed, but why? |
528 | if (thread_errno() == ENOENT) { | |
529 | fileExists = false; | |
530 | } else { | |
531 | return thread_errno(); | |
532 | } | |
533 | } else { | |
534 | fileExists = true; | |
535 | isDirectory = ((statBuf.st_mode & S_IFMT) == S_IFDIR); | |
536 | } | |
8ca704e1 A |
537 | |
538 | ||
9ce05555 A |
539 | if (exists != NULL) { |
540 | *exists = fileExists; | |
541 | } | |
8ca704e1 | 542 | |
9ce05555 A |
543 | if (posixMode != NULL) { |
544 | if (fileExists) { | |
8ca704e1 | 545 | |
9ce05555 | 546 | *posixMode = statBuf.st_mode; |
8ca704e1 | 547 | |
9ce05555 A |
548 | } else { |
549 | *posixMode = 0; | |
550 | } | |
551 | } | |
8ca704e1 | 552 | |
9ce05555 A |
553 | if (size != NULL) { |
554 | if (fileExists) { | |
8ca704e1 | 555 | |
9ce05555 | 556 | *size = statBuf.st_size; |
8ca704e1 | 557 | |
9ce05555 A |
558 | } else { |
559 | *size = 0; | |
560 | } | |
561 | } | |
8ca704e1 | 562 | |
9ce05555 A |
563 | if (modTime != NULL) { |
564 | if (fileExists) { | |
8ca704e1 | 565 | #if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX |
cf7d2af9 A |
566 | struct timespec ts = {statBuf.st_mtime, 0}; |
567 | #else | |
568 | struct timespec ts = statBuf.st_mtimespec; | |
569 | #endif | |
570 | *modTime = CFDateCreate(alloc, _CFAbsoluteTimeFromFileTimeSpec(ts)); | |
9ce05555 A |
571 | } else { |
572 | *modTime = NULL; | |
573 | } | |
574 | } | |
8ca704e1 | 575 | |
9ce05555 A |
576 | if (ownerID != NULL) { |
577 | if (fileExists) { | |
8ca704e1 | 578 | |
9ce05555 | 579 | *ownerID = statBuf.st_uid; |
8ca704e1 | 580 | |
9ce05555 A |
581 | } else { |
582 | *ownerID = -1; | |
583 | } | |
584 | } | |
585 | ||
586 | if (dirContents != NULL) { | |
587 | if (fileExists && isDirectory) { | |
8ca704e1 | 588 | |
a48904a4 | 589 | CFMutableArrayRef contents = _CFCreateContentsOfDirectory(alloc, (char *)path, NULL, NULL, NULL); |
8ca704e1 | 590 | |
9ce05555 A |
591 | if (contents) { |
592 | *dirContents = contents; | |
593 | } else { | |
594 | *dirContents = NULL; | |
595 | } | |
596 | } else { | |
597 | *dirContents = NULL; | |
598 | } | |
599 | } | |
600 | return 0; | |
601 | } | |
602 | ||
a48904a4 | 603 | CF_PRIVATE SInt32 _CFGetFileProperties(CFAllocatorRef alloc, CFURLRef pathURL, Boolean *exists, SInt32 *posixMode, int64_t *size, CFDateRef *modTime, SInt32 *ownerID, CFArrayRef *dirContents) { |
8ca704e1 A |
604 | |
605 | char path[CFMaxPathSize]; | |
606 | ||
607 | if (!CFURLGetFileSystemRepresentation(pathURL, true, (uint8_t *)path, CFMaxPathLength)) { | |
608 | return -1; | |
609 | } | |
610 | ||
611 | return _CFGetPathProperties(alloc, path, exists, posixMode, size, modTime, ownerID, dirContents); | |
612 | } | |
613 | ||
9ce05555 | 614 | |
856091c5 A |
615 | #if DEPLOYMENT_TARGET_WINDOWS |
616 | #define WINDOWS_PATH_SEMANTICS | |
9ce05555 | 617 | #else |
856091c5 | 618 | #define UNIX_PATH_SEMANTICS |
9ce05555 A |
619 | #endif |
620 | ||
621 | #if defined(WINDOWS_PATH_SEMANTICS) | |
622 | #define CFPreferredSlash ((UniChar)'\\') | |
a48904a4 | 623 | #define CFPreferredSlashStr CFSTR("\\") |
9ce05555 A |
624 | #elif defined(UNIX_PATH_SEMANTICS) |
625 | #define CFPreferredSlash ((UniChar)'/') | |
a48904a4 | 626 | #define CFPreferredSlashStr CFSTR("/") |
9ce05555 A |
627 | #else |
628 | #error Cannot define NSPreferredSlash on this platform | |
629 | #endif | |
630 | ||
a48904a4 A |
631 | static Boolean _hasDrive(CFStringRef path) { |
632 | if (CFStringGetLength(path) >= 2) { | |
633 | UniChar firstCharacters[2]; | |
634 | firstCharacters[0] = CFStringGetCharacterAtIndex(path, 0); | |
635 | firstCharacters[1] = CFStringGetCharacterAtIndex(path, 1); | |
636 | if (firstCharacters[1] == ':' && | |
637 | (('A' <= (firstCharacters)[0] && (firstCharacters)[0] <= 'Z') || | |
638 | ('a' <= (firstCharacters)[0] && (firstCharacters)[0] <= 'z')) | |
639 | ) { | |
640 | return true; | |
641 | } | |
642 | } | |
643 | return false; | |
644 | } | |
645 | ||
646 | static Boolean _hasNet(CFStringRef path) { | |
647 | if (CFStringGetLength(path) >= 2) { | |
648 | UniChar firstCharacters[2]; | |
649 | firstCharacters[0] = CFStringGetCharacterAtIndex(path, 0); | |
650 | firstCharacters[1] = CFStringGetCharacterAtIndex(path, 1); | |
651 | if (firstCharacters[0] == '\\' && firstCharacters[1] == '\\') return true; | |
652 | } | |
653 | return false; | |
654 | } | |
655 | ||
9ce05555 A |
656 | #define HAS_DRIVE(S) ((S)[1] == ':' && (('A' <= (S)[0] && (S)[0] <= 'Z') || ('a' <= (S)[0] && (S)[0] <= 'z'))) |
657 | #define HAS_NET(S) ((S)[0] == '\\' && (S)[1] == '\\') | |
9ce05555 A |
658 | |
659 | #if defined(WINDOWS_PATH_SEMANTICS) | |
660 | #define IS_SLASH(C) ((C) == '\\' || (C) == '/') | |
661 | #elif defined(UNIX_PATH_SEMANTICS) | |
662 | #define IS_SLASH(C) ((C) == '/') | |
9ce05555 A |
663 | #endif |
664 | ||
a48904a4 | 665 | CF_PRIVATE UniChar _CFGetSlash() { |
856091c5 A |
666 | return CFPreferredSlash; |
667 | } | |
668 | ||
a48904a4 A |
669 | CF_PRIVATE CFStringRef _CFGetSlashStr() { |
670 | return CFPreferredSlashStr; | |
671 | } | |
672 | ||
673 | CF_PRIVATE Boolean _CFIsAbsolutePath(UniChar *unichars, CFIndex length) { | |
9ce05555 A |
674 | if (length < 1) { |
675 | return false; | |
676 | } | |
677 | #if defined(WINDOWS_PATH_SEMANTICS) | |
678 | if (unichars[0] == '~') { | |
679 | return true; | |
680 | } | |
681 | if (length < 2) { | |
682 | return false; | |
683 | } | |
684 | if (HAS_NET(unichars)) { | |
685 | return true; | |
686 | } | |
687 | if (length < 3) { | |
688 | return false; | |
689 | } | |
690 | if (IS_SLASH(unichars[2]) && HAS_DRIVE(unichars)) { | |
691 | return true; | |
692 | } | |
9ce05555 A |
693 | #else |
694 | if (unichars[0] == '~') { | |
695 | return true; | |
696 | } | |
697 | if (IS_SLASH(unichars[0])) { | |
698 | return true; | |
699 | } | |
700 | #endif | |
701 | return false; | |
702 | } | |
703 | ||
a48904a4 | 704 | CF_PRIVATE Boolean _CFStripTrailingPathSlashes(UniChar *unichars, CFIndex *length) { |
9ce05555 A |
705 | Boolean destHasDrive = (1 < *length) && HAS_DRIVE(unichars); |
706 | CFIndex oldLength = *length; | |
707 | while (((destHasDrive && 3 < *length) || (!destHasDrive && 1 < *length)) && IS_SLASH(unichars[*length - 1])) { | |
708 | (*length)--; | |
709 | } | |
710 | return (oldLength != *length); | |
711 | } | |
712 | ||
a48904a4 | 713 | static Boolean _CFAppendTrailingPathSlash(UniChar *unichars, CFIndex *length, CFIndex maxLength) { |
8ca704e1 A |
714 | if (maxLength < *length + 1) { |
715 | return false; | |
716 | } | |
717 | switch (*length) { | |
718 | case 0: | |
719 | break; | |
720 | case 1: | |
721 | if (!IS_SLASH(unichars[0])) { | |
722 | unichars[(*length)++] = CFPreferredSlash; | |
723 | } | |
724 | break; | |
725 | case 2: | |
726 | if (!HAS_DRIVE(unichars) && !HAS_NET(unichars)) { | |
727 | unichars[(*length)++] = CFPreferredSlash; | |
728 | } | |
729 | break; | |
730 | default: | |
731 | unichars[(*length)++] = CFPreferredSlash; | |
732 | break; | |
733 | } | |
734 | return true; | |
735 | } | |
736 | ||
a48904a4 A |
737 | CF_PRIVATE void _CFAppendTrailingPathSlash2(CFMutableStringRef path) { |
738 | static const UniChar slash[1] = { CFPreferredSlash }; | |
739 | CFIndex len = CFStringGetLength(path); | |
740 | if (len == 0) { | |
741 | // Do nothing for this case | |
742 | } else if (len == 1) { | |
743 | UniChar character = CFStringGetCharacterAtIndex((CFStringRef)path, 0); | |
744 | if (!IS_SLASH(character)) { | |
745 | CFStringAppendCharacters(path, slash, 1); | |
746 | } | |
747 | } else if (len == 2) { | |
748 | if (!_hasDrive(path) && !_hasNet(path)) { | |
749 | CFStringAppendCharacters(path, slash, 1); | |
750 | } | |
751 | } else { | |
752 | CFStringAppendCharacters(path, slash, 1); | |
753 | } | |
754 | } | |
755 | ||
756 | CF_PRIVATE void _CFAppendConditionalTrailingPathSlash2(CFMutableStringRef path) { | |
757 | static const UniChar slash[1] = { CFPreferredSlash }; | |
758 | UniChar character = CFStringGetCharacterAtIndex((CFStringRef)path, CFStringGetLength(path) - 1); | |
759 | if (!IS_SLASH(character)) { | |
760 | CFStringAppendCharacters(path, slash, 1); | |
761 | } | |
762 | } | |
763 | ||
764 | CF_PRIVATE void _CFAppendPathComponent2(CFMutableStringRef path, CFStringRef component) { | |
765 | _CFAppendTrailingPathSlash2(path); | |
766 | CFStringAppend(path, component); | |
767 | } | |
768 | ||
769 | CF_PRIVATE Boolean _CFAppendPathComponent(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *component, CFIndex componentLength) { | |
9ce05555 A |
770 | if (0 == componentLength) { |
771 | return true; | |
772 | } | |
773 | if (maxLength < *length + 1 + componentLength) { | |
774 | return false; | |
775 | } | |
8ca704e1 | 776 | _CFAppendTrailingPathSlash(unichars, length, maxLength); |
9ce05555 A |
777 | memmove(unichars + *length, component, componentLength * sizeof(UniChar)); |
778 | *length += componentLength; | |
779 | return true; | |
780 | } | |
781 | ||
a48904a4 A |
782 | CF_PRIVATE Boolean _CFAppendPathExtension2(CFMutableStringRef path, CFStringRef extension) { |
783 | if (!path) { | |
784 | return false; | |
785 | } | |
786 | ||
787 | if (0 < CFStringGetLength(extension) && IS_SLASH(CFStringGetCharacterAtIndex(extension, 0))) { | |
788 | return false; | |
789 | } | |
790 | if (1 < CFStringGetLength(extension)) { | |
791 | if (_hasDrive(extension)) return false; | |
792 | } | |
793 | ||
794 | Boolean destHasDrive = (1 < CFStringGetLength(path)) && _hasDrive(path); | |
795 | while (((destHasDrive && 3 < CFStringGetLength(path)) || (!destHasDrive && 1 < CFStringGetLength(path))) && IS_SLASH(CFStringGetCharacterAtIndex(path, CFStringGetLength(path) - 1))) { | |
796 | CFStringDelete(path, CFRangeMake(CFStringGetLength(path) - 1, 1)); | |
797 | } | |
798 | ||
799 | if (CFStringGetLength(path) == 0) { | |
800 | return false; | |
801 | } | |
802 | ||
803 | UniChar firstChar = CFStringGetCharacterAtIndex(path, 0); | |
804 | CFIndex newLength = CFStringGetLength(path); | |
805 | switch (newLength) { | |
806 | case 0: | |
807 | return false; | |
808 | case 1: | |
809 | if (IS_SLASH(firstChar) || firstChar == '~') { | |
810 | return false; | |
811 | } | |
812 | break; | |
813 | case 2: | |
814 | if (_hasDrive(path) || _hasNet(path)) { | |
815 | return false; | |
816 | } | |
817 | break; | |
818 | case 3: | |
819 | if (IS_SLASH(CFStringGetCharacterAtIndex(path, 2)) && _hasDrive(path)) { | |
820 | return false; | |
821 | } | |
822 | break; | |
823 | } | |
824 | if (0 < newLength && firstChar == '~') { | |
825 | // Make sure we have a slash in the string | |
826 | if (!CFStringFindWithOptions(path, CFPreferredSlashStr, CFRangeMake(1, newLength - 1), 0, NULL)) { | |
827 | return false; | |
828 | } | |
829 | } | |
830 | static const UniChar dotChar = '.'; | |
831 | CFStringAppendCharacters(path, &dotChar, 1); | |
832 | CFStringAppend(path, extension); | |
833 | return true; | |
834 | } | |
835 | ||
836 | CF_PRIVATE Boolean _CFAppendPathExtension(UniChar *unichars, CFIndex *length, CFIndex maxLength, UniChar *extension, CFIndex extensionLength) { | |
9ce05555 A |
837 | if (maxLength < *length + 1 + extensionLength) { |
838 | return false; | |
839 | } | |
840 | if ((0 < extensionLength && IS_SLASH(extension[0])) || (1 < extensionLength && HAS_DRIVE(extension))) { | |
841 | return false; | |
842 | } | |
843 | _CFStripTrailingPathSlashes(unichars, length); | |
844 | switch (*length) { | |
845 | case 0: | |
846 | return false; | |
847 | case 1: | |
848 | if (IS_SLASH(unichars[0]) || unichars[0] == '~') { | |
849 | return false; | |
850 | } | |
851 | break; | |
852 | case 2: | |
853 | if (HAS_DRIVE(unichars) || HAS_NET(unichars)) { | |
854 | return false; | |
855 | } | |
856 | break; | |
857 | case 3: | |
858 | if (IS_SLASH(unichars[2]) && HAS_DRIVE(unichars)) { | |
859 | return false; | |
860 | } | |
861 | break; | |
862 | } | |
863 | if (0 < *length && unichars[0] == '~') { | |
864 | CFIndex idx; | |
865 | Boolean hasSlash = false; | |
866 | for (idx = 1; idx < *length; idx++) { | |
867 | if (IS_SLASH(unichars[idx])) { | |
868 | hasSlash = true; | |
869 | break; | |
870 | } | |
871 | } | |
872 | if (!hasSlash) { | |
873 | return false; | |
874 | } | |
875 | } | |
876 | unichars[(*length)++] = '.'; | |
877 | memmove(unichars + *length, extension, extensionLength * sizeof(UniChar)); | |
878 | *length += extensionLength; | |
879 | return true; | |
880 | } | |
881 | ||
a48904a4 | 882 | CF_PRIVATE Boolean _CFTransmutePathSlashes(UniChar *unichars, CFIndex *length, UniChar replSlash) { |
9ce05555 A |
883 | CFIndex didx, sidx, scnt = *length; |
884 | sidx = (1 < *length && HAS_NET(unichars)) ? 2 : 0; | |
885 | didx = sidx; | |
886 | while (sidx < scnt) { | |
887 | if (IS_SLASH(unichars[sidx])) { | |
888 | unichars[didx++] = replSlash; | |
889 | for (sidx++; sidx < scnt && IS_SLASH(unichars[sidx]); sidx++); | |
890 | } else { | |
891 | unichars[didx++] = unichars[sidx++]; | |
892 | } | |
893 | } | |
894 | *length = didx; | |
895 | return (scnt != didx); | |
896 | } | |
897 | ||
a48904a4 A |
898 | CF_PRIVATE CFStringRef _CFCreateLastPathComponent(CFAllocatorRef alloc, CFStringRef path, CFIndex *slashIndex) { |
899 | CFIndex len = CFStringGetLength(path); | |
900 | if (len < 2) { | |
901 | // Can't be any path components in a string this short | |
902 | if (slashIndex) *slashIndex = -1; | |
903 | return (CFStringRef)CFRetain(path); | |
904 | } | |
905 | ||
906 | // Find the last slash | |
907 | for (CFIndex i = len - 1; i >= 0; i--) { | |
908 | if (IS_SLASH(CFStringGetCharacterAtIndex(path, i))) { | |
909 | if (slashIndex) *slashIndex = i; | |
910 | return CFStringCreateWithSubstring(alloc, path, CFRangeMake(i + 1, len - i - 1)); | |
911 | } | |
912 | } | |
913 | ||
914 | // Strip any drive if we have one | |
915 | if (len > 2 && _hasDrive(path)) { | |
916 | if (slashIndex) *slashIndex = -1; | |
917 | return CFStringCreateWithSubstring(alloc, path, CFRangeMake(2, len - 2)); | |
918 | } | |
919 | ||
920 | // No slash, so just return the same string | |
921 | if (slashIndex) *slashIndex = -1; | |
922 | return (CFStringRef)CFRetain(path); | |
923 | } | |
924 | ||
925 | CF_PRIVATE CFIndex _CFStartOfLastPathComponent(UniChar *unichars, CFIndex length) { | |
9ce05555 A |
926 | CFIndex idx; |
927 | if (length < 2) { | |
928 | return 0; | |
929 | } | |
930 | for (idx = length - 1; idx; idx--) { | |
931 | if (IS_SLASH(unichars[idx - 1])) { | |
932 | return idx; | |
933 | } | |
934 | } | |
935 | if ((2 < length) && HAS_DRIVE(unichars)) { | |
936 | return 2; | |
937 | } | |
938 | return 0; | |
939 | } | |
940 | ||
a48904a4 A |
941 | CF_PRIVATE CFIndex _CFStartOfLastPathComponent2(CFStringRef path) { |
942 | CFIndex length = CFStringGetLength(path); | |
943 | if (length < 2) { | |
944 | return 0; | |
945 | } | |
946 | for (CFIndex idx = length - 1; idx; idx--) { | |
947 | if (IS_SLASH(CFStringGetCharacterAtIndex(path, idx - 1))) { | |
948 | return idx; | |
949 | } | |
950 | } | |
951 | if ((2 < length && _hasDrive(path))) { | |
952 | return 2; | |
953 | } | |
954 | return 0; | |
955 | } | |
956 | ||
957 | CF_PRIVATE CFIndex _CFLengthAfterDeletingLastPathComponent(UniChar *unichars, CFIndex length) { | |
9ce05555 A |
958 | CFIndex idx; |
959 | if (length < 2) { | |
960 | return 0; | |
961 | } | |
962 | for (idx = length - 1; idx; idx--) { | |
963 | if (IS_SLASH(unichars[idx - 1])) { | |
964 | if ((idx != 1) && (!HAS_DRIVE(unichars) || idx != 3)) { | |
965 | return idx - 1; | |
966 | } | |
967 | return idx; | |
968 | } | |
969 | } | |
970 | if ((2 < length) && HAS_DRIVE(unichars)) { | |
971 | return 2; | |
972 | } | |
973 | return 0; | |
974 | } | |
975 | ||
a48904a4 A |
976 | CF_PRIVATE CFIndex _CFStartOfPathExtension2(CFStringRef path) { |
977 | if (CFStringGetLength(path) < 2) { | |
978 | return 0; | |
979 | } | |
980 | Boolean hasDrive = _hasDrive(path); | |
981 | for (CFIndex idx = CFStringGetLength(path) - 1; idx; idx--) { | |
982 | UniChar thisCharacter = CFStringGetCharacterAtIndex(path, idx); | |
983 | if (IS_SLASH(thisCharacter)) { | |
984 | return 0; | |
985 | } | |
986 | if (thisCharacter != '.') { | |
987 | continue; | |
988 | } | |
989 | if (idx == 2 && hasDrive) { | |
990 | return 0; | |
991 | } | |
992 | return idx; | |
993 | } | |
994 | return 0; | |
995 | } | |
996 | ||
997 | CF_PRIVATE CFIndex _CFStartOfPathExtension(UniChar *unichars, CFIndex length) { | |
9ce05555 A |
998 | CFIndex idx; |
999 | if (length < 2) { | |
1000 | return 0; | |
1001 | } | |
1002 | for (idx = length - 1; idx; idx--) { | |
1003 | if (IS_SLASH(unichars[idx - 1])) { | |
1004 | return 0; | |
1005 | } | |
1006 | if (unichars[idx] != '.') { | |
1007 | continue; | |
1008 | } | |
1009 | if (idx == 2 && HAS_DRIVE(unichars)) { | |
1010 | return 0; | |
1011 | } | |
1012 | return idx; | |
1013 | } | |
1014 | return 0; | |
1015 | } | |
1016 | ||
a48904a4 A |
1017 | CF_PRIVATE CFIndex _CFLengthAfterDeletingPathExtension2(CFStringRef path) { |
1018 | CFIndex start = _CFStartOfPathExtension2(path); | |
1019 | return ((0 < start) ? start : CFStringGetLength(path)); | |
1020 | } | |
1021 | ||
1022 | CF_PRIVATE CFIndex _CFLengthAfterDeletingPathExtension(UniChar *unichars, CFIndex length) { | |
9ce05555 A |
1023 | CFIndex start = _CFStartOfPathExtension(unichars, length); |
1024 | return ((0 < start) ? start : length); | |
1025 | } | |
1026 | ||
a48904a4 A |
1027 | #if DEPLOYMENT_TARGET_WINDOWS |
1028 | #define DT_DIR 4 | |
1029 | #define DT_REG 8 | |
1030 | #define DT_LNK 10 | |
1031 | #endif | |
1032 | ||
1033 | // NOTE: on Windows the filename is UTF16-encoded, the fileNameLen is result of wcslen. This function automatically skips '.' and '..', and '._' files | |
1034 | CF_PRIVATE void _CFIterateDirectory(CFStringRef directoryPath, Boolean (^fileHandler)(CFStringRef fileName, uint8_t fileType)) { | |
1035 | char directoryPathBuf[CFMaxPathSize]; | |
1036 | if (!CFStringGetFileSystemRepresentation(directoryPath, directoryPathBuf, CFMaxPathSize)) return; | |
1037 | ||
1038 | #if DEPLOYMENT_TARGET_WINDOWS | |
1039 | CFIndex cpathLen = strlen(directoryPathBuf); | |
1040 | // Make sure there is room for the additional space we need in the win32 api | |
1041 | if (cpathLen + 2 < CFMaxPathSize) { | |
1042 | WIN32_FIND_DATAW file; | |
1043 | HANDLE handle; | |
1044 | ||
1045 | directoryPathBuf[cpathLen++] = '\\'; | |
1046 | directoryPathBuf[cpathLen++] = '*'; | |
1047 | directoryPathBuf[cpathLen] = '\0'; | |
1048 | ||
1049 | // Convert UTF8 buffer to windows appropriate UTF-16LE | |
1050 | // Get the real length of the string in UTF16 characters | |
1051 | CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, directoryPathBuf, kCFStringEncodingUTF8); | |
1052 | cpathLen = CFStringGetLength(cfStr); | |
1053 | // Allocate a wide buffer to hold the converted string, including space for a NULL terminator | |
1054 | wchar_t *wideBuf = (wchar_t *)malloc((cpathLen + 1) * sizeof(wchar_t)); | |
1055 | // Copy the string into the buffer and terminate | |
1056 | CFStringGetCharacters(cfStr, CFRangeMake(0, cpathLen), (UniChar *)wideBuf); | |
1057 | wideBuf[cpathLen] = 0; | |
1058 | CFRelease(cfStr); | |
1059 | ||
1060 | handle = FindFirstFileW(wideBuf, (LPWIN32_FIND_DATAW)&file); | |
1061 | if (handle != INVALID_HANDLE_VALUE) { | |
1062 | do { | |
1063 | CFIndex nameLen = wcslen(file.cFileName); | |
1064 | if (file.cFileName[0] == '.' && (nameLen == 1 || (nameLen == 2 && file.cFileName[1] == '.'))) { | |
1065 | continue; | |
1066 | } | |
1067 | ||
1068 | CFStringRef fileName = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const uint8_t *)file.cFileName, nameLen * sizeof(wchar_t), kCFStringEncodingUTF16, NO); | |
1069 | if (!fileName) { | |
1070 | continue; | |
1071 | } | |
1072 | ||
1073 | Boolean isDirectory = file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; | |
1074 | Boolean result = fileHandler(fileName, isDirectory ? DT_DIR : DT_REG); | |
1075 | CFRelease(fileName); | |
1076 | if (!result) break; | |
1077 | } while (FindNextFileW(handle, &file)); | |
1078 | ||
1079 | FindClose(handle); | |
1080 | } | |
1081 | free(wideBuf); | |
1082 | } | |
1083 | #else | |
1084 | DIR *dirp; | |
1085 | struct dirent *dent; | |
1086 | if ((dirp = opendir(directoryPathBuf))) { | |
1087 | while ((dent = readdir(dirp))) { | |
1088 | #if DEPLOYMENT_TARGET_LINUX | |
1089 | CFIndex nameLen = strlen(dent->d_name); | |
1090 | #else | |
1091 | CFIndex nameLen = dent->d_namlen; | |
1092 | #endif | |
1093 | if (0 == nameLen || 0 == dent->d_fileno || ('.' == dent->d_name[0] && (1 == nameLen || (2 == nameLen && '.' == dent->d_name[1]) || '_' == dent->d_name[1]))) { | |
1094 | continue; | |
1095 | } | |
1096 | ||
1097 | CFStringRef fileName = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, dent->d_name); | |
1098 | if (!fileName) { | |
1099 | continue; | |
1100 | } | |
1101 | ||
1102 | Boolean result = fileHandler(fileName, dent->d_type); | |
1103 | CFRelease(fileName); | |
1104 | if (!result) break; | |
1105 | } | |
1106 | (void)closedir(dirp); | |
1107 | } | |
1108 | #endif | |
1109 | } | |
9ce05555 | 1110 |