This is File.m in view mode; [Download] [Up]
/***********************************************************************\
Common class for accessing files in all Convert programs
Copyright (C) 1993 David John Burrowes
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The author, David John Burrowes, can be reached at:
davidjohn@kira.net.netcom.com
David John Burrowes
1926 Ivy #10
San Mateo, CA 94403-1367
\***********************************************************************/
/*
====================================================================
This is the implementation file for the File class. Full documentation for this class can be found in the File.rtf file. I will not duplicate all that fine information here.
This is $Revision: 1.8 $ of this file
It was last modified by $Author: death $ on $Date: 93/04/04 23:44:26 $
Note that this file was created while using the New Century Schoolbook Roman typeface. You may find that some things line up strangely if you don't use that family.
* $Log: File.m,v $
Revision 1.8 93/04/04 23:44:26 death
Sun Apr 4 23:44:26 PDT 1993
Revision 1.7 93/01/10 15:07:54 death
Sun Jan 10 15:07:54 PST 1993
Revision 1.6 92/07/26 13:58:06 death
Update so all works with the font converter...
Revision 1.4 92/04/05 22:51:36 death
Miscelaneous revisions. This is the last version of version 1.
Revision 1.3 92/03/29 12:36:14 death
Bug in the open method. If we failed, we returned OK, if we succed, we reported failure.
Revision 1.2 92/03/29 12:29:06 death
Oops. Managed to check in the wrong version. this one has the code reflecting the new location of the init methods
Revision 1.1 92/03/29 12:18:44 death
Initial revision
*====================================================================
*/
//
// Import our own definition
//
#import "File.h"
#import <stdio.h>
#import <string.h>
#import <stdlib.h>
#import <sys/param.h> // for maxpathlen
#import <libc.h> // for getwd
#import <sys/file.h> // for open()
#import <streams/streams.h> // for streams stuff (exception codes)
#include <sys/time.h> // for gettimeofday
#if (NSmajor == 3)
# import <errno.h>
#endif
@implementation File
//
// @@@ bugs... doesn't deal with links properly. =(
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: CreateAndOpenFor
// Parameters: the means of accessing the specified file
// Returns: self
// Stores: none
// Description:
// This method will try to create a new file and open it. The file is presumed
// to be the one whose path is stored in the instance variables of this object.
// If the file already exists, or if an error occurrs when trying to creat it,
// an error is stored. Otherwise the file is opened, and the object is ready for use.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- CreateAndOpenFor: (AccessType) access
{
//
// If the object is working with a file already give an error.
// otherwise, do our stuff.
//
if (TheFile != NULL)
[self StoreErrorCode: ERR_SOMEFILEOPEN
AndText: "Object already in use for another file."];
{
if ([self FileExists: FileName] == YES)
[self StoreErrorCode: ERR_FILEEXISTS
AndText: "Can't create the requested file. It already exists."];
else
{
[self CreateFile: FileName];
if ([self GetErrorCode] != ERR_OK)
[self StoreErrorCode: ERR_CREATEFAIL
AndText: "Can't create the requested file."];
else
[self OpenWithAccess: access];
// Error info stored by this method.
}
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: OpenExistingFor
// Parameters: the means of accessing the specified file
// Returns: self
// Stores: none
// Description:
// This method will try to open an existing file. The file is presumed
// to be the one whose path is stored in the instance variables of this object.
// If the file does not exist, an error is stored. Otherwise the file is opened,
// and the object is ready for use.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- OpenExistingFor: (AccessType) access
{
//
// If the object is working with a file already give an error.
// otherwise, do our stuff.
//
if (TheFile != NULL)
[self StoreErrorCode: ERR_SOMEFILEOPEN
AndText: "Object already in use for another file."];
{
if ([self FileExists: FileName] == NO)
[self StoreErrorCode: ERR_FILEDOESNTEXIST
AndText: "Can't locate the requested file."];
else
[self OpenWithAccess: access];
// Error info stored by this method.
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: ClearAndOpenExistingFor
// Parameters: the means of accessing the specified file
// Returns: self
// Stores: none
// Description:
// This method will try to open an existing fil, and then clear (truncate) it.
// The file is presumed to be the one whose path is stored in the instance
// variables of this object. If the file does not exist, or if an error occurrs while
// clearing it, an error is stored. Otherwise the file is opened,
// and the object is ready for use.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ClearAndOpenExistingFor: (AccessType) access
{
//
// If the object is working with a file already give an error.
// otherwise, do our stuff.
//
if (TheFile != NULL)
[self StoreErrorCode: ERR_SOMEFILEOPEN
AndText: "Object already in use for another file."];
{
if ([self FileExists: FileName] == NO)
[self StoreErrorCode: ERR_FILEDOESNTEXIST
AndText: "Can't locate the requested file."];
else
{
[self ClearFile: FileName];
if ([self GetErrorCode] != ERR_OK)
[self StoreErrorCode: ERR_CLEARFAIL
AndText: "Can't clear the file!"];
else
[self OpenWithAccess: access];
// Error info stored by this method.
}
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: ClearAndOpenFor
// Parameters: the means of accessing the specified file
// Returns: self
// Stores: none
// Description:
// This method will try to open a file. If it doesn't exist already, it will create it.
// if it does, then it will clear it (truncate). The file is then opened if all went well
// The file is presumed to be the one whose path is stored in the instance
// variables of this object.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ClearAndOpenFor: (AccessType) access
{
//
// If the object is working with a file already give an error.
// otherwise, do our stuff.
//
if (TheFile != NULL)
[self StoreErrorCode: ERR_SOMEFILEOPEN
AndText: "Object already in use for another file."];
{
if ([self FileExists: FileName] == NO)
{
[self CreateFile: FileName];
if ([self GetErrorCode] != ERR_OK)
[self StoreErrorCode: ERR_CREATEFAIL
AndText: "Can't create the requested file."];
}
else
{
[self ClearFile: FileName];
if ([self GetErrorCode] != ERR_OK)
[self StoreErrorCode: ERR_CLEARFAIL
AndText: "Can't clear the file!"];
}
//
// The file exists and is of 0 length. open it now.
//
if ([self GetErrorCode] == ERR_OK)
[self OpenWithAccess: access];
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: OpenFor
// Parameters: the means of accessing the specified file
// Returns: self
// Stores: none
// Description:
// This method will try to open a file. If it doesn't exist already, it will create it.
// The file is then opened if all went well.
// The file is presumed to be the one whose path is stored in the instance
// variables of this object.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- OpenFor: (AccessType) access
{
//
// If the object is working with a file already give an error.
// otherwise, do our stuff.
//
if (TheFile != NULL)
[self StoreErrorCode: ERR_SOMEFILEOPEN
AndText: "Object already in use for another file."];
{
if ([self FileExists: FileName] == NO)
{
[self CreateFile: FileName];
if ([self GetErrorCode] != ERR_OK)
[self StoreErrorCode: ERR_CREATEFAIL
AndText: "Can't create the requested file."];
}
else
[self StoreErrorCode: ERR_OK AndText: "File exists. all is peachy."];
//
// The file exists. open it now.
//
if ([self GetErrorCode] == ERR_OK)
[self OpenWithAccess: access];
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: OpenWithAccess:
// Parameters: the means of accessing the specified file
// Returns: self
// Stores: none
// Description:
// This is a lower level opening routine. It should probably never be called from
// the outside, but instead be called by the (presently) five opening routines
// above this in the listing.
// It's purpose is simple: open the file that this object is going to work with.
// If the file does'nt exist, it returns an error. Thus, it is up to the caller to
// assure that the file exists and is in th proper form before calling this.
// Bugs:
// Not accounting for the fact that I may not have *permission* (666 kinda thing)
// to do any of these...
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- OpenWithAccess: (AccessType) operation
{
struct stat *statBuf;
[self ResetResults];
//
// Make sure that the file exists, and that we aren't already working with
// an opened file. If all is good, open it appropriately
//
if (([self FileExists: FileName] == YES) && (TheFile == NULL))
{
switch (operation)
{
case FILE_READ:
TheFile = NXMapFile(FileName, NX_READONLY);
break;
case FILE_WRITE:
TheFile = NXMapFile(FileName, NX_WRITEONLY);
break;
case FILE_READWRITE:
TheFile = NXMapFile(FileName, NX_READWRITE);
break;
case FILE_APPEND:
TheFile = NXMapFile(FileName, NX_WRITEONLY);
//
// Appending must be at the end of the file...
// @@@ do we need + 1 here?
//
[self MoveTo: [self FileSize]];
break;
default:
[self StoreErrorCode: ERR_BADACCESS
AndText: "An invalid access operation was requested. No file opened."];
break;
}
}
//
// If the file wasn't opened, or some other problem arose, generate a negative reply.
//
if ((TheFile == NULL) && ([self GetErrorCode] != ERR_BADACCESS))
[self StoreErrorCode: ERR_CANTOPEN
AndText: "The specified file could not be opened (bad permissions? Bad path?)"];
else
{
AccessMode = operation;
//
// Store the real inode now, in case we could not get it during the init.
//
[self FileInfo];
if ([self GetErrorCode] != ERR_OK)
TheInode = 0;
else
{
statBuf = (struct stat *) [self GetPointerFrom: SECOND_RESULT];
TheInode = statBuf->st_ino;
}
}
//
// Store whether the file was opened or not
//
if ((TheFile != NULL) && ([self GetErrorCode] == ERR_OK))
{
FileIsOpen = YES;
FileLocation = fileAtStart;
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: initAndUse:
// Parameters: none
// Returns: self
// Stores: none
// Description:
// This funky method is a pretty standard init method. It sets up all our instance
// variables, and does nothing more.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- initAndUse: (roCString) pathname
{
struct stat *statbuf;
[super init];
FileName = [self GetFullPathFrom: pathname];
AccessMode = FILE_NOACCESS;
TheFile = NULL;
//
// Get our inode, if possible.
//
[self FileInfo];
if ([self GetErrorCode] != ERR_OK)
{
// @@@ Maybe this shouldn't be this way...
[self StoreErrorCode: ERR_OK AndText: "Hmmmmmm"];
TheInode = 0;
}
else
{
statbuf = (struct stat *) [self GetPointerFrom: SECOND_RESULT];
TheInode = statbuf->st_ino;
}
FileIsOpen = NO;
FileLocation = fileAtStart;
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: initAndUseTemporary
// Parameters: none
// Returns: self
// Stores: none
// Description:
// This method is used when one wishes to have a temporary file to use.
// Calling this method will cause a temporary file to be created somwhere.
// one can then use it as a completely ordinary file with this object.
// Bugs:
// At the moment, this is implemented with the tmpname call. Because we
// ignore the TMP_MAX constant (the object shouldn't be tracking how many
// temp files are open in the program, and the program shouldn't know we're
// making use of tmpnam), we run the risk of opening too many temp files.
// Thus, this should be re-implemented ourselves sometime (perhaps to:
// /tmp/FileObj_TempXXXXXXXX, where X is a number-thing that will actually
// be a date from a date object, or some something more unique...
// History
// 93.01.03 djb Added call to stat, because it seems you have to call tmpnam repeatedly
// to get your unique name. YICK! Please move to the above. Then
// replaced with intermediate hack using a pretty unique name using the
// above and the process id, seconds since 1970, and microseconds.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- initAndUseTemporary
{
struct timeval tp;
struct timezone tzp;
[super init];
FileName = NewCString(63);
gettimeofday(&tp, &tzp);
// 93.01.24 djb Added the 'long' designation to the %d's at the NS3 compiler's suggestion.
sprintf(FileName, "/tmp/TempFileDataP%dS%ldU%ld",
getpid(), tp.tv_sec, tp.tv_usec);
/*
This approach does not work. Nor do previsu simpler ones.
FileName = NewCString(L_tmpnam);
ctr = 0;
do
{
FileName = tmpnam(FileName);
result = stat(FileName, &mystat);
ctr++;
}
while ((errno != 2) && (ctr < TMP_MAX));
*/
AccessMode = FILE_NOACCESS;
TheFile = NULL;
FileIsOpen = NO;
FileLocation = fileAtStart;
return self;
}
//============================================================
//============================================================
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: ClearFile:
// Parameters: a CString that contains a path to a file
// Returns: self
// Stores: none
// Description:
// This little kludgey method just clears the specified file. If anything bad
// happens, it stores an error code and text. At the moment, it truncates the file
// by opening it with the O_TRUNC flag to the open() routine. yech. This
// implementation had BETTER remain transparent to this and all subclasses,
// for it's sure to change at some point!
// Bugs:
// Ickly implementation.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ClearFile: (CString) pathname
{
int fd;
errno = 0;
fd =open(pathname, O_RDONLY|O_TRUNC, 666);
if (errno != 0)
[self StoreErrorCode: ERR_CLEARFAIL AndText: "The open() routine didn't O_TRUNC it!"];
close (fd);
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: CreateFile:
// Parameters: a CString that contains a path to a file
// Returns: self
// Stores: none
// Description:
// This little kludgey method just creates the specified file. If anything bad
// happens, it stores an error code and text. At the moment, it creates the file
// by opening it with the O_CREAT flag to the open() routine. yech. This
// implementation had BETTER remain transparent to this and all subclasses,
// for it's sure to change at some point!
// Bugs:
// Ickly implementation.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- CreateFile: (CString) pathname
{
int fd;
errno = 0;
fd =open(pathname, O_RDONLY|O_CREAT, 0666);
if (errno != 0)
[self StoreErrorCode: ERR_CREATEFAIL AndText: "The open() routine didn't create it!"];
else
[self StoreErrorCode: ERR_OK AndText: "The file was created"];
close (fd);
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: FileExists:
// Parameters: a CString that contains a path to a file
// Returns: True if the file exists
// Stores: the return value
// Description:
// This method has only one purpose: it returns YES if the specified file
// exists, and NO otherwise. We determine this by whether stat() reports no
// errors when trying to get info about the file.
// Bugs:
// Also, we always return an ERR_OK value... certianly *something* can go wrong?
// stat() is sensitifive to 8 bit character names. All I can say is that I find this
// to be an unacceptable drawback. However, I haven't the time or energy right
// now to get around this (and for all I know, the doc is just wrong).
// History
// 93.07.18 djb Revised to use stat(), finally!
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) FileExists: (CString) pathname
{
struct stat statBuf;
Integer statCode;
statCode = stat(pathname, &statBuf);
if (statCode != 0)
{
[self StoreErrorCode: ERR_OK AndText: "The file does not exist or can not be reached"];
return NO;
}
else
{
[self StoreErrorCode: ERR_OK AndText: "The file does indeed exist"];
return YES;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: GetFullPathFrom
// Parameters: a CString that contains a full or partial path to a file
// Returns: a new CString containing the full path to the file
// Stores: the new CString
// Description:
// The purpose of this string is to take a full or partial pathname (i.e.
// /me/Library/file.ext or Library/file.ext or file.ext), and return the full
// pathname to the file.
// If we were passed a full pathname (starts with the / character), then we
// make a full copy of it.
// otherwise, use the function getwd to get the path to the current directory,
// and fuse the two together.
// Note that we do not reset the results here. Since this is an internal
// method, the caller, in the object, may not want things cleared.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (CString) GetFullPathFrom: (roCString) pathname
{
char rootedpath[MAXPATHLEN];
CString fullpath;
char* pathresult;
//
// If the path is a complete path, then just copy it literally.
//
if (pathname[0] == '/')
{
fullpath = (CString) malloc(strlen(pathname) + 1);
strcpy(fullpath, pathname);
[self StoreErrorCode: ERR_OK AndText: "Got filename!"];
}
else
{
//
// Otherwise, we have a partial path. Get path to our current location
// (presumably where the partial path starts from), and fuse the two
// (if getwd() returns an error, something is wrong, and so we risk returning
// just the partial path)
//
pathresult = getwd(rootedpath);
if (*pathresult != 0)
{
fullpath = (CString) malloc(strlen(rootedpath)+1+strlen(pathname) + 1);
sprintf(fullpath, "%s/%s", rootedpath, pathname);
[self StoreErrorCode: ERR_OK AndText: "Got filename!"];
}
else
{
fullpath = (CString) malloc(strlen(pathname) + 1);
strcpy(fullpath, pathname);
[self StoreErrorCode: ERR_STRANGEPATH AndText: "Pathname may be corrupted!"];
}
}
return fullpath;
}
//============================================================
//============================================================
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: Close
// Parameters: a flag to indicate whether we should save the file
// Returns: self
// Stores: none
// Description:
// This closes the file, discarding any changes.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- Close
{
[self ResetResults];
if (TheFile != NULL)
{
NXCloseMemory(TheFile, NX_FREEBUFFER);
[self StoreErrorCode: ERR_OK AndText: "Closed right up"];
}
else
[self StoreErrorCode: ERR_FILENOTOPEN AndText: "No file open to close!"];
FileIsOpen = NO;
FileLocation = fileAtStart;
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: CloseAndSave
// Parameters: none
// Returns: self
// Stores: none
// Description:
// This closes the file,saving its contents before doing so.
// Bugs:
// how about some checking to make sure we dumped it all OK??
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- CloseAndSave
{
[self ResetResults];
if (TheFile != NULL)
{
if (AccessMode != FILE_READ)
NXSaveToFile(TheFile, FileName);
NXCloseMemory(TheFile, NX_FREEBUFFER);
[self StoreErrorCode: ERR_OK AndText: "Closed right up"];
}
else
[self StoreErrorCode: ERR_FILENOTOPEN AndText: "No file open to close!"];
FileIsOpen = NO;
FileLocation = fileAtStart;
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: CloseAndDelete
// Parameters: none
// Returns: self
// Stores: the error we got back from the system routine. (don't depend on this)
// Description:
// This closes the file, and then deletes it.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- CloseAndDelete
{
Integer status;
[self ResetResults];
[self Close];
status = remove(FileName);
if (status != 0)
{
[self StoreErrorCode: ERR_CANTDELETE AndText: "Could not delete file. unknown why"];
[self StoreInteger: status];
}
else
[self StoreErrorCode: ERR_OK AndText: "Deleted the file just fine"];
FileIsOpen = NO;
FileLocation = fileAtStart;
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: free:
// Parameters: none
// Returns: self
// Stores: none
// Description:
// This frees the object, which involves closing our connection to the physical
// file, WITHOUT SAVING, and then disposing of our instance variables,
// and then freeing our parent.
// Bugs:
// The call to close is commented out here because it's causing trouble if you
// say: [myFile CloseAndSave] ; [myFile free]; Streams problem. we SHOULD
// be somehow nulling the stream, in the Close methods, and closing here only if
// it isn't null.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- free
{
//[self Close];
free(FileName);
return [super free];
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: AdvanceBytes:
// Parameters: the number of bytes we should advance
// Returns: self
// Stores: none
// Description:
// This changes our current position in the file by advancing us up a specified number
// of bytes in it. We add an exception handler partially for the ease so we don't
// have to do the error checking ourselves. Partially because it allows us to
// check for other exceptions and kinda trap for them. And partially for the
// novelty of it.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- AdvanceBytes: (FilePosDelta) byteLoc;
{
[self ResetResults];
if (TheFile == NULL)
[self StoreErrorCode: ERR_FILENOTOPEN AndText: "The file wasn't even open!"];
else
{
//
// Try to advance the requested number of bytes. If an exception occurrs,
// and it's a seek error, just stick us at the end quietly. Otherwise, store
// an error code and leave things as they are.
//
NX_DURING
NXSeek(TheFile, byteLoc, NX_FROMCURRENT);
[self StoreErrorCode: ERR_PEACHY AndText: "We found what we NXsaught."];
NX_HANDLER
switch(NXLocalHandler.code)
{
case NX_illegalSeek:
NXSeek(TheFile, 0, NX_FROMEND);
[self StoreErrorCode: ERR_OK AndText: "Moved to the end."];
FileLocation = fileAtEOF;
break;
default :
[self StoreErrorCode: ERR_BADADVANCE
AndText: "A unknown exception occurred when advancing."];
break;
}
NX_ENDHANDLER
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: BackupBytes:
// Parameters: the number of bytes we should backup
// Returns: self
// Stores: none
// Description:
// This changes our current position in the file by backing us up a specified number
// of bytes in it. We set up an exception handler to deal with the case if the
// user tries to seek before the beginning. We could, perhaps, do the traping
// ourselves (compute current loc, distance from beginning, whether the byteLoc
// falls within that request... etc), but this seems easier, and deals with any other
// unexpected exceptions too.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- BackupBytes: (FilePosDelta) byteLoc;
{
[self ResetResults];
if (TheFile == NULL)
[self StoreErrorCode: ERR_FILENOTOPEN AndText: "The file wasn't even open!"];
else
{
//
// Try to backup the requested number of bytes. If an exception occurrs,
// and it's a seek error, just stick us at the beginning quietly. Otherwise, store
// an error code and leave things as they are.
//
NX_DURING
NXSeek(TheFile, -byteLoc, NX_FROMCURRENT);
[self StoreErrorCode: ERR_OK AndText: "We found what we NXsaught."];
NX_HANDLER
switch(NXLocalHandler.code)
{
case NX_illegalSeek:
NXSeek(TheFile, 0, NX_FROMSTART);
[self StoreErrorCode: ERR_OK AndText: "Moved to beginning."];
FileLocation = fileAtEOF;
break;
default :
[self StoreErrorCode: ERR_BADBACKUP
AndText: "A unknown exception occurred when backing up."];
break;
}
NX_ENDHANDLER
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: GetBasename
// Parameters: none
// Returns: the copy of a string containing the current filename's basename
// Stores: A pointer to the same string we return
// Description:
// This simply extracts the basename of the current file name. The basename is
// the part of the name that comes after thedirectory names, but before the extension.
// If we are using the file /foo/bar/hotfile.rtf, this returns hotfile
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (CString) GetBasename
{
CString lastSlash, lastDot, tempName, finish, start;
Integer nameSize;
[self ResetResults];
//
// Determine the length of the basename by figuring out where its start is
// (either right after the /, or at the start) and the end (before . or at the end)
//
lastSlash = strrchr(FileName, '/');
lastDot = strrchr(FileName, '.');
if (lastSlash == NullCString)
start = FileName;
else
start = &lastSlash[1];
if ((lastDot == NullCString) || (lastDot < lastSlash))
finish = &FileName[strlen(FileName)];
else
finish = lastDot;
nameSize = finish - start;
//
// Allocate the proper space for the basename, store the text, and return it.
//
tempName = (CString) malloc(nameSize+1);
strncpy(tempName, start, nameSize);
tempName[nameSize] = EndOfCString;
[self StoreErrorCode: ERR_OK AndText: "Located whatever basename was to be found."];
[self StoreCString: tempName];
return tempName;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: GetCurrentPosition
// Parameters: none
// Returns: A position in the file
// Stores: A position in the file as an Integer
// Description:
// this returns a FilePos value which indicates a current position in the file, as
// a count of the number of bytes since the beginning.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (FilePos) GetCurrentPosition
{
FilePos pos;
[self ResetResults];
if (TheFile == NULL)
{
[self StoreErrorCode: ERR_FILENOTOPEN AndText: "No position in an unopened file."];
pos = 0;
}
else
{
pos = NXTell(TheFile);
[self StoreErrorCode: ERR_OK AndText: "Got the file position."];
}
[self StoreInteger: pos];
return pos;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: GetExtension
// Parameters: none
// Returns: the copy of a string containing the current filename's extension
// Stores: A pointer to the same string we return
// Description:
// This simply extracts the extension of the current file name. The extension is
// the part of the name that comes after the last . in the filename. If there is no
// extension, this will simply return an empty string.
// If we are using the file /foo/bar/hotfile.rtf, this returns rtf
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (CString) GetExtension
{
//
// Determine where the last / and . are. We need the / to be sure we don't get a . further
// up in the path somewhere.
//
CString lastSlash, lastDot, start, tempName;
[self ResetResults];
//
// Locate the last . in the file name, and the last /. If we found no dot, or it came
// before the slash in the name, then there is no extension, so start with a null CString.
// otherwise, set the start of the exension to just after the last dot.
//
lastSlash = strrchr(FileName, '/');
lastDot = strrchr(FileName, '.');
if ((lastDot == NullCString) || (lastDot < lastSlash))
start = NullCString;
else
start = &lastDot[1];
//
// Allocate space for the extension in a temporary string, and copy it into
// TempName.
//
tempName = (CString) malloc(strlen(start)+1);
strcpy(tempName, start);
[self StoreErrorCode: ERR_OK AndText: "Located whatever extension was to be found."];
[self StoreCString: tempName];
return tempName;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: GetFilename
// Parameters: none
// Returns: the copy of a string containing the full filename, without
// Stores: A pointer to the same string we return
// Description:
// this returns the path to the file that we are using without including the file name
// itselfor the slash before the filename. If there are no /'s (and thus, oddly, no directories
// refered to), a null string is returned. If we are using the file /foo/bar/hotfile.rtf, this
// returns hotfile.rtf
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (CString) GetFilename
{
CString start;
CString tempName;
[self ResetResults];
//
// Locate the slash before the Filename, and make start point after it
// (which happens to be the start of our filename)
//
start = strrchr(FileName, '/');
if (start != NullCString)
start = &start[1];
else
start = FileName;
//
// Allocate the proper space for the filename, copy the text, and return it.
//
tempName = (CString) malloc(strlen(start)+1);
strcpy(tempName, start);
[self StoreErrorCode: ERR_OK AndText: "Extracted the filename"];
[self StoreCString: tempName];
return tempName;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: GetDirectory
// Parameters: none
// Returns: the copy of a string containing the pathname to this directory
// Stores: A pointer to the same string we return
// Description:
// this returns the path to the file that we are using without including the file name
// itselfor the slash before the filename. If there are no /'s (and thus, oddly, no directories
// refered to), a null string is returned. If we are using the filen /foo/bar/hotfile.rtf, this returns
// /foo/bar
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (CString) GetDirectory
{
//
// Declare a temporary string to store the file name, clear the results, and copy
// the actual file name into the temp string.
//
CString lastSlash;
CString tempName = (CString) malloc(strlen(FileName)+1);
[self ResetResults];
strcpy(tempName, FileName);
//
// Locate the last slash in the path name. If we find it, make it the end of the string instad,
// otherwise clear the whole string. Return it in any case.
//
lastSlash = strrchr(tempName, '/');
if (lastSlash != NullCString)
{
lastSlash[0] = EndOfCString;
[self StoreErrorCode: ERR_OK AndText: "Got the directory"];
}
else
{
tempName[0]=EndOfCString;
[self StoreErrorCode: ERR_NODIRECTORY AndText: "There were no directory names"];
}
[self StoreCString: tempName];
return tempName;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: GetPathname:
// Parameters: none
// Returns: the copy of a string containing the pathname to this file
// Stores: A pointer to the same string we return
// Description:
// This merely makes a copy of the string that we have stored for the pathname
// and returns it to the caller. Given the filename /foo/bar/hotfile.rtf, this returns
// /foo/bar/hotfile.rtf
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (CString) GetPathname
{
CString tempName;
[self ResetResults];
//
// Allocate the proper space for the pahtname, store the text, and return it.
//
tempName = (CString) malloc(strlen(FileName)+1);
strcpy(tempName, FileName);
[self StoreErrorCode: ERR_OK AndText: "Gotcha your path"];
[self StoreCString: tempName];
return tempName;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: MoveTo:
// Parameters: the byte position we should move to in the file
// Returns: self
// Stores: The position we moved to
// Description:
// This changes our position in the file to the position byteLoc bytes from the
// beginning of the file. The new position in the file is stored before we return.
// Deal with exceptions being raised around the NXSeek code. If an error occurs,
// then we will assume that it's because we tried to move past the eof, and so we
// set the position at the end of a file. if an unknown exception occurs, do nothing.
// Bugs:
// I don't know if the assumption that an NX_illegalSeek exception necessarily means
// we tried to go past the end of the stream. If not, there's problems here.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (id) MoveTo: (PositiveInteger) byteLoc
{
Boolean result;
[self ResetResults];
if (TheFile == NULL)
[self StoreErrorCode: ERR_FILENOTOPEN
AndText: "The file wasn't even open! How d'ya expect me to move around in it?!"];
else if (AccessMode == FILE_APPEND)
[self StoreErrorCode: ERR_FILEAPPENDONLY
AndText: "Can not change position in a file set for only appending"];
else
{
//
// Try to move the requested number of bytes. If an exception occurrs,
// and it's a seek error, just stick us at the end quietly (assume the
// error was due to running over the end. Otherwise, store
// an error code and leave things as they are.
//
NX_DURING
NXSeek(TheFile, byteLoc, NX_FROMSTART);
[self StoreErrorCode: ERR_OK AndText: "Moved OK"];
NX_HANDLER
switch(NXLocalHandler.code)
{
case NX_illegalSeek:
NXSeek(TheFile, 0, NX_FROMEND);
[self StoreErrorCode: ERR_OK AndText: "Moved to the end."];
FileLocation = fileAtEOF;
break;
default :
[self StoreErrorCode: ERR_BADADVANCE
AndText: "A unknown exception occurred when advancing."];
break;
}
NX_ENDHANDLER
result = NXAtEOS(TheFile);
if (result != NO)
FileLocation = fileAtEOF;
else if ( NXTell(TheFile) == 0)
FileLocation = fileAtStart;
else
FileLocation = fileInMiddle;
[self StorePositiveInteger: NXTell(TheFile)];
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: ReadByte
// Parameters: none
// Returns: the byte we read
// Stores: The byte we read
// YES if EOF was encountered
// Description:
// This reads a byte from our file. If all goes well, we return the byte. Otherwise,
// we return 0 and store an error
// Bugs:
// BOOL NXAtEOS(NXStream *stream)
// Says it should not be used on streams open for writing. What about read/write?
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Byte) ReadByte
{
Byte byteIn = 0;
Boolean result;
[self ResetResults];
if (TheFile == NULL)
[self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"];
else if ((AccessMode == FILE_WRITE) || (AccessMode == FILE_APPEND))
[self StoreErrorCode: ERR_FILEWRITEONLY
AndText: "Can not read from a file set for only appending or writing"];
else
{
result = NXAtEOS(TheFile);
if (result != NO)
{
[self StoreErrorCode: ERR_EOF AndText: "End of file found"];
FileLocation = fileAtEOF;
[self StorePositiveInteger: 0];
[self PutBoolean: YES Into: SECOND_RESULT];
}
else
{
byteIn = NXGetc(TheFile);
[self StoreErrorCode: ERR_OK AndText: "Read the byte"];
[self StorePositiveInteger: byteIn];
[self PutBoolean: NO Into: SECOND_RESULT];
}
}
return byteIn;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: Read:BytesInto:
// Parameters: the number of bytes we are to read
// the buffer that we are to read info
// Returns: self
// Stores: The number of bytes we succesfully read
// YES if we found EOF at the end of the current read
// Description:
// This attempts to read in numBytes bytes from our file into the buffer. It does no
// checking to assure that the buffer is of the proper size. After reading the bytes, it
// stores the number of bytes it succeeded in reading. If the number of bytes written
// is not equal to the number requested, an error is stored.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (id) Read: (PositiveInteger) numBytes BytesInto: (ByteString) buffer
{
PositiveInteger BytesRead;
Boolean result;
[self ResetResults];
if (TheFile == NULL)
[self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"];
else if ((AccessMode == FILE_WRITE) || (AccessMode == FILE_APPEND))
[self StoreErrorCode: ERR_FILEWRITEONLY
AndText: "Can not read from a file set for only appending or writing"];
else
{
result = NXAtEOS(TheFile);
if (result != NO)
{
[self StoreErrorCode: ERR_EOF AndText: "End of file found"];
FileLocation = fileAtEOF;
[self StorePositiveInteger: 0];
[self PutBoolean: YES Into: SECOND_RESULT];
}
else
{
BytesRead = NXRead(TheFile, buffer, numBytes);
//
// Deal with the consequences of our actions
//
if (BytesRead != numBytes)
[self StoreErrorCode: ERR_READINGERROR AndText: "Read the wrong number of bytes"];
else
[self StoreErrorCode: ERR_OK AndText: "Read the bytes."];
[self StorePositiveInteger: BytesRead];
[self PutBoolean: NO Into: SECOND_RESULT];
}
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: WriteByte
// Parameters: the byte we are to write
// Returns: self
// Stores: The byte we think we wrote
// Description:
// This writes a specified byte to our file. It then checks to make sure we wrote
// that byte. If we failed to do that, we generate an error.
// Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (id) WriteByte: (Byte) theByte
{
PositiveInteger byteOut;
[self ResetResults];
if (TheFile == NULL)
[self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"];
else if (AccessMode == FILE_READ)
[self StoreErrorCode: ERR_FILEREADONLY AndText: "Can not write to read-only file"];
else
{
byteOut = NXPutc(TheFile, theByte);
if (byteOut == theByte)
[self StoreErrorCode: ERR_OK AndText: "Wrote the byte"];
else
[self StoreErrorCode: ERR_WROTEBADCHAR AndText: "Wrote the wrong byte"];
[self StorePositiveInteger: byteOut];
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Routine: Write:BytesFrom:
// Parameters: the number of bytes we are to write
// the buffer that we are to write from
// Returns: self
// Stores: The number of bytes we succesfully wrote
// Description:
// This writes a specified number of bytes from the buffer we have been passed to
// the file that we manipulate. If we fail to write all the bytes requested, we store an
// error. We always store the number of bytes we wrote.
// Bugs:
// We never flush the data out (NXFlush does naught for a memory stream)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (id) Write: (PositiveInteger) numBytes BytesFrom: (ByteString) buffer
{
PositiveInteger bytesWrote;
[self ResetResults];
if (TheFile == NULL)
[self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened"];
else if (AccessMode == FILE_READ)
[self StoreErrorCode: ERR_FILEREADONLY AndText: "Can not write to read-only file"];
else
{
bytesWrote = NXWrite(TheFile, buffer, numBytes);
//
// Deal with the consequences of our actions
//
if (bytesWrote != numBytes)
[self StoreErrorCode: ERR_WRITINGERROR
AndText: "Wrote the wrong number of bytes"];
else
[self StoreErrorCode: ERR_OK AndText: "Wrote the bytes."];
[self StorePositiveInteger: bytesWrote];
}
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method: FileSize
// Parameters: none
// Returns: An integer reflecting the size of the file. Set to 0 if an error occured
// Stores: The return value, if all went well, otherwise nothing
// Description:
// This determines how many bytes long the file that this object manipulates is,
// and returns this number to the caller.
// Bugs:
// Only usable if the file has been opened...
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (PositiveInteger) FileSize
{
PositiveInteger currentLoc, eofLoc;
[self ResetResults];
if (TheFile == NULL)
{
[self StoreErrorCode: ERR_FILENOTOPEN AndText: "File was not properly opened."];
eofLoc = 0;
[self StorePositiveInteger: 0];
}
else
{
currentLoc = NXTell(TheFile);
NXSeek(TheFile, 0, NX_FROMEND);
eofLoc = NXTell(TheFile);
NXSeek(TheFile, currentLoc, NX_FROMSTART);
[self StoreErrorCode: ERR_OK AndText: "Obtained size of file."];
[self StorePositiveInteger: eofLoc];
}
return eofLoc;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method: FileInfo
// Parameters: none
// Returns: self
// Stores: the access mode is stored in return position 1
// a pointer to an OS-dependant structure is in 2
// Description:
// This returns information to the caller that isn't accessable through other
// methods. At this point, it returns the access method for the file, currently being
// used. It also returns a pointer to a stat structure (see the man page for stat).
// The stat structure may be replaced by a better structure in a future
// version
// Bugs:
// info like file size won't be accurate to the caller.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- FileInfo
{
struct stat *statbuf = (struct stat *) malloc(sizeof(struct stat));
if (stat(FileName, statbuf) != 0)
[self StoreErrorCode: ERR_CANTGETINFO AndText: "Could not get machine dep. info"];
else
{
[self StoreErrorCode: ERR_OK AndText: "The info is yours!"];
[self PutInteger: AccessMode Into: FIRST_RESULT];
[self CopyPointer: (Pointer) statbuf WithLength: sizeof(struct stat) Into: SECOND_RESULT];
}
free((char*) statbuf);
return self;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Method: SameFileAs
// Parameters: the path to another file
// Returns: A boolean value. yes indicates that the pathname refers to the same
// file this object is accessing. no means either they are different or
// the other file does not exist. (or that this one doesn't exist)
// Stores: This boolean.
// Description:
// Used to determine if some other file is the same as this one.. Pass the name of
// the other file with a path, and this will check to see it if it refers to the same
// file that it is using. Note that this doesn't understand symbolic links, really.
// Bugs:
// Inadequately deals with the negative cases where one or both files might not
// actually exist.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Boolean) SameFileAs: (CString) someOtherFile
{
struct stat *statbuf = (struct stat *) malloc(sizeof(struct stat));
CString pathToFile = [self GetFullPathFrom: someOtherFile];
ErrorCode result;
Boolean answer = NO;
if (stat(pathToFile, statbuf) != 0)
result = -1;
else
{
if (TheInode == statbuf->st_ino)
result = 0;
else
result = 1;
}
FreeCString(pathToFile);
// 92.01.01 djb Added freeing of statbuf to remove a memory leak.
free((char*) statbuf);
switch (result)
{
case -1:
[self StoreErrorCode: ERR_OK AndText: "The file does not even exist!"];
answer = NO;
break;
case 0:
[self StoreErrorCode: ERR_OK AndText: "The info is yours!"];
answer = YES;
break;
case 1:
[self StoreErrorCode: ERR_OK AndText: "The info is yours!"];
answer = NO;
break;
}
[self StoreBoolean: answer];
return answer;
}
@endThese are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.