//---------------------------------------------------------------------------- // ObjectWindows // (C) Copyright 1993, 1994 by Borland International, All Rights Reserved // // Implements classes TStorageDocument, TDocFile, streams //---------------------------------------------------------------------------- #define INC_OLE2 #include #include // force Windows headers in before OLE #include #include // Simple refcount debug assistant // #if defined(CHECK_REFCOUNT) static void RefCountCheck(IStorage far* si) { uint32 count = si->AddRef(); count = si->Release(); } #else # define RefCountCheck(si) #endif const int B_size = 516; // default buffer size const char DefaultStreamName[] = "Contents"; // // class TStorageBuf // ----- ----------- // class _OWLCLASS_RTL TStorageBuf : public streambuf { public: // constructors, destructor TStorageBuf _FAR * open(IStorage& stg, LPCSTR name, int omode); TStorageBuf(); // make a closed TStorageBuf virtual ~TStorageBuf(); int is_open() { return opened; } // is the file open IStream* fd() { return strm; } TStorageBuf _FAR * close(); // flush and close file // TStorageBuf _FAR * attach(IStream*); // attach this TStorageBuf to opened IStream virtual int overflow(int = EOF); virtual int underflow(); virtual int sync(); virtual streampos seekoff(streamoff, ios::seek_dir, int); virtual streambuf _FAR * setbuf(char _FAR *, int); protected: // IStorage* stg; // parent storage IStream* strm; int mode; // the opened mode short opened; // non-zero if stream is open uint64 last_seek; char lahead[2]; // current input char if unbuffered }; // // class TStorageStreamBase // ----- ------------------ // class _OWLCLASS_RTL TStorageStreamBase : virtual public ios { public: TStorageStreamBase(IStorage& stg, const char far* name, int mode); ~TStorageStreamBase() {} void setbuf(char _FAR *, int); void close(); TStorageBuf buf; }; // // class TStorageInStream // ----- ---------------- // class _OWLCLASS_RTL TStorageInStream : public TStorageStreamBase, public TInStream { public: TStorageInStream(TStorageDocument& doc, const char far* name, int mode) : TInStream(doc, name, mode), TStorageStreamBase(*doc.StorageI,name, mode) {} ~TStorageInStream() {} }; // // class TStorageOutStream // ----- ----------------- // class _OWLCLASS_RTL TStorageOutStream : public TStorageStreamBase, public TOutStream { public: TStorageOutStream(TStorageDocument& doc, const char far* name, int mode) : TOutStream(doc, name, mode), TStorageStreamBase(*doc.StorageI,name, mode) {} ~TStorageOutStream() {} }; //---------------------------------------------------------------------------- // class TStorageDocument // TStorageDocument::~TStorageDocument() { ReleaseDoc(); SetDirty(false); // ~TDocument() will Close() after destroying children } // // Release the IStorage and close the document // bool TStorageDocument::ReleaseDoc() { CanRelease = true; // now we can release the storage return Close(); } // // Open the compound file with a given path // bool TStorageDocument::Open(int omode, const char far* name) { if (StorageI > 0) { return true; } do { // establish try block HRESULT hres; IStorage* parentStg; int pmode = 0; if (!omode) omode = GetOpenMode(); if (GetParentDoc()) { pmode = GetParentDoc()->GetOpenMode(); if (!(GetParentDoc()->Open(pmode ? pmode : omode, name))) return false; } ++OpenCount; if (!omode) omode = pmode; if (!(omode & (ofRead | ofWrite))) break; // Provide default share mode flags & enforce root storage restrictions // when in direct mode // if (!(omode & shMask)) { if (!(omode & ofTransacted) && !GetParentDoc()) { // direct mode root stg if (omode & ofWrite) omode |= ofRead | shNone; // let others do nothing if we are writing else omode |= shRead; // let others only read if we are readonly } else { if (omode & ofWrite) omode |= shRead; // let others only read if we are writing else omode |= shReadWrite; // let others read/write if we are readonly } } ThisOpen = omode; SetOpenMode(omode); // remember the open mode if (name && name[0]) SetDocPath(name); else name = GetDocPath(); long shareMode = ((omode & shMask) - shCompat) >> 5; long grfMode = ((omode & (ofRead|ofWrite)) - 1) | shareMode | ((long)(omode & (ofTransacted|ofPreserve|ofPriority)) << 4) | ((omode & ofTemporary) ? STGM_DELETEONRELEASE : 0); long childMode = (grfMode ^ shareMode) | STGM_SHARE_EXCLUSIVE; bool stgopen = true; bool stgcreate = false; if (omode & ofWrite) { if (omode & ofNoReplace) { stgopen = false; stgcreate = true; } else if (!(omode & ofNoCreate)) { stgcreate = true; if (omode & ofTruncate || !(omode & (ofAtEnd | ofAppend))) { stgopen = false; grfMode |= STGM_CREATE; childMode |= STGM_CREATE; } } } if (GetParentDoc()) { if (!GetParentDoc()->GetProperty(FindProperty("IStorage Instance"), &parentStg, 0)) break; if (stgopen) { hres = parentStg->OpenStorage(OleStr(name), 0, childMode, 0, 0, &StorageI); if (SUCCEEDED(hres)) stgcreate = false; } if (stgcreate) { hres = parentStg->CreateStorage(OleStr(name), childMode, 0, 0, &StorageI); } } else { if (stgopen) { hres = ::StgOpenStorage(OleStr(name), 0, grfMode, 0, 0, &StorageI); if (SUCCEEDED(hres)) stgcreate = false; } if (stgcreate) { hres = ::StgCreateDocfile(OleStr(name), grfMode, 0, &StorageI); } } RefCountCheck(StorageI); if (!SUCCEEDED(hres)) break; NotifyViews(vnDocOpened,ThisOpen); return true; // successful return } while (0); // dummy for break scoping, never executed // exception handling // if (GetParentDoc()) GetParentDoc()->Close(); --OpenCount; return false; } const int stgRdWrMask = (int)(STGM_READWRITE | STGM_READ | STGM_WRITE); const int stgShareMask = (int)(STGM_SHARE_DENY_NONE | STGM_SHARE_DENY_READ | STGM_SHARE_DENY_WRITE| STGM_SHARE_EXCLUSIVE); const long stgModeMask = (STGM_TRANSACTED | STGM_PRIORITY | STGM_CONVERT); // // Give an IStorage to document. This typically happens for OLE servers. // bool TStorageDocument::SetStorage(IStorage* stg, bool remember) { if (stg == StorageI) return true; // already set if (StorageI) { RefCountCheck(StorageI); if (remember) { StorageI->Release(); // Release the ole one OrgStorageI = 0; } else OrgStorageI = StorageI; } StorageI = stg; if (!StorageI) return true; // done StorageI->AddRef(); RefCountCheck(StorageI); STATSTG stgInfo; if (!SUCCEEDED(stg->Stat(&stgInfo, 0))) return false; SetOpenMode((int)((stgInfo.grfMode & stgRdWrMask) + 1) | (int)(((stgInfo.grfMode & stgShareMask) << 5) + shCompat) | (int)((stgInfo.grfMode & stgModeMask) >> 4) | ((stgInfo.grfMode & STGM_DELETEONRELEASE) ? ofTemporary : 0)); ThisOpen = GetOpenMode(); if (remember) if (stgInfo.pwcsName) SetDocPath(OleStr(stgInfo.pwcsName)); else SetDocPath(" "); if (stgInfo.pwcsName) { IMalloc* memmgr; if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &memmgr))) { memmgr->Free(stgInfo.pwcsName); //!CQ really do this here? memmgr->Release(); } } // NotifyViews(vnDocOpened,ThisOpen); return true; } // // Restores the original root IStorage before the save operation // bool TStorageDocument::RestoreStorage() { if (OrgStorageI) { if (StorageI) StorageI->Release(); StorageI = OrgStorageI; OrgStorageI = 0; } return true; } // // Create an istorage based on a memeroy handle // bool TStorageDocument::OpenHandle(int omode, HANDLE hGlobal) { int pmode = 0; if (!omode) omode = GetOpenMode(); if (!omode) omode = pmode; if (!(omode & (ofRead | ofWrite))) return false; if (!(omode & shMask)) omode |= shNone; ThisOpen = omode; SetOpenMode(omode); // remember the open mode long shareMode = ((omode & shMask) - shCompat) >> 5; long grfMode = ((omode & (ofRead|ofWrite)) - 1) | shareMode | ((long)(omode & (ofTransacted|ofPreserve|ofPriority)) << 4) | STGM_CREATE; if (!SUCCEEDED(CreateILockBytesOnHGlobal(hGlobal, (omode & ofTemporary)!=0, &LockBytes))) return false; if (!SUCCEEDED(StgCreateDocfileOnILockBytes(LockBytes, grfMode, 0, &StorageI))) { LockBytes->Release(); LockBytes = 0; return false; } ++OpenCount; NotifyViews(vnDocOpened,omode); return true; } // // Replace the IStorage with an istorage based on a memory handle // bool TStorageDocument::SetHandle(int omode, HANDLE hGlobal, bool create, bool remember) { int pmode = 0; if (!omode) omode = GetOpenMode(); if (!omode) omode = pmode; if (!(omode & (ofRead | ofWrite))) return false; if (!(omode & shMask)) omode |= shNone; ThisOpen = omode; SetOpenMode(omode); // remember the open mode long shareMode = ((omode & shMask) - shCompat) >> 5; long grfMode = ((omode & (ofRead|ofWrite)) - 1) | shareMode | ((long)(omode & (ofTransacted|ofPreserve|ofPriority)) << 4); if (!SUCCEEDED(CreateILockBytesOnHGlobal(hGlobal, (omode & ofTemporary)!=0, &LockBytes))) return false; IStorage* storageI = 0; if (!SUCCEEDED(StgOpenStorageOnILockBytes(LockBytes, 0, grfMode, 0, 0, &storageI))) { if (create && !SUCCEEDED(StgCreateDocfileOnILockBytes(LockBytes, grfMode |STGM_CREATE, 0, &storageI))) { LockBytes->Release(); LockBytes = 0; return false; } if (!storageI) return false; } RefCountCheck(storageI); // Don't change the embedded flag bool embed = IsEmbedded(); SetStorage(storageI, remember); SetEmbedded(embed); storageI->Release(); // release extra refcount held by SetStorage return true; } // // Get the global handle from ILockBytes // bool TStorageDocument::GetHandle(HGLOBAL* handle) { if (!LockBytes) return false; GetHGlobalFromILockBytes(LockBytes, handle); return true; } // // // bool TStorageDocument::Close() { if (!StorageI || !CanRelease) return true; if (StorageI) StorageI->Release(); StorageI = 0; NotifyViews(vnDocClosed,ThisOpen); SetDirty(false); CanRelease = false; if (GetParentDoc()) GetParentDoc()->Close(); return true; } // // // void TStorageDocument::DetachStream(TStream& strm) { TDocument::DetachStream(strm); TStorageDocument::Close(); } // // // TInStream* TStorageDocument::InStream(int omode, const char far* strmId) { TInStream* inStream; if (omode == ofParent) omode = IsOpen() ? ThisOpen : GetOpenMode(); if (!(omode & ofRead)) return 0; if (!TStorageDocument::Open(GetOpenMode() ? GetOpenMode() | ofRead : omode, strmId)) return 0; inStream = new TStorageInStream(*this, strmId, omode); if (inStream && !inStream->good()) { delete inStream; TStorageDocument::Close(); // close return 0; } return inStream; } // // // TOutStream* TStorageDocument::OutStream(int omode, const char far* strmId) { TOutStream* outStream; if (omode == ofParent) omode = IsOpen() ? ThisOpen : GetOpenMode(); if (!(omode & ofWrite)) return 0; if (!TStorageDocument::Open(GetOpenMode() ? GetOpenMode() | ofWrite : omode, strmId)) return 0; outStream = new TStorageOutStream(*this, strmId, omode); // change if (outStream && !outStream->good()) { delete outStream; TStorageDocument::Close(); // change return 0; } SetDirty(); // we don't really know at this point if it will be dirty!! return outStream; } // // // bool TStorageDocument::SetDocPath(const char far* path) { TDocument::SetDocPath(path); return true; } // // // bool TStorageDocument::Commit(bool force) { if (!TDocument::Commit(force)) // flush views and child docs return false; if (!StorageI) return true; // return OK if storage already released CommitTransactedStorage(); SetDirty(false); return true; } // // // bool TStorageDocument::CommitTransactedStorage() { HRESULT cres = StorageI->Commit(0); // try 2phase commit first if (!SUCCEEDED(cres)) // check for STG_S_TRYOVERWRITE, but GetScode not in lib cres = StorageI->Commit(STGC_OVERWRITE); // try less robust method if (!SUCCEEDED(cres)) return false; return true; } // // // bool TStorageDocument::Revert(bool clear) { if (!StorageI) return true; // return OK if storage already released if (!TDocument::Revert(clear) || !SUCCEEDED(StorageI->Revert())) return false; SetDirty(false); return true; } // // // static char* PropNames[] = { "Create Time", // CreateTime "Modify Time", // ModifyTime "Access Time", // AccessTime "Storage Size", // StorageSize "IStorage Instance",// IStorageInstance }; // // // static int PropFlags[] = { pfGetBinary|pfGetText, // CreateTime pfGetBinary|pfGetText, // ModifyTime pfGetBinary|pfGetText, // AccessTime pfGetBinary|pfGetText, // StorageSize pfGetBinary, // IStorage }; // // // const char* TStorageDocument::PropertyName(int index) { if (index <= PrevProperty) return TDocument::PropertyName(index); else if (index < NextProperty) return PropNames[index-PrevProperty-1]; else return 0; } // // // int TStorageDocument::PropertyFlags(int index) { if (index <= PrevProperty) return TDocument::PropertyFlags(index); else if (index < NextProperty) return PropFlags[index-PrevProperty-1]; else return 0; } // // // int TStorageDocument::FindProperty(const char far* name) { for (int i = 0; i < NextProperty-PrevProperty-1; i++) if (strcmp(PropNames[i], name) == 0) return i+PrevProperty+1; return 0; } // // // int TStorageDocument::GetProperty(int prop, void far* dest, int textlen) { STATSTG stgInfo; switch (prop) { case IStorageInstance: if (textlen) return 0; *(IStorage*far*)dest = StorageI; return sizeof(IStorage*); default: if (StorageI) { StorageI->Stat(&stgInfo, STATFLAG_NONAME); switch (prop) { case StorageSize: if (!textlen) { *(ulong far*)dest = uint64(stgInfo.cbSize).LowPart; return sizeof(ulong); } else { char buf[10]; int len = wsprintf(buf, "%ld", uint64(stgInfo.cbSize).LowPart); if (textlen > len) textlen = len; memcpy(dest, buf, textlen); *((char far*)dest + textlen) = 0; return len; } case AccessTime: #if defined(BI_PLAT_WIN32) return FormatFileTime(&stgInfo.atime, dest, textlen); #endif case CreateTime: #if defined(BI_PLAT_WIN32) return FormatFileTime(&stgInfo.ctime, dest, textlen); #endif case ModifyTime: return FormatFileTime(&stgInfo.mtime, dest, textlen); } } return TDocument::GetProperty(prop, dest, textlen); } } // // // bool TStorageDocument::SetProperty(int prop, const void far* src) { // docfile properties currently not settable // return TDocument::SetProperty(prop, src); } // // // IMPLEMENT_STREAMABLE1(TStorageDocument, TDocument); // // // void* TStorageDocument::Streamer::Read(ipstream& is, uint32 /*version*/) const { ReadBaseObject((TDocument*)GetObject(), is); return GetObject(); } // // // void TStorageDocument::Streamer::Write(opstream& os) const { WriteBaseObject((TDocument*)GetObject(), os); } //---------------------------------------------------------------------------- // class TStorageStreamBase // TStorageStreamBase::TStorageStreamBase(IStorage& stg, const char far* name, int mode) : buf() { ios::init(&buf); if (buf.is_open()) clear(ios::failbit); // fail - already open else if (buf.open(stg, name, mode)) clear(ios::goodbit); // successful open else clear(ios::badbit); // open failed } void TStorageStreamBase::setbuf(char* newbuf, int len) { if (buf.setbuf(newbuf, len)) clear(ios::goodbit); else setstate(ios::failbit); } void TStorageStreamBase::close() { if (buf.close()) clear(ios::goodbit); else setstate(ios::failbit); } //---------------------------------------------------------------------------- // class TStorageBuf // // // make a closed TStorageBuf // TStorageBuf::TStorageBuf() { mode = 0; opened = 0; char* p = new char[B_size]; if (p) { setb(p, p+B_size, 1); // ~streambuf() will delete buffer setp(p+4, p+4); setg(p, p+4, p+4); } } // // We assume that mode= means that we attached to an already-open file, // and should not now close it. We do flush it in any case. // TStorageBuf::~TStorageBuf() { if (mode) close(); else overflow(EOF); } // // Open or create IStream with mode and protection, attach to this TStorageBuf. // TStorageBuf* TStorageBuf::open(IStorage& stg, const char far* name, int omode) { //int share = omode & shMask; //if (share < shCompat) // share = shNone; //how = (share-shCompat) >> 5; if (opened || !omode) return 0; if (!name) name = DefaultStreamName; bool stgopen = true; bool stgcreate = false; uint32 how = STGM_SHARE_EXCLUSIVE; // must open streams and child stg exclusive if (omode & ofWrite) { if (!(mode & (ofAtEnd | ofAppend | ofRead))) omode |= ofTruncate; // output implies truncate unless in, app, or ate if (omode & ofRead) how |= STGM_READWRITE; else how |= STGM_WRITE; if (omode & ofNoReplace) { stgopen = false; stgcreate = true; } else if (!(omode & ofNoCreate)) { stgcreate = true; if (omode & ofTruncate) { stgopen = false; how |= STGM_CREATE; } } } else if (omode & ofRead) how |= STGM_READ; else return 0; // must specfify in, out, or in/out //if (omode & ofAppend) // what does this mean for docfile?!! // how |= O_APPEND; // Now try to open or create // if (stgopen) { HRESULT hres = stg.OpenStream(OleStr(name), 0, how, 0, &strm); if (SUCCEEDED(hres)) stgcreate = false; else return 0; } if (stgcreate) { HRESULT hres = stg.CreateStream(OleStr(name), how, 0, 0, &strm); if (!SUCCEEDED(hres)) return 0; } // Finish up // opened = 1; mode = omode; if ((omode & ofAtEnd) != 0 && !SUCCEEDED(strm->Seek(int64(), STREAM_SEEK_END, (ULARGE_INTEGER*)&last_seek))) { strm->Release(); strm = 0; return 0; } char* b = base(); // buffer address int pb = b ? ((blen() > 8) ? 4 : 1) : 0; // putback area size setp(b+pb, b+pb); setg(b, b+pb, b+pb); return this; } #if 0 // // attach this TStorageBuf to open IStream -- assume fd is actually open // TStorageBuf* TStorageBuf::attach(IStream* f) { STATSTG stat; if (opened) return 0; if (f->Stat(&stat, STATFLAG_NONAME) != 0) return 0; if (f->CreateStream(STREAMNAME,stat.grfMode & ~STGM_TRANSACTED,0,0,&strm)!) return 0; stg = f; // assumed to be valid opened = 1; int rwmode = stat.grfMode & (STGM_READ | STGM_WRITE | STGM_READWRITE); mode = rwmode != STGM_WRITE ? ofRead : 0; if (rwmode != STGM_READ) mode |= ofWrite; char* b = base(); // buffer address if (!b) { b = new char[B_size]; if (b) setb(b, b+B_size, 1); // ~streambuf() will delete buffer } int pb = b ? ((blen() > 8) ? 4 : 1) : 0; // putback area size setp(b+pb, b+pb); setg(b, b+pb, b+pb); return this; } #endif streambuf* TStorageBuf::setbuf(char* b, int len) { if (opened && base()) return 0; // already open with a buffer -- no change int pb; // putback area size if (b && len > 0) // use b as the new buffer pb = (len > 8) ? 4 : 1; // guard against tiny buffers else { // unbuffered len = pb = 0; b = 0; } setb(b, b+len, 0); // will delete old buffer if needed setp(b+pb, b+pb); setg(b, b+pb, b+pb); return this; } // // Seek file to position. // We take a simple approach, and don't check for small position changes // within the current buffer. // streampos TStorageBuf::seekoff(streamoff off, ios::seek_dir dir, int /* mode ignored */) { long loff = off; HRESULT hres; unsigned long actual; int count = out_waiting(); if (count) { // flush the output hres = strm->Write(pbase(), (ulong)count, &actual); if (!SUCCEEDED(hres) || (int)actual != count) return EOF; } else if (dir == ios::cur) if ((count = in_avail()) != 0) { loff -= count; // if we're in text mode, need to allow for newlines // in the buffer // if ((mode & ofBinary) == 0) { char *tptr = gptr(); while (tptr != egptr()) if (*tptr++ == '\n') loff--; } } uint32 w = (dir == ios::beg) ? STREAM_SEEK_SET : ((dir == ios::cur) ? STREAM_SEEK_CUR : /* ios::end */ STREAM_SEEK_END); if (!SUCCEEDED(strm->Seek(int64(loff), w, (ULARGE_INTEGER*)&last_seek))) return EOF; //?!! if (!unbuffered() && base()) { // set up get and put areas int pb = (blen() > 8) ? 4 : 1; // putback area size char *b = base(); setp(b+pb, b+pb); setg(b, b+pb, b+pb); } return last_seek.LowPart; } // // // int TStorageBuf::sync() { HRESULT hres; if (!opened) return EOF; ulong actual; int count = out_waiting(); if (count) { char* curp; char* srcp = pbase(); char* endp = srcp + count; // convert LF's to CR/LF if text mode // if ((mode & ofBinary) == 0) { for (curp = srcp; curp < endp; curp++) { if (*curp == '\n') { *curp = '\r'; count = (int)(curp - srcp + 1); hres = strm->Write(srcp, (unsigned long)count, &actual); if (!SUCCEEDED(hres) || (int)actual != count) return EOF; *(srcp = curp) = '\n'; } } count = (int)(curp - srcp); // write what remains in the buffer below } hres = strm->Write(srcp, (unsigned long)count, &actual); if (!SUCCEEDED(hres) || (int)actual != count) return EOF; // reset get and put areas // int pb = (blen() > 8) ? 4 : 1; // putback area size char *b = base(); setp(b+pb, b+blen()); setg(b, b+pb, b+pb); } else if (in_avail()) { if (!SUCCEEDED(strm->Seek(int64(long(-in_avail())), STREAM_SEEK_CUR, (ULARGE_INTEGER*)&last_seek))) return EOF; setg(eback(), gptr(), gptr()); setp(gptr(), gptr()); } return 0; } // // // int TStorageBuf::underflow() { HRESULT hres; ulong actual; unsigned count; // input character count int c; // the return value if (!opened || (mode & (ofRead | ofWrite)) == ofWrite) return EOF; if (in_avail()) // no action needed return (unsigned char)*gptr(); if (!unbuffered() && base()) { // this is buffered if (sync() != 0) return EOF; // find buffer data // int pb = (blen() > 8) ? 4 : 1; // putback area size char* begp = base() + pb; // read in a new buffer // hres = strm->Read(begp, blen()-pb, &actual); if (!SUCCEEDED(hres)) return EOF; count = (unsigned)actual; // remove CR's if text mode // if ((mode & ofBinary) == 0) { char* endp = begp + count; char* dstp = 0; char* srcp = 0; char* curp; for (curp = begp; curp < endp; curp++) { if (*curp == '\r') { if (dstp) { memcpy(dstp, srcp, (int)(curp - srcp)); dstp += (int)(curp - srcp); } else dstp = curp; srcp = curp + 1; } } if (dstp) { endp = dstp + (int)(curp - srcp); if (curp != srcp) memcpy(dstp, srcp, (int)(curp - srcp)); } count = (int)(endp - begp); } // set up get and put areas // setg(base(), begp, begp + count); setp(begp, begp); if (count) c = (unsigned char)*gptr(); } else { // this is not buffered for (;;) { hres = strm->Read(lahead, 1, &actual); if (!SUCCEEDED(hres) || actual == 0) { c = EOF; setg(0, 0, 0); } else { c = (unsigned char)lahead[0]; if ((mode & ofBinary) == 0 && c == '\r') continue; setg(lahead, lahead, lahead+1); } break; } } if (!count) c = EOF; // end of file return c; } // // always flush // int TStorageBuf::overflow(int c) { if (!opened || (mode & (ofRead | ofWrite)) == ofRead) return EOF; if (unbuffered() || !base()) { if (c != EOF) { int count; char b[2]; if (c == '\n' && (mode & ofBinary) == 0) { b[0] = '\r'; b[1] = (char)c; count = 2; } else { b[0] = (char)c; count = 1; } ulong actual = 0; strm->Write(&c, (ulong)count, &actual); if ((int)actual != count) return EOF; } } else { // now we know this is buffered and state is not bad // resets get and put areas // if (sync() != 0) return EOF; // reset get and put areas // int pb = (blen() > 8) ? 4 : 1; // putback area size char *b = base(); setp(b+pb, b+blen()); setg(b, b+pb, b+pb); if (c != EOF) { sputc(c); gbump(1); // pptr and gptr must be the same } } return 1; } // // flush and close file // TStorageBuf* TStorageBuf::close() { if (!opened) return 0; // nothing to do int ores = 0; // result of overflow() if (out_waiting() && overflow(EOF) == EOF) ores = 1; if ((mode & ofWrite) /* && !strm->Commit(0) */) ores = 1; strm->Release(); //! Question: should we release if commit fails?!! strm = 0; opened = 0; return ores ? 0 : this; }