/* static char *SCCSID = "@(#)pmrexxio.c        6.5 92/02/28";			    */
/*********************	START OF SPECIFICATIONS	 ******************************/
/*									      */
/*   SOURCE FILE NAME:	pmrexxio.c					      */
/*									      */
/*   DESCRIPTIVE NAME:	PM I/O environment for REXX command files.	      */
/*									      */
/*	Provide a means for REXX command files to do Pull, Say and	      */
/*	Trace interactions when called from a PM application by simulating    */
/*	stdin, stdout and stderr I/O operations.			      */
/*									      */
/*   COPYRIGHT:		IBM Corporation 1991				      */
/*									      */
/*   STATUS:		Version 2.00					      */
/*									      */
/*   FUNCTION:								      */
/*	     This program provides a Presentation Manager Shell for	      */
/*	RexxStart programs.						      */
/*									      */
/*									      */
/*   NOTES:								      */
/*	DEPENDENCIES:							      */
/*	    This function has dependencies on the following		      */
/*	files for compilation.						      */
/*	    pmrexxio.h - Definitions for control of the PMREXXIO	      */
/*			 dialog.					      */
/*	    pmrxiodt.h - Definitions necessary for the resource		      */
/*			 file compilation.				      */
/*	    rhdtatyp.h - Macros, structures, typedefs and defines	      */
/*			 local to and necessary for this program.	      */
/*	    The C runtime multi-threaded header files			      */
/*									      */
/*  EXECUTION INSTRUCTIONS:						      */
/*	    PMREXXIO is invoked via a call to RexxCreateIOWindow.	      */
/*	This call redirects stdin, stdout and stderr to pips which	      */
/*	are processed by PMREXXIO.  The calling application enables	      */
/*	execution of a Rexx program by posting message REXX_STARTPROC,	      */
/*	passing the id of the thread the Rexx program will execute on.	      */
/*	On completion of the Rexx program, the message REXX_ENDPROC	      */
/*	should be sent to the window.  RexxDestroyIOWindow should be	      */
/*	called to perform cleanup of the I/O resources at program	      */
/*	termination.							      */
/*									      */
/*  EXPECTED OUTPUT:							      */
/*	    The REXX procedure file will be executed with all		      */
/*	output directed into PM windows.				      */
/*									      */
/*  PROCEDURES:								      */
/*    RexxCreateIOWindow:  Initialize I/O subsystem			      */
/*    RexxDestroyIOWindow:  Terminate I/O subsystem			      */
/*    EntryDlgProc:   Dialog procedure for the input entry field	      */
/*    initialize:     Initialize data structures and threads		      */
/*    io_routine:     Process the REXX IO system exit			      */
/*    MainWndProc:    Main window procedure				      */
/*    SizeWindow:     Size the output window and input window		      */
/*    ClipBoard:      Process MLE clipboard functions			      */
/*    RXIOCmds:	      Process WM_COMMAND messages from the main window proc   */
/*    Import:	      Add information from import buffer to the MLE	      */
/*    SelectFont:     Interface to the font selection dialog		      */
/*    stdinpipe:      Send input to the standard input of a child process     */
/*    stdoutpipe:     Send standard output from child proc to output window   */
/*									      */
/***********************  END OF SPECIFICATIONS	 ******************************/
#include <malloc.h>
#include <process.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Define the sections of the OS/2 header files that we need.		      */

#define	 INCL_RXSYSEXIT		       /* REXX system exits		      */
#define	 INCL_RXARI		       /* REXX Asynchronous Request Interface */
#define	 INCL_DOSSEMAPHORES	       /* OS/2 semaphore support	      */
#define	 INCL_DOSERRORS		       /* OS/2 errors			      */
#define	 INCL_DOSPROCESS	       /* Process and thread support	      */
#define	 INCL_DOSQUEUES		       /* Queues			      */
#define	 INCL_DOSMISC		       /* Miscellaneous doscalls	      */
#define	 INCL_DOSNLS		       /* NLS (code page) support	      */
#define	 INCL_DOSMODULEMGR	       /* OS/2 module support		      */
#define	 INCL_GPILCIDS		       /* Physical and logical fonts with     */
				       /* lcids				      */
#define	 INCL_GPIPRIMITIVES	       /* Drawing primitives and primitive    */
				       /* attributes			      */
#define	 INCL_WINCOUNTRY	       /* Code page support		      */
#define	 INCL_WINDIALOGS	       /* Dialog boxes			      */
#define	 INCL_WINENTRYFIELDS	       /* Entry fields			      */
#define	 INCL_WINFRAMEMGR	       /* Frame manager			      */
#define	 INCL_WINHELP		       /* Help manager definitions	      */
#define	 INCL_WININPUT		       /* Mouse and keyboard input	      */
#define	 INCL_WINMESSAGEMGR	       /* Message management		      */
#define	 INCL_WINSHELLDATA	       /* Profile calls			      */
#define	 INCL_WINSWITCHLIST	       /* Task list calls		      */
#define	 INCL_WINTIMER		       /* Timer routines		      */
#define	 INCL_WINWINDOWMGR	       /* General window management	      */
#define	 INCL_WINMENUS		       /* Menu controls			      */
#define	 INCL_WINMLE		       /* Multiple line edit fields	      */
#define	 INCL_WINPOINTERS	       /* Mouse pointers		      */
#define	 INCL_WINSTDFONT	       /* Standard Font dialog		      */
#define	 INCL_WINERRORS		       /* Standard Font dialog		      */
#include <os2.h>
#include <rexxsaa.h>		       /* Include the REXX header file	      */

/* Defines, typedefs and function prototypes for pmrexx			      */

#include "rhdtatyp.h"
#include "pmrxiodt.h"
#include "pmrexxio.h"

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: RexxCreateIOWindow				      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Initialize a Rexx stdin/stdout/stderr transaction subsystem. */
/*									      */
/*   FUNCTION:								      */
/*		 Creates Rexx I/O windows and redirects stdin, stdout, and    */
/*		 stderr to pipes.					      */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:  RexxCreateIOWindow(HWND hwndOwner, PHWND phwndClient)	      */
/*									      */
/*   INPUT:								      */
/*	 phwndClient is the handle of the I/O window client window.	      */
/*									      */
/*   EXIT-NORMAL:							      */
/*	 Exit with handle to I/O frame window.				      */
/*									      */
/*   EXIT-ERROR:							      */
/*	 If unable to duplicate the write handle for stderr pipe, exit rc=5.  */
/*	 If unable to register the window class, exit rc=1.		      */
/*	 If no procedure name was specified, exit with rc=3.		      */
/*	 If window was not displayed, exit with return code from the REXX     */
/*	   procedure.							      */
/*	 Otherwise: exit with created frame and client handles.		      */
/*									      */
/*   EFFECTS:								      */
/*	 Initializes the Rexx Presentation Manager I/O subsystem.	      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 getstring	- To obtain the title bar name and window name.	      */
/*	 initialize	- Sets up the environment for the program. Starts all */
/*			  necessary threads, sets up the input queues, etc.   */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 WinQueryAnchorBlock	     WinRegisterClass			      */
/*	 WinCreateStdWindow						      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

HWND EXPENTRY RexxCreateIOWindow(
   HWND hwndOwner,		       /* Owner handle			      */
   PHWND phwndClient)		       /* Returned client window	      */
 {

   /* We set the creation flags to be the standard window frame flags	      */
   /*	 but without scroll bars and without a task list entry.		      */

  ULONG flCreate = FCF_STANDARD&~(FCF_VERTSCROLL|FCF_HORZSCROLL|FCF_ACCELTABLE);
  PUCHAR windowtext;		       /* Text for title bar		      */
  ULONG	 argnum = 1;		       /* Counter for looking at arguments    */
  HAB	 hab;			       /* anchor block			      */
  HMODULE handle;		       /* module handle			      */
  HWND	  hwndFrame;		       /* frame handle			      */

				       /* get desktop anchor		      */
  hab = WinQueryAnchorBlock(hwndOwner);
  if (!hab) {			       /* no anchor block?		      */
    return NULLHANDLE;		       /* return with nothing		      */
  }

  if (DosQueryModuleHandle(MODULE_NAME, &handle)) {
    return NULLHANDLE;		       /* return with nothing		      */
  }

  if (!WinRegisterClass(	       /* Register Window Class		      */
      hab,			       /* Anchor block handle		      */
      PMREXXCLASSNAME,		       /* Window Class name		      */
      (PFNWP)MainWndProc,	       /* Address of Window Procedure	      */
      CS_SIZEREDRAW,		       /* Class Style			      */
      sizeof(PUCHAR))) {	       /* Extras words for a far ptr.	      */
    return  NULLHANDLE;		       /* Exit if we couldn't register the    */
  }				       /* class				      */

   /* Get the title bar text						      */

  windowtext = getstring(hab, handle, WINDOW_TEXT);

   /* Create the standard window					      */

  hwndFrame = WinCreateStdWindow(hwndOwner,/* Supplied owner		      */
      0,			       /* Frame Style			      */
      &flCreate,		       /* Control Data			      */
      PMREXXCLASSNAME,		       /* Window Class name		      */
      NULL,			       /* Window Text			      */
      WS_VISIBLE,		       /* Client style			      */
      handle,			       /* Module handle==this module	      */
      RXHOST_WIN,		       /* Window ID			      */
      phwndClient);		       /* Client Window handle		      */
				       /* set the window text		      */
  WinSetWindowText(WinWindowFromID(hwndFrame, (USHORT)FID_TITLEBAR),
      windowtext);
  free(windowtext);		       /* release window text		      */
  return hwndFrame;		       /* return created frame handle	      */
 }

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: RexxDestroyIOWindow				      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Terminate a Rexx stdin/stdout/stderr transaction subsystem.  */
/*									      */
/*   FUNCTION:								      */
/*		 Cleans up the Rexx I/O subsystem.			      */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:  RexxDestroyIOWindow(HWND hwndClient)			      */
/*									      */
/*   INPUT:								      */
/*	 phwndClient is the handle of the I/O window client window.	      */
/*									      */
/*   EXIT-NORMAL:							      */
/*									      */
/*   EXIT-ERROR:							      */
/*									      */
/*   EFFECTS:								      */
/*	 Terminates the Rexx Presntation Manager I/O subsystem.		      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 WinQueryWindowPtr	     WinDestroyHelpInstance		      */
/*	 WinAssociateHelpInstance    WinDestroyWindow			      */
/*	 DosSuspendThread	     RexxDeregisterExit			      */
/*	 DosFreeMem							      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

void EXPENTRY RexxDestroyIOWindow(HWND hwnd)
{
  PRHWINDATA pWinData = NULL;	       /* pointer to the windata structure    */

  pWinData = WinQueryWindowPtr(WinWindowFromID(hwnd, (USHORT)FID_CLIENT),
      (SHORT)0);
   /* And now, clean everything up.				       */
   if (pWinData->HelpInst) {
      WinDestroyHelpInstance(pWinData->HelpInst);
      WinAssociateHelpInstance(NULLHANDLE, pWinData->frame);
   }
				       /* cleanup semaphores		      */
   DosCloseMutexSem(pWinData->pipe_in.q_sem);
   DosCloseMutexSem(pWinData->trace_in.q_sem);
   DosCloseEventSem(pWinData->pipe_in.q_data);
   DosCloseEventSem(pWinData->trace_in.q_data);

   RexxDeregisterExit(PMRXIO_EXIT, NULL);
   WinDestroyWindow(pWinData->frame);
   DosFreeMem(pWinData);	       /* release storage		      */
}

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: initialize					      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Initialize the data structures and threads necessary for the */
/*		 program to operate.					      */
/*									      */
/*   FUNCTION:								      */
/*		 Initializes the input queues and their semaphores, set up    */
/*		 the file handles for redirection of standard input/output and*/
/*		 error streams, allocate stacks for and begin the threads.     /
/*		 subcommand environments.				      */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:  initialize						      */
/*									      */
/*   INPUT:	  none							      */
/*									      */
/*   EXIT-NORMAL: returns 0						      */
/*									      */
/*   EXIT-ERROR:  returns error code from call causing the error.	      */
/*									      */
/*   EFFECTS:								      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 makepipe	- Creates a pipe with the read and write handles at   */
/*			  desired positions.				      */
/*	 setinherit	- Sets the inheritance characteristcs of the passed   */
/*			  file handle.					      */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 DosDupHandle							      */
/*	 DosCreateMutexSem						      */
/*	 DosCreateEventSem						      */
/*	 DosGetInfoBlocks						      */
/*	 RexxRegisterExitExe						      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

  LONG initialize(PRHWINDATA pWinData)
  {
    LONG	 rc;		       /* return code			      */
    PTIB	 TI;		       /* Thread info block pointer	      */
    PPIB	 PI;		       /* Process info block pointer	      */
    PRHWINDATA	 userdata[2];	       /* registered user data		      */

   /* Initialization Section:						      */
   /* First initialize the semaphores for the input queue.		      */
   /* Using a queue and a separate thread ensures that we don't               */
   /*	tie up the window procedure if the pipe backs up.		      */

    DosCreateMutexSem(NULL, &pWinData->pipe_in.q_sem, 0, FALSE);
    DosCreateMutexSem(NULL, &pWinData->trace_in.q_sem, 0, FALSE);
    DosCreateEventSem(NULL, &pWinData->pipe_in.q_data, FALSE, FALSE);
    DosCreateEventSem(NULL, &pWinData->trace_in.q_data, FALSE, FALSE);

   /* Next, set up the redirection pipes to capture standard		      */
   /*  I/O from subcommand processing.					      */
   /* First make sure all C handles are closed.				      */

    DosClose(STDIN);		       /* close stdin, stdout, stderr	      */
    DosClose(STDOUT);
    DosClose(STDERR);

   /* Since we closed all open file handles, we are free to use any *
    * handles we want.							      */
   /* First, standard input						      */

    makepipe(&pWinData->stdin_r, STDIN, &pWinData->stdin_w, 4, PIPE_SIZE);
    setinherit(pWinData->stdin_r, TRUE);
    setinherit(pWinData->stdin_w, FALSE);

   /* Next, standard output						      */

    makepipe(&pWinData->stdout_r, 5, &pWinData->stdout_w, STDOUT, PIPE_SIZE);
    setinherit(pWinData->stdout_w, TRUE);
    setinherit(pWinData->stdout_r, FALSE);

   /* And, finally, standard error					      */
   /* Just dup the standard output and handle it once.			      */

    pWinData->stderr_w = STDERR;

    if (rc = DosDupHandle(pWinData->stdout_w, &pWinData->stderr_w)) {
      return  rc;
    }

    DosGetInfoBlocks(&TI, &PI);	       /* Get the thread information	      */
    pWinData->proc_id = PI->pib_ulpid; /* set the process id		      */

    userdata[0] = pWinData;	       /* save our anchor		      */
				       /* register I/O handler		      */
    RexxRegisterExitExe(PMRXIO_EXIT, (PFN)io_routine, (PUCHAR)userdata);

   /* And, of course, start the various threads!			      */

    rc = DosCreateThread(&pWinData->in_tid, (PFNTHREAD)stdinpipe, (ULONG)pWinData,
	(ULONG)0, STACK_SIZE);
    if (!rc) rc = DosCreateThread(&pWinData->in_tid, (PFNTHREAD)stdoutpipe, (ULONG)pWinData,
	(ULONG)0, STACK_SIZE);
    return rc;
  }

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: MainWndProc					      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Main window procedure for the PMREXX program.		      */
/*									      */
/*   FUNCTION:								      */
/*		 This procedure handles all interesting messages for the      */
/*		 client area for the PMREXX program. The messages handled     */
/*		 are:							      */
/*	 WM_CREATE	- Allocates and initializes the RHWINDATA structure   */
/*			  (defined in rhdtatyp.h) and places a pointer to it  */
/*			  into the window words.			      */
/*	 WM_TIMER	- A 100 millisecond timer is set when output is added */
/*			  to a buffer for later adding to the MLE output      */
/*			  window. Thus if many lines are being added, output  */
/*			  time is improved, and window flashing is reduced.   */
/*			  If the timer pops, then no output has been	      */
/*			  added for the 100 milliseconds and it is time to    */
/*			  add the output to the MLE.			      */
/*	 WM_ERASEBACKGROUND - Just return TRUE to force a background erase.   */
/*	 WM_SIZE	- Calls routine SizeWindow to re-adjust the sizes     */
/*			  of the MLE and input areas.			      */
/*	 WM_PAINT	- Paints the header for the input window, and lets    */
/*			  the child controls do the rest.		      */
/*	 WM_COMMAND	- All WM_COMMAND messages are processed in routine    */
/*			  RXIOCmds.					      */
/*	 WM_CHAR	- Handles tab and backtab keys to switch the focus    */
/*			  window and the break key as a halt procedure.	      */
/*	 REXX_MAKE_VISIBLE - Is a user defined message requesting that	      */
/*			  the window be made visible. The window is initially */
/*			  created invisible and is not made visible until     */
/*			  something is output, or until some input is	      */
/*			  requested. This way, a REXX procedure that does not */
/*			  need this window, will not clutter up the screen    */
/*			  with it (e.g. a DM exec that uses DM for all I/O).  */
/*	 REXX_MAKE_INVISIBLE - Is a user defined message requesting that      */
/*			  the window be made invisible.			      */
/*	 REXX_ENDPROC	- This signals the end of a Rexx procedure.  The      */
/*			  I/O subsystem is flushed and reinitialized.	      */
/*	 REXX_STARTPROC - This signals the start of a Rexx procedure.  This   */
/*			  identifies the thread running the procedure.	      */
/*	 RXIOB_MSGBOX	- Puts up a message box with the requested message.   */
/*	 START_TIMER	- Starts a timer. When the timer pops, the buffered   */
/*			  output is added to the MLE. See also, WM_TIMER and  */
/*			  ADD_STDOUTWIN. This message is posted by the thread */
/*			  reading the input pipe.			      */
/*	 ADD_STDOUTWIN	- This user defined message is a request to add	      */
/*			  output to the output window. To reduce window	      */
/*			  flicker and improve performance, data is internally */
/*			  buffered until the buffer is full, or until there   */
/*			  is a lull in output, before being sent on to the    */
/*			  MLE window. See also the WM_TIMER message.	      */
/*	 WM_SAVEAPPLICATION - Save the current font and window information    */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:							      */
/*		 Standard window procedure linkage is followed.		      */
/*									      */
/*   INPUT:								      */
/*		 Standard window procedure inputs are passed in.	      */
/*									      */
/*   EXIT-NORMAL:							      */
/*									      */
/*   EXIT-ERROR:							      */
/*									      */
/*   EFFECTS:								      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 getfixedfont	- Get a fixed font for the default display	      */
/*	 getstring	- Retrieve a string resource			      */
/*	 RestoreWindow	- Gets saved window position from user profile	      */
/*	 RHWinErrorBox	- Display an error message box			      */
/*	 SizeWindow	- Sizes all the child windows (MLE & Entry field) to  */
/*			  fit the frame window.				      */
/*	 RXIOCmds	- This routine processes the various WM_COMMAND	      */
/*			  messages.					      */
/*	 Import		- Sends the data from the internal buffer to the MLE. */
/*			  See the WM_TIMER and ADD_STDOUTWIN messages.	      */
/*	 SaveWindow	- Saves current window position to user profile	      */
/*	 SetCheckMark	- Check/uncheck a menu item			      */
/*	 ProcessSaveQuit - Interface to the Save/Quit dialog		      */
/*									      */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 DosAllocMem			   WinLoadString		      */
/*	 DosCreateMutexSem		   WinMessageBox		      */
/*	 DosFreeMem			   WinPostMsg			      */
/*	 DosReleaseMutexSem		   WinQueryAnchorBlock		      */
/*	 DosRequestMutexSem		   WinQueryWindow		      */
/*	 DosSuspendThread		   WinQueryWindowPos		      */
/*	 GpiCharStringAt		   WinQueryWindowPtr		      */
/*	 GpiQueryCurrentPosition	   WinReleasePS			      */
/*	 RexxDeregisterExit		   WinSendMsg			      */
/*	 WinAlarm			   WinSetFocus			      */
/*	 WinAssociateHelpInstance	   WinSetWindowPtr		      */
/*	 WinCreateHelpInstance		   WinShowWindow		      */
/*	 WinDefWindowProc		   WinStartTimer		      */
/*	 WinDestroyHelpInstance		   WinStopTimer			      */
/*	 WinGetPS			   WinWindowFromID		      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

  MRESULT EXPENTRY MainWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
  {
    ULONG      rc;		       /* return code			      */
    PRHWINDATA pWinData = NULL;	       /* pointer to the windata structure    */
    HPS	       hps;		       /* presentation space handle	      */
    PIMPORT    im_ptr;		       /* Pointer to buffer for data waiting  */
    ULONG      postcount;	       /* number of posted lines	      */
    FONTMETRICS fm;		       /* font size information		      */
    HWND       hEntry;		       /* entry dialog handle		      */
				       /* Creation flags for the MLE requests */
				       /* scroll bars.			      */
    ULONG flCreate = MLS_HSCROLL|MLS_VSCROLL|MLS_BORDER;

    pWinData = WinQueryWindowPtr(hwnd, (SHORT)0);

    switch (msg) {

      case  WM_CREATE :

	 /* When the window is created, we also create a structure to hold    */
	 /*   all the window specific data.				      */

	if (pWinData = malloc(sizeof(*pWinData))) {
	  memset(pWinData, '\0', sizeof(*pWinData));

	  pWinData->hab = WinQueryAnchorBlock(hwnd);
				       /* and client handle		      */
	  pWinData->client = hwnd;
				       /* save frame handle		      */
	  pWinData->frame = WinQueryWindow(hwnd, QW_PARENT);
	  if (DosQueryModuleHandle(MODULE_NAME, &pWinData->hResource)) {
	    return NULL;	       /* return with nothing		      */
	  }
	  pWinData->imp_ptr = NULL;    /* This is the timer to be used for    */
				       /* import time-out.		      */

	  pWinData->timer = (USHORT)WinStartTimer(pWinData->hab, (HWND)0,
						 (USHORT)0, (ULONG)0);
				       /* get code page information	      */
	  DosQueryCtryInfo(sizeof(COUNTRYINFO), &pWinData->CountryCode,
	      &pWinData->CountryInfo, &postcount);
				       /* Attempt to find a fixed font for the*/
				       /* display, if we are not successful,  */
				       /* we will use the system font.	      */
	  getfixedfont(hwnd, &pWinData->MleFontAttrs.fAttrs);
				       /* system font attributes	      */
	  pWinData->infocus = FALSE;
				       /* get the input text		      */
				       /* prompt from the resource file	      */
	  pWinData->pszInputText = getstring(pWinData->hab,
	  pWinData->hResource, INPUT_TITLE);

	  WinSetWindowPtr(hwnd, (SHORT)0, pWinData);
				       /* Now allocate a segment for the      */
				       /* import buffer for the MLE	      */

	  pWinData->imp_ptr = (PIMPORT)(pWinData->ImportBuffer);
				       /* initialize the buffer		      */
	  im_ptr = pWinData->imp_ptr;
	  im_ptr->total_size = ALLOCSIZE;
	  im_ptr->inuse = HDRSIZE;
	  *(PUCHAR)(im_ptr->data) = '\0';
				       /* create the semaphore		      */
	  DosCreateMutexSem(NULL, &im_ptr->sem, 0, FALSE);

   /* Set up the threads and initialize the data structures		      */

	  rc = initialize(pWinData);

   /* If initialization failed, alert the user and exit			      */

	  if (rc) {
	    WinAlarm(HWND_DESKTOP, WA_ERROR);
	    WinMessageBox(HWND_DESKTOP, NULLHANDLE,
		getstring(pWinData->hab, pWinData->hResource, INIT_FAILED),
		getstring(pWinData->hab, pWinData->hResource, RXHB_MSGBOXHDR), 1,
		MB_CANCEL|MB_CUACRITICAL|MB_MOVEABLE);
	  }
				       /* Find out the character	      */
				       /* height for the input entry field    */
	  hps = WinGetPS(pWinData->client);
	  GpiQueryFontMetrics(hps, (LONG)sizeof fm, &fm);
	  pWinData->in_hgt = (ULONG )fm.lMaxBaselineExt+2;
	  WinReleasePS(hps);
				       /* Create the child MLE for	      */
				       /* output			      */
	  pWinData->outmle = WinCreateWindow(pWinData->client, WC_MLE, NULL,
	      flCreate, 0, 0, 0, 0, pWinData->client, HWND_TOP,
	      MLE_WNDW, NULL, NULL);

	  if (!pWinData->outmle) {     /* If we couldn't do it, tell the user */
				       /* why.				      */
	    RHWinErrorBox(pWinData->hab, hwnd, pWinData->hResource,
		MLE_CREATE_ERROR, MB_OK|MB_CUAWARNING);
	  }			       /* Delay redrawing window until	      */
				       /* we're done updating it              */
	  WinEnableWindowUpdate(pWinData->client, FALSE);
				       /* Set the format for the MLE	      */

	  WinSendMsg(pWinData->outmle, MLM_FORMAT, MLE_TXT_FMT, (MPARAM)0);
				       /* Make the MLE readonly		      */

	  WinSendMsg(pWinData->outmle, MLM_SETREADONLY, MPFROMLONG(TRUE),
	      (MPARAM)0);
				       /* set the new font		      */
	  WinSendMsg(pWinData->outmle, MLM_SETFONT,
	      &(pWinData->MleFontAttrs.fAttrs), (MPARAM)0);
				       /* Update the window		      */
	  WinShowWindow(pWinData->outmle, TRUE);
				       /* set the title width		      */
	  pWinData->wtitle = (strlen(pWinData->pszInputText)+5)*(USHORT)
	      fm.lAveCharWidth;
				       /* Load the dialog box for the input   */
				       /* window			      */
	  hEntry = WinLoadDlg(pWinData->client, pWinData->client,
	      (PFNWP)EntryDlgProc, (HMODULE)pWinData->hResource, RH_EF1, NULL);

	  pWinData->insle = hEntry;    /* save input window handle Set the    */
				       /* maximum input length to ANSLEN      */
	  WinSendMsg(hEntry, EM_SETTEXTLIMIT, MPFROMSHORT(ANSLEN), (MPARAM)0);
				       /* Set the offset to the first	      */
				       /* character in the input box to 0     */
	  WinSendMsg(hEntry, EM_SETFIRSTCHAR, (MPARAM)0, (MPARAM)0);
				       /* Set the first character to be	      */
				       /* selected as 0			      */
	  WinSendMsg(hEntry, EM_SETSEL, (MPARAM)0, (MPARAM)0);
				       /* Update the windows		      */
	  WinShowWindow(hEntry, TRUE);
	  WinEnableWindowUpdate(hEntry, TRUE);
	  WinEnableWindowUpdate(pWinData->client, TRUE);
	  WinSetFocus(HWND_DESKTOP, pWinData->outmle);
	}
	else {			       /* If we couldn't allocate the data    */
				       /* structure, then we cannot continue, */
				       /* so alert the user, and exit.	      */
	  WinSendMsg(WinWindowFromID(hwnd, (USHORT)FID_CLIENT),
	      RXIOB_MSGBOX,
	      MPFROM2SHORT(RX_ALLOCERR, RXHB_MSGBOXHDR),
	      MPFROMLONG(MB_ENTER|MB_CUACRITICAL|MB_MOVEABLE));
	  return (MRESULT)TRUE;
	}
	break;

      case  WM_TIMER :

	if (hwnd) {		       /* We have gone for the designated time*/
				       /* without adding any new data to the  */
				       /* output buffer, so it is now time to */
				       /* import the information to the MLE   */
				       /* output window.		      */
	  WinStopTimer(pWinData->hab, hwnd, pWinData->timer);
				       /* Get the semaphore so that the	      */
				       /* input pipe thread won't interfere.  */

	  DosRequestMutexSem(pWinData->imp_ptr->sem, SEM_INDEFINITE_WAIT);
	  Import(pWinData);
				       /* Now clear the			      */
				       /* thread so that the input pipe can   */
				       /* continue.			      */
	  DosReleaseMutexSem(pWinData->imp_ptr->sem);
	}

	break;

      case  WM_ERASEBACKGROUND :

	return (MRESULT)TRUE;	       /* Return TRUE to indicate we want the */
				       /* background cleared to the system    */
				       /* default window background color.    */
      case  WM_SIZE :

	SizeWindow(hwnd, pWinData);    /* Re-size the client area windows.    */
	break;

      case  WM_PAINT :

	if (hps = WinGetPS(hwnd)) {    /* The title for the input window needs*/
				       /* to be re-painted by us. The child   */
				       /* windows do all the rest of the      */
				       /* drawing for us.		      */
	  POINTL p;
	  SWP wp;
	  HWND hOwner;
	  PUCHAR ptr;		       /* Ensure that we get a proper handle  */
				       /* to the client area.		      */

	  hOwner = WinQueryWindow(hwnd, QW_PARENT);
				       /* Calculate the proper positioning.   */
	  WinQueryWindowPos(pWinData->client, &wp);

	  p.x = BORDER_MARGIN *2;
	  p.y = wp.cy-pWinData->in_hgt-(BORDER_MARGIN *2),ptr =
	      pWinData->pszInputText;  /* Output the title, and find the      */
				       /* resulting position for the next     */
				       /* character. That is where we will    */
				       /* start the input box.		      */
	  GpiCharStringAt(hps, &p, (LONG)strlen(ptr), ptr);
	  GpiQueryCurrentPosition(hps, &p);
	  pWinData->wtitle = (ULONG )p.x;
	  WinReleasePS(hps);
	}
	break;

      case  WM_COMMAND :
				       /* WM_COMMAND processing in RXIOCMDS   */
	return (RXIOCmds(hwnd, mp1, pWinData));
	break;

      case  WM_CHAR :

	 /* Check for a valid (complete) key message.			      */

	if (!(SHORT1FROMMP(mp1)&KC_KEYUP) &&
	    (SHORT1FROMMP(mp1)&KC_VIRTUALKEY) ) {

	  switch (SHORT2FROMMP(mp2)) {
	    case  VK_TAB :
	    case  VK_BACKTAB :	       /* For tab and back-tab, switch focus  */
				       /* windows			      */
	      return (MRESULT)WinSetFocus(HWND_DESKTOP,
		  pWinData->infocus?pWinData->outmle: pWinData->insle);
	      break;

	    default  :
	      break;
	  }			       /* endswitch			      */
	}
	break;

      case  REXX_STARTPROC :

	 /* This is the request to start everything up. It is sent by the     */
	 /*   main procedure at startup time.				      */

	pWinData->rexx_tid = LONGFROMMP(mp1);

	break;

      case  REXX_ENDPROC : {

	PLIST_ENTRY q_elem;	       /* pointer for an element in the input */
				       /* queue				      */
				       /* Clear out any entries on input queue*/
	if (!DosRequestMutexSem(pWinData->pipe_in.q_sem, SEM_INDEFINITE_WAIT)) {
	  for (q_elem = pWinData->pipe_in.q_1st; q_elem; q_elem = q_elem->next) {
	     free(q_elem->ptr);
	     free(q_elem);
	  }
				       /* Set the queue to empty	      */
	  DosResetEventSem(pWinData->pipe_in.q_data, &postcount);
				       /* Clear the list		      */
	  pWinData->pipe_in.q_1st = NULL;
				       /* We're done with the queue           */
	  DosReleaseMutexSem(pWinData->pipe_in.q_sem);
	}

	 /* Clear the input pipe, so that any unprocessed data is deleted.    */
	 /* The only way to do this is to close the pipe, and then reopen it. */
	DosClose(pWinData->stdin_r);
	DosClose(pWinData->stdin_w);
	makepipe(&pWinData->stdin_r, STDIN, &pWinData->stdin_w, 4, PIPE_SIZE);
	setinherit(pWinData->stdin_r, TRUE);
	setinherit(pWinData->stdin_w, FALSE);
	break;
      }

      case  REXX_MAKE_INVISIBLE :

	if (pWinData->visible) {
	  pWinData->visible = FALSE;   /* reset flag to invisible	      */
	    WinShowWindow(pWinData->frame, FALSE);
				       /* Make it invisible.		      */
	}
	break;

      case  REXX_MAKE_VISIBLE :

	 /* At start-up, the window is left invisible. When output or	      */
	 /*   trace mode input occurs for the first time, this message is     */
	 /*   sent to make the window visible. It is at this time that	      */
	 /*   the Help Manager gets initialized.			      */

	if (!pWinData->visible) {
	  HELPINIT hmiData;

	  pWinData->visible = TRUE;    /* Set so we don't get called          */
				       /* unnecessarily			      */
	  RestoreWindow(hwnd, pWinData);/* From last invocation		      */
	  WinShowWindow(pWinData->frame, TRUE);
				       /* Make it visible. Initialize the Help*/
				       /* Manager data structure	      */
	  memset(&hmiData, '\0', sizeof hmiData);
	  hmiData.cb = sizeof(hmiData);
	  hmiData.pszHelpWindowTitle =
	      getstring(pWinData->hab, pWinData->hResource, PMREXX_HELP_TITLE);
	  hmiData.phtHelpTable = (PHELPTABLE)(PMREXX_HELPTABLE|RESOURCE_FLAG);
	  hmiData.pszHelpLibraryName =
	      getstring(pWinData->hab, pWinData->hResource, PMREXX_HELPFILENAME);
	  hmiData.fShowPanelId = CMIC_HIDE_PANEL_ID;
	  hmiData.hmodHelpTableModule = pWinData->hResource;
	  hmiData.hmodAccelActionBarModule = pWinData->hResource;
	  hmiData.idAccelTable = 0;
	  hmiData.idActionBar = 0;
				       /* Attempt to create help instance     */

	  if (pWinData->HelpInst = WinCreateHelpInstance(pWinData->hab,
	      &hmiData)) {
				       /* If the create succeeded, then we    */
				       /* need to associate it with the frame */
				       /* window.			      */

	    if (!WinAssociateHelpInstance(pWinData->HelpInst, pWinData->frame)) {
				       /* If the associate failed, inform the */
				       /* user and destroy the instance.      */
	      RHWinErrorBox(pWinData->hab, hwnd, pWinData->hResource,
		  ERROR_HELPASSOC, MB_OK|MB_CUAWARNING);
	      WinDestroyHelpInstance(pWinData->HelpInst);
	      pWinData->HelpInst = NULLHANDLE;
	    }
	  }

	  else {
				       /* If the create failed, inform the    */
				       /* user.				      */
	    RHWinErrorBox(pWinData->hab, hwnd, pWinData->hResource,
		ERROR_HELPCREATE, MB_OK|MB_CUAWARNING);
	  }
	  WinSetFocus(HWND_DESKTOP, pWinData->insle);

	}
	break;

      case  RXIOB_MSGBOX : {	       /* Put up a message box. The mp1	      */
				       /* parameter has the resource ids for  */
				       /* the message and the title.	      */
	  USHORT msgid = SHORT1FROMMP(mp1);
	  PUCHAR header = getstring(pWinData->hab, pWinData->hResource,
	      SHORT2FROMMP(mp1));
	  PUCHAR content = getstring(pWinData->hab, pWinData->hResource,
	      SHORT1FROMMP(mp1));
				       /* If we are			      */
				       /* displaying the ended message, set   */
				       /* the owner to the desktop so we don't*/
				       /* steal the focus back from other     */
				       /* apps.				      */

	  LONG	rc = WinMessageBox(HWND_DESKTOP, hwnd, content, header,
				   msgid, SHORT1FROMMP(mp2));
	  free(header);
	  free(content);
	  return  MPFROMSHORT(rc);
	}
	break;

      case  START_TIMER :

	 /* Start a timer to force output after a period of inactivity.	      */

	if (!pWinData->visible) {      /* Make the window visible, if not     */
				       /* already.			      */
	  WinSendMsg(WinWindowFromID(hwnd, (USHORT)FID_CLIENT),
		     REXX_MAKE_VISIBLE, (MPARAM)0, (MPARAM)0);
	}
	WinStartTimer(pWinData->hab, hwnd, pWinData->timer, TIMER_DELAY);
	pWinData->imp_queued = FALSE;
	break;

      case  ADD_STDOUTWIN :

	 /* Add an output line to the MLE buffer, and start the timer.	      */

	if (!pWinData->visible) {      /* Make the window visible, if not     */
				       /* already.			      */
	  WinSendMsg(WinWindowFromID(hwnd, (USHORT)FID_CLIENT),
		     REXX_MAKE_VISIBLE, (MPARAM)0, (MPARAM)0);
	}
	if (pWinData->client == hwnd) {
	  HWND	  wnd = pWinData->outmle;
	  PIMPORT im_ptr = pWinData->imp_ptr;
	  PUCHAR  ptr = (PUCHAR)im_ptr->data;
	  PUCHAR  tmp1 = (PUCHAR)mp1;  /* Obtain the buffer semaphore to      */
				       /* prevent interference with the input */
				       /* pipe thread.			      */

	  DosRequestMutexSem(im_ptr->sem, SEM_INDEFINITE_WAIT);
				       /* If the string is too		      */
				       /* large, break it up into pieces and  */
				       /* add to the window.		      */
	  while (strlen(tmp1) > (ULONG)IMPORTSIZE-HDRSIZE-1) {
				       /* Save the character we are about to  */
				       /* null out			      */
	    UCHAR tch = *(tmp1+IMPORTSIZE-HDRSIZE-1);
				       /* Add smaller string to the window    */
	    *(tmp1+IMPORTSIZE-HDRSIZE-1) = '\0';

	    WinSendMsg(hwnd, ADD_STDOUTWIN, tmp1, (MPARAM)mp1);
				       /* Then bump the pointer and restore   */
				       /* the character.		      */
	    tmp1 += IMPORTSIZE - HDRSIZE - 1;
	    *tmp1 = tch;
	  }			       /* endwhile See if there is room left  */
				       /* in the buffer.		      */

	  while (((im_ptr->total_size)-(im_ptr->inuse)) <= strlen(tmp1)) {
				       /* need to import what we have	      */
	    WinStopTimer(pWinData->hab, hwnd, pWinData->timer);
				       /* empty the buffer		      */
	    Import(pWinData);
	  }

	  strcat(ptr, tmp1);	       /* Copy the data to the end of the     */
				       /* buffer			      */
	  im_ptr->inuse = HDRSIZE+strlen(ptr);/* Reset the use count	      */
				       /* and free the semaphore	      */
	  DosReleaseMutexSem(im_ptr->sem);
	  free(mp1);		       /* free the text			      */
				       /* Start the timer to force output if  */
				       /* nothing added for the specified     */
				       /* time.				      */
	  WinStartTimer(pWinData->hab, hwnd, pWinData->timer, TIMER_DELAY);
	  pWinData->imp_queued = FALSE;
	  return (MRESULT)TRUE;
	}

	return (MRESULT)FALSE;
	break;

      case  HM_QUERY_KEYS_HELP :

	return (MRESULT)RX_KEYSHELP;

      case  WM_SAVEAPPLICATION :

	SaveWindow(pWinData);
	break;

      default  :
	break;
    }
    return (WinDefWindowProc(hwnd, msg, mp1, mp2));

  }

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: RXIOCmds						      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Process WM_COMMAND messages for the window procedure.	      */
/*									      */
/*   FUNCTION:								      */
/*		 This routine processes all the WM_COMMAND messages that have */
/*		 been passed into the MainWndProc. The messages handled are:  */
/*	 RXIOA_TRCSTEP	- This requests that the REXX trace be advanced one   */
/*			  step. It is equivalent to an input of a blank line  */
/*			  to a REXX trace read. It is only valid when REXX is */
/*			  waiting for trace input.			      */
/*	 RXIOA_TRCLAST	- This requests that the REXX re-trace the last	      */
/*			  clause executed. Is is equivalent to an input of an */
/*			  equal sign to a REXX trace read. It is only valid   */
/*			  when REXX is waiting for trace input.		      */
/*	 RXIOA_TRCOFF	- This requests that the REXX turn off interactive    */
/*			  tracing. Is is equivalent to an input of a "TRACE   */
/*			  OFF" statement to a REXX trace read. It is only     */
/*			  valid when REXX is waiting for trace input.	      */
/*	 RXIOA_TRACE   - This is a request to turn interactive tracing on    */
/*			  or off. It is only valid when the REXX procedure    */
/*			  has ended and the user has not yet restarted it.    */
/*			  This is because REXX only looks at the trace flag   */
/*			  when it is first called to execute a procedure.     */
/*	 RXIOA_HALT	- This requests that the REXX HALT indicator be	      */
/*			  raised, requesting the procedure to be interrupted. */
/*	 RXIOA_CLEAR	- This is a request to clear the marked area of the   */
/*			  focus window.					      */
/*	 RXIOA_PASTE	- Paste from the clipboard into the input window.     */
/*	 RXIOA_COPY	- This is a request to copy the marked area of the    */
/*			  focus window into the clipboard.		      */
/*	 RXIOA_ALL	- This is a request to mark the entire output area.   */
/*	 RXIOA_FONTS	- Present the user with a dialog to select a font for */
/*			  the output window.				      */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:							      */
/*	 MRESULT RXIOCmds(HWND hwnd, MPARAM mp1, PRHWINDATA pWinData)	      */
/*									      */
/*   INPUT:								      */
/*	 hwnd		- The window handle of the window that received the   */
/*			  command.					      */
/*	 mp1		- The first parameter of the WM_COMMAND message.      */
/*	 pWinData	- A pointer to the RHWINDATA structure of the window. */
/*									      */
/*   EXIT-NORMAL:							      */
/*	 returns TRUE if the file was saved successfully or user response     */
/*	 from SAVEAS dialog.						      */
/*   EXIT-ERROR:							      */
/*	 returns FALSE if the file was not saved successfully.		      */
/*									      */
/*   EFFECTS:								      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 Add_Q_Element	- Adds an entry to an input queue.		      */
/*	 RHWinErrorBox	- Display an error message box.			      */
/*	 ClipBoard	  - Perform clipboard related commands.		      */
/*	 SizeWindow	- Resize the child windows within the client area.  */
/*	 SelectFont	- Interface to the font selection dialog	      */
/*	 SetCheckMark	- Check/uncheck a menu item			      */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 DosRequestMutexSem		WinInvalidateRect		      */
/*	 DosReleaseMutexSem		WinLoadDlg			      */
/*	 GpiQueryFontMetrics		WinLoadString			      */
/*	 WinAlarm			WinPostMessage			      */
/*	 WinCreateWindow		WinReleasePS			      */
/*	 WinDlgBox			WinSendMsg			      */
/*	 WinEnableWindowUpdate		WinShowWindow			      */
/*	 WinGetPS			WinWindowFromID			      */
/*									      */
/*									      */
/*									      */
/*									      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

MRESULT RXIOCmds(HWND hwnd, MPARAM mp1, PRHWINDATA pWinData)
{
    CHAR Temp = '\0';		       /* temporary character		      */
				       /* dialog			      */
    PLIST_ENTRY q_elem;		       /* pointer for an element in the input */
				       /* queue				      */

    switch (LOUSHORT(mp1)) {

      case  RXIOA_TRCSTEP :

	 /* Trace the next clause. This is accomplished by inputting a	      */
	 /*   null line.						      */

	if (q_elem = malloc(sizeof(*q_elem))) {
	  q_elem->ptr = strdup("");
	  q_elem->len = strlen(q_elem->ptr);
	  Add_Q_Element(&pWinData->trace_in, q_elem);
	}
	break;

      case  RXIOA_TRCLAST :

	 /* Re-trace the previous clause. This is accomplished by	      */
	 /*   inputting an equal (=) sign alone on a line.		      */

	if (q_elem = malloc(sizeof(*q_elem))) {
	  q_elem->ptr = strdup("=");
	  q_elem->len = strlen(q_elem->ptr);
	  Add_Q_Element(&pWinData->trace_in, q_elem);
	}
	break;

      case  RXIOA_TRCOFF :

	 /* Turn tracing off by calling RexxResetTrace			      */

	RexxResetTrace(pWinData->proc_id, pWinData->rexx_tid);
	pWinData->tracebit = FALSE;
				       /* Clear the checkmark		      */
	SetCheckMark(pWinData->frame, RXIOA_TRACE, FALSE);
				       /* send a null line to end pause	      */
	if (q_elem = malloc(sizeof(*q_elem))) {
	  q_elem->ptr = strdup("");
	  q_elem->len = strlen(q_elem->ptr);
	  Add_Q_Element(&pWinData->trace_in, q_elem);
	}
	break;

      case  RXIOA_TRACE :

	 /* Change the state of the interactive trace indiciator. This	      */
	 /*   will turn tracing on immediately (with the next phrase	      */
	 /*   executed).						      */

	if (pWinData->tracebit = (BOOL)(!pWinData->tracebit)) {
	  RexxSetTrace(pWinData->proc_id, pWinData->rexx_tid);
	}
	else {
	  RexxResetTrace(pWinData->proc_id, pWinData->rexx_tid);
	}
	SetCheckMark(pWinData->frame, RXIOA_TRACE, pWinData->tracebit);

	break;

      case  RXIOA_HALT :

	 /* Change the Halt Procedure indicator. This will cause REXX to      */
	 /*   terminate execution of the procedure when the next phrase	      */
	 /*   is executed.						      */

	RexxSetHalt(pWinData->proc_id, pWinData->rexx_tid);

	if (!pWinData->RxHalt) {
	  pWinData->RxHalt = TRUE;
	  SetCheckMark(pWinData->frame, RXIOA_HALT, pWinData->RxHalt);
	}
	break;

      /************************************************************************/
      /* These messages relate to clipboard operations			      */
      /************************************************************************/

      case  RXIOA_CLEAR :

	if (pWinData->infocus) {       /* clear the input window, because it  */
				       /* has the focus			      */
	  WinSendMsg(pWinData->insle, EM_CLEAR, (MPARAM)0, (MPARAM)0);
	}
	else {			       /* clear the MLE			      */
	  ClipBoard(hwnd, MLM_CLEAR, RXIOH_ERR_CLEAR, pWinData);
	}
	break;

      case  RXIOA_PASTE :

	 /* put the text from the clipboard into the input window	      */

	WinSendMsg(pWinData->insle, EM_PASTE, (MPARAM)0, (MPARAM)0);
	break;

      case  RXIOA_COPY :

	if (pWinData->infocus) {       /* Copy text from input window	      */
	  WinSendMsg(pWinData->insle, EM_COPY, (MPARAM)0, (MPARAM)0);
	}
	else {			       /* Get the text from the MLE	      */
	  ClipBoard(hwnd, MLM_COPY, RXIOH_ERR_COPY, pWinData);
	}
	break;

      /************************************************************************/
      /* Select all							      */
      /************************************************************************/

      case  RXIOA_ALL : {

	  ULONG	 usFileSize;

	  WinEnableWindowUpdate(hwnd, FALSE);
				       /* disable updates Get length of the   */
				       /* output in the MLE		      */
	  usFileSize = (ULONG )WinSendMsg(WinWindowFromID(hwnd,
					  (USHORT)MLE_WNDW),
					  MLM_QUERYTEXTLENGTH,
					  (MPARAM)0, (MPARAM)0);
				       /* Select all of the output in the MLE */
	  WinSendMsg(WinWindowFromID(hwnd, (USHORT)MLE_WNDW),
		     MLM_SETSEL, (MPARAM)0L, MPFROMSHORT(usFileSize));
	  WinInvalidateRect(hwnd, NULL, TRUE);
				       /* update the window		      */
	  WinEnableWindowUpdate(hwnd, TRUE);
				       /* Make MLE so that it can't be undone */
	  WinSendMsg(WinWindowFromID(hwnd, (USHORT)MLE_WNDW),
		     MLM_RESETUNDO, (MPARAM)0, (MPARAM)0);
	}
	break;


      /************************************************************************/
      /* Present Font dialog; the dialog will change the PS attribute	      */
      /************************************************************************/

      case  RXIOA_FONTS :
	WinSendMsg(WinWindowFromID(hwnd, (USHORT) MLE_WNDW),
		   MLM_RESETUNDO, (MPARAM)0, (MPARAM)0);
	SelectFont(pWinData);
	break;

      /************************************************************************/
      /* Display Help for help panel					      */
      /************************************************************************/

      case  RXIOM_HELPHELP :

	if (pWinData->HelpInst != NULLHANDLE) {
	  WinSendMsg(pWinData->HelpInst, HM_DISPLAY_HELP, (MPARAM)0, (MPARAM)0);
	}
	else {
	  WinSendMsg(pWinData->client,
		     RXIOB_MSGBOX,
		     MPFROM2SHORT(NO_HELP_MGR, HELP_ERR),
		     MPFROMLONG(MB_ENTER|MB_CUACRITICAL|MB_MOVEABLE));
	}
	break;

      /************************************************************************/
      /* Display Extended help panel					      */
      /************************************************************************/

      case  RXIOM_EXTHELP :

	if (pWinData->HelpInst != NULLHANDLE) {
	  WinSendMsg(pWinData->HelpInst, HM_EXT_HELP, (MPARAM)0, (MPARAM)0);
	}
	else {
	  WinSendMsg(pWinData->client,
		     RXIOB_MSGBOX,
		     MPFROM2SHORT(NO_HELP_MGR, HELP_ERR),
		     MPFROMLONG(MB_ENTER|MB_CUACRITICAL|MB_MOVEABLE));
	}
	break;

      /************************************************************************/
      /* Display keys help panel					      */
      /************************************************************************/

      case  RXIOM_KEYSHELP :

	if (pWinData->HelpInst != NULLHANDLE) {
	  WinSendMsg(pWinData->HelpInst, HM_KEYS_HELP, (MPARAM)0, (MPARAM)0);
	}
	else {
	  WinSendMsg(pWinData->client, RXIOB_MSGBOX,
		     MPFROM2SHORT(NO_HELP_MGR, HELP_ERR),
		     MPFROMLONG(MB_ENTER|MB_CUACRITICAL|MB_MOVEABLE));
	}
	break;

      /************************************************************************/
      /* Display help index						      */
      /************************************************************************/

      case  RXIOM_INDEXHELP :

	if (pWinData->HelpInst != NULLHANDLE) {
	  WinSendMsg(pWinData->HelpInst, HM_HELP_INDEX, (MPARAM)0, (MPARAM)0);
	}
	else {
	  WinSendMsg(pWinData->client, RXIOB_MSGBOX,
		     MPFROM2SHORT(NO_HELP_MGR, HELP_ERR),
		     MPFROMLONG(MB_ENTER|MB_CUACRITICAL|MB_MOVEABLE));
	}
	break;

      /************************************************************************/
      /* Option not implemented: Warn the user				      */
      /************************************************************************/

      default  :
	WinAlarm(HWND_DESKTOP, WA_ERROR);
    }				       /* endswitch			      */
    return (MRESULT)FALSE;
}

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: ClipBoard						      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Process the MLE clipboard functions by passing the message   */
/*		 on to the MLE window.					      */
/*									      */
/*   FUNCTION:								      */
/*		 All of the clipboard related functions are handled very      */
/*		 simliarly and so are just passed directly on to the MLE      */
/*		 window for processing. In case of a failure, a message	      */
/*		 box is popped up to notify the user.			      */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:							      */
/*	    CipBoard(hWnd, MLECMsg, ErrMsg, pWinData)			      */
/*									      */
/*   INPUT:								      */
/*	 hWnd		- The handle of the window that received the message  */
/*	 MLECMsg	- The message to be processed.			      */
/*	 ErrMsg		- The resource ID of the error message to use in      */
/*			  case of an error.				      */
/*									      */
/*   EXIT-NORMAL:							      */
/*									      */
/*   EXIT-ERROR:							      */
/*									      */
/*   EFFECTS:								      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 WinSendMsg							      */
/*	 WinWindowFromID						      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

  void ClipBoard(HWND hwnd, USHORT MLECMsg, ULONG ErrMsg,
      PRHWINDATA pWinData) {

   /* If an error occurs, put up a message box				      */

    if (!((MRESULT)WinSendMsg(WinWindowFromID(hwnd, (USHORT)MLE_WNDW),
	MLECMsg, (MPARAM)0, (MPARAM)0)))

      WinSendMsg(pWinData->client, RXIOB_MSGBOX,
		 MPFROM2SHORT(ErrMsg, RXIOH_ERR_CLIPBRD),
		 MPFROMLONG(MB_ENTER|MB_CUACRITICAL|MB_MOVEABLE));
  }

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: Import						      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Add the information collected into the import buffer into    */
/*		 the MLE window.					      */
/*									      */
/*   FUNCTION:								      */
/*		 Adding information to the MLE involves considerable	      */
/*		 overhead. To do so on each individual line would be	      */
/*		 unacceptably slow for any considerable amount of output.     */
/*		 Therefore, output is collected into a buffer and output      */
/*		 to the MLE when the buffer is full, or there has been no     */
/*		 output added for some period of time. This is the routine    */
/*		 to take the information collected in the buffer area and     */
/*		 add it to the output window.				      */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:							      */
/*	    void Import(PRHWINDATA pWinData)				      */
/*									      */
/*   INPUT:								      */
/*	 pWinData	- Pointer to global control block.		      */
/*									      */
/*   EXIT-NORMAL:							      */
/*									      */
/*   EXIT-ERROR:							      */
/*									      */
/*   EFFECTS:								      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 RHWinErrorBox	- Display an error message box.			      */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 WinPostMsg							      */
/*	 WinQueryWindow							      */
/*	 WinSendMsg							      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

  void Import(PRHWINDATA pWinData) {
    IPT ipt = (IPT)NULL;	       /* Integer pointer type for MLE	      */


    if (pWinData->imp_ptr->inuse > HDRSIZE) {

      /* Get the number of characters currently in the MLE		      */

      ipt = (IPT)WinSendMsg(pWinData->outmle, MLM_QUERYTEXTLENGTH,
	  (MPARAM)0, (MPARAM)0);

      /* If we had an error setting up the transfer buffer, display a message */

      if (!WinSendMsg(pWinData->outmle, MLM_SETIMPORTEXPORT,
	  pWinData->imp_ptr->data,
	  MPFROMLONG(pWinData->imp_ptr->total_size-HDRSIZE))) {
	RHWinErrorBox(pWinData->hab, pWinData->outmle, pWinData->hResource,
	    MLE_IMEXPORT_ERROR, MB_OK|MB_CUACRITICAL);
	WinPostMsg(WinQueryWindow(pWinData->outmle, QW_PARENT),
		   WM_CLOSE, (MPARAM)0, (MPARAM)0);
	return ;
      }
      WinEnableWindowUpdate(pWinData->outmle, FALSE);

      /* Set the MLE to not translate the input				      */
      WinSendMsg(pWinData->outmle, MLM_FORMAT, MPFROMSHORT(MLFIE_NOTRANS),
	  (MPARAM)0);

      /* Set the MLE so we can add data to it				      */
      WinSendMsg(pWinData->outmle, MLM_SETREADONLY, FALSE, (MPARAM)0);

      /* Add the data to the MLE, if we get an error, display a message	      */
				       /* upon return, point to end of data   */
      if (!WinSendMsg(pWinData->outmle, MLM_IMPORT, &ipt,
	  MPFROMLONG(pWinData->imp_ptr->inuse-HDRSIZE))) {

	RHWinErrorBox(pWinData->hab, pWinData->outmle, pWinData->hResource,
	    MLE_IMPORT_ERROR, MB_OK|MB_CUACRITICAL);

	 /* It probably failed because the line is too long, so append a      */
	 /* new line and try again.					      */
	WinSendMsg(pWinData->outmle, MLM_SETSEL, MPFROMLONG(ipt),
	    MPFROMLONG(ipt));
	WinSendMsg(pWinData->outmle, MLM_INSERT, "\r", (MPARAM)0);

	 /* Move the selection to the end of the input			      */
	ipt = (IPT)WinSendMsg(pWinData->outmle, MLM_QUERYTEXTLENGTH, (MPARAM)0,
	    (MPARAM)0);

	if (!WinSendMsg(pWinData->outmle, MLM_IMPORT, &ipt,
	    MPFROMLONG(pWinData->imp_ptr->inuse-HDRSIZE) )) {
				       /* If we still couldn't import         */
				       /* anything, give up.		      */
	  WinPostMsg(WinQueryWindow(pWinData->outmle, QW_PARENT),
		     WM_CLOSE, (MPARAM)0, (MPARAM)0);
	}
      }

      /* Update the ptr structure					      */

      *(HDRSIZE+(PUCHAR)pWinData->imp_ptr) = '\0';
      pWinData->imp_ptr->inuse = HDRSIZE+strlen(HDRSIZE+(PUCHAR)pWinData->imp_ptr);

      /* Move the cursor to the end of the new data			      */
      WinSendMsg(pWinData->outmle, MLM_SETSEL, MPFROMLONG(ipt), MPFROMLONG(ipt));

      WinEnableWindowUpdate(pWinData->outmle, TRUE);

      /* Set the MLE to normal export mode				      */
      WinSendMsg(pWinData->outmle, MLM_FORMAT, MLE_TXT_FMT, (MPARAM)0);

      /* Set the MLE back to readonly					      */
      WinSendMsg(pWinData->outmle, MLM_SETREADONLY, MPFROMLONG(TRUE), (MPARAM)0);

    }
  }

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: SizeWindow					      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Size the MLE output window and the Entry Field input window  */
/*		 to fit the client area.				      */
/*									      */
/*   FUNCTION:								      */
/*		 Whenever the client window is sized, this routine is	      */
/*		 called to resize the MLE output window and the Entry Field   */
/*		 control used for input to fit properly within the new	      */
/*		 client area.						      */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:							      */
/*	    void SizeWindow(HWND hwnd, PRHWINDATA pWinData)		      */
/*									      */
/*   INPUT:								      */
/*	 hwnd		- The handle of the client window.		      */
/*	 pWinData	- A pointer to the RHWINDATA structure of the window. */
/*									      */
/*   EXIT-NORMAL:							      */
/*									      */
/*   EXIT-ERROR:							      */
/*									      */
/*   EFFECTS:								      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 WinQueryWindow							      */
/*	 WinQueryWindowPos						      */
/*	 WinSetWindowPos						      */
/*	 WinWindowFromID						      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

  void SizeWindow(HWND hwnd, PRHWINDATA pWinData) {
    HWND hOwner;		       /* Window handle of parent window      */
    SWP windowPos;		       /* info on window position	      */
    ULONG  in_hgt = pWinData->in_hgt;  /* Height for input entry field	      */


    if (!pWinData->visible)
      return ;			       /* If the window is not visible, do    */
				       /* nothing			      */
    hOwner = WinQueryWindow(hwnd, QW_PARENT);

   /* Get Window positions						      */
    WinQueryWindowPos(WinWindowFromID(hOwner, (USHORT) FID_CLIENT), &windowPos);

   /* adjust MLE and input field, if created				      */
    if (pWinData->outmle && pWinData->insle) {

      WinSetWindowPos(pWinData->outmle,	  /* set window a bit smaller	      */
		      HWND_TOP,
		      (SHORT)BORDER_MARGIN,
		      (SHORT)BORDER_MARGIN-1,
		      (SHORT)(windowPos.cx-(BORDER_MARGIN *2)+1),
		      (SHORT)(windowPos.cy-in_hgt-(BORDER_MARGIN *6)),
		      SWP_SIZE|SWP_MOVE|SWP_ZORDER|SWP_SHOW);

      WinSetWindowPos(pWinData->insle,	 /* set window a bit smaller	      */
		      HWND_TOP,
		      (SHORT)(BORDER_MARGIN *3+pWinData->wtitle),
		      (SHORT)(windowPos.cy-in_hgt-BORDER_MARGIN *3),
		      (SHORT)(windowPos.cx-BORDER_MARGIN *5-pWinData->wtitle),
		      (SHORT)pWinData->in_hgt,
		      SWP_SIZE|SWP_MOVE|SWP_ZORDER|SWP_SHOW);
    }
    return ;
  }

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: io_routine					      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Process the REXX IO exit.				      */
/*									      */
/*   FUNCTION:								      */
/*		 This function has been registered to handle the IO exits     */
/*		 for the REXX procedure. The interface is defined by REXXSAA. */
/*		 For the input requests (RXSIOTRD and RXSIODTR), first the    */
/*		 window is made visible, if it isn't already, and then the    */
/*		 trace options are enabled/disabled as appropriate. Then      */
/*		 a return code is set to allow normal operation of I/O by     */
/*		 REXX.							      */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:							      */
/*	    LONG  io_routine( LONG  exitno, LONG  subfunc, PUCHAR parmblock)  */
/*									      */
/*   INPUT:								      */
/*	 The arguments are defined by the REXX interface specifications.      */
/*									      */
/*   EXIT-NORMAL:							      */
/*									      */
/*   EXIT-ERROR:							      */
/*									      */
/*   EFFECTS:								      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 Remove_Q_Element: Remove a queue element, wait if none there	      */
/*	 SetCheckMark	- Check/uncheck a menu item			      */
/*	 SetOptions	- Enable/disable menu items.			      */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

LONG  io_routine(LONG  exitno, LONG  subfunc, PUCHAR parmblock)
{
    LONG	   rc;		       /* rc=1 says use standard I/O routines */
    PLIST_ENTRY	   q_elem;	       /* entry in queue		      */
    RXSIOSAY_PARM *sparm = (RXSIOSAY_PARM *)parmblock;/* struc for IO exit    */
    PRHWINDATA	   userdata[2];	       /* registered user data		      */
    PRHWINDATA	   pWinData;	       /* global data anchor		      */
    USHORT	   exists;	       /* existance flag		      */

				       /* retrieve the anchor		      */
    if (RexxQueryExit(PMRXIO_EXIT, NULL, &exists, (PUCHAR)userdata))
				       /* failed?			      */
      return RXEXIT_RAISE_ERROR;       /* raise a system failure error	      */

    rc = RXEXIT_NOT_HANDLED;	       /* let others go through		      */
    pWinData = userdata[0];	       /* get saved global anchor	      */

    if (exitno == RXSIO) {

      switch (subfunc) {

	case  RXSIODTR :	       /* For trace read we want to enable the*/
				       /* trace menu items so we must perform */
				       /* the read ourselves.		      */
				       /* Enable all the options	      */
	  SetOptions(pWinData->frame, RXIOA_TRCSTEP, TRUE);
	  SetOptions(pWinData->frame, RXIOA_TRCLAST, TRUE);
	  SetOptions(pWinData->frame, RXIOA_TRCOFF, TRUE);

	  if (!pWinData->tracebit) {   /* Set tracing active flag in menu     */
	    SetCheckMark(pWinData->frame, RXIOA_TRACE,
		(BOOL)(!pWinData->tracebit));
	  }
	  pWinData->trace_inp = TRUE;  /* Set the trace active flag for IO    */
	  pWinData->in_type = TRC_IN;

	  q_elem = Remove_Q_Element(&pWinData->trace_in);/* And get input     */
	  pWinData->in_type = STD_IN;
	  pWinData->trace_inp = FALSE; /* Reset the trace active flag for IO  */
				       /* Disable all the options	      */
	  SetOptions(pWinData->frame, RXIOA_TRCSTEP, FALSE);
	  SetOptions(pWinData->frame, RXIOA_TRCLAST, FALSE);
	  SetOptions(pWinData->frame, RXIOA_TRCOFF, FALSE);

	  if (!pWinData->tracebit) {   /* Reset the trace flag as appropriate */
	    SetCheckMark(pWinData->frame, RXIOA_TRACE, pWinData->tracebit);
	  }

	 /* Copy the input line to the return area.			      */

	  strcpy(sparm->rxsio_string.strptr, q_elem->ptr);
	  sparm->rxsio_string.strlength = strlen(q_elem->ptr);
	  free(q_elem->ptr);	       /* And free the memory.		      */
	  free(q_elem);
	  rc = RXEXIT_HANDLED;	       /* Set the return code to	      */
	  break;		       /* input complete.		      */

	case  RXSIOTRD :	       /* If a read occurs and the window is  */
				       /* not visible, make it visible.	      */

	  if (!pWinData->visible) {

	    while (!WinPostMsg(pWinData->client, (USHORT)REXX_MAKE_VISIBLE,
		(MPARAM)0, (MPARAM)0)) {
	      DosSleep((ULONG)TIMER_DELAY);
	    }
	  }
	  break;

	default	 :		       /* For all else, let REXX do the	      */
	  break;		       /* work.				      */
      }				       /* endswitch			      */
    }
    return  rc;
}

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: stdinpipe						      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Send input lines to the standard input of child processes.   */
/*									      */
/*   FUNCTION:								      */
/*		 This function is the thread that waits for input for child   */
/*		 processes started by calls to the subcommand environment,    */
/*		 passes that input through to the pipe connected to their     */
/*		 standard input handle.					      */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:							      */
/*	    void stdinpipe()						      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 Remove_Q_Element - Removes an entry from an input queue. Waits for   */
/*			    one if the queue is empty.			      */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 DosWaitEventSem						      */
/*	 DosWrite							      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

  void stdinpipe(PRHWINDATA pWinData) {
    PLIST_ENTRY q_elem;		       /* element from queue		      */
    UCHAR	str[ANSLEN+5];	       /* data from queue entry		      */
    ULONG	written;	       /* bytes written by DosWrite	      */

    while (TRUE) {
      q_elem = Remove_Q_Element(&pWinData->pipe_in);
      strcpy(str, q_elem->ptr);
      strcat(str, "\r\n");

      DosWrite(pWinData->stdin_w, str, strlen(str), &written);
      free(q_elem->ptr);
      free(q_elem);
    }
    DosExit(EXIT_THREAD, 0);	       /* end the thread		      */
  }

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: stdoutpipe					      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Send lines from the standard output of child processes to    */
/*		 the output window.					      */
/*									      */
/*   FUNCTION:								      */
/*		 This function is the thread that waits for output from child */
/*		 processes started by calls to the subcommand environment,    */
/*		 passes that output through to the output window.	      */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:							      */
/*	    void stdoutpipe()						      */
/*									      */
/*   INPUT:								      */
/*									      */
/*   EXIT-NORMAL:							      */
/*									      */
/*   EXIT-ERROR:							      */
/*									      */
/*   EFFECTS:								      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 addline - Macro to add a line to the output window		      */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 DosPostEventSem						      */
/*	 DosRequestMutexSem						      */
/*	 DosReleaseMutexSem						      */
/*	 DosWaitEventSem						      */
/*	 DosSleep							      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

  void stdoutpipe(PRHWINDATA pWinData) {
    UCHAR  buffer[MAXWIDTH];	       /* holds data from standard out	      */
    UCHAR  tchar = '\0';	       /* temporary character variable	      */
    ULONG  len;			       /* counter			      */
    ULONG  nread;		       /* bytes read			      */

    USHORT	   exists;	       /* existance flag		      */

				       /* retrieve the anchor		      */
    RexxQueryExit(PMRXIO_EXIT, NULL, &exists, NULL);

    while (!pWinData->stopoutpipe) {

      len = 0;			       /* initialize count back to 0	      */

      /* Loop here until we see a new line, or the buffer is full	      */
      do {
	if (!DosRead(pWinData->stdout_r,&tchar, 1, &nread) && nread) {
	  if (tchar == '\r') {
	    DosRead(pWinData->stdout_r,&tchar, 1, &nread);
	    if (tchar != '\n')
	      buffer[len++] = '\n';
	  }
	  buffer[len++] = (UCHAR)tchar;
	}
      }

      while (len < MAXWIDTH-1 && tchar != '\n');

      buffer[len] = END_STR;	       /* Terminate the string		      */

      if (pWinData->stopoutpipe)       /* shutdown flag?		      */
	break;			       /* time to leave			      */

      /* Now add the data into the import structure, but first get	      */
      /* the semaphore.							      */

      DosRequestMutexSem(pWinData->imp_ptr->sem, SEM_INDEFINITE_WAIT);

      /* If there is no more room in the import area, add the data	      */
      if (pWinData->imp_ptr->total_size <= pWinData->imp_ptr->inuse+len+2) {
	DosReleaseMutexSem(pWinData->imp_ptr->sem);
	addline(pWinData->client, strdup(buffer));

	 /* Give the MLE a chance to add the data			      */

	DosSleep((ULONG)TIMER_DELAY);

      /* There is room in the import area, so add the data		      */

      }

      else {
	strcat(pWinData->imp_ptr->data, buffer);
	pWinData->imp_ptr->inuse += len;
	DosReleaseMutexSem(pWinData->imp_ptr->sem);

	 /* Set (or reset) the timer					      */
	if (!pWinData->imp_queued) {
	  pWinData->imp_queued = TRUE;

	  while (!WinPostMsg(pWinData->frame, START_TIMER, NULL, NULL)) {
	    DosSleep((ULONG)TIMER_DELAY);
	  }
	}
      }
    }				       /* endwhile			      */
    DosExit(EXIT_THREAD, 0);	       /* end the thread		      */
  }

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: EntryDlgProc					      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Dialog procedure for the input entry field.		      */
/*									      */
/*   FUNCTION:								      */
/*		 This is the Dialog Procedure that is registered to handle    */
/*		 the messages coming into the dialog that establishes the     */
/*		 input entry field. The messages that are handled are:	      */
/*	 WM_BUTTON1DBLCLK - Copy the marked area from the output window into  */
/*			    the input window.				      */
/*	 WM_BUTTON2DBLCLK - Same as pressing ENTER in the input window.	      */
/*	 WM_CHAR - several characters are processed here. These include:      */
/*	   VK_NEWLINE	- take the data in the input window and place it on   */
/*			  current input queue, then clear the entry field.    */
/*	   VK_UP	- place the last entered entry into the input field.  */
/*		 All other messages and keys are handled by default	      */
/*		 processing.						      */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:							      */
/*	    This is defined by the PM interface for dialog procedures.	      */
/*									      */
/*   INPUT:								      */
/*	 item		- The id of the item to enable/disable		      */
/*	 option		- TRUE to enable the option, FALSE to disable.	      */
/*									      */
/*   EXIT-NORMAL:							      */
/*									      */
/*   EXIT-ERROR:							      */
/*									      */
/*   EFFECTS:								      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 addline	- Macro to add a line to the output window	      */
/*	 Add_Q_Element	- Adds an entry to an input queue.		      */
/*									      */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 WinDefDlgProc							      */
/*	 WinQueryWindow							      */
/*	 WinQueryWindowPtr						      */
/*	 WinQueryWindowText						      */
/*	 WinSendMsg							      */
/*	 WinSetFocus							      */
/*	 WinSetWindowText						      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

MRESULT EXPENTRY EntryDlgProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) {
    static PUCHAR last_str[2] =	 { NULL,NULL };
    LONG       len = ANSLEN;	       /* length of input buffer	      */
    UCHAR      str[ANSLEN+1];	       /* holds data from input buffer	      */
    LONG       len1;		       /* number of characters input	      */
    WNDPARAMS  wp;		       /* struc to update input window	      */
    HWND       hOwner;		       /* parent window handle		      */
    IPT	       start,end;	       /* Insertion point for MLE	      */
    PRHWINDATA pWinData;	       /* pointer to WinData structure	      */

    hOwner = WinQueryWindow(hwnd, QW_PARENT);
    pWinData = WinQueryWindowPtr(hOwner, (SHORT)0);

    switch (msg) {

      case  WM_SETFOCUS :

	pWinData->infocus = (BOOL)(mp2?TRUE:FALSE);
	break;

      case  WM_BUTTON1DBLCLK :

	start = (IPT)WinSendMsg(pWinData->outmle, MLM_QUERYSEL,
	    MPFROMSHORT(1), (MPARAM)0);
	end = (IPT)WinSendMsg(pWinData->outmle, MLM_QUERYSEL,
			      MPFROMSHORT(2), (MPARAM)0);

	if (end > start) {
	  PUCHAR ptr = (PUCHAR)malloc((LONG )(end-start+2));
	  ULONG len = end-start;
	  ULONG got;

	  if (ptr && WinSendMsg(pWinData->outmle, MLM_SETIMPORTEXPORT, ptr,
	      MPFROMLONG(end-start+1))) {

	    if (got = (ULONG)WinSendMsg(pWinData->outmle, MLM_EXPORT,
		MPFROMLONG(&start), MPFROMLONG(&len))) {
	      PUCHAR ptr1 = ptr;
	      PUCHAR ptr2;

	      while (!*ptr1)
		ptr1++;		       /* Skip over any nulls.		      */
	      ptr2 = ptr1;	       /* Now find the end		      */

	      while (*ptr2 &&	       /* at NULL, newline or		      */
		  (*ptr2 != '\n') &&   /* carraige return.		      */
		  *ptr2 != '\r' && ptr2-ptr < (INT)got)
		*ptr2++;
	      *(ptr2) = '\0';
	      WinSetWindowText(hwnd,
			       ptr1);  /* Put into SLE			      */
	    }
	  }

	  if (ptr) {
	    free(ptr);
	  }
	}
	break;

      case  WM_BUTTON2DBLCLK :

	WinSendMsg(hwnd, WM_CHAR, MPFROM2SHORT(KC_VIRTUALKEY, NULL),
		   MPFROM2SHORT(NULL, VK_NEWLINE));
	break;

      case  WM_CHAR :

	if (!(SHORT1FROMMP(mp1)&KC_KEYUP) &&
	    (SHORT1FROMMP(mp1)&KC_VIRTUALKEY) ) {

	  switch (SHORT2FROMMP(mp2)) {

	    case  VK_BREAK :
	      WinSendMsg(pWinData->client, WM_COMMAND,
		  MPFROMSHORT(RXIOA_HALT), (MPARAM)0);
	      break;

	    case  VK_NEWLINE :
	    case  VK_ENTER : {

		PLIST_ENTRY qe = malloc(sizeof *qe);

		len1 = WinQueryWindowText(hwnd, (SHORT)len, str);

		if (qe) {
		  PUCHAR str1 = malloc(len1+4);

		  if (last_str[pWinData->in_type]) {
		    free(last_str[pWinData->in_type]);
		  }
		  memcpy(str1, str, len1);
		  strcpy(str1+len1, "\n");
				       /* only add input if executing	      */
		  last_str[pWinData->in_type] = strdup(str1);

		  addline(pWinData->client, strdup(str1));
		  qe->len = len1;
		  qe->ptr = strdup(str);
		  *(str+len1) = '\0';

		  if (pWinData->trace_inp) {
		    Add_Q_Element(&pWinData->trace_in, qe);
		  }

		  else {
		    Add_Q_Element(&pWinData->pipe_in, qe);
		  }
		  free(str1);
		}
	      }
	      WinSendMsg(hwnd, EM_SETSEL, MPFROM2SHORT(0, len1), (MPARAM)0);
	      WinSendMsg(hwnd, EM_CLEAR, (MPARAM)0, (MPARAM)0);
	      return (MRESULT)TRUE;
	      break;

	    case  VK_UP :

	      if (last_str[pWinData->in_type]) {
		WinSendMsg(hwnd, EM_SETSEL, MPFROM2SHORT(0, len1), (MPARAM)0);
		WinSendMsg(hwnd, EM_CLEAR, (MPARAM)0, (MPARAM)0);
		*(last_str[pWinData->in_type] +
		    strlen(last_str[pWinData->in_type])-1) = '\0';
		wp.fsStatus = WPM_TEXT+WPM_CCHTEXT;
		wp.cchText = (SHORT)strlen(last_str[pWinData->in_type]);
		wp.pszText = strdup(last_str[pWinData->in_type]);
		WinSendMsg(hwnd, WM_SETWINDOWPARAMS, &wp, (MPARAM)0);
		*(last_str[pWinData->in_type] +
		    strlen(last_str[pWinData->in_type])) = '\r';
		return (MRESULT)TRUE;
	      }
	      break;

	    case  VK_TAB :
	    case  VK_BACKTAB :

	      return (MRESULT)WinSetFocus(HWND_DESKTOP,
		  pWinData->infocus?pWinData->outmle: pWinData->insle);
	      break;

	    default  :
	      break;
	  }
	}
      default  :
	break;
    }
    return  WinDefDlgProc(hwnd, msg, mp1, mp2);
  }

/************************** START OF SPECIFICATIONS ***************************/
/*									      */
/*   SUBROUTINE NAME: SelectFont					      */
/*									      */
/*   DESCRIPTIVE NAME:							      */
/*		 Interface to the Font Selection dialog.		      */
/*									      */
/*   FUNCTION:								      */
/*		 This routine sets up the parameters and calls the Font	      */
/*		 Selection dialog routines. Responses from the dialog will    */
/*		 be via WM_CONTROL messages to the parent window procedure.   */
/*									      */
/*   NOTES:								      */
/*									      */
/*   ENTRY POINT:							      */
/*	LINKAGE:							      */
/*	    ULONG  SelectFont(PRHWINDATA pWinData)			      */
/*									      */
/*   INPUT:								      */
/*	 pWinData	- Pointer to the WinData structure of the parent      */
/*			  window.					      */
/*									      */
/*   EXIT-NORMAL:							      */
/*									      */
/*   EXIT-ERROR:							      */
/*									      */
/*   EFFECTS:								      */
/*									      */
/*   INTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 FontDialog - Load and initialize the Font dialog		      */
/*									      */
/*   EXTERNAL REFERENCES:						      */
/*	ROUTINES:							      */
/*	 WinProcessDlg							      */
/*	 WinSendMsg							      */
/*									      */
/**************************** END OF SPECIFICATIONS ***************************/

  ULONG	 SelectFont(PRHWINDATA pWinData) {
    PFONTDLG   fntd;		       /* font dialog control		      */
    BOOL       rc;		       /* success/failure flag		      */

    fntd = &pWinData->MleFontAttrs;    /* point to control structure	      */
    fntd->cbSize = sizeof(FONTDLG);
    if (!fntd->pszFamilyname)	      {/* do first time set up		     */
				       /* set the buffer		     */
      fntd->pszFamilyname = pWinData->MleFamilyName;
      strcpy(fntd->pszFamilyname, fntd->fAttrs.szFacename);
      fntd->fxPointSize = MAKEFIXED(8,0);
    }
    fntd->pszPreview = getstring(pWinData->hab, pWinData->hResource,
	SAMPLE_TEXT);
    fntd->pszTitle = getstring(pWinData->hab, pWinData->hResource,
	FONT_TITLE);
    fntd->fl = FNTS_CENTER + FNTS_HELPBUTTON;
    fntd->clrFore = CLR_NEUTRAL;    /* Selected foreground color */
    fntd->clrBack = CLR_BACKGROUND; /* Selected Background color */
    fntd->usWeight = FWEIGHT_NORMAL;
    fntd->usWidth = FWIDTH_NORMAL;
    fntd->hpsScreen = WinGetPS(pWinData->outmle);
    fntd->fAttrs.usCodePage = (USHORT)pWinData->CountryInfo.codepage;
    fntd->clrFore = CLR_BLACK;
    fntd->clrBack = CLR_WHITE;
    fntd->usFamilyBufLen = MAXWIDTH;
    fntd->pszPtSizeList = NULL;	       /* use default point sizes*/
    rc = TRUE;

    /* Disable the window for update, and call the font dialog	 */
    WinEnableWindowUpdate(pWinData->outmle, FALSE);  
    if (!WinFontDlg(HWND_DESKTOP, pWinData->frame, fntd) ||
	fntd->lReturn != DID_OK) {
      rc = FALSE;
      } else {

       /**********************************************************/
       /* If outline font, calculate the maxbaselineext and	 */
       /*  avecharwidth for the point size selected		 */
       /**********************************************************/
       if ( fntd->fAttrs.fsFontUse == FATTR_FONTUSE_OUTLINE )  
       {							    
	 HDC   hDC;						    
	 LONG  lxFontResolution;				    
	 LONG  lyFontResolution;				    
	 SIZEF sizef;						    

	 /********************************************************/
	 /* Query device context for the screen and then query	 */
	 /* the resolution of the device for the device context. */
	 /********************************************************/
	 hDC = GpiQueryDevice(fntd->hpsScreen);					
	 DevQueryCaps( hDC, CAPS_HORIZONTAL_FONT_RES,		    
		       (LONG)1, &lxFontResolution);		    
	 DevQueryCaps( hDC, CAPS_VERTICAL_FONT_RES,		    
		       (LONG)1, &lyFontResolution);		    

	 /********************************************************/
	 /* Calculate the size of the character box, based on the*/
	 /* point size selected and the resolution of the device.*/
	 /* The size parameters are of type FIXED, NOT int.	 */
	 /*  NOTE: 1 point == 1/72 of an inch.			 */
	 /********************************************************/
	 sizef.cx = (FIXED)(((fntd->fxPointSize) / 72 ) *	
			   lxFontResolution );			    
	 sizef.cy = (FIXED)(((fntd->fxPointSize) / 72 ) *	
			   lyFontResolution );			    

	 fntd->fAttrs.lMaxBaselineExt =				
			       MAKELONG( HIUSHORT( sizef.cy ), 0 ); 
	 fntd->fAttrs.lAveCharWidth =				
			       MAKELONG( HIUSHORT( sizef.cx ), 0 ); 
	}/* endif fsFontUse == FATTR_FONTUSE_OUTLINE */		 


      } /* endif !WinFontDlg */
    WinReleasePS(fntd->hpsScreen);

    if (rc) {
      WinSendMsg(pWinData->outmle,     /* set font of multiline edit field    */
	  MLM_SETFONT, MPFROMP((PFATTRS)&fntd->fAttrs), (MPARAM)0);
    }
    WinEnableWindowUpdate(pWinData->outmle, TRUE);  
    free(fntd->pszTitle);
    free(fntd->pszPreview);
    return (TRUE);
  }
