//******************************************************************************
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//
//  PROGRAM: listdde.c
//
//  PURPOSE: DDE interface functions for ListFile DDE application
//
//  FUNCTIONS:
//
//  ListDDEInitiate       - Handle INITIATE for primary DDE conversation
//  ListDDEAck            - Handle ACK for primary DDE conversation
//  ListDDEPoke           - Handle POKE for primary DDE conversation
//  ListDDERequest        - Handle REQUEST for primary DDE conversation
//  ListDDETerminate      - Handle TERMINATE for primary DDE conversation
//
//  StartFileConversation - Initiate secondary DDE conversation
//
//  FileDDEAck            - Handle ACK for secondary DDE conversation
//  FileDDEData           - Handle DATA for secondary DDE conversation
//  FileDDETerminate      - Handle TERMINATE for secondary DDE conversation
//
//  lstrncpy              - String copy with long pointers
//  ParsePS               - Parse session presentation space ( PS )
//
//  Copyright (c) 1990, 1996 IBM Corporation. All rights reserved.
//
//******************************************************************************

//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//
//  In the LISTFILE application two DDE conversations take place. The
//  primary conversation is initiated by the PC/3270 receive dialog function
//  acting as a DDE clint. The LISTFILE application completes the conversation
//  link by acknowledging the he initiate. from the PC/3270 receive dialog.
//  After LISTFILE has received the file mask to use with the CMS LISTFILE
//  via a DDE_POKE message, from the client, and received a request for the
//  the list of files from the client via a DDE_REQUEST message it opens
//  a hidden window to conduct the secondary conversation. The hidden
//  window, acting as a client, sends a DDE_INITIATE message to the PC/3270
//  session to start the secondary conversation. Once the secondary
//  conversation is acknowledged by the PC/3270 session the secondary
//  conversation will send the session the CMS LISTFILE command, and begin
//  reading the presentation space to retrieve the cms file names returned
//  by the CMS listfile command.
//
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT */

#include <windows.h>                // required for all Windows applications
#include <string.h>                 // C string functions
#include <stdio.h>                  // C functions
#include <stdlib.h>                 // C functions

#include "dde_c.h"                  // required for PCOM DDE Appls
#include "listfile.h"               // specific to this program
#include "listdata.h"               // Global Data

//******************************************************************************
//
// Local Function Prototypes
//
//******************************************************************************

BOOL  StartFileConversation (void);             //Starts 2nd conversation
LPSTR lstrncpy              (LPSTR,LPSTR,WORD); //copy n chars
WORD  ParsePS               (LPSTR,WORD,WORD);  //parse presentation space

//******************************************************************************
//
// Local Data
//
//******************************************************************************

uchar  z3270List[17];
uchar  zGetList[17];
uchar  zFileMask[17];
uchar  zListFile[17];
uchar  zIBM3270[11];
uchar  zSESSION[9];
uchar  zKEYS[6];
uchar  zPS[4];

//******************************************************************************
//
//  FUNCTION: InitDDE()
//
//  PURPOSE:
//
//  Initialize various DDE atoms, and any other required DDE initializations.
//
//******************************************************************************

BOOL InitDDE( void )
{
  LoadString(hGlobalInst, LS_16STR_FORMAT, zFmtBuffer, sizeof(zFmtBuffer));
  LoadString(hGlobalInst, LS_DDE_TOKENS,   zMsgBuffer, sizeof(zMsgBuffer));

  sscanf(zMsgBuffer,                           // Input String
         zFmtBuffer,                           // sscanf format to use
         z3270List,                            // Server Name
         zListFile,                            // Server Topic
         zFileMask,                            // Poke Data Item  file mask
         zGetList,                             // Request Item for list of files
         zIBM3270,                             // Session Server Name
         zSESSION,                             // Session Topic
         zKEYS,                                // Session Data Items
         zPS);                                 // "
                                               //
  lstrcat(z3270List, zCmdLineSessionID );      // Append Session ID
  lstrcat(zSESSION, zCmdLineSessionID );       // Append Session ID

  return(TRUE);
}

//******************************************************************************
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//
//  FUNCTION: ListDDEInitiate(HWND , unsigned , WORD , LONG )
//
//  PURPOSE:
//
//  Accepts the DDE_INITIATE message from the PC/3270 receive dialog, and
//  acknowledges the conversation if it can be supported.
//
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//******************************************************************************

void ListDDEInitiate(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
  ATOM aApplication  = LOWORD( lParam );
  ATOM aTopic        = HIWORD( lParam );

  ATOM a3270List     = GlobalAddAtom( z3270List );  // Temp Test Atoms
  ATOM aListFile     = GlobalAddAtom( zListFile );  //

  if( aApplication == a3270List )
  {
    if( aTopic == aListFile || aTopic == 0 )
    {
      hRcvDlgClient = (HWND)wParam;               // Save away handle
      bFirstScreen  = TRUE;                       // Init first screen flag
      a3270List     = GlobalAddAtom( z3270List ); // Add Atoms
      aListFile     = GlobalAddAtom( zListFile ); //
      if(!SendMessage(hRcvDlgClient, WM_DDE_ACK, (WPARAM)hWnd, MAKELONG(aApplication, aTopic)))
      {
        GlobalDeleteAtom( aApplication ); // Error, delete client's ATOMS
        GlobalDeleteAtom( aTopic       ); // Error, delete client's ATOMS
      }
    }
  }

  GlobalDeleteAtom( a3270List );        // Delete Test Atoms
  GlobalDeleteAtom( aListFile );
}

//******************************************************************************
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//
//  FUNCTION: ListDDEPoke(HWND , unsigned , WORD , LONG)
//
//  PURPOSE:
//
//  Processes and acknowledges a DDE_POKE message containing a host file name
//  mask to be used with the CMS LISTFILE commaned sent to the VM session.
//
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//******************************************************************************

void FAR ListDDEPoke(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
  HWND      hWndClient = (HWND)wParam;               //
  HANDLE    hFileMask;                               //
  ATOM      aItem, aFileMask;                        //
  WORD      uAck       = DDE_NACK;                   // Initial ACK Value
  BOOL      bTerminate = FALSE;                      // Terminate Flag
  LPDDE_GETLIST lpGetList;                           //
  UINT          uiLo, uiHi;                          //
                                                     //
  aFileMask  = GlobalAddAtom( zFileMask );           // Test Atom
  UnpackDDElParam(WM_DDE_POKE, lParam, (PUINT)&uiLo, (PUINT)&uiHi);
  hFileMask = (HANDLE)uiLo;
  aItem     = (ATOM)uiHi;

  if( aItem == aFileMask && hFileMask != NULL)       //
  {                                                  //
    if( (lpGetList = (LPDDE_GETLIST) GlobalLock(hFileMask)) != NULL )                           //
    {                                                //
      uAck = DDE_ACK;                                // ACK
      lstrcpy(zHostFileMask, lpGetList->DDEgetlist.Data); // Save mask for REQUEST
      if(lpGetList->DDEpoke.fRelease)                // Release the memory
      {                                              //
        GlobalUnlock(hFileMask);                     //
        GlobalFree(hFileMask);                       //
      }                                              //
      else                                           //
      {                                              //
        GlobalUnlock(hFileMask);                     //
      }                                              //
    }                                                //
    else                                             //
    {                                                //
      bTerminate = TRUE;                             // Error, terminate conv
    }                                                //
  }                                                  //
  else                                               //
  {                                                  //
    bTerminate = TRUE;                               // Error, terminate conv
  }                                                  //
                                                     //
  if( !PostMessage(hWndClient, WM_DDE_ACK, (WPARAM)hWnd, PackDDElParam(WM_DDE_ACK, uAck, aItem)) )
  {                                                  //
    GlobalDeleteAtom( aItem );                       //
    bTerminate = TRUE;                               //
  }                                                  //
                                                     //
  if( bTerminate )                                   // Terminate Conversation?
  {                                                  // Yes
    Terminate( CONVERSATIONS );                      //
  }                                                  //
                                                     //
  GlobalDeleteAtom( aFileMask );                     //
  FreeDDElParam(WM_DDE_POKE, lParam);
}

//******************************************************************************
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//
//  FUNCTION: ListDDERequest(HWND , unsigned , WORD , LONG )
//
//  PURPOSE:
//
//  Processes and acknowledges a DDE_REQUEST message containing the request
//  for the file list.
//
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//******************************************************************************

void FAR ListDDERequest(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
  WORD   wFormat    = LOWORD(lParam);               //
  WORD   wStatus    = DDE_NACK;                     //
  ATOM   aItem      = HIWORD(lParam);               //
  BOOL   bTerminate = FALSE;                        // Terminate Flag
                                                    //
  ATOM   aGetList  = GlobalAddAtom( zGetList );     // Test Atom
                                                    //
  if( wFormat == CF_DSPTEXT && aItem == aGetList )  //
  {                                                 //
    if( !StartFileConversation() )                  // Start 2nd conversation
    {                                               // Error
      bTerminate = TRUE;                            // Terminate
    }                                               //
  }                                                 //
  else                                              // Request Error!
  {                                                 // NACK it and
    bTerminate = TRUE;                              // Terminate
  }                                                 //
                                                    //
  if( bTerminate )                                  // Terminate Conversation?
  {                                                 // Yes
    Terminate( CONVERSATIONS );                     //
  }                                                 //
                                                    //
  GlobalDeleteAtom( aGetList );                     // Delete Test Atom
}

//******************************************************************************
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//
//  FUNCTION: ListDDETerminate(HWND , unsigned , WORD , LONG )
//
//  PURPOSE:
//
//  Processes a DDE_TERMINATE message from the client and send the client
//  a DDE_TERMINATE message to complete the DDE termination process.
//
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//******************************************************************************

void FAR ListDDETerminate(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
  Terminate( CONVERSATIONS );            // Terminate DDE Conversations
}

//##############################################################################

//******************************************************************************
//
//  FUNCTION:  StartFileConversation(void)
//
//  PURPOSE:
//
//  This function is called from ListDDERequeest where ListFile receives the
//  request for the file list. The secondary DDE convseration is initiated
//  in this function.
//
//******************************************************************************

BOOL StartFileConversation( void )             //
{                                              //
  BOOL         bRC = FALSE;                    // Error until proven otherwise
  ATOM         aIBM3270,aSESSION,aKEYS;        //
  LPXCOMMANDS  lpData;                         //
                                               //
  uCurFileDDEMsg = 0;                          // current file dde message
                                               //
  aIBM3270 = GlobalAddAtom( zIBM3270 );        // Create Temp atoms for
  aSESSION = GlobalAddAtom( zSESSION );        // DDE traffic
  aKEYS    = GlobalAddAtom( zKEYS    );        // DDE traffic
                                               //
  uCurFileDDEMsg = WM_DDE_INITIATE;            //
                                               //
  aIBM3270 = GlobalAddAtom( zIBM3270 );        // Create atoms for
  aSESSION = GlobalAddAtom( zSESSION );        // DDE traffic
                                               //
  if( !SendMessage( (HWND)-1, uCurFileDDEMsg, (WPARAM)hWndHidden, MAKELONG(aIBM3270, aSESSION)) )
  {                                            //
    GlobalDeleteAtom( aIBM3270  );             // Error, delete atoms
    GlobalDeleteAtom( aSESSION  );             //
  }                                            //
                                               //
  if( hSessionServer == NULL )                 // did we get a DDE link up
  {                                            // No, better handle error
     bRC = FALSE;                              //
  }                                            //
  else                                         // yes,poke the LISTFILE
  {
     if( (hData = GlobalAlloc(DDE_GLOBAL,(DWORD) sizeof(XCOMMANDS))) != NULL )
     {
       if( (lpData = (LPXCOMMANDS) GlobalLock(hData)) != NULL ) //
       {
          LoadString(hGlobalInst, LS_LISTFILE_COMMAND, zFmtBuffer, sizeof(zFmtBuffer));

          wsprintf( lpData->zXCommands,      // Target Buffer
                    (LPSTR) zFmtBuffer,      // Format "LISTFILE %s ..."
                    '\"',                    // DQUOTE
                    (LPSTR) zHostFileMask,   // File Mask
                    '\"' );                  // DQUOTE
                                             //
          GlobalUnlock(hData);               // Unlock the buffer
                                             //
          uCurFileDDEMsg = WM_DDE_EXECUTE;   //
                                             //
          if( !PostMessage(hSessionServer, uCurFileDDEMsg, (WPARAM)hWndHidden, (LPARAM)hData) )
          {                                  //
            GlobalFree( hData );             // Error, Free the data
          }                                  //
          else                               //
          {                                  //
             bRC = TRUE;                     // Initial Success!
          }                                  //
       }                                     //
       else                                  // Lock failed
       {                                     //
          GlobalFree( hData );               //
       }                                     //
     }                                       //
     else                                    // Alloc Failed
     {                                       //
     }                                       //
  }                                          //
                                             //
  GlobalDeleteAtom( aIBM3270 );              // Delete Temporary Atoms
  GlobalDeleteAtom( aSESSION );              //
  GlobalDeleteAtom( aKEYS    );              //
                                             //
  return( bRC );                             // Return the RC
}

//******************************************************************************
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//
//  FUNCTION: FileDDEAck(HWND , unsigned , WORD , LONG )
//
//  PURPOSE:
//
//  Process DDE_ACK messages from the PC/3270 session, and if needed,
//  request the presentation space from the session by sending a DDE_REQUEST
//  message.
//
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//******************************************************************************

void FAR FileDDEAck(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
  ATOM   aPS;
  HGLOBAL hCommands;
  WORD    wStatus;
  UINT    uiLo, uiHi;

  if (uCurFileDDEMsg == WM_DDE_INITIATE)
  {                                              // Are we trying to Init DDE?
     hSessionServer = (HWND)wParam;              // yes, save handle.
     GlobalDeleteAtom(LOWORD(lParam));           // Delete INITIATE atoms
     GlobalDeleteAtom(HIWORD(lParam));           //
     uNumberOfFiles = 0;                         //
  }                                              //
  else if(uCurFileDDEMsg == WM_DDE_EXECUTE)      // from listfile cmd or clear
  {                                              //
     UnpackDDElParam(WM_DDE_ACK, lParam, (PUINT)&uiLo, (PUINT)&uiHi);
     wStatus   = (WORD)uiLo;
     hCommands = (HGLOBAL)uiHi;
     FreeDDElParam(WM_DDE_ACK, lParam);

     if( (wStatus        & DDE_ACK) != DDE_ACK ) // NACK ?
     {                                           //
       GlobalFree(hCommands);                    // free hCommands
       Terminate( CONVERSATIONS );               // Terminate conversation
     }                                           //
     else                                        // ACK
     {                                           //
       aPS = GlobalAddAtom((LPSTR) zPS);         // Add PS atom
       uCurFileDDEMsg = WM_DDE_REQUEST;          // Request PS
       if( !PostMessage(hSessionServer, uCurFileDDEMsg, (WPARAM)hWnd, MAKELONG(CF_TEXT, aPS)) )
       {                                         //
         GlobalDeleteAtom( aPS );                // Error, Delete Atom
         Terminate( CONVERSATIONS );             // Terminate conversation
       }                                         //
     }                                           //
  }                                              //
  else if(uCurFileDDEMsg == WM_DDE_REQUEST)      //
  {                                              // if we get this it is a NACK
    Terminate( CONVERSATIONS );                  // Terminate conversation
  }                                              //
}                                                //

//******************************************************************************
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//
//  FUNCTION: FileDDEData(HWND , unsigned , WORD , LONG )
//
//  PURPOSE:
//
//  This function processes DDE_DATA messages from the session and parses
//  the file names from the presentation space buffer returned with the
//  message.
//
//
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//******************************************************************************

void FAR FileDDEData(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
  HANDLE        hPS;                             // Handle to PS
  ATOM          aItem;                           // Atom for PS

  ATOM          aPS, aGetList;
  unsigned char zRows[5], zCols[5];
  WORD          uRows, uCols, uBytesInFileList;
  lpDDE_PS_CF_TEXT  lpPS;
  LPXCOMMANDS       lpData;
  LPDDE_LISTFILES   lpList;
  UINT              uiLo, uiHi;

  UnpackDDElParam(WM_DDE_DATA, lParam, (PUINT)&uiLo, (PUINT)&uiHi);
  hPS       = (HANDLE)uiLo;
  aItem     = (ATOM)uiHi;

  if( hPS != NULL )
  {
    if((lpPS = (lpDDE_PS_CF_TEXT) GlobalLock(hPS)) != NULL)
    {
      lstrncpy(zRows,lpPS->DDEps.PSROWS,4);
      lstrncpy(zCols,lpPS->DDEps.PSCOLS,4);
      zRows[4] = '\0';
      zCols[4] = '\0';
      uRows = atoi(zRows);
      uCols = atoi(zCols);
      switch( ParsePS(lpPS->DDEps.PS, uRows, uCols) )
      {
        case PSDATA_MORE:
          if( (hData = GlobalAlloc(DDE_GLOBAL, (DWORD)sizeof(XCOMMANDS))) != NULL )
          {
            if( (lpData = (LPXCOMMANDS) GlobalLock(hData)) != NULL )
            {
              LoadString(hGlobalInst,                   // Instance
                         LS_CLEARSCREEN_COMMAND,        // String ID
                         lpData->zXCommands,            // Target Buffer
                         sizeof(lpData->zXCommands));   // Size of target
                                                        //
              GlobalUnlock(hData);                      // Unlock the buffer
                                                        //
              uCurFileDDEMsg = WM_DDE_EXECUTE;          //
                                                        //
              if( !PostMessage(hSessionServer, uCurFileDDEMsg, (WPARAM)hWnd, (LPARAM)hData) )
              {                                         //
                GlobalFree( hData );                    // Error, Free the data
              }                                         //
            }                                           //
            else                                        // Lock failure
            {                                           //
              GlobalFree(hData);                        //
            }                                           //
          }                                             //
          else                                          // Alloc failure
          {                                             //
          }                                             //
          break;                                        //

        case PSDATA_REREAD:                      // Do not clear, reread PS
          aPS = GlobalAddAtom((LPSTR) zPS);      // Add PS atom
          uCurFileDDEMsg = WM_DDE_REQUEST;       // Request ps
          if( !PostMessage(hSessionServer, uCurFileDDEMsg, (WPARAM)hWnd, MAKELONG(CF_TEXT, aPS)) )
          {                                      // Error,
            GlobalDeleteAtom( aPS );             // Delete PS atom
          }                                      //
          break;                                 //
                                                 //
        case PSDATA_END:                         // Post DATA to Receive
          uBytesInFileList = uNumberOfFiles * SIZEOFFILENAME;
          if( (hData = GlobalAlloc(DDE_GLOBAL, (DWORD)sizeof(DDE_LISTFILES)+uBytesInFileList+2)) != NULL )
          {
            if( (lpList = (LPDDE_LISTFILES) GlobalLock(hData)) != NULL )
            {
               lpList->DDEdata.fRelease       = TRUE;
               lpList->DDEdata.cfFormat       = CF_DSPTEXT;
               lpList->DDElistfiles.iItemsInList   = uNumberOfFiles;
               lpList->DDElistfiles.uFilenameWidth = SIZEOFFILENAME;

               memcpy(lpList->DDElistfiles.Data, zListFileNames, uBytesInFileList);

               lpList->DDElistfiles.Data[uBytesInFileList]   = NULL_CHAR;
               lpList->DDElistfiles.Data[uBytesInFileList+1] = NULL_CHAR;

               GlobalUnlock(hData);

               aGetList = GlobalAddAtom((LPSTR) zGetList); //DDE atom

               if( !PostMessage(hRcvDlgClient, WM_DDE_DATA, (WPARAM)hGlobalWnd,
                                PackDDElParam(WM_DDE_DATA, (UINT)hData, aGetList)));
               {
                 GlobalDeleteAtom( aGetList ); // Error, delete atom
               }

               // ** Do the terminate server logic

               PostMessage(hSessionServer, WM_DDE_TERMINATE, (WPARAM)hWnd, 0L);
               hSessionServer = NULL;                       // reset handle
            }                                           //
            else                                        // BAD LOCK
            {                                           //
               GlobalFree(hData);                       //
               Terminate( CONVERSATIONS );              // Terminate Conver
            }                                           //
          }                                             //
          else                                          // BAD ALLOC
          {                                             //
            Terminate( CONVERSATIONS );                 // Terminate Conver
          }                                             //
          break;                                        //
                                                        //
        default:                                        // PSDATA_ERROR
          //error case                                  //
          //do the terminate logic                      //
          break;                                        //
      }                                                 //
                                                        //
      if(lpPS->DDEdata.fAckReq)                         // ACK required?
      {                                                 //
        if( !PostMessage(hSessionServer, WM_DDE_ACK, (WPARAM)hWnd, PackDDElParam(WM_DDE_ACK, DDE_ACK, aItem)))
        {                                               //
          GlobalDeleteAtom(aItem);                      //
        }                                               //
      }                                                 //
                                                        //
      if(lpPS->DDEdata.fRelease)                        // Release required?
      {                                                 //
        GlobalUnlock(hPS);                              //
        GlobalFree(hPS);                                //
      }                                                 //
      else                                              //
      {                                                 //
        GlobalUnlock(hPS);                              //
      }                                                 //
    }                                                   //
    else                                                // BAD LOCK
    {                                                   //
      GlobalFree(hPS);                                  // Free the handle
      Terminate( CONVERSATIONS );                       // Terminate Conver
    }                                                   //
  }                                                     //
  else                                                  // Bad Handle
  {                                                     //
  }
  FreeDDElParam(WM_DDE_DATA, lParam);
}

//******************************************************************************
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//
//  FUNCTION: FileDDETerminate(HWND , unsigned , WORD , LONG )
//
//  PURPOSE:
//
//  Processes a DDE_TERMINATE message from the session and sends the session
//  a DDE_TERMINATE message to complete the DDE termination process.
//
//**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT**BOX-COMMENT
//******************************************************************************

void FAR FileDDETerminate(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
  Terminate( CONVERSATION_SESSION );              // Terminate Conversations
}

//******************************************************************************
//
//  FUNCTION: lstrncpy( LPSTR , LPSTR , WORD )
//
//  PURPOSE:
//
//  Long pointer strncpy function.
//
//
//******************************************************************************

LPSTR lstrncpy( LPSTR lpzString1, LPSTR lpzString2, WORD uLength )
{
  int i;
  LPSTR p = lpzString1;
  uchar ch;

  for( i = 0; i < uLength; i++)
  {
    ch = *(lpzString2++);
    *(lpzString1++) = ch;
    if( ch == NULL_CHAR )
    {
      return( p );                  /* String was less than uLength in Length */
    }
  }

  *(lpzString1) = NULL_CHAR;        /* copied up to uLength, add Null on End */

  return( p );
}

//******************************************************************************
//
//  FUNCTION: ParsePS(LPSTR , WORD , WORD )
//
//  PURPOSE:
//
//  COMMENTS:
//
//  Each line of a CF_TEXT PS ends in 0x0D 0x0A so the lines are
//  actually uCols+2 characters long.
//
//  RETURNS:
//
//    PSDATA_ERROR  0
//    PSDATA_END    1
//    PSDATA_MORE   2
//    PSDATA_REREAD 3
//
//******************************************************************************

WORD ParsePS(LPSTR lpPS, WORD uRows, WORD uCols)
{                                                      //
  WORD uRetCode = PSDATA_MORE;                         //
  WORD uOffset  = 0;                                   //
  WORD uLine    = 0;                                   //
  WORD uLineStart;                                     //
  BOOL bReady   = FALSE;                               //
                                                       //
  if( bFirstScreen )                                   // First Screen
  {                                                    // Yes, IGNORE first line
    uLineStart = 1;                                    //
    bFirstScreen = FALSE;                              // reset flag
  }                                                    //
  else                                                 // Otherwise
  {                                                    //
    uLineStart = 0;                                    // Start with first
  }                                                    // line
                                                       //
  for(uLine = uLineStart; uLine < uRows; uLine++)      // check each line of the PS
  {                                                    // to see if it contains a
    uOffset = uLine * (uCols + 2);                     // filename or Ready
                                                       //
    lstrncpy(zMsgBuffer,lpPS+uOffset,uCols);           //
    zMsgBuffer[uCols] = '\0';                          // copy & make into str
                                                       //
    if( (*(lpPS+uOffset+8)  == SPACE_CHAR) &&          // If there are spaces
        (*(lpPS+uOffset+9)  != SPACE_CHAR) &&          // at cols 8, 17, & 20
        (*(lpPS+uOffset+17) == SPACE_CHAR) &&          // non spaces at cols 0,
        (*(lpPS+uOffset+18) != SPACE_CHAR) &&          // 9, & 18 then the line
        (uNumberOfFiles < MAXFILENAMES) )              // is a file name.
    {                                                  //
      lstrncpy(&zListFileNames[uNumberOfFiles][0],     // add found name to
              lpPS+uOffset,SIZEOFFILENAME);            // names array
      zListFileNames[uNumberOfFiles][SIZEOFFILENAME-1] //
        = NULL_CHAR;                                   //
      uNumberOfFiles++;                                //
    }                                                  //
                                                       //
    strupr(zMsgBuffer);                                // Upcase it ...
                                                       //
    if(strstr(zMsgBuffer,zReady) != NULL)              // Check if CMS READY is
    {                                                  // present indicating end
      bReady = TRUE;                                   // of data.
     }                                                 //
  }

  //--------------------------------------------------------
  //check lower right status message and respond accordingly

  lstrncpy(zMsgBuffer,lpPS+((uRows-1) * (uCols + 2)),uCols);
                                                   //
  zMsgBuffer[uCols] = NULL_CHAR;                   // Copy line & make a string
                                                   //
  strupr(zMsgBuffer);                              // Upcase it ...
                                                   //
  if(strstr(zMsgBuffer, zMore) != NULL)            //
  {                                                // here we should send a
     uRetCode = PSDATA_MORE;                       // clear and read the next
  }                                                // screen of names
  else if(strstr(zMsgBuffer,zRunning) != NULL)     //
  {                                                //
    if(bReady)                                     // running + ready tells us
    {                                              // we are done
      uRetCode = PSDATA_END;                       //
    }                                              //
    else                                           // this case means cms is
    {                                              // not done writing file
      uRetCode = PSDATA_REREAD;                    // names to the screen,
    }                                              // so we should just
  }                                                // reread the screen
  else                                             //
  {                                                //
    uRetCode = PSDATA_ERROR;                       // shouldn't happen
  }                                                //
                                                   //
   return(uRetCode);                               // return status
}

//******************************************************************************
//
//  FUNCTION: Terminate( WORD uTerminateType )
//
//  PURPOSE:
//
//  When an error is encountered in processing the listfile request, this
//  function will be called to terminate the dde conversations, or the
//  entire application.
//
//  COMMENTS:
//
//  1. Values that can be passed to this function:
//
//     APPLICATION   - Terminates conversations, and application
//
//     CONVERSATIONS - Terminates conversations, but leaves the
//                     application enabled for other requests.
//
//  RETURNS: void
//
//******************************************************************************

void Terminate( WORD uTerminateType )
{
  switch( uTerminateType )
  {
    case APPLICATION:
    default:
      DestroyWindow(hWndHidden);  // Destroy hidden window
      DestroyWindow(hGlobalWnd);  // Destroy visible window

      //*** Drops through

    case CONVERSATIONS:
      if( hRcvDlgClient != NULL )
      {
        PostMessage(hRcvDlgClient, WM_DDE_TERMINATE, (WPARAM)hGlobalWnd, 0L );
        hRcvDlgClient = NULL;
      }
      if( hSessionServer != NULL )
      {
        PostMessage(hSessionServer, WM_DDE_TERMINATE, (WPARAM)hWndHidden, 0L );
        hSessionServer = NULL;
      }
      break;

    case CONVERSATION_SESSION:
      if( hSessionServer != NULL )
      {
        PostMessage(hSessionServer, WM_DDE_TERMINATE, (WPARAM)hWndHidden, 0L );
        hSessionServer = NULL;
      }
      break;
  }
}
