windows-nt/Source/XPSP1/NT/com/ole32/stg/utils/df2t/df2t.cxx
2020-09-26 16:20:57 +08:00

697 lines
24 KiB
C++

//+----------------------------------------------------------------------------// File: DF2T.CXX
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1991 - 1992.
//
// Contents: utility program for converting a Docfile to a directory tree.
// Given a Docfile,it is walked with an enumerator and each
// embedded IStorage creates a directory in the specified
// destination tree while each IStream creates a file within
// the appropriate directory. Finally, a tree compare is
// performed on the source and destination trees.
//
// Classes: IStorage - Container class
// IStream - Contained stream class
// IEnumSTATSTG - Enumerator class for IStorages
//
// Command line: df2c -f/-b -(n)ameofDocfile -(s)rcTree -(d)estTree -T -W
// -f forward conversion (docfile --> tree)
// -b backward conversion (tree --> docfile)
// -n name for root docfile
// -s name of the source directory tree
// -d name of the destination directory tree
// -t specifies STGM_TRANSACTED mode
// -w specifies STGM_SHARE_DENY_WRITE mode for root docfile
//
// -f and -b cannot be both specified in one command.
//
// The default conversion is from Docfile to tree.
// Therefore, -n and -d(est) must be specified when forward
// conversion -f is used. Otherwise, -b should be accompanied
// by -s(rc) and -n(doc).
//
// This utility defaults to running in DIRECT mode so that all
// operations at all levels are performed immediately on the
// base copy of the root docfile.
//
// With the -T switch, this runs in STGM_TRANSACTED mode for
// all IStorage creation and instantiation so that scratch
// streams are created and committed, but only made permanent
// in the persistent version by Commit of the root docfile.
//
// With the -W switch, this runs in STGM_TRANSACTED mode
// with STGM_SHARE_DENY_WRITE for root docfile creation and
// instantiation so that operations are performed on the base
// copy of the docfile.
//
// Requires: Windows 3.x. MSF.DLL and DOCFILE.DLL should be in same dir
// as executable or in \windows\system
//
// Returns: 0 if successful, exits with error code otherwise
//
// Notes: uses whcar and 'mbtowcs' conversions for wide char support
// returns STG_E_NOTCURRENT if docfile-to-be-created
// already exists.
//
// Created: RichE, January 13, 1992 Original program
// Revised: t-chrisy, June 25, 1992 convert the test program to a utility
// dftc.cxx --> df2t.cxx
//-----------------------------------------------------------------------------
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <direct.h>
#include "w4ctsupp.hxx"
#define WILD_CARD "\\*.*"
#define BACK_SLASH "\\"
#define NS_INCL (STGTY_STORAGE | STGTY_STREAM)
#define FIND_ATTRS _A_SUBDIR | _A_NORMAL | _A_RDONLY | _A_HIDDEN
#define FILE_BUF_SIZE 32768 // msf is optimized for file size of 4K or 512bytes
#define DOCTOTREE 0
#define TREETODOC 1
//function prototypes
void CopyTreeToDocfile(IStorage *pstgParent);
void CopyDocfileToTree(IStorage *pstgParent, TCHAR *ptcsDFName);
void CopyFileToStream(IStorage *pstgParent, char *FileName);
void CopyStreamToFile(IStorage *pstgParent, TCHAR *ptcsStreamName);
int GetOptions(int argc, char *argv[]);
void Usage(char *pszProgName);
//buffers for file/dir path calls and docfile name (default assignments)
char szDestDocfile[_MAX_PATH + 1] = "";
char szSrcPath[_MAX_PATH + 1] = "";
char szDestPath[_MAX_PATH + 1] = "";
int iSrcPathEnd = sizeof(_MAX_PATH); //length minus terminating '\0'
int iDestPathEnd = sizeof(_MAX_PATH);
//buffers for converting to/from wide characters
TCHAR wcsWideName[_MAX_PATH + 1];
char szName[_MAX_PATH + 1];
//modifiers to flags for root and child docfiles (GetOptions may modify)
DWORD dwRootFlags = STGM_READWRITE | STGM_SHARE_DENY_WRITE;
DWORD dwChildFlags = STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
void utMemFree(void *pv)
{
IMalloc FAR* pMalloc;
if (SUCCEEDED(GetScode(CoGetMalloc(MEMCTX_TASK, &pMalloc))))
{
pMalloc->Free(pv);
pMalloc->Release();
}
}
//+----------------------------------------------------------------------------
// Function: main, public
//
// Synopsis: main body of program, controls overall flow
//
// Effects: initialize. Depending on the direction of converionsn,
// call functions to create a tree, given a DocFile;
// call functions to create a DocFile, given a tree.
//
// Created: RichE January 13, 1992 original program
// Revised: t-chrisy June 25, 1992 modified to convert docfile to tree;
// and vice versa.
//
//-----------------------------------------------------------------------------
void main(int argc, char *argv[])
{
IStorage *pstgDest = NULL;
SCODE scRc;
short ret;
#if WIN32 == 300
if (FAILED(scRc = GetScode(CoInitializeEx(NULL, COINIT_MULTITHREADED))))
#else
if (FAILED(scRc = GetScode(CoInitialize(NULL))))
#endif
{
fprintf(stderr, "CoInitialize failed with sc = %lx\n", scRc);
exit(1);
}
ret=GetOptions(argc, argv);
if (ret==DOCTOTREE)
{
tprintf(bDestOut, "Converting Docfile to tree....\n");
if ((!strcmp(szDestDocfile,"\0"))||(!strcmp(szDestPath,"\0")))
{
tprintf(bDestOut, "Invalid switches used.\n");
tprintf(bDestOut,
"If -f is indicated, -n and -d must also be specified.\n");
exit(1);
}
}
else
{
tprintf(bDestOut, "Converting a directory tree to Docfile....\n");
if ((!strcmp(szDestDocfile,"\0")||(!strcmp(szSrcPath,"\0"))))
{
tprintf(bDestOut, "Invalid switches used.\n");
tprintf(bDestOut,
"If -b is chosen, -s and -n must also be specified.\n");
exit(1);
}
}
if (dwRootFlags & STGM_TRANSACTED)
{
tprintf(bDestOut, "STGM_TRANSACTED mode ");
if ((dwRootFlags & 0x70) == STGM_SHARE_EXCLUSIVE)
{
tprintf(bDestOut, "with STGM_SHARE_EXCLUSIVE");
}
else
{
tprintf(bDestOut, "without STGM_SHARE_EXCLUSIVE");
}
}
else
{
tprintf(bDestOut, "STGM_DIRECT mode (with STGM_SHARE_EXCLUSIVE)");
}
tprintf(bDestOut, " operation\n");
MakeWide(wcsWideName, szDestDocfile);
if (ret==DOCTOTREE) // Docfile to tree...
{
MakePath(szDestPath);
CopyDocfileToTree((IStorage *) NULL, wcsWideName);
}
else
{
// Create root docfile with specified mode, nuke if already exists
tprintf(bDestOut, "Create root docfile %s\n", szDestDocfile);
scRc = GetScode(StgCreateDocfile(wcsWideName,
dwRootFlags | STGM_CREATE, 0,
&pstgDest));
tprintf(bDestOut, "Returned %lu\n", scRc);
tprintf(bDestOut, "Root docfile %s %s, pointer %p\n", szDestDocfile,
pstgDest == NULL ? "FALSE" : "TRUE", pstgDest);
if (pstgDest == NULL || FAILED(scRc))
{
ErrExit(DEST_LOG, scRc, "Error creating root docfile %s\n",
szDestDocfile);
}
CopyTreeToDocfile(pstgDest);
}
}
//+----------------------------------------------------------------------------
// Function: CopyTreeToDocfile, private
//
// Synopsis: traverses and reads source tree and creates docfile image
//
// Effects: for each directory in source tree, an IStorage is created, for each
// file, a contained stream is created. this function is recursive.
//
// Arguments: [pstgParent] - current parent IStorage for new containees
//
// Created: RichE, January 13, 1992
// Revised: RichE March 5, 1992 Df APIs to method calls
// Revised: RichE March 6, 1992 TRANSACTED mode operation
// Revised: RichE March 17, 1992 convert to OLE interfaces
//-----------------------------------------------------------------------------
void CopyTreeToDocfile(IStorage *pstgParent)
{
struct find_t FileInfo;
SCODE scRc;
USHORT usEndOfBasePath;
IStorage *pstgChild;
// Save pointer to base of pure path at this level
usEndOfBasePath = strlen(szSrcPath) + 1;
scRc = _dos_findfirst(strcat(szSrcPath, WILD_CARD), FIND_ATTRS, &FileInfo);
while (scRc==0)
{
// If not '.' or '..' directories
if (FileInfo.name[0] != '.')
{
// Restore pure path and add current file/dir name to it
szSrcPath[usEndOfBasePath] = NIL;
strcat(szSrcPath, FileInfo.name);
if (FileInfo.attrib == _A_SUBDIR)
{
MakeWide(wcsWideName, FileInfo.name);
// Create new IStorage inside current one,
// use dir name for name
tprintf(bDestOut,
"Create embedded DF %s inside pstgParent %p\n",
szSrcPath, pstgParent);
scRc = GetScode(pstgParent->CreateStorage(wcsWideName,
dwChildFlags |
STGM_FAILIFTHERE,
0, 0,
&pstgChild));
tprintf(bDestOut, "Returned: %lu, pointer = %p\n",
scRc, pstgChild);
if (pstgChild == NULL || FAILED(scRc))
{
ErrExit(DEST_LOG, scRc,
"Error creating child IStorage %s\n",
FileInfo.name);
}
CopyTreeToDocfile(pstgChild);
}
else
{
CopyFileToStream(pstgParent, FileInfo.name);
}
}
scRc = _dos_findnext(&FileInfo);
}
if (dwRootFlags & STGM_TRANSACTED)
{
tprintf(bDestOut, "Committing pstgParent %p\n", pstgParent);
if (scRc = GetScode(pstgParent->Commit(STGC_ONLYIFCURRENT)))
{
ErrExit(DEST_LOG, scRc, "Error committing IStorage %p\n",
pstgParent);
}
}
tprintf(bDestOut, "Releasing pstgParent %p\n", pstgParent);
pstgParent->Release();
}
//+----------------------------------------------------------------------------
// Function: CopyFileToStream, private
//
// Synopsis: copies supplied file to stream inside of parent IStorage
//
// Arguments: [pstgParent] - parent IStorage for stream created
// [FileName] - name of source file to copy to stream
//
// Created: RichE, January 13, 1992
// Revised: RichE March 5, 1992 Df APIs to method calls
// Revised: RichE March 6, 1992 TRANSACTED mode operation
// Revised: RichE March 15, 1992 streams no longer TRANSACTED
// Revised: RichE March 17, 1992 convert to OLE interfaces
//-----------------------------------------------------------------------------
void CopyFileToStream(IStorage *pstgParent, char *FileName)
{
IStream *pstmStream = NULL;
FILE *FileToCopy;
SCODE scRc;
ULONG cNumRead;
ULONG cNumWritten;
BYTE *FileBuf;
tprintf(bDestOut, " File %s\n", szSrcPath);
FileToCopy = fopen(szSrcPath, "rb");
if (FileToCopy == NULL)
{
ErrExit(DEST_LOG, ERR, "Cannot open file %s\n", szSrcPath);
}
MakeWide(wcsWideName, FileName);
// Create a stream inside parent IStorage
tprintf(bDestOut,
"Create embedded stream inside parent pstgParent = %p\n",
pstgParent);
scRc = GetScode(pstgParent->CreateStream(wcsWideName, STGM_STREAM |
STGM_FAILIFTHERE, 0, 0,
&pstmStream));
tprintf(bDestOut, "Returned: %lu, pointer = %p\n", scRc, pstmStream);
if (pstmStream == NULL || FAILED(scRc))
{
ErrExit(DEST_LOG, scRc, "Error creating stream %s\n", szSrcPath);
}
FileBuf = (BYTE * ) Allocate(FILE_BUF_SIZE * sizeof(BYTE));
//while still reading from source file, write what was just read to Stream
while (cNumRead = fread(FileBuf, 1, FILE_BUF_SIZE, FileToCopy))
{
if (ferror(FileToCopy))
{
ErrExit(DEST_LOG, ERR, "Error during stream read of %s\n",
szSrcPath);
}
tprintf(bDestOut, "Try Stream write of %lu bytes on stream %p\n",
cNumRead, pstmStream);
scRc = GetScode(pstmStream->Write(FileBuf, cNumRead, &cNumWritten));
tprintf(bDestOut, "Returned: %lu, bytes written %lu\n",
scRc, cNumWritten);
if (cNumWritten != cNumRead)
{
tprintf(bDestOut, "Write: scRc = %lu, cNumWritten = %lu, ",
scRc, cNumWritten);
tprintf(bDestOut, "cNumRead = %lu\n", cNumRead);
}
if (FAILED(scRc))
{
ErrExit(DEST_LOG, ERR, "Error writing stream %p\n", pstmStream);
}
}
tprintf(bDestOut, "Releasing stream %p\n", pstmStream);
pstmStream->Release();
fclose(FileToCopy);
free(FileBuf);
}
//+----------------------------------------------------------------------------// Function: CopyDocfileToTree, private
//
// Synopsis: enumerates and reads docfile and creates directory tree
//
// Effects: for each child IStorage in the root docfile, a subdir is created,
// for each child stream, a file is created. this function is
// recursive.
//
// Arguments: [pstgParent] - current parent IStorage for reading containees
// [ptcsDFName] - name of IStorage to instantiate
//
// Created: RichE, January 13, 1992
// Revised: RichE March 5, 1992 Df APIs to method calls
// Revised: RichE March 6, 1992 TRANSACTED mode operation
// Revised: RichE March 17, 1992 convert to OLE interfaces
// Revised: t-chrisy June 30, 1992 removed the section on unlinking docfile
//-----------------------------------------------------------------------------
void CopyDocfileToTree(IStorage *pstgParent, TCHAR *ptcsDFName)
{
IStorage *pstgSrc = NULL;
IEnumSTATSTG *penmWalk;
USHORT usEndOfBasePath;
SCODE scRc;
STATSTG sstg;
int iRc;
// Add back slash & save pointer to base of pure path at this level
strcat(szDestPath, BACK_SLASH);
usEndOfBasePath = strlen(szDestPath);
MakeSingle(szName, ptcsDFName);
// If not first call (parent != NULL) then instantiate child IStorage with
// method call, else instantiate root docfile via Df API call
if (pstgParent != NULL)
{
tprintf(bDestOut, "Get embedded IStorage %s in parent %p\n",
szName, pstgParent);
scRc = GetScode(pstgParent->OpenStorage(ptcsDFName, NULL, dwChildFlags,
NULL, 0, &pstgSrc));
}
else
{
tprintf(bDestOut, "Instantiate root docfile %s\n", szName);
dwRootFlags |= STGM_READ;
scRc = GetScode(StgOpenStorage(ptcsDFName, NULL, dwRootFlags, NULL,
0, &pstgSrc));
}
tprintf(bDestOut, "Return code: %lu, IStorage pointer %p\n",
scRc, pstgSrc);
if (pstgSrc == NULL || FAILED(scRc))
{
ErrExit(DEST_LOG, scRc, "Error instantiating IStorage %s\n", szName);
}
// Get an enumerator on the IStorage we just instantiated
scRc = GetScode(pstgSrc->EnumElements(0, NULL, 0, &penmWalk));
tprintf(bDestOut, "Got enumerator %p on IStorage %p, returned %lu\n",
penmWalk, pstgSrc, scRc);
if (penmWalk == NULL || FAILED(scRc))
{
ErrExit(DEST_LOG, scRc,
"Error obtaining enumerator for IStorage %p\n", pstgSrc);
}
// Loop until GetNext returns other than S_OK, then break out of loop
while (TRUE)
{
if (GetScode(penmWalk->Next(1, &sstg, NULL)) != S_OK)
{
tprintf(bDestOut, "No more to enumerate with enumerator %p\n",
penmWalk);
break;
}
else
{
MakeSingle(szName, sstg.pwcsName);
tprintf(bDestOut, "Got item type %lu, Name %s, w/enumerator %p\n",
sstg.type, szName, penmWalk);
// Restore to path + BACK_SLASH and add file/dir name to DestPath
szDestPath[usEndOfBasePath] = NIL;
strcat(szDestPath, szName);
tprintf(bDestOut, "Path Name: %s is ", szDestPath);
if (sstg.type == STGTY_STORAGE)
{
tprintf(bDestOut, "STGTY_STORAGE\n");
iRc = _mkdir(szDestPath);
tprintf(bDestOut,
"Trying to make directory %s, returned %d\n",
szDestPath, iRc);
CopyDocfileToTree(pstgSrc, sstg.pwcsName);
}
else
{
tprintf(bDestOut, "STGTY_STREAM\n");
CopyStreamToFile(pstgSrc, sstg.pwcsName);
}
}
utMemFree(sstg.pwcsName);
}
tprintf(bDestOut, "Releasing enumerator %p\n", penmWalk);
penmWalk->Release();
}
//+----------------------------------------------------------------------------
// Function: CopyStreamToFile, private
//
// Synopsis: copies supplied embedded stream to file in current dubdir
//
// Arguments: [pstgParent] - parent IStorage for stream to copy
// [ptcsStreamName] - name of stream to copy to file
//
// Created: RichE, January 13, 1992
// Revised: RichE March 5, 1992 Df APIs to method calls
// Revised: RichE March 6, 1992 TRANSACTED mode operation
// Revised: RichE March 15, 1992 streams no longer TRANSACTED
// Revised: RichE March 17, 1992 convert to OLE interfaces
//-----------------------------------------------------------------------------
void CopyStreamToFile(IStorage *pstgParent, TCHAR *ptcsStreamName)
{
IStream *pstmStream = NULL;
FILE *FileToWrite;
ULONG cNumRead = 1;
ULONG cNumWritten;
BYTE *FileBuf;
SCODE scRc;
tprintf(bDestOut, "Copying embedded stream to file %s\n", szDestPath);
// Instantiate named stream within parent IStorage
tprintf(bDestOut, "Get stream in parent %p\n", pstgParent);
scRc = GetScode(pstgParent->OpenStream(ptcsStreamName, NULL, STGM_STREAM,
0, &pstmStream));
tprintf(bDestOut, "Returned: %lu, stream pointer %p\n", scRc, pstmStream);
if (pstmStream == NULL || FAILED(scRc))
{
ErrExit(DEST_LOG, scRc, "Error opening stream %s\n", szDestPath);
}
FileToWrite = fopen(szDestPath, "wb");
if (FileToWrite == NULL)
{
ErrExit(DEST_LOG, ERR, "Cannot open file %s\n", szDestPath);
}
FileBuf = (BYTE * ) Allocate(FILE_BUF_SIZE * sizeof(BYTE));
// While still reading Stream, write what was just read to file.
tprintf(bDestOut, "Starting to read stream %p\n", pstmStream);
while (cNumRead > 0)
{
scRc = GetScode(pstmStream->Read(FileBuf, FILE_BUF_SIZE, &cNumRead));
tprintf(bDestOut, "Read %lu bytes from stream %p, returned %lu\n",
cNumRead, pstmStream, scRc);
if (FAILED(scRc))
{
ErrExit(DEST_LOG, scRc, "Error reading stream %p\n", pstmStream);
}
cNumWritten = (ULONG)fwrite(FileBuf, 1, (size_t)cNumRead, FileToWrite);
if (ferror(FileToWrite))
{
ErrExit(DEST_LOG, ERR, "Error writing to file %s\n", szDestPath);
}
if (cNumWritten != cNumRead)
{
tprintf(bDestOut, "Fwrite: cNumRead = %lu, cNumWritten = %lu\n",
cNumRead, cNumWritten);
}
}
tprintf(bDestOut, "Attempting to release stream %p in IStorage %p\n",
pstmStream, pstgParent);
pstmStream->Release();
fclose(FileToWrite);
free(FileBuf);
}
//+----------------------------------------------------------------------------
// Function: GetOptions, private
// Returns: DOCTOTREE or TREETODOC to indicate the direction of conversion.
//
// Synopsis: parses command line and sets global program options/variables
//
// Arguments: [argc] and [**argv] passed from main() function
//
// Modifies: [szSrcPath, szDestPath, szDestDocfile]
//
// Created: RichE, January 13, 1992
// Revised: RichE, March 15, 1992 added -T and -W switches
// Revised: RichE, March 19, 1992 fixed bug displaying error usage
// Revised: t-chrisy, June 25, 1992 added -f and -b switches
//-----------------------------------------------------------------------------
int GetOptions(int argc, char *argv[])
{
char *pszArg;
char *pszProgName;
BOOL ArgsOK = TRUE;
short ret=DOCTOTREE;
// Bump past command name (argv[0])
pszProgName = *argv++;
// For each command line arg, if it begins with a '-' or '/' check the
// next letter for a valid argument. return error for invalid args
while ((pszArg = *argv++) != NULL)
{
if (*pszArg == '-' || *pszArg == '/')
{
switch (tolower(*(++pszArg)))
{
case 'w':
dwRootFlags |= STGM_TRANSACTED;
dwChildFlags |= STGM_TRANSACTED;
break;
case 't':
dwRootFlags |= STGM_TRANSACTED;
dwChildFlags |= STGM_TRANSACTED;
break;
case 's':
strcpy(szSrcPath, ++pszArg);
iSrcPathEnd = strlen(pszArg);
break;
case 'd':
strcpy(szDestPath, ++pszArg);
iDestPathEnd = strlen(pszArg);
break;
case 'z':
LogFile(++pszArg,LOG_INIT);
break;
case 'y':
SetDebugMode(tolower(*(++pszArg)));
break;
case 'n':
if (strlen(++pszArg) <= _MAX_PATH)
{
strcpy(szDestDocfile, pszArg);
}
else
{
tprintf(DEST_LOG, "Dest DocFile Name too long: %s\n",
pszArg);
tprintf(DEST_LOG, "Max len = %d\n", _MAX_PATH);
tprintf(DEST_LOG, "Specified len = %d\n", strlen(pszArg));
ArgsOK = FALSE;
}
break;
case 'f':
// Docfile to tree
dwRootFlags &= ~0x03; // clear access bits
dwRootFlags |= STGM_READ;
ret=DOCTOTREE;
break;
case 'b':
// Tree to docfile
dwRootFlags &= ~0x70;
dwRootFlags |= STGM_SHARE_EXCLUSIVE;
ret=TREETODOC;
break;
default:
ArgsOK = FALSE;
break;
}
}
else
ArgsOK = FALSE;
if (ArgsOK == FALSE)
{
tprintf(DEST_LOG,
"Invalid command line argument: %s\n", --pszArg);
Usage(pszProgName);
}
}
return ret;
}
//+----------------------------------------------------------------------------
// Function: Usage, private
//
// Synopsis: displays program syntax and example and exits with error
//
// Arguments: [pszProgramName] - name of this executable file for error usage
//
// Created: RichE, January 15, 1992
// Revised: t-chrisy, June 30, 1992 Added -f and -b options.
//+----------------------------------------------------------------------------
void Usage(char *pszProgName)
{
tprintf(DEST_ERR, "Usage: %s\n", pszProgName);
tprintf(DEST_ERR, " [-f] - forward conversion"
"(docfile-->tree) DEFAULT\n");
tprintf(DEST_ERR, " -n and -d must be specified.");
tprintf(DEST_ERR, " [-b] - backward conversion"
"(tree-->docfile)\n");
tprintf(DEST_ERR, " -s and -n must be specified.");
tprintf(DEST_ERR, " [-sSRCDIR] \n");
tprintf(DEST_ERR, " [-nDOCFILENAME] \n");
tprintf(DEST_ERR, " [-dDESTDIR] \n");
tprintf(DEST_ERR, " [-t] - for transacted mode\n");
tprintf(DEST_ERR, " [-w] - for deny write mode\n");
ErrExit(DEST_ERR, ERR, " ex: %df2t -b -sc:\\dos -nd:\\docfile.dfl\n",
pszProgName);
}