5 // Created by Mac-arena the Bored Zo on Wed Jun 18 2004.
6 // Copyright 2005-2006 The Growl Project.
8 // This file is under the BSD License, refer to License.txt for details
10 #include <Carbon/Carbon.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
15 #include "CFGrowlAdditions.h"
18 # define MIN(a,b) ((a) < (b) ? (a) : (b))
21 extern Boolean CFStringGetFileSystemRepresentation() __attribute__((weak_import));
22 extern CFIndex CFStringGetMaximumSizeOfFileSystemRepresentation(CFStringRef string) __attribute__((weak_import));
24 char *createFileSystemRepresentationOfString(CFStringRef str) {
26 #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4
27 /* CFStringGetFileSystemRepresentation will cause a link error despite the weak_import attribute above on 10.5 when compiling with 10.2 compatibility using gcc 3.3.
28 * PPC will therefore always use the 10.3 and below method of creating a file system representation.
30 if (CFStringGetFileSystemRepresentation) {
31 CFIndex size = CFStringGetMaximumSizeOfFileSystemRepresentation(str);
32 buffer = malloc(size);
33 CFStringGetFileSystemRepresentation(str, buffer, size);
38 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, str, kCFURLPOSIXPathStyle, false);
39 if (!CFURLGetFileSystemRepresentation(url, false, (UInt8 *)buffer, 512)) {
48 STRING_TYPE createStringWithDate(CFDateRef date) {
49 CFLocaleRef locale = CFLocaleCopyCurrent();
50 CFDateFormatterRef dateFormatter = CFDateFormatterCreate(kCFAllocatorDefault,
52 kCFDateFormatterMediumStyle,
53 kCFDateFormatterMediumStyle);
55 CFStringRef dateString = CFDateFormatterCreateStringWithDate(kCFAllocatorDefault,
58 CFRelease(dateFormatter);
62 STRING_TYPE createStringWithContentsOfFile(CFStringRef filename, CFStringEncoding encoding) {
63 CFStringRef str = NULL;
65 char *path = createFileSystemRepresentationOfString(filename);
67 FILE *fp = fopen(path, "rb");
69 fseek(fp, 0, SEEK_END);
70 unsigned long size = ftell(fp);
71 fseek(fp, 0, SEEK_SET);
72 unsigned char *buffer = malloc(size);
73 if (buffer && fread(buffer, 1, size, fp) == size)
74 str = CFStringCreateWithBytes(kCFAllocatorDefault, buffer, size, encoding, true);
83 STRING_TYPE createStringWithStringAndCharacterAndString(STRING_TYPE str0, UniChar ch, STRING_TYPE str1) {
84 CFStringRef cfstr0 = (CFStringRef)str0;
85 CFStringRef cfstr1 = (CFStringRef)str1;
86 CFIndex len0 = (cfstr0 ? CFStringGetLength(cfstr0) : 0);
87 CFIndex len1 = (cfstr1 ? CFStringGetLength(cfstr1) : 0);
88 unsigned length = (len0 + (ch != 0xffff) + len1);
90 UniChar *buf = malloc(sizeof(UniChar) * length);
94 CFStringGetCharacters(cfstr0, CFRangeMake(0, len0), buf);
100 CFStringGetCharacters(cfstr1, CFRangeMake(0, len1), &buf[i]);
102 return CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, buf, length, /*contentsDeallocator*/ kCFAllocatorMalloc);
105 char *copyCString(STRING_TYPE str, CFStringEncoding encoding) {
106 CFIndex size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), encoding) + 1;
107 char *buffer = calloc(size, 1);
108 CFStringGetCString(str, buffer, size, encoding);
112 STRING_TYPE copyCurrentProcessName(void) {
113 ProcessSerialNumber PSN = { 0, kCurrentProcess };
114 CFStringRef name = NULL;
115 OSStatus err = CopyProcessName(&PSN, &name);
117 NSLog(CFSTR("in copyCurrentProcessName in CFGrowlAdditions: Could not get process name because CopyProcessName returned %li"), (long)err);
123 URL_TYPE copyCurrentProcessURL(void) {
124 ProcessSerialNumber psn = { 0, kCurrentProcess };
127 OSStatus err = GetProcessBundleLocation(&psn, &fsref);
129 NSLog(CFSTR("in copyCurrentProcessURL in CFGrowlAdditions: Could not get application location, because GetProcessBundleLocation returned %li\n"), (long)err);
131 URL = CFURLCreateFromFSRef(kCFAllocatorDefault, &fsref);
135 STRING_TYPE copyCurrentProcessPath(void) {
136 CFURLRef URL = copyCurrentProcessURL();
137 CFStringRef path = CFURLCopyFileSystemPath(URL, kCFURLPOSIXPathStyle);
142 URL_TYPE copyTemporaryFolderURL(void) {
146 OSStatus err = FSFindFolder(kOnAppropriateDisk, kTemporaryFolderType, kCreateFolder, &ref);
148 NSLog(CFSTR("in copyTemporaryFolderPath in CFGrowlAdditions: Could not locate temporary folder because FSFindFolder returned %li"), (long)err);
150 url = CFURLCreateFromFSRef(kCFAllocatorDefault, &ref);
154 STRING_TYPE copyTemporaryFolderPath(void) {
155 CFStringRef path = NULL;
157 CFURLRef url = copyTemporaryFolderURL();
159 path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
166 DATA_TYPE readFile(const char *filename)
169 // read the file into a CFDataRef
170 FILE *fp = fopen(filename, "r");
172 fseek(fp, 0, SEEK_END);
173 long dataLength = ftell(fp);
174 fseek(fp, 0, SEEK_SET);
175 unsigned char *fileData = malloc(dataLength);
176 fread(fileData, 1, dataLength, fp);
178 data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, fileData, dataLength, kCFAllocatorMalloc);
185 URL_TYPE copyURLForApplication(STRING_TYPE appName)
187 CFURLRef appURL = NULL;
188 OSStatus err = LSFindApplicationForInfo(/*inCreator*/ kLSUnknownCreator,
192 /*outAppURL*/ &appURL);
193 return (err == noErr) ? appURL : NULL;
196 STRING_TYPE createStringWithAddressData(DATA_TYPE aAddressData) {
197 struct sockaddr *socketAddress = (struct sockaddr *)CFDataGetBytePtr(aAddressData);
198 // IPv6 Addresses are "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"
199 // at max, which is 40 bytes (0-terminated)
200 // IPv4 Addresses are "255.255.255.255" at max which is smaller
201 char stringBuffer[40];
202 CFStringRef addressAsString = NULL;
203 if (socketAddress->sa_family == AF_INET) {
204 struct sockaddr_in *ipv4 = (struct sockaddr_in *)socketAddress;
205 if (inet_ntop(AF_INET, &(ipv4->sin_addr), stringBuffer, 40))
206 addressAsString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s:%d"), stringBuffer, ipv4->sin_port);
208 addressAsString = CFSTR("IPv4 un-ntopable");
209 } else if (socketAddress->sa_family == AF_INET6) {
210 struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)socketAddress;
211 if (inet_ntop(AF_INET6, &(ipv6->sin6_addr), stringBuffer, 40))
212 // Suggested IPv6 format (see http://www.faqs.org/rfcs/rfc2732.html)
213 addressAsString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("[%s]:%d"), stringBuffer, ipv6->sin6_port);
215 addressAsString = CFSTR("IPv6 un-ntopable");
217 addressAsString = CFSTR("neither IPv6 nor IPv4");
219 return addressAsString;
222 STRING_TYPE createHostNameForAddressData(DATA_TYPE aAddressData) {
223 char hostname[NI_MAXHOST];
224 struct sockaddr *socketAddress = (struct sockaddr *)CFDataGetBytePtr(aAddressData);
225 if (getnameinfo(socketAddress, CFDataGetLength(aAddressData),
226 hostname, sizeof(hostname),
227 /*serv*/ NULL, /*servlen*/ 0,
231 return CFStringCreateWithCString(kCFAllocatorDefault, hostname, kCFStringEncodingASCII);
234 DATA_TYPE copyIconDataForPath(STRING_TYPE path) {
235 CFDataRef data = NULL;
237 //false is probably safest, and is harmless when the object really is a directory.
238 CFURLRef URL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, /*isDirectory*/ false);
240 data = copyIconDataForURL(URL);
247 DATA_TYPE copyIconDataForURL(URL_TYPE URL)
249 CFDataRef data = NULL;
253 if (CFURLGetFSRef(URL, &ref)) {
255 SInt16 label_noOneCares;
256 OSStatus err = GetIconRefFromFileInfo(&ref,
257 /*inFileNameLength*/ 0U, /*inFileName*/ NULL,
258 kFSCatInfoNone, /*inCatalogInfo*/ NULL,
259 kIconServicesNoBadgeFlag | kIconServicesUpdateIfNeededFlag,
263 NSLog(CFSTR("in copyIconDataForURL in CFGrowlAdditions: could not get icon for %@: GetIconRefFromFileInfo returned %li\n"), URL, (long)err);
265 IconFamilyHandle fam = NULL;
266 err = IconRefToIconFamily(icon, kSelectorAllAvailableData, &fam);
268 NSLog(CFSTR("in copyIconDataForURL in CFGrowlAdditions: could not get icon for %@: IconRefToIconFamily returned %li\n"), URL, (long)err);
271 data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)*(Handle)fam, GetHandleSize((Handle)fam));
272 HUnlock((Handle)fam);
273 DisposeHandle((Handle)fam);
275 ReleaseIconRef(icon);
283 URL_TYPE createURLByMakingDirectoryAtURLWithName(URL_TYPE parent, STRING_TYPE name)
285 CFURLRef newDirectory = NULL;
287 CFAllocatorRef allocator = parent ? CFGetAllocator(parent) : name ? CFGetAllocator(name) : kCFAllocatorDefault;
289 if (parent) parent = CFRetain(parent);
291 char *cwdBytes = alloca(PATH_MAX);
292 getcwd(cwdBytes, PATH_MAX);
293 parent = CFURLCreateFromFileSystemRepresentation(allocator, (const unsigned char *)cwdBytes, strlen(cwdBytes), /*isDirectory*/ true);
295 newDirectory = parent;
300 NSLog(CFSTR("in createURLByMakingDirectoryAtURLWithName in CFGrowlAdditions: parent directory URL is NULL (please tell the Growl developers)\n"), parent);
303 name = CFRetain(name);
305 name = CFURLCopyLastPathComponent(parent);
306 CFURLRef newParent = CFURLCreateCopyDeletingLastPathComponent(allocator, parent);
312 NSLog(CFSTR("in createURLByMakingDirectoryAtURLWithName in CFGrowlAdditions: name of directory to create is NULL (please tell the Growl developers)\n"), parent);
315 if (!CFURLGetFSRef(parent, &parentRef))
316 NSLog(CFSTR("in createURLByMakingDirectoryAtURLWithName in CFGrowlAdditions: could not create FSRef for parent directory at %@ (please tell the Growl developers)\n"), parent);
318 FSRef newDirectoryRef;
320 struct HFSUniStr255 nameUnicode;
321 CFRange range = { 0, MIN(CFStringGetLength(name), USHRT_MAX) };
322 CFStringGetCharacters(name, range, nameUnicode.unicode);
323 nameUnicode.length = range.length;
325 struct FSRefParam refPB = {
327 .nameLength = nameUnicode.length,
328 .name = nameUnicode.unicode,
329 .whichInfo = kFSCatInfoNone,
331 .textEncodingHint = kTextEncodingUnknown,
332 .newRef = &newDirectoryRef,
335 OSStatus err = PBCreateDirectoryUnicodeSync(&refPB);
336 if (err == dupFNErr) {
337 //dupFNErr == file (or folder) exists already. this is fine.
338 err = PBMakeFSRefUnicodeSync(&refPB);
341 NSLog(CFSTR("PBCreateDirectoryUnicodeSync or PBMakeFSRefUnicodeSync returned %li; calling CFURLCreateFromFSRef"), (long)err); //XXX
342 newDirectory = CFURLCreateFromFSRef(allocator, &newDirectoryRef);
343 NSLog(CFSTR("CFURLCreateFromFSRef returned %@"), newDirectory); //XXX
345 NSLog(CFSTR("in createURLByMakingDirectoryAtURLWithName in CFGrowlAdditions: could not create directory '%@' in parent directory at %@: FSCreateDirectoryUnicode returned %li (please tell the Growl developers)"), name, parent, (long)err);
359 #ifndef COPYFORK_BUFSIZE
360 # define COPYFORK_BUFSIZE 5242880U /*5 MiB*/
363 static OSStatus copyFork(const struct HFSUniStr255 *forkName, const FSRef *srcFile, const FSRef *destDir, const struct HFSUniStr255 *destName, FSRef *outDestFile) {
364 OSStatus err, closeErr;
365 struct FSForkIOParam srcPB = {
367 .forkNameLength = forkName->length,
368 .forkName = forkName->unicode,
369 .permissions = fsRdPerm,
371 unsigned char debuggingPathBuf[PATH_MAX] = "";
372 OSStatus debuggingPathErr;
374 err = PBOpenForkSync(&srcPB);
376 debuggingPathErr = FSRefMakePath(srcFile, debuggingPathBuf, PATH_MAX);
377 if (debuggingPathErr != noErr)
378 snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for source file: FSRefMakePath returned %li)", (long)debuggingPathErr);
379 NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBOpenForkSync (source: %s) returned %li"), debuggingPathBuf, (long)err);
383 /*the first thing to do is get the name of the destination file, if one
385 *and while we're at it, we get the catalogue info as well.
387 struct FSCatalogInfo catInfo;
388 struct FSRefParam refPB = {
390 .whichInfo = kFSCatInfoGettableInfo & kFSCatInfoSettableInfo,
394 .outName = destName ? NULL : (struct HFSUniStr255 *)(destName = alloca(sizeof(struct HFSUniStr255))),
397 err = PBGetCatalogInfoSync(&refPB);
399 debuggingPathErr = FSRefMakePath(srcFile, debuggingPathBuf, PATH_MAX);
400 if (debuggingPathErr != noErr)
401 snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for source file: FSRefMakePath returned %li)", (long)debuggingPathErr);
402 NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBGetCatalogInfoSync (source: %s) returned %li"), debuggingPathBuf, (long)err);
405 refPB.nameLength = destName->length;
406 refPB.name = destName->unicode;
407 refPB.textEncodingHint = kTextEncodingUnknown;
408 refPB.newRef = &destFile;
410 const char *functionName = "PBMakeFSRefUnicodeSync"; //for error-reporting message
412 err = PBMakeFSRefUnicodeSync(&refPB);
413 if ((err != noErr) && (err != fnfErr)) {
414 handleMakeFSRefError:
415 debuggingPathErr = FSRefMakePath(destDir, debuggingPathBuf, PATH_MAX);
416 if (debuggingPathErr != noErr)
417 snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for destination directory: FSRefMakePath returned %li)", (long)debuggingPathErr);
420 CFStringRef debuggingFilename = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
423 /*contentsDeallocator*/ kCFAllocatorNull);
424 if (!debuggingFilename)
425 debuggingFilename = CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, "(could not get filename for destination file: CFStringCreateWithCharactersNoCopy returned NULL)", kCFStringEncodingASCII, /*contentsDeallocator*/ kCFAllocatorNull);
427 NSLog(CFSTR("in copyFork in CFGrowlAdditions: %s (destination: %s/%@) returned %li"), functionName, debuggingPathBuf, debuggingFilename, (long)err);
429 if (debuggingFilename) CFRelease(debuggingFilename);
431 //that file doesn't exist in that folder; create it.
432 err = PBCreateFileUnicodeSync(&refPB);
434 /*make sure the Finder knows about the new file.
435 *FNNotify returns a status code too, but this isn't an
436 * essential step, so we just ignore it.
438 FNNotify(destDir, kFNDirectoryModifiedMessage, kNilOptions);
439 } else if (err == dupFNErr) {
440 /*dupFNErr: the file already exists.
441 *we can safely ignore this error.
445 functionName = "PBCreateFileUnicodeSync";
446 goto handleMakeFSRefError;
452 memcpy(outDestFile, &destFile, sizeof(destFile));
454 struct FSForkIOParam destPB = {
456 .forkNameLength = forkName->length,
457 .forkName = forkName->unicode,
458 .permissions = fsWrPerm,
460 err = PBOpenForkSync(&destPB);
461 NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBOpenForkSync (dest) returned %li"), (long)err);
463 debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
464 if (debuggingPathErr != noErr)
465 snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for dest file: FSRefMakePath returned %li)", (long)debuggingPathErr);
466 NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBOpenForkSync (destination: %s) returned %li"), debuggingPathBuf, (long)err);
468 void *buf = malloc(COPYFORK_BUFSIZE);
470 srcPB.buffer = destPB.buffer = buf;
471 srcPB.requestCount = COPYFORK_BUFSIZE;
472 while (err == noErr) {
473 err = PBReadForkSync(&srcPB);
476 if (srcPB.actualCount == 0)
480 debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
481 if (debuggingPathErr != noErr)
482 snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for source file: FSRefMakePath returned %li)", (long)debuggingPathErr);
483 NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBReadForkSync (source: %s) returned %li"), debuggingPathBuf, (long)err);
485 destPB.requestCount = srcPB.actualCount;
486 err = PBWriteForkSync(&destPB);
488 debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
489 if (debuggingPathErr != noErr)
490 snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for dest file: FSRefMakePath returned %li)", (long)debuggingPathErr);
491 NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBWriteForkSync (destination: %s) returned %li"), debuggingPathBuf, (long)err);
499 closeErr = PBCloseForkSync(&destPB);
500 if (closeErr != noErr) {
501 debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
502 if (debuggingPathErr != noErr)
503 snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for dest file: FSRefMakePath returned %li)", (long)debuggingPathErr);
504 NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBCloseForkSync (destination: %s) returned %li"), debuggingPathBuf, (long)err);
506 if (err == noErr) err = closeErr;
510 closeErr = PBCloseForkSync(&srcPB);
511 if (closeErr != noErr) {
512 debuggingPathErr = FSRefMakePath(&destFile, debuggingPathBuf, PATH_MAX);
513 if (debuggingPathErr != noErr)
514 snprintf((char *)debuggingPathBuf, PATH_MAX, "(could not get path for source file: FSRefMakePath returned %li)", (long)debuggingPathErr);
515 NSLog(CFSTR("in copyFork in CFGrowlAdditions: PBCloseForkSync (source: %s) returned %li"), debuggingPathBuf, (long)err);
517 if (err == noErr) err = closeErr;
523 static OSStatus GrowlCopyObjectSync(const FSRef *fileRef, const FSRef *destRef, FSRef *destFileRef) {
525 struct HFSUniStr255 forkName;
526 struct FSForkIOParam forkPB = {
531 .outForkName = &forkName,
535 err = PBIterateForksSync(&forkPB);
536 NSLog(CFSTR("PBIterateForksSync returned %li"), (long)err);
538 if (err != errFSNoMoreItems)
539 NSLog(CFSTR("in GrowlCopyObjectSync in CFGrowlAdditions: PBIterateForksSync returned %li"), (long)err);
541 err = copyFork(&forkName, fileRef, destRef, /*destName*/ NULL, /*outDestFile*/ destFileRef);
542 //copyFork prints its own error messages
544 } while (err == noErr);
545 if (err == errFSNoMoreItems) err = noErr;
550 URL_TYPE createURLByCopyingFileFromURLToDirectoryURL(URL_TYPE file, URL_TYPE dest)
552 CFURLRef destFileURL = NULL;
554 FSRef fileRef, destRef, destFileRef;
555 Boolean gotFileRef = CFURLGetFSRef(file, &fileRef);
556 Boolean gotDestRef = CFURLGetFSRef(dest, &destRef);
558 NSLog(CFSTR("in createURLByCopyingFileFromURLToDirectoryURL in CFGrowlAdditions: CFURLGetFSRef failed with source URL %@"), file);
559 else if (!gotDestRef)
560 NSLog(CFSTR("in createURLByCopyingFileFromURLToDirectoryURL in CFGrowlAdditions: CFURLGetFSRef failed with destination URL %@"), dest);
565 * 10.2 has a problem with weak symbols in frameworks so we use
566 * MAC_OS_X_VERSION_MIN_REQUIRED >= 10.3.
568 #if defined(NSAppKitVersionNumber10_3) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3
569 if (FSCopyObjectSync) {
570 err = FSCopyObjectSync(&fileRef, &destRef, /*destName*/ NULL, &destFileRef, kFSFileOperationOverwrite);
573 err = GrowlCopyObjectSync(&fileRef, &destRef, &destFileRef);
574 #if defined(NSAppKitVersionNumber10_3) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3
579 destFileURL = CFURLCreateFromFSRef(kCFAllocatorDefault, &destFileRef);
581 NSLog(CFSTR("in createURLByCopyingFileFromURLToDirectoryURL in CFGrowlAdditions: CopyObjectSync returned %li for source URL %@"), (long)err, file);
587 PLIST_TYPE createPropertyListFromURL(URL_TYPE file, u_int32_t mutability, CFPropertyListFormat *outFormat, STRING_TYPE *outErrorString)
589 CFPropertyListRef plist = NULL;
592 NSLog(CFSTR("in createPropertyListFromURL in CFGrowlAdditions: cannot read from a NULL URL"));
594 CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, file);
596 NSLog(CFSTR("in createPropertyListFromURL in CFGrowlAdditions: could not create stream for reading from URL %@"), file);
598 if (!CFReadStreamOpen(stream))
599 NSLog(CFSTR("in createPropertyListFromURL in CFGrowlAdditions: could not open stream for reading from URL %@"), file);
601 CFPropertyListFormat format;
602 CFStringRef errorString = NULL;
604 plist = CFPropertyListCreateFromStream(kCFAllocatorDefault,
611 NSLog(CFSTR("in createPropertyListFromURL in CFGrowlAdditions: could not read property list from URL %@ (error string: %@)"), file, errorString);
613 if (outFormat) *outFormat = format;
616 *outErrorString = errorString;
618 CFRelease(errorString);
621 CFReadStreamClose(stream);