//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1993, 1996 by Borland International, All Rights Reserved
/// \file
/// Implementation of classes TStorageDocument, TDocFile, and associated streams
//
//----------------------------------------------------------------------------
#include <ocf/pch.h>
 
#include <owl/docview.h>      // force Windows headers in before OLE
#include <owl/string.h>
#include <ocf/stgdoc.h>
#include <stdio.h>
 
#include <streambuf>
 
namespace ocf {
 
using namespace owl;
 
using namespace std;
 
OWL_DIAGINFO;
 
//
// Simple refcount debug assistant
//
#if defined(CHECK_REFCOUNT)
static void RefCountCheck(IStorage * si) {
  uint32 count = si->AddRef();
  count = si->Release();
}
#else
# define RefCountCheck(si)
#endif
 
const int B_size    = 516;                // default buffer size
const _TCHAR DefaultStreamName[] = _T("Contents");
 
//
// class TStorageBuf
// ~~~~~ ~~~~~~~~~~~
class  TStorageBuf : public streambuf {
    bool buffered_;
    char *base_;
    char *ebuf_;
  public:
    // Constructors, destructor
    //
    TStorageBuf  * open(IStorage& stg, LPCTSTR name, int omode);
    TStorageBuf();                       // make a closed TStorageBuf
    virtual ~TStorageBuf();
 
    int is_open() { return opened; }     // is the file open
    IStream* fd() { return strm; }
 
    TStorageBuf  * close();          // flush and close file
//  TStorageBuf  * attach(IStream*); // attach this TStorageBuf to opened IStream
 
    int_type eof(){
      return traits_type::eof();
    }
 
    virtual int_type overflow(int_type c = traits_type::eof());
    virtual int_type underflow();
    virtual int sync();
    virtual streambuf * setbuf(char_type *, streamsize);
 
    virtual pos_type seekoff(off_type off, ios_base::seekdir way,
                             ios_base::openmode which = ios_base::in |
                                                        ios_base::out);
    virtual pos_type seekpos(pos_type sp,
                             ios_base::openmode which =
                                       ios_base::in | ios_base::out);
    int out_waiting(){
      return pptr() ? (int)(pptr() - pbase()) : 0;
    }
 
    void setb(char *beg, char *end, int = 0){
      streamsize size_buf = end - beg;
      buffered_ = beg && size_buf;
      setbuf(beg, size_buf);
    }
 
    int unbuffered(){
      return !buffered_;
    }
 
    char *base(){
      return base_;
    }
 
    int blen(){
      return (int) (ebuf_ - base_);
    }
 
  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 TStorageStreamBase : virtual public ios {
  public:
    TStorageStreamBase(IStorage& stg, LPCTSTR name, int mode);
   ~TStorageStreamBase() {}
    void    setbuf(char  *, int);
    void    close();
 
    TStorageBuf buf;
};
 
//
// class TStorageInStream
// ~~~~~ ~~~~~~~~~~~~~~~~
class TStorageInStream : public TStorageStreamBase,
                                       public TInStream {
  public:
    TStorageInStream(TStorageDocument& doc,  LPCTSTR name, int mode)
                           : TInStream(doc,         name,     mode),
                   TStorageStreamBase(*doc.StorageI,name,     mode) {}
   ~TStorageInStream() {}
};
 
//
// class TStorageOutStream
// ~~~~~ ~~~~~~~~~~~~~~~~~
class TStorageOutStream : public TStorageStreamBase,
                                        public TOutStream {
  public:
    TStorageOutStream(TStorageDocument& doc,  LPCTSTR name, int mode)
                           : TOutStream(doc,         name,     mode),
                    TStorageStreamBase(*doc.StorageI,name,     mode) {}
   ~TStorageOutStream() {}
};
 
inline long IosToStgOpenMode(int omode)
{
  return ((omode & (ofRead|ofWrite)) - 1);
}
 
inline long IosToStgShareMode(int omode)
{
  return ((omode & shMask) - shCompat) >> 5;
}
 
inline long IosToStgTempMode(int omode)
{
  return (omode & ofTemporary) ? STGM_DELETEONRELEASE : 0;
}
 
inline long IosToStgMode(int omode)
{
  return IosToStgOpenMode(omode) |
         IosToStgShareMode(omode) |
         ((long)(omode & (ofTransacted|ofPreserve|ofPriority)) << 4);
}
 
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);
 
inline int StgToIosOpenMode(long grfMode)
{
  return (int)((grfMode & stgRdWrMask) + 1);
}
 
inline int StgToIosShareMode(long grfMode)
{
  return (int)(((grfMode & stgShareMask) << 5) + shCompat);
}
 
inline int StgToIosTempMode(long grfMode)
{
  return (grfMode & STGM_DELETEONRELEASE) ? ofTemporary : 0;
}
 
inline int StgToIosMode(long grfMode)
{
  return StgToIosOpenMode(grfMode) |
         StgToIosShareMode(grfMode) |
         (int)((grfMode & stgModeMask) >> 4);
}
 
//----------------------------------------------------------------------------
// 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, LPCTSTR name)
{
  if (StorageI)
    return true;
 
  do {  // establish try block
    HRESULT hr;
    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 grfMode = IosToStgMode(omode) | IosToStgTempMode(omode);
    long childMode = (grfMode & ~stgShareMask) | 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(_T("IStorage Instance")),
                                       &parentStg, 0))
        break;
      if (stgopen) {
        hr = parentStg->OpenStorage(OleStr(name), 0, childMode, 0, 0, &StorageI);
        if (SUCCEEDED(hr))
          stgcreate = false;
      }
      if (stgcreate) {
        hr = parentStg->CreateStorage(OleStr(name), childMode, 0, 0, &StorageI);
      }
    }
    else {
      if (stgopen) {
        hr = ::StgOpenStorage(OleStr(name), 0, grfMode, 0, 0, &StorageI);
        if (SUCCEEDED(hr))
          stgcreate = false;
      }
      if (stgcreate) {
        hr = ::StgCreateDocfile(OleStr(name), grfMode, 0, &StorageI);
      }
    }
    RefCountCheck(StorageI);
 
    if (!SUCCEEDED(hr))
      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;
}
 
//
/// 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(StgToIosMode(stgInfo.grfMode) | StgToIosTempMode(stgInfo.grfMode));
 
  ThisOpen = GetOpenMode();
  if (remember)
  {
    if (stgInfo.pwcsName)
      SetDocPath(OleStr(stgInfo.pwcsName));
    else
      SetDocPath(_T("  "));
  }
 
  if (stgInfo.pwcsName) {
    IMalloc* memmgr;
    if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &memmgr))) {
      memmgr->Free(stgInfo.pwcsName);    // Free the string from the Stat call
      memmgr->Release();
    }
  }
 
//  NotifyViews(vnDocOpened,ThisOpen);
  return true;
}
 
//
/// Restore the original root IStorage before the save operation
//
bool
TStorageDocument::RestoreStorage()
{
  if (OrgStorageI) {
    if (StorageI)
      StorageI->Release();
 
    StorageI = OrgStorageI;
    OrgStorageI = 0;
  }
 
  return true;
}
 
//
/// Get a new IStorage, typically in a SaveAs situation. Release StorageI and
/// set it to the new storage if all OK.
//
IStorage*
TStorageDocument::GetNewStorage()
{
  PRECONDITION(StorageI);
 
  STATSTG stgInfo;
  if (!SUCCEEDED(StorageI->Stat(&stgInfo, STATFLAG_NONAME)))
    return 0;
 
  // Create another root storage based on the new doc path
  // Should close the current document and open a new one.
  //
  IStorage* newStorage = 0;
 
  HRESULT hr;
  hr = ::StgOpenStorage(OleStr(GetDocPath()), 0, stgInfo.grfMode, 0, 0, &newStorage);
  if (!SUCCEEDED(hr))
    hr = ::StgCreateDocfile(OleStr(GetDocPath()), stgInfo.grfMode, 0, &newStorage);
 
  if (SUCCEEDED(hr)) {
    StorageI->Release(); // Release the old root storage
    StorageI = newStorage;
  }
 
  return newStorage;
}
 
//
/// Create an IStorage based on a memory 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 grfMode = IosToStgMode(omode) | 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 grfMode = IosToStgMode(omode);
  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, LPCTSTR 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, LPCTSTR 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(LPCTSTR 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 hr = StorageI->Commit(0);  // try 2phase commit first
  if (!SUCCEEDED(hr)) // check for STG_S_TRYOVERWRITE, but GetScode not in lib
    hr = StorageI->Commit(STGC_OVERWRITE);   // try less robust method
  if (!SUCCEEDED(hr))
    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 LPCTSTR PropNames[] = {
  _T("Create Time"),      // CreateTime
  _T("Modify Time"),      // ModifyTime
  _T("Access Time"),      // AccessTime
  _T("Storage Size"),     // StorageSize
  _T("IStorage Instance"),// IStorageInstance
};
 
//
//
//
static int PropFlags[] = {
  pfGetBinary|pfGetText,   // CreateTime
  pfGetBinary|pfGetText,   // ModifyTime
  pfGetBinary|pfGetText,   // AccessTime
  pfGetBinary|pfGetText,   // StorageSize
  pfGetBinary,             // IStorage
};
 
//
//
//
LPCTSTR
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(LPCTSTR name)
{
  for (int i = 0; i < NextProperty-PrevProperty-1; i++)
    if (_tcscmp(PropNames[i], name) == 0)
      return i+PrevProperty+1;
  return 0;
}
 
//
//
//
int
TStorageDocument::GetProperty(int prop, void * dest, int textlen)
{
  // Formats a file date + time string.
  //
  const auto formatFileTime = [](const FILETIME& pft, void* dest, int textlen) -> int
  {
    tchar buf[40];
    int len;
 
    if (!textlen)
    {
      *(FILETIME*) dest = pft;
      return sizeof(FILETIME);
    }
    SYSTEMTIME dt;
    FileTimeToSystemTime(&pft, &dt);
    len = _sntprintf(buf, COUNTOF(buf), _T("%d/%d/%d %02d:%02d:%02d.%02d"),
      dt.wMonth, dt.wDay, dt.wYear,
      dt.wHour, dt.wMinute, dt.wSecond, dt.wMilliseconds / 10);
    if (textlen > len)
      textlen = len;
    memcpy(dest, buf, textlen * sizeof(tchar));
    *((LPTSTR) dest + textlen) = _T('\0');
    return len;
  };
 
  STATSTG stgInfo;
 
  switch (prop) {
    case IStorageInstance:
      if (textlen)
        return 0;
      *(IStorage**)dest = StorageI;
      return sizeof(IStorage*);
 
    default:
      if (StorageI) {
        StorageI->Stat(&stgInfo, STATFLAG_NONAME);
        switch (prop) {
          case StorageSize:
            if (!textlen) {
              *(ulong *)dest = (uint32)(*(uint64*)&stgInfo.cbSize);
              return sizeof(ulong);
            }
            else {
              TCHAR buf[10];
              int len = _stprintf(buf, _T("%ld"), (uint32)(*(uint64*)&stgInfo.cbSize));
              if (textlen > len)
                textlen = len;
              memcpy(dest, buf, textlen);
              *((LPTSTR)dest + textlen) = 0;
              return len;
            }
          case AccessTime:
            return formatFileTime(stgInfo.atime, dest, textlen);
          case CreateTime:
            return formatFileTime(stgInfo.ctime, dest, textlen);
          case ModifyTime:
            return formatFileTime(stgInfo.mtime, dest, textlen);
        }
      }
      return TDocument::GetProperty(prop, dest, textlen);
  }
}
 
//
//
//
bool
TStorageDocument::SetProperty(int prop, const void * src)
{
  // docfile properties currently not settable
  //
  return TDocument::SetProperty(prop, src);
}
 
IMPLEMENT_STREAMABLE1(TStorageDocument, TDocument);
 
#if OWL_PERSISTENT_STREAMS
 
//
//
//
void*
TStorageDocument::Streamer::Read(ipstream& is, uint32 /*version*/) const
{
  owl::ReadBaseObject((TDocument*)GetObject(), is);
  return GetObject();
}
 
//
//
//
void
TStorageDocument::Streamer::Write(opstream& os) const
{
  owl::WriteBaseObject((TDocument*)GetObject(), os);
}
 
#endif
 
//----------------------------------------------------------------------------
//  class TStorageStreamBase
//
 
//
//
//
TStorageStreamBase::TStorageStreamBase(IStorage& stg, LPCTSTR 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
#if defined(BI_COMP_BORLANDC)
    setstate(ios::failbit);
#else
    clear(ios::failbit);
#endif
}
 
void TStorageStreamBase::close()
{
  if (buf.close())
    clear(ios::goodbit);
  else
#if defined(BI_COMP_BORLANDC)
    setstate(ios::failbit);
#else
    clear(ios::failbit);
#endif
}
 
//----------------------------------------------------------------------------
//  class TStorageBuf
//
 
//
// make a closed TStorageBuf
//
TStorageBuf::TStorageBuf()
:
  buffered_(false),
  base_(0),
  ebuf_(0)
{
  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());
  delete[] base_;
}
 
//
/// Open or create IStream with mode and protection, attach to this TStorageBuf.
//
TStorageBuf* TStorageBuf::open(IStorage& stg, LPCTSTR 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 hr = stg.OpenStream(OleStr(name), 0, how, 0, &strm);
    if (SUCCEEDED(hr))
      stgcreate = false;
    else
      return 0;
  }
  if (stgcreate) {
    HRESULT hr = stg.CreateStream(OleStr(name), how, 0, 0, &strm);
    if (!SUCCEEDED(hr))
      return 0;
  }
 
  // Finish up
  //
  opened = 1;
  mode = omode;
  LARGE_INTEGER uli = {0,0};
  if ((omode & ofAtEnd) != 0
      && !SUCCEEDED(strm->Seek(uli, 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_type* b, streamsize 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;
  }
  delete[] base_;
  base_ = b;
  ebuf_ = b + len;
  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.
//
TStorageBuf::pos_type TStorageBuf::seekoff(off_type off,
                         ios_base::seekdir dir, ios_base::openmode /* which */)
{
  int64 loff = static_cast<int64>(off);
  int count = out_waiting();
 
  if (count) {  // flush the output
    ulong actual;
    HRESULT hr = strm->Write(pbase(), (ulong)count, &actual);
    if (!SUCCEEDED(hr) || (int)actual != count)
      return eof();
  }
  else if (dir == ios::cur) {
    count = static_cast<int>(in_avail());
    if (count != 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++ == _T('\n'))
            loff--;
      }
    }
  }
 
  uint32 w = (dir == ios::beg) ? STREAM_SEEK_SET
       : ((dir == ios::cur) ? STREAM_SEEK_CUR
       :       /* ios::end */ STREAM_SEEK_END);
  int64 i64pos = loff;
  if (!SUCCEEDED(strm->Seek(*(LARGE_INTEGER*)&i64pos, 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 (uint32)last_seek;
}
 
TStorageBuf::pos_type TStorageBuf::seekpos(pos_type sp,
                 ios_base::openmode which){
  return seekoff(streamoff(sp), ios::beg, which);
}
 
//
//
//
int TStorageBuf::sync()
{
  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 == _T('\n')) {
          *curp = _T('\r');
          count = (int)(curp - srcp + 1);
          HRESULT hr = strm->Write(srcp, (unsigned long)count, &actual);
          if (!SUCCEEDED(hr) || (int)actual != count)
            return eof();
          *(srcp = curp) = _T('\n');
        }
      }
      count = (int)(curp - srcp);  // write what remains in the buffer below
    }
    HRESULT hr = strm->Write(srcp, (ulong)count, &actual);
    if (!SUCCEEDED(hr) || (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()) {
     int64 i64 = int64(long(-in_avail()));
    if (!SUCCEEDED(strm->Seek(*(LARGE_INTEGER*)&i64, STREAM_SEEK_CUR, (ULARGE_INTEGER*)&last_seek)))
      return eof();
    setg(eback(), gptr(), gptr());
    setp(gptr(), gptr());
  }
  return 0;
}
 
//
//
//
TStorageBuf::int_type TStorageBuf::underflow()
{
  ulong  actual;
  uint   count;  // input character count
  int    c;      // the return value
 
  if (!opened || (mode & (ofRead | ofWrite)) == ofWrite)
    return eof();
 
  if (in_avail())                   // no action needed
    return (utchar)*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
    //
    HRESULT hr = strm->Read(begp, blen()-pb, &actual);
    if (!SUCCEEDED(hr))
      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 == _T('\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 = (utchar)*gptr();
 
  }
  else {     // this is not buffered
    for (;;) {
      HRESULT hr = strm->Read(lahead, 1, &actual);
      if (!SUCCEEDED(hr) || actual == 0) {
        c = eof();
        setg(0, 0, 0);
      }
      else {
        c = (utchar)lahead[0];
        if ((mode & ofBinary) == 0  && c == _T('\r'))
          continue;
        setg(lahead, lahead, lahead+1);
      }
      break;
    }
  }
  if (!count)
    c = eof();    // end of file
  return c;
}
 
//
/// always flush
//
TStorageBuf::int_type TStorageBuf::overflow(int_type c)
{
  if (!opened || (mode & (ofRead | ofWrite)) == ofRead)
    return eof();
 
  if (unbuffered() || !base()) {
    if (c != eof()) {
      int count;
      char b[2];
      if (c == _T('\n') && (mode & ofBinary) == 0) {
        b[0] = _T('\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;
}
 
} // OCF namespace
 
//==============================================================================
 
 

V220 Suspicious sequence of types castings: memsize -> 32-bit integer -> memsize. The value being cast: '(curp - srcp)'.

V220 Suspicious sequence of types castings: memsize -> 32-bit integer -> memsize. The value being cast: '(curp - srcp)'.

V220 Suspicious sequence of types castings: memsize -> 32-bit integer -> memsize. The value being cast: '(curp - srcp)'.

V1004 The 'b' pointer was used unsafely after it was verified against nullptr. Check lines: 975, 976.

V1048 The 'omode' variable was assigned the same value.

V1048 The 'omode' variable was assigned the same value.

V547 Expression 'StorageI' is always true.

V614 Potentially uninitialized variable 'count' used.

V668 There is no sense in testing the 'inStream' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V668 There is no sense in testing the 'outStream' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V668 There is no sense in testing the 'p' pointer against null, as the memory was allocated using the 'new' operator. The exception will be generated in the case of memory allocation error.

V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: strm, last_seek, lahead.

V576 Incorrect format. Consider checking the third actual argument of the 'sprintf' function. The SIGNED integer type argument is expected.

V769 The 'b' pointer in the 'b + len' expression could be nullptr. In such case, resulting value will be senseless and it should not be used.