git.fiddlerwoaroof.com
Source/NSData_HMext.m
0c532ca4
 //  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, unsigned int maxLen, unsigned int* len, NSZone* zone)
 {
 	const char	*thePath = 0;
 	FILE		*theFile = 0;
 	void		*tmp = 0;
 	int			c;
 	long		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];
 		unsigned 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;
 	unsigned	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)];
 			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