588 lines
14 KiB
C++
588 lines
14 KiB
C++
//+--------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1992.
|
|
//
|
|
// File: util.cxx
|
|
//
|
|
// Contents: DRT support routines
|
|
//
|
|
// History: 22-Sep-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#include "headers.cxx"
|
|
#pragma hdrstop
|
|
|
|
#include <stdarg.h>
|
|
#include <direct.h>
|
|
#include <io.h>
|
|
|
|
#if DBG == 1
|
|
#include <dfdeb.hxx>
|
|
#endif
|
|
|
|
#define DEFAULT_DATA_DIR "."
|
|
|
|
BOOL fExitOnFail = TRUE;
|
|
|
|
char szOrigDir[_MAX_PATH] = ".";
|
|
|
|
// Preserve the current directory and change
|
|
// directory into the data directory
|
|
void SetData(void)
|
|
{
|
|
char *pszDataDir;
|
|
|
|
_getcwd(szOrigDir, _MAX_PATH);
|
|
pszDataDir = getenv("DRTDATA");
|
|
if (pszDataDir == NULL)
|
|
pszDataDir = DEFAULT_DATA_DIR;
|
|
_chdir(pszDataDir);
|
|
}
|
|
|
|
// Clean up the data directory
|
|
void CleanData(void)
|
|
{
|
|
_unlink(OlecsOut(DRTDF));
|
|
_unlink(OlecsOut(MARSHALDF));
|
|
}
|
|
|
|
// Restore the original directory
|
|
void UnsetData(void)
|
|
{
|
|
_chdir(szOrigDir);
|
|
}
|
|
|
|
// Output a message if fVerbose is true
|
|
void out(char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (fVerbose)
|
|
{
|
|
va_start(args, fmt);
|
|
vprintf(fmt, args);
|
|
va_end(args);
|
|
}
|
|
}
|
|
|
|
// internal error print
|
|
void _errorprint (char *fmt, va_list args)
|
|
{
|
|
#if !defined(FLAT) || defined(FPRINTF_WORKS)
|
|
fprintf(stderr, "** Fatal error **: ");
|
|
vfprintf(stderr, fmt, args);
|
|
#else
|
|
printf("** Fatal error **: ");
|
|
vprintf(fmt, args);
|
|
#endif
|
|
}
|
|
|
|
// error print
|
|
void errorprint (char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start (args, fmt);
|
|
|
|
_errorprint (fmt, args);
|
|
|
|
va_end (args);
|
|
}
|
|
|
|
// Print out an error message and terminate the DRT
|
|
void error(int code, char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
_errorprint (fmt, args);
|
|
|
|
va_end(args);
|
|
CleanData();
|
|
UnsetData();
|
|
exit(code);
|
|
}
|
|
|
|
|
|
// Converts a TCHAR string to a char pointer in a temporary buffer
|
|
// This implementation treats the conversion buffer as a circular
|
|
// buffer so more than one string can be held (depending on the size
|
|
// of the strings)
|
|
|
|
#define BUFSIZE 1024
|
|
|
|
char *OlecsOut(OLECHAR const *ptcs)
|
|
{
|
|
#ifdef OLEWIDECHAR
|
|
static char szBuffer[BUFSIZE];
|
|
static char *pszBuf = szBuffer;
|
|
char *pszTmp;
|
|
|
|
if (ptcs == NULL)
|
|
return NULL;
|
|
if (wcslen(ptcs) >= (size_t)(BUFSIZE-(pszBuf-szBuffer)))
|
|
pszBuf = szBuffer;
|
|
wcstombs(pszBuf, ptcs, BUFSIZE);
|
|
szBuffer[BUFSIZE-1] = 0;
|
|
pszTmp = pszBuf;
|
|
pszBuf += strlen(pszBuf)+1;
|
|
return pszTmp;
|
|
#else
|
|
return (char *)ptcs;
|
|
#endif
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
SCODE sc;
|
|
char *text;
|
|
} StatusCodeText;
|
|
|
|
static StatusCodeText scodes[] =
|
|
{
|
|
S_OK, "S_OK",
|
|
S_FALSE, "S_FALSE",
|
|
STG_E_INVALIDFUNCTION, "STG_E_INVALIDFUNCTION",
|
|
STG_E_FILENOTFOUND, "STG_E_FILENOTFOUND",
|
|
STG_E_PATHNOTFOUND, "STG_E_PATHNOTFOUND",
|
|
STG_E_TOOMANYOPENFILES, "STG_E_TOOMANYOPENFILES",
|
|
STG_E_ACCESSDENIED, "STG_E_ACCESSDENIED",
|
|
STG_E_INVALIDHANDLE, "STG_E_INVALIDHANDLE",
|
|
STG_E_INSUFFICIENTMEMORY, "STG_E_INSUFFICIENTMEMORY",
|
|
STG_E_INVALIDPOINTER, "STG_E_INVALIDPOINTER",
|
|
STG_E_NOMOREFILES, "STG_E_NOMOREFILES",
|
|
STG_E_DISKISWRITEPROTECTED, "STG_E_DISKISWRITEPROTECTED",
|
|
STG_E_SEEKERROR, "STG_E_SEEKERROR",
|
|
STG_E_WRITEFAULT, "STG_E_WRITEFAULT",
|
|
STG_E_READFAULT, "STG_E_READFAULT",
|
|
STG_E_SHAREVIOLATION, "STG_E_SHAREVIOLATION",
|
|
STG_E_LOCKVIOLATION, "STG_E_LOCKVIOLATION",
|
|
STG_E_FILEALREADYEXISTS, "STG_E_FILEALREADYEXISTS",
|
|
STG_E_INVALIDPARAMETER, "STG_E_INVALIDPARAMETER",
|
|
STG_E_MEDIUMFULL, "STG_E_MEDIUMFULL",
|
|
STG_E_ABNORMALAPIEXIT, "STG_E_ABNORMALAPIEXIT",
|
|
STG_E_INVALIDHEADER, "STG_E_INVALIDHEADER",
|
|
STG_E_INVALIDNAME, "STG_E_INVALIDNAME",
|
|
STG_E_UNKNOWN, "STG_E_UNKNOWN",
|
|
STG_E_UNIMPLEMENTEDFUNCTION, "STG_E_UNIMPLEMENTEDFUNCTION",
|
|
STG_E_INVALIDFLAG, "STG_E_INVALIDFLAG",
|
|
STG_E_INUSE, "STG_E_INUSE",
|
|
STG_E_NOTCURRENT, "STG_E_NOTCURRENT",
|
|
STG_E_REVERTED, "STG_E_REVERTED",
|
|
STG_E_CANTSAVE, "STG_E_CANTSAVE",
|
|
STG_E_OLDFORMAT, "STG_E_OLDFORMAT",
|
|
STG_E_OLDDLL, "STG_E_OLDDLL",
|
|
STG_E_SHAREREQUIRED, "STG_E_SHAREREQUIRED",
|
|
STG_E_NOTFILEBASEDSTORAGE, "STG_E_NOTFILEBASEDSTORAGE",
|
|
STG_E_EXTANTMARSHALLINGS, "STG_E_EXTANTMARSHALLINGS",
|
|
STG_S_CONVERTED, "STG_S_CONVERTED"
|
|
};
|
|
#define NSCODETEXT (sizeof(scodes)/sizeof(scodes[0]))
|
|
|
|
// Convert a status code to text
|
|
char *ScText(SCODE sc)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i<NSCODETEXT; i++)
|
|
if (scodes[i].sc == sc)
|
|
return scodes[i].text;
|
|
return "?";
|
|
}
|
|
|
|
// Output a call result and check for failure
|
|
HRESULT Result(HRESULT hr)
|
|
{
|
|
SCODE sc;
|
|
|
|
sc = DfGetScode(hr);
|
|
out(" - %s (0x%lX)\n", ScText(sc), sc);
|
|
if (FAILED(sc) && fExitOnFail)
|
|
error(EXIT_BADSC, "Unexpected call failure\n");
|
|
return hr;
|
|
}
|
|
|
|
// Perform Result() when the expectation is failure
|
|
HRESULT IllResult(char *pszText, HRESULT hr)
|
|
{
|
|
SCODE sc;
|
|
|
|
sc = DfGetScode(hr);
|
|
out("%s - %s (0x%lX)\n", pszText, ScText(sc), sc);
|
|
if (SUCCEEDED(sc) && fExitOnFail)
|
|
error(EXIT_BADSC, "Unexpected call success\n");
|
|
return hr;
|
|
}
|
|
|
|
// DEBUG - Check for memory leaks
|
|
void CheckMemory(void)
|
|
{
|
|
#if DBG == 1
|
|
if (fVerbose || DfGetMemAlloced() != 0)
|
|
{
|
|
out("Memory held: %lu bytes\n", DfGetMemAlloced());
|
|
if (DfGetMemAlloced() != 0)
|
|
{
|
|
DfPrintAllocs();
|
|
error(EXIT_BADSC, "Memory leak\n");
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// DEBUG - Set the debugging level
|
|
void SetDebug(ULONG ulDf, ULONG ulMsf)
|
|
{
|
|
#if DBG == 1
|
|
DfDebug(ulDf, ulMsf);
|
|
#endif
|
|
}
|
|
|
|
// Check whether a given storage has a certain
|
|
// structure or not
|
|
// Structure is given as a string with elements:
|
|
// <Type><Name><Options>[,...]
|
|
// Type - d for docfile and s for stream
|
|
// Name - Characters up to a '(' or ','
|
|
// Options - For a docfile, you can specify a recursive check
|
|
// in parentheses
|
|
//
|
|
// Example: dDocfile(sStream,dDocfile)
|
|
char *VerifyStructure(IStorage *pstg, char *pszStructure)
|
|
{
|
|
char szName[CWCSTORAGENAME], *psz;
|
|
IStorage *pstgChild;
|
|
char chType;
|
|
SCODE sc;
|
|
CStrList sl;
|
|
SStrEntry *pse;
|
|
IEnumSTATSTG *penm;
|
|
STATSTG stat;
|
|
OLECHAR atcName[CWCSTORAGENAME];
|
|
|
|
if (FAILED(sc = DfGetScode(pstg->EnumElements(0, NULL, 0, &penm))))
|
|
error(EXIT_BADSC, "VerifyStructure: Unable to create enumerator - "
|
|
"%s (0x%lX)\n", ScText(sc), sc);
|
|
for (;;)
|
|
{
|
|
sc = DfGetScode(penm->Next(1, &stat, NULL));
|
|
if (sc == S_FALSE)
|
|
break;
|
|
else if (FAILED(sc))
|
|
error(EXIT_BADSC, "VerifyStructure: Unable to enumerate - "
|
|
"%s (0x%lX)\n", ScText(sc), sc);
|
|
pse = sl.Add(stat.pwcsName);
|
|
if (pse == NULL)
|
|
error(EXIT_OOM, "VerifyStructure: Unable to allocate string\n");
|
|
pse->user.dw = stat.type;
|
|
drtMemFree(stat.pwcsName);
|
|
}
|
|
penm->Release();
|
|
while (*pszStructure && *pszStructure != ')')
|
|
{
|
|
chType = *pszStructure++;
|
|
psz = szName;
|
|
while (*pszStructure && *pszStructure != '(' &&
|
|
*pszStructure != ')' && *pszStructure != ',')
|
|
*psz++ = *pszStructure++;
|
|
*psz = 0;
|
|
ATOOLE(szName, atcName, CWCSTORAGENAME);
|
|
pse = sl.Find(atcName);
|
|
if (pse == NULL)
|
|
error(EXIT_BADSC, "VerifyStructure: '%s' not found\n", szName);
|
|
switch(chType)
|
|
{
|
|
case 'd':
|
|
if (pse->user.dw != STGTY_STORAGE)
|
|
error(EXIT_BADSC, "VerifyStructure: '%s' is not a storage\n",
|
|
szName);
|
|
sc = DfGetScode(pstg->OpenStorage(atcName, NULL,
|
|
STGP(STGM_READWRITE), NULL,
|
|
0, &pstgChild));
|
|
if (FAILED(sc))
|
|
error(EXIT_BADSC, "VerifyStructure: can't open storage "
|
|
"'%s' - %s\n", szName, ScText(sc));
|
|
if (*pszStructure == '(')
|
|
pszStructure = VerifyStructure(pstgChild, pszStructure+1)+1;
|
|
pstgChild->Release();
|
|
break;
|
|
case 's':
|
|
if (pse->user.dw != STGTY_STREAM)
|
|
error(EXIT_BADSC, "VerifyStructure: '%s' is not a stream\n",
|
|
szName);
|
|
break;
|
|
}
|
|
sl.Remove(pse);
|
|
if (*pszStructure == ',')
|
|
pszStructure++;
|
|
}
|
|
for (pse = sl.GetHead(); pse; pse = pse->pseNext)
|
|
error(EXIT_BADSC, "VerifyStructure: additional member '%s'\n",
|
|
OlecsOut(pse->atc));
|
|
return pszStructure;
|
|
}
|
|
|
|
// Creates a structure using the same syntax
|
|
// as VerifyStructure
|
|
char *CreateStructure(IStorage *pstg, char *pszStructure)
|
|
{
|
|
char szName[CWCSTORAGENAME], *psz;
|
|
IStorage *pstgChild;
|
|
IStream *pstmChild;
|
|
char chType;
|
|
SCODE sc;
|
|
OLECHAR atcName[CWCSTORAGENAME];
|
|
|
|
while (*pszStructure && *pszStructure != ')')
|
|
{
|
|
chType = *pszStructure++;
|
|
psz = szName;
|
|
while (*pszStructure && *pszStructure != '(' &&
|
|
*pszStructure != ')' && *pszStructure != ',')
|
|
*psz++ = *pszStructure++;
|
|
*psz = 0;
|
|
ATOOLE(szName, atcName, CWCSTORAGENAME);
|
|
switch(chType)
|
|
{
|
|
case 'd':
|
|
sc = DfGetScode(pstg->CreateStorage(atcName, STGP(STGM_READWRITE),
|
|
0, 0, &pstgChild));
|
|
if (FAILED(sc))
|
|
error(EXIT_BADSC, "CreateStructure: can't create storage "
|
|
"'%s' - %s\n", szName, ScText(sc));
|
|
if (*pszStructure == '(')
|
|
pszStructure = CreateStructure(pstgChild, pszStructure+1)+1;
|
|
pstgChild->Release();
|
|
break;
|
|
case 's':
|
|
sc = DfGetScode(pstg->CreateStream(atcName, STMP(STGM_READWRITE),
|
|
0, 0, &pstmChild));
|
|
if (FAILED(sc))
|
|
error(EXIT_BADSC, "CreateStructure: can't create stream "
|
|
"'%s' - %s\n", szName, ScText(sc));
|
|
pstmChild->Release();
|
|
break;
|
|
}
|
|
if (*pszStructure == ',')
|
|
pszStructure++;
|
|
}
|
|
pstg->Commit(0);
|
|
return pszStructure;
|
|
}
|
|
|
|
// Verifies the fields of a STATSTG
|
|
void VerifyStat(STATSTG *pstat, OLECHAR *ptcsName, DWORD type, DWORD grfMode)
|
|
{
|
|
if (ptcsName == NULL)
|
|
{
|
|
if (pstat->pwcsName != NULL)
|
|
error(EXIT_BADSC, "Stat name should be NULL - is %p\n",
|
|
pstat->pwcsName);
|
|
}
|
|
else if (olecscmp(pstat->pwcsName, ptcsName))
|
|
error(EXIT_BADSC, "Stat name mismatch - has '%s' vs. '%s'\n",
|
|
OlecsOut(pstat->pwcsName), OlecsOut(ptcsName));
|
|
if (pstat->type != type)
|
|
error(EXIT_BADSC, "Stat type mismatch - has %lu vs. %lu\n",
|
|
pstat->type, type);
|
|
if (pstat->grfMode != grfMode)
|
|
error(EXIT_BADSC, "Stat mode mismatch - has 0x%lX vs. 0x%lX\n",
|
|
pstat->grfMode, grfMode);
|
|
}
|
|
|
|
// Checks on a file's existence
|
|
BOOL Exists(OLECHAR *file)
|
|
{
|
|
OFSTRUCT of;
|
|
|
|
#ifndef OLEWIDECHAR
|
|
return OpenFile(file, &of, OF_EXIST | OF_SHARE_DENY_NONE) !=
|
|
HFILE_ERROR ? TRUE : FALSE;
|
|
#else
|
|
char szName[_MAX_PATH];
|
|
wcstombs(szName, file, _MAX_PATH);
|
|
return OpenFile(szName, &of, OF_EXIST | OF_SHARE_DENY_NONE) !=
|
|
HFILE_ERROR ? TRUE : FALSE;
|
|
#endif
|
|
}
|
|
|
|
// Gets a file's length
|
|
ULONG Length(OLECHAR *file)
|
|
{
|
|
ULONG cb;
|
|
|
|
#ifndef WIN32
|
|
|
|
OFSTRUCT of;
|
|
int hf;
|
|
hf = OpenFile(file, &of, OF_READ | OF_SHARE_DENY_NONE);
|
|
if (hf == HFILE_ERROR)
|
|
error(EXIT_BADSC, "Length: Unable to open '%s'\n", OlecsOut(file));
|
|
cb = (ULONG)_llseek(hf, 0, SEEK_END);
|
|
if (cb == (ULONG)HFILE_ERROR)
|
|
error(EXIT_BADSC, "Length: Unable to get length for '%s'\n",
|
|
OlecsOut(file));
|
|
_lclose(hf);
|
|
|
|
#else
|
|
|
|
// use WIN32 APIs
|
|
|
|
HANDLE hf;
|
|
|
|
#if !defined(UNICODE)
|
|
|
|
// Chicago - call ANSI CreateFile
|
|
|
|
char szName[_MAX_PATH];
|
|
if (wcstombs(szName, file, _MAX_PATH) == (size_t)-1)
|
|
return 0;
|
|
|
|
hf = CreateFile (
|
|
szName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
#else
|
|
|
|
hf = CreateFile (
|
|
file,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
#endif // !defined(UNICODE)
|
|
|
|
if (hf == INVALID_HANDLE_VALUE)
|
|
error(EXIT_BADSC, "Length: Unable to open '%s'\n", OlecsOut(file));
|
|
cb = (ULONG)GetFileSize(hf, NULL);
|
|
if (cb == (ULONG)0xFFFFFFFF)
|
|
error(EXIT_BADSC, "Length: Unable to get length for '%s'\n",
|
|
OlecsOut(file));
|
|
CloseHandle(hf);
|
|
|
|
#endif // !WIN32
|
|
|
|
return cb;
|
|
}
|
|
|
|
// Original mode when a new mode is forced
|
|
// Used by ForceDirect, ForceTransacted and Unforce
|
|
static DWORD dwTransOld;
|
|
static DWORD dwRDWOld;
|
|
|
|
// Forces direct mode to be active
|
|
// Note: this uses a static variable so it can\'t be nested
|
|
void ForceDirect(void)
|
|
{
|
|
dwTransOld = dwTransacted;
|
|
dwTransacted = STGM_DIRECT;
|
|
dwRDWOld = dwRootDenyWrite;
|
|
dwRootDenyWrite = STGM_SHARE_EXCLUSIVE;
|
|
}
|
|
|
|
// Forces transacted mode similarly to ForceDirect
|
|
void ForceTransacted(void)
|
|
{
|
|
dwTransOld = dwTransacted;
|
|
dwRDWOld = dwRootDenyWrite;
|
|
dwTransacted = STGM_TRANSACTED;
|
|
}
|
|
|
|
// Returns to the original mode after a ForceDirect or ForceTransacted
|
|
void Unforce(void)
|
|
{
|
|
dwTransacted = dwTransOld;
|
|
dwRootDenyWrite = dwRDWOld;
|
|
}
|
|
|
|
// Equality for FILETIME
|
|
BOOL IsEqualTime(FILETIME ttTime, FILETIME ttCheck)
|
|
{
|
|
// File times can be off by as much as 2 seconds due to FAT rounding
|
|
LONGLONG tmTime = *(LONGLONG *)&ttTime;
|
|
LONGLONG tmCheck = *(LONGLONG *)&ttCheck;
|
|
LONGLONG tmDelta = tmTime - tmCheck;
|
|
|
|
return tmDelta < 20000000i64 && tmDelta > -2i64 ;
|
|
}
|
|
|
|
// Get a fully qualified path for a file name
|
|
void GetFullPath(OLECHAR *file, OLECHAR *path)
|
|
{
|
|
#ifndef UNICODE
|
|
char buf[_MAX_PATH];
|
|
OFSTRUCT of;
|
|
|
|
OLETOA(file, buf, _MAX_PATH);
|
|
OpenFile(buf, &of, OF_PARSE);
|
|
ATOOLE((char *)of.szPathName, path, _MAX_PATH);
|
|
#else
|
|
OLECHAR *ptcsFile;
|
|
|
|
GetFullPathName(file, _MAX_PATH, path, &ptcsFile);
|
|
#endif
|
|
}
|
|
|
|
// Memory helper functions
|
|
|
|
HRESULT drtMemAlloc(ULONG ulcb, void **ppv)
|
|
{
|
|
HRESULT hr;
|
|
IMalloc *pMalloc = NULL;
|
|
|
|
if (SUCCEEDED(DfGetScode(hr = CoGetMalloc(MEMCTX_TASK, &pMalloc))))
|
|
{
|
|
*ppv = pMalloc->Alloc(ulcb);
|
|
pMalloc->Release();
|
|
|
|
if (*ppv == NULL)
|
|
return ResultFromScode(E_OUTOFMEMORY);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void drtMemFree(void *pv)
|
|
{
|
|
IMalloc FAR* pMalloc;
|
|
if (SUCCEEDED(GetScode(CoGetMalloc(MEMCTX_TASK, &pMalloc))))
|
|
{
|
|
pMalloc->Free(pv);
|
|
pMalloc->Release();
|
|
}
|
|
}
|
|
|
|
#pragma pack(1)
|
|
struct SplitGuid
|
|
{
|
|
DWORD dw1;
|
|
WORD w1;
|
|
WORD w2;
|
|
BYTE b[8];
|
|
};
|
|
#pragma pack()
|
|
|
|
char *GuidText(GUID const *pguid)
|
|
{
|
|
static char buf[39];
|
|
SplitGuid *psg = (SplitGuid *)pguid;
|
|
|
|
sprintf(buf, "{%08lX-%04hX-%04hX-%02X%02X-%02X%02X%02X%02X%02X%02X}",
|
|
psg->dw1, psg->w1, psg->w2, psg->b[0], psg->b[1], psg->b[2],
|
|
psg->b[3], psg->b[4], psg->b[5], psg->b[6], psg->b[7]);
|
|
return buf;
|
|
}
|