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; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.