//+----------------------------------------------------------------------------// 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 #include #include #include #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); }