// NSData_HMext.m // // Created by John Wiseman on 9/6/05. // Copyright 2005 John Wiseman. // // Licensed under the MIT license--see the accompanying LICENSE.txt // file. #import "NSData_HMext.h" #import "DebugLog.h" // Reads up to maxLen bytes from a file into a buffer. The // buffer is allocated and returned in buf, while the number of // bytes read is returned in len. // // More or less taken from GnuStep's implementation of NSData. static BOOL readContentsOfFile(NSString* path, void** buf, size_t maxLen, size_t* len, NSZone* zone) { const char *thePath = 0; FILE *theFile = 0; void *tmp = 0; size_t c; size_t fileLength; thePath = [path fileSystemRepresentation]; if (thePath == 0) { // NSWarnFLog(@"Open (%@) attempt failed - bad path", path); return NO; } theFile = fopen(thePath, "rb"); if (theFile == 0) /* We failed to open the file. */ { // NSWarnFLog(@"Open (%@) attempt failed - %s", path, // GSLastErrorStr(errno)); goto failure; } /* * Seek to the end of the file. */ c = fseek(theFile, 0L, SEEK_END); if (c != 0) { // NSWarnFLog(@"Seek to end of file (%@) failed - %s", path, // GSLastErrorStr(errno)); goto failure; } /* * Determine the length of the file (having seeked to the end of the * file) by calling ftell(). */ fileLength = ftell(theFile); if (fileLength == -1) { // NSWarnFLog(@"Ftell on %@ failed - %s", path, // GSLastErrorStr(errno)); goto failure; } /* * Rewind the file pointer to the beginning, preparing to read in * the file. */ c = fseek(theFile, 0L, SEEK_SET); if (c != 0) { // NSWarnFLog(@"Fseek to start of file (%@) failed - %s", path, // GSLastErrorStr(errno)); goto failure; } if (fileLength == 0) { unsigned char buf[BUFSIZ]; size_t bytesToRead = maxLen; /* * Special case ... a file of length zero may be a named pipe or some * file in the /proc filesystem, which will return us data if we read * from it ... so we try reading as much as we can, up to the specified * limit. */ while ((c = fread(buf, 1, (bytesToRead < BUFSIZ) ? bytesToRead : BUFSIZ, theFile)) != 0) { if (tmp == 0) { tmp = NSZoneMalloc(zone, c); } else { tmp = NSZoneRealloc(zone, tmp, fileLength + c); } if (tmp == 0) { // NSLog(@"Malloc failed for file (%@) of length %d - %s", path, // fileLength + c, GSLastErrorStr(errno)); goto failure; } memcpy(tmp + fileLength, buf, c); fileLength += c; bytesToRead -= c; } if (fileLength == maxLen) { DebugLog(DEBUG_LEVEL_DEBUG, @"Truncated indexing of %s to %d bytes", thePath, maxLen); } } else { if (fileLength > maxLen) { fileLength = maxLen; DebugLog(DEBUG_LEVEL_DEBUG, @"Truncated indexing of %s to %d bytes", thePath, maxLen); } tmp = NSZoneMalloc(zone, fileLength); if (tmp == 0) { // NSLog(@"Malloc failed for file (%@) of length %d - %s", path, // fileLength, GSLastErrorStr(errno)); goto failure; } c = fread(tmp, 1, fileLength, theFile); if (c != (int)fileLength) { // NSWarnFLog(@"read of file (%@) contents failed - %s", path, // GSLastErrorStr(errno)); goto failure; } } *buf = tmp; *len = fileLength; fclose(theFile); return YES; /* * Just in case the failure action needs to be changed. */ failure: if (tmp != 0) { NSZoneFree(zone, tmp); } if (theFile != 0) { fclose(theFile); } return NO; } @implementation NSData (NSData_Extensions) /** * Returns a data object encapsulating the contents of the specified file * (but only up to the first theMaxSize bytes of the file). * Invokes -initWithContentsOfFile: * * Based on GnuStep's -[NSData dataWithContentsOfFile]. */ + (id) dataWithContentsOfFile: (NSString*)path maxSize:(int)theMaxSize error:(NSError**)error { NSData *d; d = [NSData allocWithZone: NSDefaultMallocZone()]; d = [d initWithContentsOfFile: path maxSize:theMaxSize error:error]; return [d autorelease]; } /** * Initializes the receiver with up to theMaxSize bytes of the specified file. * Returns the resulting object. * Returns nil if the file does not exist or can not be read for some reason, * in which case error information will (probably) be returned as well. * * Based on GnuStep's -[NSData initWithContentsOfFile]. */ - (id) initWithContentsOfFile: (NSString*)path maxSize:(int)theMaxSize error:(NSError**)error { void *fileBytes = NULL; size_t fileLength = 0; NSZone *zone; zone = NSDefaultMallocZone(); if (readContentsOfFile(path, &fileBytes, theMaxSize, &fileLength, zone) == NO) { if (error) { NSNumber *errorCode = [NSNumber numberWithInt:errno]; NSString *errorDescription = [NSString stringWithCString:strerror(errno) encoding:NSUTF8StringEncoding]; NSString* errorPath = path; NSMutableDictionary *errorAttribs = [NSMutableDictionary dictionaryWithCapacity:2]; [errorAttribs setObject:errorCode forKey:@"Errno"]; [errorAttribs setObject:errorDescription forKey:@"Description"]; [errorAttribs setObject:errorPath forKey:@"Path"]; *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:errorAttribs]; } [self dealloc]; return nil; } else { self = [self initWithBytesNoCopy:fileBytes length:fileLength freeWhenDone:YES]; } return self; } @end