/****************************************************************************
**
**  Name:  memCmp.c
**
**  Description:
**     This is the memory compare command code for the Thunderbird
**     memory server.
**
**
**
**  $Log:   S:/tbird/mt2_68k/mem/memcmp.c_v  $
** 
**    Rev 1.0   20 Feb 1998 12:15:44   Winky
** Initial revision.
** 
**    Rev 1.9   21 Aug 1996 10:12:08   kevin
** changed ID of help entry
** 
**    Rev 1.8   18 Jan 1996 15:00:50   kevin
** corrected ID of the entry of memory help
** 
**    Rev 1.7   18 Jan 1996 14:56:24   kevin
** added help entry and fixed a bug of mem compare
** 
**    Rev 1.5   08 Dec 1995 11:18:06   kevin
** 
**    Rev 1.4   08 Dec 1995 11:12:50   kevin
** fixed bugs in cliCompareMemory() and SpaceKeyword(). list #130,138
** 
**    Rev 1.3   28 Nov 1995 09:06:02   kevin
** changed include file
** 
**    Rev 1.2   28 Nov 1995 08:50:26   kevin
** added compare stuff
** 
**    Rev 1.0   20 Nov 1995 15:51:42   gene
** Initial revision.
** 
**    Rev 1.1   26 Sep 1995 08:58:26   gene
** added Compare space
** 
** 
**    Rev 1.0   24 Mar 1995 13:54:08   kevin
** Initial revision.
**  $Header:   S:/tbird/mt2_68k/mem/memcmp.c_v   1.0   20 Feb 1998 12:15:44   Winky  $
**
**
**  Copyright (C) 1995 Microtek International.  All rights reserved.
**
*****************************************************************************/

/*
 * -----------------------------------------
 *  General purpose section function index
 * -----------------------------------------
 *
 *  DestroyCopyAddrDescriptors(void)
 *  CreateCopyAddrDescriptors(void)
 *    alloc and dealloc descriptors for the user interface
 *    address objects.
 *
 *  SetAddressSpaceDefaults(void)
 *    set up any defaults that the parser/dialog may
 *    be expecting.
 *
 *
 * -------------------------------------
 *  parser section function index
 * -------------------------------------
 *
 *  IsCopyCmdKeyword( LPSTR str )
 *     test passed string againt tokens for the copy command
 *
 *  ParseCliCopyMemory(LPSTR cmdString, U32 argc, U32 argv[])
 *     Parse command line and convert results into address
 *     descriptors and other variables, the native form used by
 *     the MemCopy() call in memory.c   (this is just what the other
 *     CLI functions do, they just do it locally, with in line code).
 *
 *
 *   MemCliCopyMemory(LPSTR cmdString, U32 argc, U32 argv[])
 *     Local entry point from MemCli.c DLL entry point.
 *     Calls ParseCliCopyMemory, then MemCopy().
 *     This extra level is performed to let us keep
 *     the native form (descriptors, ie, the cpy_* variables)
 *     local to this module.
 *
 *
 *
 * -------------------------------------
 *  dialog section function index
 * -------------------------------------
 *  MemCpyXferToLocal(HWND hDlg)
 *  MemCpyUnloadControls(HWND hDlg)
 *  MemCpyLoadControlsFromLocal(HWND hDlg)
 *    manages a local copy of memory parameters, just in case the user
 *    wants to press cancel.
 *
 *
 *   BOOL EXPORT MemCpyDlg(HWND hDlg, .... . LONG lParam)
 *      the dialog proc
 *
 *
 *  See also:
 *    Dialog Template:  mcpydlg.dlg, mcpydlg.res
 *    Dialog Constants: mcpydlg.h
 *
 *    note:  IDD_CPY_... are dialog controls, per convention
 *
 *
 *
 */


                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/

#ifndef _ADDR_
#include "addr.h"
#endif

#ifndef _BASEWIND_
#include "basewind.h"
#endif

#ifndef _CLISRV_
#include "clisrv.h"
#endif

#ifndef _CLIULIB_
#include "cliulib.h"
#endif

#ifndef _HLPENTRY_
#include "hlpentry.h"
#endif

#ifndef _HEAP_
#include "heap.h"
#endif

#ifndef _MEMCLI_
#include "memcli.h"
#endif

#ifndef _MEMLOCAL_
#include "memlocal.h"
#endif

#ifndef _PROC_
#include "proc.h"
#endif

#ifndef _SDPROBE_
#include "sdprobe.h"
#endif

#ifndef _SSHARED_
#include "sshared.h"
#endif

#ifndef __STDLIB_H
#include "stdlib.h"
#endif

#ifndef __STDIO_H
#include "stdio.h"
#endif

#ifndef __STRING_H
#include "string.h"
#endif

#ifndef _TBIRDMEM_
#include "tbirdmem.h"
#endif

#ifndef  _SDS2ABI_
#include "sds2abi.h"
#endif

#ifndef  _MCPYDLG_
#include "mcpydlg.h"
#endif
                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/

// size of symbol fields in dialog boxes.
#define   ADDR_LENGTHS   60


// text for the keywords
#define SD_TEXT      "sd"
#define SP_TEXT      "sp"
#define UD_TEXT      "ud"
#define UP_TEXT      "up"
#define TO_TEXT      "to"
#define LENGTH_TEXT  "length"


                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/
// Handle of the DLL instance: saved by LibMain
extern HANDLE hLib;

// Handle to send to shell
extern HANDLE cliServerHandle;

                       /***************************
                        *                         *
                        *    PRIVATE VARIABLES    *
                        *                         *
                        ***************************/

// ---------------------------------------------------------
//    SHARED DATA "GLOBAL" TO FUNCTIONS IN THIS MODULE
// ---------------------------------------------------------
//  This is the native form of the memory copy command
//  the parser (ParseCliCopyMemory) and dialog both
//  place results here.
//
// used for symbol parsing and range calculations
// that use Address server (addr.dll)

PRIVATE DESCRIPTOR  cpySrcDesc    = NULL;   // passed to MemCmp()
PRIVATE DESCRIPTOR  cpyDestDesc   = NULL;
PRIVATE DESCRIPTOR  cpySrcEndDesc = NULL;   // used to parse symbols into length

// data as passed to firmware is NOT entirely in descriptors
// some components are passed instead.
PRIVATE U32         cpyLength;        // passed in SD_COPY_LENGTH

//--------------------------------------------------------
//  Variables local to the dialog
//--------------------------------------------------------
// these are dialog-local copies (dlgLocal...) of params, to be
// thrown away if user presses cancel.
//   loaded from native form by MemCpyXferToLocal(..)
//
//--------------------------------------------------------
//

// controls CreateCopyAddrDescriptors activity
PRIVATE  BOOLEAN  descriptorsCreated = FALSE;
PRIVATE CHAR dlgLocalSrcStartAddr[ADDR_LENGTHS];
PRIVATE CHAR dlgLocalSrcEndAddr[ADDR_LENGTHS];
PRIVATE CHAR dlgLocalDestStartAddr[ADDR_LENGTHS];
PRIVATE BOOLEAN  dlgLocalPartitionUseLength;
PRIVATE ADDR_SPACE dlgLocalDestSpace;

                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/


RETCODE PRIVATE DestroyCopyAddrDescriptors(VOID);
RETCODE PRIVATE CreateCopyAddrDescriptors(VOID);
BOOLEAN PRIVATE isSpaceKeyword(LPSTR str,ADDR_SPACE *retSpace);
RETCODE PRIVATE MemCompUnloadControls(HWND hDlg);

                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/


/**************************************************************
**    DestroyCopyAddrDescriptors
**
**  note that addr library will accept NULL descriptor.
**    it just returns an error code...
**    return the first error that appears
**
** ************************************************************
*/

RETCODE PRIVATE DestroyCopyAddrDescriptors(VOID) {

   RETCODE err;
   RETCODE returnedErr = GOOD;

   // prefer memory leaks to access of uninitialized memory
   descriptorsCreated = FALSE;
   if (cpySrcDesc != NULL) {
      err = AdrDestroyAddress(cpySrcDesc);
      if(returnedErr==GOOD)
         returnedErr=err;
   }
   if (cpySrcEndDesc != NULL) {
      err = AdrDestroyAddress(cpySrcEndDesc);
      if(returnedErr==GOOD)
         returnedErr=err;
   }
   if (cpyDestDesc != NULL) {
      err = AdrDestroyAddress(cpyDestDesc);
      if(returnedErr==GOOD)
         returnedErr=err;
   }
   return returnedErr;
}


/**************************************************************
**  CreateCopyAddrDescriptors( )
**
** create address descriptors for the copy.
**  if any are not created, calls DestroyCopyAddrDescriptors()
**
**  we ignore any errors from DestroyCopyAddrDescriptors()...we
**  want to return original reason for the error.
**
***************************************************************
*/
RETCODE PRIVATE CreateCopyAddrDescriptors(VOID) {

   RETCODE err = GOOD;


   if(descriptorsCreated == TRUE)
      return GOOD;
   cpySrcDesc    = NULL;
   cpySrcEndDesc = NULL;
   cpyDestDesc   = NULL;

   err = AdrCreateAddress(&cpySrcDesc);
   if (err != GOOD)  {
      DestroyCopyAddrDescriptors();
      return err;
   }
   err = AdrCreateAddress(&cpySrcEndDesc);
   if (err != GOOD)  {
      DestroyCopyAddrDescriptors();
      return err;
   }
   err = AdrCreateAddress(&cpyDestDesc);
   if (err != GOOD)  {
      DestroyCopyAddrDescriptors();
      return err;
   }
   descriptorsCreated = TRUE;
   return GOOD;
}

/**************************************************************
**   KeywordCheck( LPSTR string )
**
**   Test incoming token against the possible keywords.
**
**  NOTE:  use "strnicmp" rather than *NON* ANSI "strncmpi"
*****************************************************************
**/

BOOLEAN PRIVATE isSpaceKeyword(LPSTR str,ADDR_SPACE *retSpace) {
//   U8 paramLen = strlen(str);
   BOOLEAN found = TRUE;

   // keywords
   if (stricmp(str, SD_TEXT)==0)
      *retSpace = SPACE_SD ;
   else if (stricmp(str, SP_TEXT)==0)
      *retSpace = SPACE_SP ;
   else if (stricmp(str, UD_TEXT)==0)
      *retSpace = SPACE_UD ;
   else if (stricmp(str, UP_TEXT)==0)
      *retSpace = SPACE_UP ;
   else
      found = FALSE ;
   return (found);
}
/*****************************************************************
**
** MemCliCompareMemory
**  registered with CLI in the .RC file
**
** Description:
**    Cli interface to the compare memory
**
** Syntax: compare <from_start_address> <from_end_address | LENGTH <len>>
**         TO <to_address>
**
** Input:
**    cmdString
**    argc
**    argv
**
** Output: no parameters
**
**
**     DLL entry point from CLI.
**     Calls ParseCliCompareMemory, then MemComapre().
**
**
*****************************************************************************/
#pragma argsused
RETCODE EXPORT MemCliCompareMemory(LPSTR cmdString, U32 argc, U32 argv[]) {

   RETCODE  err;
   HCURSOR  hSaveCursor, hHourGlass;
   LPSTR      endptr;         // used by strtoul
   ADDR_COMPARE result;       // result of comparison
   LOOP_VAR   args, State = 0;
   BOOLEAN    compareResult;
   U32        retAddr1, retAddr2;
   U16        retData1, retData2;
   CHAR  outputBuff[80];
   CHAR  srcSpaceText[4] = "sd", destSpaceText[4] = "sd";
   CHAR *parserWord;
   ACCESS_SIZE size;
   ADDR_SPACE tmpSpace = SPACE_SD;

   MemGetAccessSize(&size);
// check for minimum number of tokens (5)
   if (argc < 5 || argc > 8)
      return ER_CLI_SYNTAX;
   
   if ((err = CreateCopyAddrDescriptors()) != GOOD)
      return err;

// Parse the command        
   for (args=1; args < argc; args++) {
      parserWord = &cmdString[(U16)argv[args]];
      switch(State) {
         case 0 : if ((err=AdrConvTextToAddress(cpySrcDesc, parserWord)) != GOOD) {
                     DestroyCopyAddrDescriptors() ;
                     return err ;
                  }
                  State = 1 ;
                  break ;
         case 1 : if (strnicmp(parserWord, LENGTH_TEXT, 7)==0) {
                     State = 5 ;
                     break ;
                  } else if ((err=AdrConvTextToAddress(cpySrcEndDesc, parserWord)) != GOOD) {
                     DestroyCopyAddrDescriptors();
                     return err;
                  }
                  State = 2 ;
                  err = AdrCompareAddresses(cpySrcDesc, cpySrcEndDesc, &result);
                  if (result != SECOND_ADDR_GREATER) {
                     DestroyCopyAddrDescriptors() ;
                     return ER_CLI_SYNTAX;
                  }
                  // extract difference between two addresses for length
                  if ((err = AdrRangeOfAddresses(cpySrcDesc, cpySrcEndDesc, &cpyLength)) != GOOD) {
                     DestroyCopyAddrDescriptors() ;
                     return err;
                  }
                  break ;
         case 2 : if (strnicmp(parserWord, TO_TEXT, 2)==0) {
                     State = 3 ;
                     break ;
                  } else if (isSpaceKeyword(parserWord,&tmpSpace)) {
                     if ((err=AdrSetAddrSpace(cpySrcDesc,tmpSpace)) != GOOD) {
                        DestroyCopyAddrDescriptors() ;
                        return err ;
                     }
                     strcpy(srcSpaceText,parserWord);
                     State = 6 ;
                     break ;
                  } else return ER_CLI_SYNTAX ;
         case 3 : if ((err=AdrConvTextToAddress(cpyDestDesc, parserWord)) != GOOD) {
                     DestroyCopyAddrDescriptors() ;
                     return err ;
                  }
                  State = 4 ;
                  break ;
         case 4 : if (isSpaceKeyword(parserWord,&tmpSpace)) {
                     if ((err=AdrSetAddrSpace(cpyDestDesc,tmpSpace)) != GOOD) {
                        DestroyCopyAddrDescriptors() ;
                        return err ;
                     }
                     strcpy(destSpaceText,parserWord);
                     State = 7 ;
                     break ;
                  } else return ER_CLI_SYNTAX ;
         case 5 : cpyLength = strtoul(parserWord,&endptr,0);
                  State = 2 ;
                  break ;
         case 6 : if (strnicmp(parserWord, TO_TEXT, 2)!=0)
                     return ER_CLI_SYNTAX ;
                  State = 3 ;
                  break ;
         case 7 :
         default : DestroyCopyAddrDescriptors() ;
                   return ER_CLI_SYNTAX ;
      }
   }

   //  perform the compare.
   hHourGlass = LoadCursor(NULL, IDC_WAIT);
   hSaveCursor = SetCursor(hHourGlass);
   err = MemCompare(cpySrcDesc, cpyDestDesc, cpyLength, &compareResult, 
                    &retAddr1, &retAddr2, &retData1, &retData2);
   SetCursor(hSaveCursor);
   // do not destroy the descriptors they are reused by the dialog.
   if (err) return err;
   if (compareResult) 
      SendCliMessage(cliServerHandle,(LPSTR)"Memory no difference.");
   else {
      if (size == BYTE_SIZE)
         sprintf(outputBuff,
            "Memory different at: (%s:0x%06lX)=0x%02X, (%s:0x%06lX)=0x%02X",
            strupr(srcSpaceText), retAddr1, retData1&0x00ff,
            strupr(destSpaceText), retAddr2, retData2&0x00ff);
      else
         sprintf(outputBuff,
            "Memory different at: (%s:0x%06lX)=0x%04X, (%s:0x%06lX)=0x%04X",
            strupr(srcSpaceText), retAddr1, retData1,
            strupr(destSpaceText), retAddr2, retData2);
      SendCliMessage(cliServerHandle,outputBuff);
   }
   DestroyCopyAddrDescriptors() ;
   return (GOOD);
}

/***********************************************************
**
**  MemCompUnloadControls(HWND hDlg)
**
**
** Fetch contents of dialog box controls and convert to
** native form.
**
** Called from:   IDOK mesaage in dialog proc
** inputs:  hDlg  dialog window handle
**
** global variables changed:
**    cpySrcDesc
**    cpyDestDesc
**    cpyLength
**  if successful all are changed.
**
**  if "not successful" only SOME are changed.
**  This is a syntax error.  We simply return the error code.
**  The error is then displayed by a call to the errortext
**  server from within the dialog procedure and the "exit dialog"
**  is NOT permitted.
**
*/

RETCODE PRIVATE MemCompUnloadControls(HWND hDlg) {

   RETCODE    err;
   LPSTR      endptr;         // used by strtoul

   GetDlgItemText(hDlg,IDD_COMP_SRC_START, dlgLocalSrcStartAddr, ADDR_LENGTHS);
   GetDlgItemText(hDlg,IDD_COMP_SRC_END,   dlgLocalSrcEndAddr,   ADDR_LENGTHS);
   GetDlgItemText(hDlg,IDD_COMP_DEST_START,dlgLocalDestStartAddr,ADDR_LENGTHS);

   err = AdrConvTextToAddress(cpySrcDesc, dlgLocalSrcStartAddr );

   if (err != GOOD)
      return err;


// save as length ONLY.
   // dlgLocalSrcEndAddr contains a length field


// if mode is absolute, convert absolute to length
   if (!dlgLocalPartitionUseLength) {
      err = AdrConvTextToAddress(cpySrcEndDesc, dlgLocalSrcEndAddr );
      if (err != GOOD)
         return err;

   // calculate difference to get length
   // extract difference between two addresses
         err = AdrRangeOfAddresses( cpySrcDesc, cpySrcEndDesc, &cpyLength);
         if (err != GOOD)
            return err;
   }
   else {
//   // process length (a number ONLY, no symbols)
//   // allow format to determine base
        cpyLength = strtoul( dlgLocalSrcEndAddr ,&endptr, 0);

//      dlgLocalPartitionEndOrLength = IDD_CPY_END_USES_LENGTH;

   }


// destination
   err = AdrConvTextToAddress(cpyDestDesc,
                                  dlgLocalDestStartAddr );
   if (err != GOOD)
      return err;


   // within dialog, address space always is "SD" for CPU32 or
   // CPU16.

   err = AdrSetAddrSpace(cpyDestDesc, dlgLocalDestSpace);
   if (err != GOOD)
      return err;

//   // get the target/map (holds TRUE/FALSE)
//   cpySrcTarget  = dlgLocalSrcTarget;
//   cpyDestTarget = dlgLocalDestTarget;
   return GOOD;
}

/******************************************************
**
**   MemCompDlgProc
**
** The dialog window procedure
**
**  returns result to DialogBox callerwith EndDialog() function::
**    TRUE:   user pressed OK
**    FALSE:  user pressed cancel
**
**  After pressing OK, if parse is not successful (or
**  some other error occurs), we display the error, and
**  permit a retry.  User can exit with Cancel, though.
**
**  Note that errors are NOT propagated up to Actor level.
**
**
**  The params follow Windows 3.0 conventions, NOT those of Win3.1
**
**  Return value is standard also: a Windows BOOL, NOT the
**  Powerviews BOOLEAN!
**  returns TRUE of message was handled, FALSE if not handled.
**
*******************************************************/
#pragma argsused
BOOL EXPORT MemCompDlgProc(HWND hDlg, WORD message, WORD wParam, LONG lParam) {

   RETCODE  err = GOOD;
   ADDR_SPACE space[] = {SPACE_UP, SPACE_UD, SPACE_SP, SPACE_SD};

   switch (message)
   {
      case WM_INITDIALOG:
         dlgLocalSrcStartAddr[0] = dlgLocalSrcEndAddr[0] =
         dlgLocalDestStartAddr[0] = '\0';
         dlgLocalPartitionUseLength = FALSE;

         CheckRadioButton (hDlg, IDD_COMP_USE_END, IDD_COMP_USE_LENGTH,
                           IDD_COMP_USE_END);

//  default space is SPACE_SD
         dlgLocalDestSpace = SPACE_SD;
         CheckRadioButton(hDlg,IDD_SPACE_UP,IDD_SPACE_SD,IDD_SPACE_SD);

         RegisterHelpEntry(HI_MESSAGEBOXEX, hDlg, 2044); // entry for mem help
//         if (err != GOOD) {
//            MemDisplayError(hDlg, err);
//            return TRUE;
//         }
//         MemCpyLoadControlsFromLocal(hDlg);
         return TRUE;

      case WM_COMMAND:
         switch(wParam) {
            case IDOK:
               err = MemCompUnloadControls(hDlg);
               if (err != GOOD)  {
                  MemDisplayError(hDlg, err);   // parse error
                  return TRUE;
               }
               EndDialog(hDlg, TRUE);
               return TRUE;

            case IDHELP:
               WinHelp(hDlg, "PWRVIEWS.HLP", HELP_CONTEXT, 2044); // entry for mem help
               return FALSE;

            case IDCANCEL:
               EndDialog(hDlg, FALSE);
               return TRUE;

            case IDD_SPACE_UP :
            case IDD_SPACE_UD :
            case IDD_SPACE_SP :
            case IDD_SPACE_SD :
               CheckRadioButton(hDlg,IDD_SPACE_UP,IDD_SPACE_SD,wParam);
               dlgLocalDestSpace = space[wParam-IDD_SPACE_UP];
               return TRUE;

            case IDD_COMP_USE_END :
            case IDD_COMP_USE_LENGTH:
               dlgLocalPartitionUseLength = wParam-IDD_COMP_USE_END;
               CheckRadioButton (hDlg, IDD_COMP_USE_END,
                                 IDD_COMP_USE_LENGTH,
                                 wParam );
            return TRUE;
        }
   }
   return (FALSE);   // Didn't process a message
}

/******************************************************
**
** MemDlgCompare   compare using a dialog
**
**   Execute the dialog
**
**  called from ACTOR memory presenter menu item...
**
*********************************************************/

RETCODE EXPORT MemDlgCompMemory(HWND hWnd,ADDR_SPACE srcSpace)   {

   int      dlgRet;    // "int" is return type of DialogBox()
   RETCODE  compErr;
   HCURSOR  hSaveCursor, hHourGlass;
   U32 retAddr1, retAddr2;
   U16 retData1, retData2;
   BOOLEAN    compareResult;
   CHAR outputBuff[50]="", spaceText[7][4]= {"","UD","UP","","","SD","SP"};
   CHAR  srcSpaceText[4], destSpaceText[4];
   ADDR_SPACE destSpace;
   ACCESS_SIZE size;

   compErr = CreateCopyAddrDescriptors();
   if (compErr != GOOD)
      return compErr;
   MemGetAccessSize(&size);

   dlgRet = DialogBox(hLib,           // instance (for dlg template)
            "MEM_COMP_DLG",
            hWnd,                     // parent handle
            MemCompDlgProc);  // within DLL, no proc instance address reqd


// if user pressed OK, perform the copy
// using the native (parsed) form.
   if (dlgRet == TRUE)  {

      hHourGlass = LoadCursor(NULL, IDC_WAIT);
      hSaveCursor = SetCursor(hHourGlass);

      compErr = AdrSetAddrSpace(cpySrcDesc, srcSpace);
      if (compErr != GOOD)
         return compErr;
      compErr = MemCompare(cpySrcDesc, cpyDestDesc, cpyLength, &compareResult,
                       &retAddr1, &retAddr2, &retData1, &retData2);

      SetCursor(hSaveCursor);

      if (compErr) return compErr;
      if (compareResult)
         MessageBox(hWnd,(LPSTR)"Memory no difference.",(LPSTR)"Memory Compare",MB_OK);
      else {
         strcpy(srcSpaceText,spaceText[srcSpace]);
         if ((compErr = AdrGetAddrSpace(cpyDestDesc,&destSpace)) != GOOD)
            return compErr;
         strcpy(destSpaceText,spaceText[destSpace]);
         if (size == BYTE_SIZE)
            sprintf(outputBuff,
               "Memory different at: (%s:0x%06lX)=0x%02X, (%s:0x%06lX)=0x%02X",
               strupr(srcSpaceText), retAddr1, retData1&0x00ff,
               strupr(destSpaceText), retAddr2, retData2&0x00ff);
         else
            sprintf(outputBuff,
               "Memory different at: (%s:0x%06lX)=0x%04X, (%s:0x%06lX)=0x%04X",
               strupr(srcSpaceText), retAddr1, retData1,
               strupr(destSpaceText), retAddr2, retData2);
         MessageBox(hWnd,(LPSTR)outputBuff,(LPSTR)"Memory Compare",MB_OK);
      }

   }

   DestroyCopyAddrDescriptors() ;
   return GOOD;
}

/******************************** E O F ***********************************/
