/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef _MDB_ # include "mdb.h" #endif #ifndef _MORK_ # include "mork.h" #endif #ifndef _MORKNODE_ # include "morkNode.h" #endif #ifndef _MORKENV_ # include "morkEnv.h" #endif #ifndef _MORKFILE_ # include "morkFile.h" #endif #ifdef MORK_WIN # include "io.h" # include #endif #include "mozilla/Unused.h" #include "nsString.h" // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 // ````` ````` ````` ````` ````` // { ===== begin morkNode interface ===== /*public virtual*/ void morkFile::CloseMorkNode( morkEnv* ev) // CloseFile() only if open { if (this->IsOpenNode()) { this->MarkClosing(); this->CloseFile(ev); this->MarkShut(); } } /*public virtual*/ morkFile::~morkFile() // assert CloseFile() executed earlier { MORK_ASSERT(mFile_Frozen == 0); MORK_ASSERT(mFile_DoTrace == 0); MORK_ASSERT(mFile_IoOpen == 0); MORK_ASSERT(mFile_Active == 0); } /*public non-poly*/ morkFile::morkFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) : morkObject(ev, inUsage, ioHeap, morkColor_kNone, (morkHandle*)0), mFile_Frozen(0), mFile_DoTrace(0), mFile_IoOpen(0), mFile_Active(0) , mFile_SlotHeap(0), mFile_Name(0), mFile_Thief(0) { if (ev->Good()) { if (ioSlotHeap) { nsIMdbHeap_SlotStrongHeap(ioSlotHeap, ev, &mFile_SlotHeap); if (ev->Good()) mNode_Derived = morkDerived_kFile; } else ev->NilPointerError(); } } NS_IMPL_ISUPPORTS_INHERITED(morkFile, morkObject, nsIMdbFile) /*public non-poly*/ void morkFile::CloseFile( morkEnv* ev) // called by CloseMorkNode(); { if (this->IsNode()) { mFile_Frozen = 0; mFile_DoTrace = 0; mFile_IoOpen = 0; mFile_Active = 0; if (mFile_Name) this->SetFileName(ev, nullptr); nsIMdbHeap_SlotStrongHeap((nsIMdbHeap*)0, ev, &mFile_SlotHeap); nsIMdbFile_SlotStrongFile((nsIMdbFile*)0, ev, &mFile_Thief); this->MarkShut(); } else this->NonNodeError(ev); } // } ===== end morkNode methods ===== // ````` ````` ````` ````` ````` /*static*/ morkFile* morkFile::OpenOldFile(morkEnv* ev, nsIMdbHeap* ioHeap, const PathChar* inFilePath, mork_bool inFrozen) // Choose some subclass of morkFile to instantiate, in order to read // (and write if not frozen) the file known by inFilePath. The file // returned should be open and ready for use, and presumably positioned // at the first byte position of the file. The exact manner in which // files must be opened is considered a subclass specific detail, and // other portions or Mork source code don't want to know how it's done. { return morkStdioFile::OpenOldStdioFile(ev, ioHeap, inFilePath, inFrozen); } /*static*/ morkFile* morkFile::CreateNewFile(morkEnv* ev, nsIMdbHeap* ioHeap, const PathChar* inFilePath) // Choose some subclass of morkFile to instantiate, in order to read // (and write if not frozen) the file known by inFilePath. The file // returned should be created and ready for use, and presumably positioned // at the first byte position of the file. The exact manner in which // files must be opened is considered a subclass specific detail, and // other portions or Mork source code don't want to know how it's done. { return morkStdioFile::CreateNewStdioFile(ev, ioHeap, inFilePath); } void morkFile::NewMissingIoError(morkEnv* ev) const { ev->NewError("file missing io"); } /*static*/ void morkFile::NonFileTypeError(morkEnv* ev) { ev->NewError("non morkFile"); } /*static*/ void morkFile::NilSlotHeapError(morkEnv* ev) { ev->NewError("nil mFile_SlotHeap"); } /*static*/ void morkFile::NilFileNameError(morkEnv* ev) { ev->NewError("nil mFile_Name"); } void morkFile::SetThief(morkEnv* ev, nsIMdbFile* ioThief) { nsIMdbFile_SlotStrongFile(ioThief, ev, &mFile_Thief); } void morkFile::SetFileName(morkEnv* ev, const PathChar* inName) // inName can be nil { nsIMdbHeap* heap = mFile_SlotHeap; if (heap) { PathChar* name = mFile_Name; if (name) { mFile_Name = 0; ev->FreeString(heap, name); } if (ev->Good() && inName) mFile_Name = ev->CopyString(heap, inName); } else this->NilSlotHeapError(ev); } void morkFile::NewFileDownError(morkEnv* ev) const // call NewFileDownError() when either IsOpenAndActiveFile() // is false, or when IsOpenActiveAndMutableFile() is false. { if (this->IsOpenNode()) { if (this->FileActive()) { if (this->FileFrozen()) { ev->NewError("file frozen"); } else ev->NewError("unknown file problem"); } else ev->NewError("file not active"); } else ev->NewError("file not open"); } void morkFile::NewFileErrnoError(morkEnv* ev) const // call NewFileErrnoError() to convert std C errno into AB fault { const char* errnoString = strerror(errno); ev->NewError(errnoString); // maybe pass value of strerror() instead } // ````` ````` ````` ````` newlines ````` ````` ````` ````` #if defined(MORK_MAC) static const char morkFile_kNewlines[] = "\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015\015"; # define morkFile_kNewlinesCount 16 #else # if defined(MORK_WIN) static const char morkFile_kNewlines[] = "\015\012\015\012\015\012\015\012\015\012\015\012\015\012\015\012"; # define morkFile_kNewlinesCount 8 # else # ifdef MORK_UNIX static const char morkFile_kNewlines[] = "\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012\012"; # define morkFile_kNewlinesCount 16 # endif /* MORK_UNIX */ # endif /* MORK_WIN */ #endif /* MORK_MAC */ mork_size morkFile::WriteNewlines(morkEnv* ev, mork_count inNewlines) // WriteNewlines() returns the number of bytes written. { mork_size outSize = 0; while (inNewlines && ev->Good()) // more newlines to write? { mork_u4 quantum = inNewlines; if (quantum > morkFile_kNewlinesCount) quantum = morkFile_kNewlinesCount; mork_size quantumSize = quantum * mork_kNewlineSize; mdb_size bytesWritten; this->Write(ev->AsMdbEnv(), morkFile_kNewlines, quantumSize, &bytesWritten); outSize += quantumSize; inNewlines -= quantum; } return outSize; } NS_IMETHODIMP morkFile::Eof(nsIMdbEnv* mev, mdb_pos* outPos) { nsresult outErr = NS_OK; mdb_pos pos = -1; morkEnv* ev = morkEnv::FromMdbEnv(mev); pos = Length(ev); outErr = ev->AsErr(); if (outPos) *outPos = pos; return outErr; } NS_IMETHODIMP morkFile::Get(nsIMdbEnv* mev, void* outBuf, mdb_size inSize, mdb_pos inPos, mdb_size* outActualSize) { nsresult rv = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { mdb_pos outPos; Seek(mev, inPos, &outPos); if (ev->Good()) rv = Read(mev, outBuf, inSize, outActualSize); } return rv; } NS_IMETHODIMP morkFile::Put(nsIMdbEnv* mev, const void* inBuf, mdb_size inSize, mdb_pos inPos, mdb_size* outActualSize) { nsresult outErr = NS_OK; *outActualSize = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { mdb_pos outPos; Seek(mev, inPos, &outPos); if (ev->Good()) Write(mev, inBuf, inSize, outActualSize); outErr = ev->AsErr(); } return outErr; } // { ----- begin path methods ----- NS_IMETHODIMP morkFile::Path(nsIMdbEnv* mev, mdbYarn* outFilePath) { nsresult outErr = NS_OK; if (outFilePath) outFilePath->mYarn_Fill = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { ev->StringToYarn(GetFileNameString(), outFilePath); outErr = ev->AsErr(); } return outErr; } // } ----- end path methods ----- // { ----- begin replacement methods ----- NS_IMETHODIMP morkFile::Thief(nsIMdbEnv* mev, nsIMdbFile** acqThief) { nsresult outErr = NS_OK; nsIMdbFile* outThief = 0; morkEnv* ev = morkEnv::FromMdbEnv(mev); if (ev) { outThief = GetThief(); NS_IF_ADDREF(outThief); outErr = ev->AsErr(); } if (acqThief) *acqThief = outThief; return outErr; } // } ----- end replacement methods ----- // { ----- begin versioning methods ----- // ````` ````` ````` ````` ````` // { ===== begin morkNode interface ===== /*public virtual*/ void morkStdioFile::CloseMorkNode( morkEnv* ev) // CloseStdioFile() only if open { if (this->IsOpenNode()) { this->MarkClosing(); this->CloseStdioFile(ev); this->MarkShut(); } } /*public virtual*/ morkStdioFile::~morkStdioFile() // assert CloseStdioFile() executed earlier { if (mStdioFile_File) CloseStdioFile(mMorkEnv); MORK_ASSERT(mStdioFile_File == 0); } /*public non-poly*/ void morkStdioFile::CloseStdioFile( morkEnv* ev) // called by CloseMorkNode(); { if (this->IsNode()) { if (mStdioFile_File && this->FileActive() && this->FileIoOpen()) { this->CloseStdio(ev); } mStdioFile_File = 0; this->CloseFile(ev); this->MarkShut(); } else this->NonNodeError(ev); } // } ===== end morkNode methods ===== // ````` ````` ````` ````` ````` // compatible with the morkFile::MakeFile() entry point /*static*/ morkStdioFile* morkStdioFile::OpenOldStdioFile( morkEnv* ev, nsIMdbHeap* ioHeap, const PathChar* inFilePath, mork_bool inFrozen) { morkStdioFile* outFile = 0; if (ioHeap && inFilePath) { const char* mode = (inFrozen) ? "rb" : "rb+"; outFile = new (*ioHeap, ev) morkStdioFile(ev, morkUsage::kHeap, ioHeap, ioHeap, inFilePath, mode); if (outFile) { outFile->SetFileFrozen(inFrozen); } } else ev->NilPointerError(); return outFile; } /*static*/ morkStdioFile* morkStdioFile::CreateNewStdioFile( morkEnv* ev, nsIMdbHeap* ioHeap, const PathChar* inFilePath) { morkStdioFile* outFile = 0; if (ioHeap && inFilePath) { const char* mode = "wb+"; outFile = new (*ioHeap, ev) morkStdioFile(ev, morkUsage::kHeap, ioHeap, ioHeap, inFilePath, mode); } else ev->NilPointerError(); return outFile; } NS_IMETHODIMP morkStdioFile::BecomeTrunk(nsIMdbEnv* ev) // If this file is a file version branch created by calling AcquireBud(), // BecomeTrunk() causes this file's content to replace the original // file's content, typically by assuming the original file's identity. { return Flush(ev); } NS_IMETHODIMP morkStdioFile::AcquireBud(nsIMdbEnv* mdbev, nsIMdbHeap* ioHeap, nsIMdbFile** acquiredFile) // AcquireBud() starts a new "branch" version of the file, empty of content, // so that a new version of the file can be written. This new file // can later be told to BecomeTrunk() the original file, so the branch // created by budding the file will replace the original file. Some // file subclasses might initially take the unsafe but expedient // approach of simply truncating this file down to zero length, and // then returning the same morkFile pointer as this, with an extra // reference count increment. Note that the caller of AcquireBud() is // expected to eventually call CutStrongRef() on the returned file // in order to release the strong reference. High quality versions // of morkFile subclasses will create entirely new files which later // are renamed to become the old file, so that better transactional // behavior is exhibited by the file, so crashes protect old files. // Note that AcquireBud() is an illegal operation on readonly files. { NS_ENSURE_ARG(acquiredFile); MORK_USED_1(ioHeap); nsresult rv = NS_OK; morkFile* outFile = 0; morkEnv* ev = morkEnv::FromMdbEnv(mdbev); if (this->IsOpenAndActiveFile()) { FILE* file = (FILE*)mStdioFile_File; if (file) { // #ifdef MORK_WIN // truncate(file, /*eof*/ 0); // #else /*MORK_WIN*/ PathChar* name = mFile_Name; if (name) { if (MORK_FILECLOSE(file) >= 0) { this->SetFileActive(morkBool_kFalse); this->SetFileIoOpen(morkBool_kFalse); mStdioFile_File = 0; file = MORK_FILEOPEN( name, "wb+"); // open for write, discarding old content if (file) { mStdioFile_File = file; this->SetFileActive(morkBool_kTrue); this->SetFileIoOpen(morkBool_kTrue); this->SetFileFrozen(morkBool_kFalse); } else this->new_stdio_file_fault(ev); } else this->new_stdio_file_fault(ev); } else this->NilFileNameError(ev); // #endif /*MORK_WIN*/ if (ev->Good() && this->AddStrongRef(ev->AsMdbEnv())) { outFile = this; AddRef(); } } else if (mFile_Thief) { rv = mFile_Thief->AcquireBud(ev->AsMdbEnv(), ioHeap, acquiredFile); } else this->NewMissingIoError(ev); } else this->NewFileDownError(ev); *acquiredFile = outFile; return rv; } mork_pos morkStdioFile::Length(morkEnv* ev) const { mork_pos outPos = 0; if (this->IsOpenAndActiveFile()) { FILE* file = (FILE*)mStdioFile_File; if (file) { long start = MORK_FILETELL(file); if (start >= 0) { long fore = MORK_FILESEEK(file, 0, SEEK_END); if (fore >= 0) { long eof = MORK_FILETELL(file); if (eof >= 0) { long back = MORK_FILESEEK(file, start, SEEK_SET); if (back >= 0) outPos = eof; else this->new_stdio_file_fault(ev); } else this->new_stdio_file_fault(ev); } else this->new_stdio_file_fault(ev); } else this->new_stdio_file_fault(ev); } else if (mFile_Thief) mFile_Thief->Eof(ev->AsMdbEnv(), &outPos); else this->NewMissingIoError(ev); } else this->NewFileDownError(ev); return outPos; } NS_IMETHODIMP morkStdioFile::Tell(nsIMdbEnv* ev, mork_pos* outPos) const { nsresult rv = NS_OK; NS_ENSURE_ARG(outPos); morkEnv* mev = morkEnv::FromMdbEnv(ev); if (this->IsOpenAndActiveFile()) { FILE* file = (FILE*)mStdioFile_File; if (file) { long where = MORK_FILETELL(file); if (where >= 0) *outPos = where; else this->new_stdio_file_fault(mev); } else if (mFile_Thief) mFile_Thief->Tell(ev, outPos); else this->NewMissingIoError(mev); } else this->NewFileDownError(mev); return rv; } NS_IMETHODIMP morkStdioFile::Read(nsIMdbEnv* ev, void* outBuf, mork_size inSize, mork_num* outCount) { nsresult rv = NS_OK; morkEnv* mev = morkEnv::FromMdbEnv(ev); if (this->IsOpenAndActiveFile()) { FILE* file = (FILE*)mStdioFile_File; if (file) { long count = (long)MORK_FILEREAD(outBuf, inSize, file); if (count >= 0) { *outCount = (mork_num)count; } else this->new_stdio_file_fault(mev); } else if (mFile_Thief) mFile_Thief->Read(ev, outBuf, inSize, outCount); else this->NewMissingIoError(mev); } else this->NewFileDownError(mev); return rv; } NS_IMETHODIMP morkStdioFile::Seek(nsIMdbEnv* mdbev, mork_pos inPos, mork_pos* aOutPos) { mork_pos outPos = 0; nsresult rv = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mdbev); if (this->IsOpenOrClosingNode() && this->FileActive()) { FILE* file = (FILE*)mStdioFile_File; if (file) { long where = MORK_FILESEEK(file, inPos, SEEK_SET); if (where >= 0) outPos = inPos; else this->new_stdio_file_fault(ev); } else if (mFile_Thief) mFile_Thief->Seek(mdbev, inPos, aOutPos); else this->NewMissingIoError(ev); } else this->NewFileDownError(ev); *aOutPos = outPos; return rv; } NS_IMETHODIMP morkStdioFile::Write(nsIMdbEnv* mdbev, const void* inBuf, mork_size inSize, mork_size* aOutSize) { mork_num outCount = 0; nsresult rv = NS_OK; morkEnv* ev = morkEnv::FromMdbEnv(mdbev); if (this->IsOpenActiveAndMutableFile()) { FILE* file = (FILE*)mStdioFile_File; if (file) { mozilla::Unused << fwrite(inBuf, 1, inSize, file); if (!ferror(file)) outCount = inSize; else this->new_stdio_file_fault(ev); } else if (mFile_Thief) mFile_Thief->Write(mdbev, inBuf, inSize, &outCount); else this->NewMissingIoError(ev); } else this->NewFileDownError(ev); *aOutSize = outCount; return rv; } NS_IMETHODIMP morkStdioFile::Flush(nsIMdbEnv* mdbev) { morkEnv* ev = morkEnv::FromMdbEnv(mdbev); if (this->IsOpenOrClosingNode() && this->FileActive()) { FILE* file = (FILE*)mStdioFile_File; if (file) { MORK_FILEFLUSH(file); } else if (mFile_Thief) mFile_Thief->Flush(mdbev); else this->NewMissingIoError(ev); } else this->NewFileDownError(ev); return NS_OK; } // ````` ````` ````` ````` ````` ````` ````` ````` // protected: // protected non-poly morkStdioFile methods void morkStdioFile::new_stdio_file_fault(morkEnv* ev) const { FILE* file = (FILE*)mStdioFile_File; int copyErrno = errno; // facilitate seeing error in debugger // bunch of stuff not ported here if (!copyErrno && file) { copyErrno = ferror(file); errno = copyErrno; } this->NewFileErrnoError(ev); } // ````` ````` ````` ````` ````` ````` ````` ````` // public: // public non-poly morkStdioFile methods /*public non-poly*/ morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap) : morkFile(ev, inUsage, ioHeap, ioSlotHeap), mStdioFile_File(0) { if (ev->Good()) mNode_Derived = morkDerived_kStdioFile; } morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, const PathChar* inName, const char* inMode) // calls OpenStdio() after construction : morkFile(ev, inUsage, ioHeap, ioSlotHeap), mStdioFile_File(0) { if (ev->Good()) this->OpenStdio(ev, inName, inMode); } morkStdioFile::morkStdioFile(morkEnv* ev, const morkUsage& inUsage, nsIMdbHeap* ioHeap, nsIMdbHeap* ioSlotHeap, void* ioFile, const PathChar* inName, mork_bool inFrozen) // calls UseStdio() after construction : morkFile(ev, inUsage, ioHeap, ioSlotHeap), mStdioFile_File(0) { if (ev->Good()) this->UseStdio(ev, ioFile, inName, inFrozen); } void morkStdioFile::OpenStdio(morkEnv* ev, const PathChar* inName, const char* inMode) // Open a new FILE with name inName, using mode flags from inMode. { if (ev->Good()) { if (!inMode) inMode = ""; mork_bool frozen = (*inMode == 'r'); // cursory attempt to note readonly if (this->IsOpenNode()) { if (!this->FileActive()) { this->SetFileIoOpen(morkBool_kFalse); if (inName && *inName) { this->SetFileName(ev, inName); if (ev->Good()) { FILE* file = MORK_FILEOPEN(inName, inMode); if (file) { mStdioFile_File = file; this->SetFileActive(morkBool_kTrue); this->SetFileIoOpen(morkBool_kTrue); this->SetFileFrozen(frozen); } else this->new_stdio_file_fault(ev); } } else ev->NewError("no file name"); } else ev->NewError("file already active"); } else this->NewFileDownError(ev); } } void morkStdioFile::UseStdio(morkEnv* ev, void* ioFile, const PathChar* inName, mork_bool inFrozen) // Use an existing file, like stdin/stdout/stderr, which should not // have the io stream closed when the file is closed. The ioFile // parameter must actually be of type FILE (but we don't want to make // this header file include the stdio.h header file). { if (ev->Good()) { if (this->IsOpenNode()) { if (!this->FileActive()) { if (ioFile) { this->SetFileIoOpen(morkBool_kFalse); this->SetFileName(ev, inName); if (ev->Good()) { mStdioFile_File = ioFile; this->SetFileActive(morkBool_kTrue); this->SetFileFrozen(inFrozen); } } else ev->NilPointerError(); } else ev->NewError("file already active"); } else this->NewFileDownError(ev); } } void morkStdioFile::CloseStdio(morkEnv* ev) // Close the stream io if both and FileActive() and FileIoOpen(), but // this does not close this instances (like CloseStdioFile() does). // If stream io was made active by means of calling UseStdio(), // then this method does little beyond marking the stream inactive // because FileIoOpen() is false. { if (mStdioFile_File && this->FileActive() && this->FileIoOpen()) { FILE* file = (FILE*)mStdioFile_File; if (MORK_FILECLOSE(file) < 0) this->new_stdio_file_fault(ev); mStdioFile_File = 0; this->SetFileActive(morkBool_kFalse); this->SetFileIoOpen(morkBool_kFalse); } } NS_IMETHODIMP morkStdioFile::Steal(nsIMdbEnv* ev, nsIMdbFile* ioThief) // If this file is a file version branch created by calling AcquireBud(), // BecomeTrunk() causes this file's content to replace the original // file's content, typically by assuming the original file's identity. { morkEnv* mev = morkEnv::FromMdbEnv(ev); if (mStdioFile_File && FileActive() && FileIoOpen()) { FILE* file = (FILE*)mStdioFile_File; if (MORK_FILECLOSE(file) < 0) new_stdio_file_fault(mev); mStdioFile_File = 0; } SetThief(mev, ioThief); return NS_OK; } #if defined(MORK_WIN) void mork_fileflush(FILE* file) { fflush(file); } #endif /*MORK_WIN*/ // 456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789