[comp.sources.amiga] v89i126: minrexx - add a rexx port to your programs v0.4

page%swap@Sun.COM (Bob Page) (05/11/89)

Submitted-by: kim@uts.amdahl.com (Kim E. DeVaughn)
Posting-number: Volume 89, Issue 126
Archive-name: libraries/minrexx04.1

This is the latest version of Tom Rokicki's code to show people how to
add a 7-function ARexx port to their own code.

[uuencoded executable included.  ..bob]

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	README
#	freedraw.c
#	minrexx.c
#	minrexx.h
#	Makefile
#	freeedraw.uu
#	aspline.fd
#	bspline.fd
#	sample.fd
#	saspline.fd
#	sbspline.fd
#	version.fd
# This is archive 1 of a 1-part kit.
# This archive created: Thu May 11 00:22:53 1989
echo "extracting README"
sed 's/^X//' << \SHAR_EOF > README
XHere's "minrexx", a simple ARexx interface which can be easily patched into
Xalmost any program, and an example from Fish Disk 1, where I add an ARexx
Xport to freedraw.
X
XFor documentation on Minrexx, refer to the .c and .h file.
X
XFor documentation on the Freedraw port, see the portions of freedraw.c
Xdelimited by "#ifdef TALKTOREXX".
X
XEnjoy!
SHAR_EOF
echo "extracting freedraw.c"
sed 's/^X//' << \SHAR_EOF > freedraw.c
X#ifdef TALKTOREXX
X/*
X *   This program is an example of how to add a Rexx port to a given
X *   program.  I thought it particularly appropriate to grab a program
X *   off Fred Fish Disk 1.  All the REXX stuff is bracketed by `ifdef
X *   TALKTOREXX', so you can identify it easily.  If you compile with
X *   TALKTOREXX unset, you will get the default program with no REXX
X *   port.
X *
X *   The REXX port on this program adds another 3K to the executable
X *   size, but a lot of functionality comes with that.  You can draw
X *   from Rexx, spawn macros from Rexx, etc.  But go to the next
X *   TALKTOREXX for more information.
X *
X *   To run a rexx macro on startup, simply give that rexx macro as
X *   part of the command line, as in
X *
X *      freedraw sample
X *
X *   or
X *
X *      freedraw bspline 20 100 20 20 280 20 280 100
X *
X *   All modifications are by Radical Eye Software, and all modifications
X *   are placed in the public domain.
X */
X#endif
X/************************************************************************/
X/***                FreeDraw - PD graphics for Amiga                  ***/
X/***                                                                  ***/
X/***      This is an extremely simple graphics editor which works in  ***/
X/***   the windowing environment of the Amiga.  It is very limited    ***/
X/***   in features, but I hope to add a lot more, and I would be      ***/
X/***   happy to receive assistance from anyone who wants to give it.  ***/
X/***      The basic idea of this program is to provide some minimal   ***/
X/***   image editing functions which can be used to develop images    ***/
X/***   for other programs.  I know there will be a lot of Paint type  ***/
X/***   type programs avaialable soon, but what are we supposed to use ***/
X/***   now?  The most important features to add now will probably be  ***/
X/***   those related to "cut and paste", disk srtorage and retrieval, ***/
X/***   and "single-pixel" editing like Mac's "fatbits".               ***/
X/***      I intend to use the IFF standard for the image storage and  ***/
X/***   retrieval and will be coding a "Files" menu soon.  The work    ***/
X/***   required for "cut and paste" should be almost trivial, but I   ***/
X/***   still may not get to it for a while.  Fatbits editing from one ***/
X/***   window to another involves some manipulations of the RastPorts ***/
X/***   which still elude me, as I have only recently begun to use the ***/
X/***   Amiga and don't yet understand some important details of these ***/
X/***   structures.  This would be a great item for one of the genius  ***/
X/***   members of the Amiga programming community to provide.         ***/
X/***      There are only two menu topics in this version, so using it ***/
X/***   is really quite easy.  Boxes are not allowed to be drawn in    ***/
X/***   areas outside of the window where border gadgets are located,  ***/
X/***   and the pen-draw mode also clips to these same boundaries.  If ***/
X/***   you have begun to draw a box by clicking the left button while ***/
X/***   the cursor is located in the FreeDraw window, then you can     ***/
X/***   cancel that box by clicking the right button.  In the pen mode ***/
X/***   pressing and holding the left button will draw.   Colors are   ***/
X/***   selected by simply releasing the menu button over the desired  ***/
X/***   color in the Color menu.   The erase feature always clears the ***/
X/***   window to the currently selected color.                        ***/
X/***      This is no gem of programming style, but you're getting it  ***/
X/***   for the right price so be patient with its design flaws.  New  ***/
X/***   versions will appear here on BIX as soon as I can get them in  ***/
X/***   shape for release.  I apologize to anyone who objects to my    ***/
X/***   lack of coding grace, but I just want to get the project off   ***/
X/***   the ground, and improvements will be forthcoming.  There are   ***/
X/***   a lot of comments, but I didn't know what needed to be made    ***/
X/***   clear so I just commented everything.                          ***/
X/***                                                                  ***/
X/***      If you like the idea of a PD graphics program and would be  ***/
X/***   interested in doing some development work, then please write   ***/
X/***   me at the address listed below, or call if you prefer.  I do   ***/
X/***   want to know if there is any interest in such a project, so    ***/
X/***   I will be glad to discuss any ideas you might have.  Also, as  ***/
X/***   I do not currently use CompuServe or any other major nets, I   ***/
X/***   would appreciate if someone would post this listing there.     ***/
X/***      I hope somebody enjoys this.  Have Fun.                     ***/
X/***                                           Rick Ross 11/14/85     ***/
X/***                                                                  ***/
X/***        My address:                                               ***/
X/***                    Richard M. Ross, Jr.                          ***/
X/***                    Eidetic Imaging                               ***/
X/***                    740 N. 22nd Street                            ***/
X/***                    Philadelphia, PA  19130                       ***/
X/***                                                                  ***/
X/***                    Phone - (215) 236-7388                        ***/
X/************************************************************************/
Xchar *VERSION = "Freedraw 0.01 by Richard M. Ross" ;
X/*  compiler directives to fetch the necessary header files */
X
X#include <exec/types.h>
X#include <exec/exec.h>
X#include <intuition/intuition.h>
X#include <intuition/intuitionbase.h>
X#include <graphics/gfx.h>
X#include <graphics/regions.h>
X#include <graphics/copper.h>
X#include <graphics/gels.h>
X#include <graphics/gfxbase.h>
X#include <devices/keymap.h>
X#include <hardware/blit.h>
X
X/*   These definitions are used by intuition for
X *   calls to OpenLibrary() in order to ensure
X *   that an appropriate ROM revision is
X *   available.
X */
X
X#define INTUITION_REV 1L
X#define GRAPHICS_REV  1L
X
X/*   Intuition always wants to see these declarations */
Xstruct IntuitionBase *IntuitionBase;
Xstruct GfxBase *GfxBase;
X
X/*   This is the Window structure declaration.
X *   Nothing fancy is going on here, but note
X *   the Flags and IDCMPFlags members of the
X *   structure define which messages will be
X *   sent by Intuition.  I haven't used them all
X *   and if you want to change the settings you
X *   should probably do it her instead of using
X *   ModifyIDCMP later.
X */
X
Xstruct NewWindow NewWindow = {
X   10,
X   10,
X   600,
X   180,
X   0,
X   1,
X   CLOSEWINDOW | MOUSEMOVE | MOUSEBUTTONS | MENUPICK
X   | NEWSIZE | INACTIVEWINDOW,
X   WINDOWCLOSE | SMART_REFRESH | ACTIVATE | WINDOWDRAG
X   | WINDOWDEPTH | WINDOWSIZING | REPORTMOUSE,
X   NULL,
X   NULL,
X   (UBYTE *)"AMIGA FreeDraw 0.01",
X   NULL,
X   NULL,
X   100, 35,
X   -1, -1,
X   WBENCHSCREEN,
X};
X#ifdef TALKTOREXX
X/*
X *   We need our include file.
X */
X#include "minrexx.h"
X/*
X *   These are the REXX functions defined at the bottom of the file.
X */
Xvoid rexxcolor(), rexxbox(), rexxfbox(), rexxline(), rexxtofront(),
X    rexxtoback(), rexxexit(), rexxversion(), rexxspawn() ;
Xint disp() ;
X/*
X *   Here is our command association list.  Note that in this case,
X *   we are setting the userdata field to be a function to call.
X *   Dispatch will still take place through disp(), so common head
X *   and tail stuff can go there.
X *
X *   Commands are all lower case, so we match either upper or lower.
X *   (This is a requirement of minrexx.)
X */
Xstruct rexxCommandList rcl[] = {
X   { "color", (APTR)&rexxcolor },
X   { "box", (APTR)&rexxbox },
X   { "fbox", (APTR)&rexxfbox },
X   { "line", (APTR)&rexxline },
X   { "tofront", (APTR)&rexxtofront },
X   { "toback", (APTR)&rexxtoback },
X   { "exit", (APTR)&rexxexit },
X   { "version", (APTR)&rexxversion },
X   { "spawn", (APTR)&rexxspawn },
X   { NULL, NULL } } ;
X#endif
X/*******************************************************************/
X/*      DrawBox - Simple routine to draw an unfilled rectangle     */
X/*   It accepts the  coordinates of the top-left and lower-right   */
X/*   points of the rectangle, a pointer to the Window structure,   */
X/*   and the color in which to render the rectangle.  The current  */
X/*   FgPen color of the window is preserved thru the call.  No     */
X/*   clipping is done.                                             */
X/*******************************************************************/
Xvoid DrawBox( tlx, tly, brx, bry, window, color )
XSHORT tlx, tly;                  /* top-left x,y coordinates */
XSHORT brx, bry;                  /* lower-right x,y coordinates */
Xstruct Window *window;           /* pointer to target window */
XBYTE color;                      /* color to use for render */
X   {
X   BYTE OldColor = window->RPort->FgPen;   /* save window's FgPen */
X
X   SetAPen( window->RPort, (long)color );        /* set draw color for box  */
X   Move(window->RPort, (long)tlx, (long)tly);          /* move to top-left point  */
X   Draw(window->RPort, (long)brx, (long)tly);          /* and draw to each of the */
X   Draw(window->RPort, (long)brx, (long)bry);          /* four corners of the box */
X   Draw(window->RPort, (long)tlx, (long)bry);
X   Draw(window->RPort, (long)tlx, (long)tly);
X   SetAPen( window->RPort, (long)OldColor );     /* restore old FgPen */
X   }
X
X
X/*********************************************************/
X/*                 Color Select Menu                     */
X/*                                                       */
X/*      This is where the menu for color selection is    */
X/*   defined.  It should be flexible enough to allow for */
X/*   increased palette sizes, but this version is only   */
X/*   for the 4-color mode of the WorkBench screen.       */
X/*********************************************************/
X
X/*   A few definitions are needed here.
X *   Note that MAXPAL should be increased
X *   to allow for palette larger than
X *   four colors.    
X */
X#define ITEMSTUFF (ITEMENABLED | HIGHBOX)
X#define CW 40
X#define CH 25
X#define MAXPAL 4
X
X/*   declare enough storage for required
X *   number of menu items and associated
X *   images.  This menu will be using
X *   graphics renditions of menu items,
X *   so the Image structures must be
X *   declared.  This menu is modeled after
X *   the one found in the IconEd source.
X */
Xstruct MenuItem coloritem[MAXPAL];
Xstruct Image colorimage[MAXPAL];
X
X/*   array of palette sizes to correspond with
X *   depth of window in bit-planes
X */
XSHORT palette[] = { 2, 4, 8, 16, 32 };
X
X
X/*****************************************************************/
X/*    The following function initializes the structure arrays    */
X/*   needed to provide the Color menu topic.                     */
X/*****************************************************************/
XInitColorItems( depth )
XSHORT depth;               /* number of bit-planes in window */
X   {
X   SHORT n, colors;
X
X   colors = palette[depth-1];
X   for( n=0; n<colors; n++ )           /* loop for max number of items */
X      {
X      coloritem[n].NextItem = &coloritem[n+1];
X      coloritem[n].ItemFill = (APTR)&colorimage[n];
X      /*   the next two items might be changed for
X       *   when bit-planes is greater than 2
X       */
X      coloritem[n].LeftEdge = 2 + CW * (n % 4);
X      coloritem[n].TopEdge = CH * (n / 4);
X      coloritem[n].Width = CW;
X      coloritem[n].Height = CH;
X      coloritem[n].Flags = ITEMSTUFF;
X      coloritem[n].MutualExclude = 0;
X      coloritem[n].SelectFill = NULL;
X      coloritem[n].Command = 0;
X      coloritem[n].SubItem = NULL;
X      coloritem[n].NextSelect = 0;
X
X      colorimage[n].LeftEdge = 1;
X      colorimage[n].TopEdge = 1;
X      colorimage[n].Width = CW-2;
X      colorimage[n].Height = CH-2;
X      colorimage[n].Depth = depth;
X      colorimage[n].ImageData = NULL;
X      colorimage[n].PlanePick = 0;
X      colorimage[n].PlaneOnOff = n;
X      }
X   coloritem[colors-1].NextItem = NULL;   /* needed for last item in list */
X   return( 0 );
X   }
X
X
X/*****************************************************/
X/*                Draw Mode Menu                     */
X/*                                                   */
X/*      Here are the code and data declarations for  */
X/*   the DrawMode menu.  Current choices are limited */
X/*   to Erase, Filled Box, Hollow Box, and PenDraw.  */
X/*****************************************************/
X
X/* define maximum number of menu items */
X#define DMODEMAX 4
X
X/*   declare storage space for menu items and
X *   their associated IntuiText structures
X */
Xstruct MenuItem DModeItem[DMODEMAX];
Xstruct IntuiText DModeText[DMODEMAX];
X
X/*****************************************************************/
X/*    The following function initializes the structure arrays    */
X/*   needed to provide the DrawMode menu topic.                  */
X/*****************************************************************/
XInitDModeItems()
X   {
X   short n;
X
X   /* initialize each meu item and IntuiText with loop */
X   for( n=0; n<DMODEMAX; n++ )
X      {
X      DModeItem[n].NextItem = &DModeItem[n+1];
X      DModeItem[n].LeftEdge = 0;
X      DModeItem[n].TopEdge = 10 * n;
X      DModeItem[n].Width = 112;
X      DModeItem[n].Height = 10;
X      DModeItem[n].Flags = ITEMTEXT | ITEMENABLED | HIGHBOX;
X      DModeItem[n].MutualExclude = 0;
X      DModeItem[n].ItemFill = (APTR)&DModeText[n];
X      DModeItem[n].SelectFill = NULL;
X      DModeItem[n].Command = 0;
X      DModeItem[n].SubItem = NULL;
X      DModeItem[n].NextSelect = 0;
X
X      DModeText[n].FrontPen = 0;
X      DModeText[n].BackPen = 1;
X      DModeText[n].DrawMode = JAM2;     /* render in fore and background */
X      DModeText[n].LeftEdge = 0;
X      DModeText[n].TopEdge = 1;
X      DModeText[n].ITextFont = NULL;
X      DModeText[n].NextText = NULL;
X      }
X   DModeItem[DMODEMAX-1].NextItem = NULL;
X
X   /* initialize text for specific menu items */
X   DModeText[0].IText = (UBYTE *)"Erase All";
X   DModeText[1].IText = (UBYTE *)"Hollow Box";
X   DModeText[2].IText = (UBYTE *)"Filled Box";
X   DModeText[3].IText = (UBYTE *)"Pen Draw";
X
X   return( 0 );
X   }
X
X
X/***************************************************/
X/*                Menu Definition                  */
X/*                                                 */
X/*      This section of code is where the simple   */
X/*   menu definition goes.  For now it supports    */
X/*   only Color and Drawmode selection, but new    */
X/*   choices can easily be added by creating       */
X/*   structures and initializations functions      */
X/*   similar to those provided above.              */
X/***************************************************/
X
X/* current number of available menu topics */
X#define MAXMENU 2
X
X/*   declaration of menu structure array for
X *   number of current topics.  Intuition
X *   will use the address of this array to
X *   set and clear the menus associated with
X *   the window.
X */
Xstruct Menu menu[MAXMENU];
X
X/**********************************************************************/
X/*   The following function initializes the Menu structure array with */
X/*  appropriate values for our simple menu strip.  Review the manual  */
X/*  if you need to know what each value means.                        */
X/**********************************************************************/
XInitMenu()
X   {
X   menu[0].NextMenu = &menu[1];
X   menu[0].LeftEdge = 10;
X   menu[0].TopEdge = 0;
X   menu[0].Width = 50;
X   menu[0].Height = 10;
X   menu[0].Flags = MENUENABLED;
X   menu[0].MenuName = "Color";           /* text for menu-bar display */
X   menu[0].FirstItem = &coloritem[0];    /* pointer to first item in list */
X
X   menu[1].NextMenu = NULL;
X   menu[1].LeftEdge = 65;
X   menu[1].TopEdge = 0;
X   menu[1].Width = 85;
X   menu[1].Height = 10;
X   menu[1].Flags = MENUENABLED;
X   menu[1].MenuName = "DrawMode";        /* text for menu-bar display */
X   menu[1].FirstItem = &DModeItem[0];    /* pointer to first item in list */
X
X   return( 0 );
X   }
X
X
X/******************************************************/
X/*                   Main Program                     */
X/*                                                    */
X/*      This is the main body of the program.         */
X/******************************************************/
X
Xstruct Window *Window;              /* ptr to applications window */
XSHORT MinX, MinY, MaxX, MaxY;       /* clipping boundary variables */
XSHORT KeepGoing = TRUE;             /* main loop control value */
X
Xmain(argc, argv)
Xint argc ;
Xchar *argv[] ;
X   {
X   struct Library *OpenLibrary() ;
X   struct Window *OpenWindow() ;
X   struct Message *GetMsg() ;
X   struct IntuiMessage *NewMessage;    /* msg structure for GetMsg() */
X   BYTE DrawColor = 1;                 /* initial drawing color */
X   SHORT OldBRX = 30, OldBRY = 30;     /* point coords used for boxes */
X   SHORT TLX = 20, TLY = 20;           /* initial top-left point coords */
X   ULONG class;                        /* used in message monitor loop */
X   USHORT code;                        /* used in message monitor loop */
X   SHORT x, y, x1, y1, x2, y2;         /* various coordinate variables */
X   USHORT MenuNum, ItemNum;
X   /*   The following is a set of declarations
X    *   for a number of flag values used by the
X    *   program.  These would perhaps be better
X    *   coded as a bit-field for all the flags,
X    *   but I'm lazy, and this is easier.
X    */
X   SHORT MouseMoved = FALSE;     /* indicates new mouse position ready */
X   SHORT ClipIt = FALSE;         /* are new point coords out of bounds? */
X   SHORT ClippedLast = FALSE;    /* was last PenDraw operation clipped? */
X   SHORT PenMode = FALSE;        /* indicates PenDraw mode is set */      
X   SHORT PenDown = FALSE;        /* if mouse moved, then should it draw? */
X   SHORT RubberBox = FALSE;      /* are we currently rubberbanding a box? */
X   SHORT FilledBox = FALSE;      /* should boxes be filled when drawn? */
X#ifdef TALKTOREXX
X/*
X *   If we are talking to REXX, we need these two additional locals.
X */
X   long rexxbit ;
X   char firstcommand[256] ;
X#endif
X
X   /* attempt to Open Library to access Intuition */
X   IntuitionBase = (struct IntuitionBase *)
X      OpenLibrary("intuition.library", INTUITION_REV);
X   if( IntuitionBase == NULL )
X      exit(FALSE);
X
X   /* attempt to OpenLibrary to access Graphics functions */
X   GfxBase = (struct GfxBase *)
X      OpenLibrary("graphics.library",GRAPHICS_REV);
X   if( GfxBase == NULL )
X      exit(FALSE);
X
X
X   /* Try to open new window for application */
X   if(( Window = OpenWindow(&NewWindow) ) == NULL)
X      exit(FALSE);
X
X   /*   set initial clipping boundaries
X    *   from the values found in the window
X    *   structure for border dimensions
X    */
X   MinX = Window->BorderLeft;
X   MinY = Window->BorderTop;
X   MaxX = Window->Width - Window->BorderRight - 1;
X   MaxY = Window->Height - Window->BorderBottom - 1;
X
X   InitColorItems( 2 );         /* initialize Color menu arrays */
X   InitDModeItems();            /* initialize DrawMode menu arrays */
X   InitMenu();                  /* initialize the menu structures */
X
X   /*   Now, having initialized the various arrays
X    *   of structures required for menu generation
X    *   we can tell Intuition to make our menus
X    *   available to the user when this window
X    *   is active.
X    */
X   SetMenuStrip( Window, &menu[0] );
X
X   /* set initial drw mode and color */
X   SetDrMd( Window->RPort, JAM1 );
X   SetAPen( Window->RPort, DrawColor );
X#ifdef TALKTOREXX
X/*
X *   For rexx, we open up a Rexx port, and send out the first command,
X *   if there was one.  We send it out asynchronously; no reason not to.
X */
X   rexxbit = upRexxPort("freedraw", rcl, "fd", &disp) ;
X   firstcommand[0] = 0 ;
X   for (x=1; x<argc; x++) {
X      strcat(firstcommand, argv[x]) ;
X      strcat(firstcommand, " ") ;
X   }
X   if (firstcommand[0]) {
X      asyncRexxCmd(firstcommand) ;
X   }
X#endif
X   /*   Everything the program needs is now
X    *   initialized and put in place.  The
X    *   program enters the following loop
X    *   and processes message continuously as
X    *   they are received from Intuition.
X    *      I guess this loop is the real workhorse
X    *   of the program.  By the way, the loop
X    *   control variable KeepGoing remains TRUE
X    *   until a CLOSEWINDOW message is received.
X    *   At that point it goes FALSE, and the
X    *   program cleans up and exits.
X    */
X   while( KeepGoing )
X      {
X
X      /* stay here until a message is received from Intuition */
X#ifdef TALKTOREXX
X/*
X *   If we're working with Rexx, we wait on the Rexx bit as well.
X *   Then, we handle any Rexx messages.
X */
X      Wait( (1L << Window->UserPort->mp_SigBit) | rexxbit);
X      dispRexxPort() ;
X#else
X      Wait( 1L << Window->UserPort->mp_SigBit);
X#endif
X      MouseMoved = FALSE;    /* clear this flag each time thru loop */
X
X      /*   since more than one message may be waiting
X       *   a reply at this point, a loop is used to
X       *   process all that have come in until no more
X       *   are ready.  Msg received is assigned to
X       *   NewMessage from the GetMsg() function.  This
X       *   value will be NULL if no message is ready,
X       *   and control passes out of the loop at that time
X       */
X      while( NewMessage=(struct IntuiMessage *)GetMsg(Window->UserPort) )
X         {
X
X         /*   copy some values from the message structure
X          *   to variables used in the switch statements
X          *   below
X          */
X         class = NewMessage->Class;
X         code = NewMessage->Code;
X         x = Window->MouseX;
X         y = Window->MouseY;
X
X         /*   SIZEVERIFY is a very high priority message
X          *   in our loop and requires some immediate
X          *   servicing.  Any outstanding draw operations
X          *   are immediately cancelled, and the DrawMode
X          *   is nulled.  This prevents any attempts to
X          *   render outside whatever new Window boundaries
X          *   the user chooses.
X          *
X          *   (not anymore, it don't.  -tgr)
X
X         if( class == SIZEVERIFY )
X            {
X            PenDown = FALSE;
X            if( RubberBox )
X               {
X               DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X               Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );
X               RubberBox = FALSE;
X               }
X            }
X          */
X
X         /*   we have all the information needed from
X          *   the message, so we can now safely reply
X          *   to it without losing data
X          */
X         ReplyMsg( NewMessage );
X
X         /*  Examine point coords from message received
X          *  and set the clipping flag if out of bounds.
X          *  If user was drawing in PenMode when message
X          *  was received, then the ClippedLast flag
X          *  should also be set to indicate this to the
X          *  next draw operation.
X          */
X         if(ClipIt = ( x < MinX || x > MaxX || y < MinY || y > MaxY ))
X            if( PenDown )
X               ClippedLast = TRUE;
X
X
X         /* enter switch on type of message received */
X         switch( class )
X         {
X            case MOUSEMOVE:
X               /*   Don't really do anything with this one
X                *   until any other, more important, messages
X                *   are received and processed.
X                */
X               MouseMoved = TRUE;
X               break;
X
X            case NEWSIZE:
X               /*  set new clipping boundaries */
X               MinX = Window->BorderLeft;
X               MinY = Window->BorderTop;
X               MaxX = Window->Width - Window->BorderRight - 1;
X               MaxY = Window->Height - Window->BorderBottom - 1;
X               break;
X
X            case CLOSEWINDOW:
X               /*   User is ready to quit, so indicate
X                *   that execution should terminate
X                *   with next iteration of the loop.
X                */
X               KeepGoing = FALSE;
X               break;
X
X            case MOUSEBUTTONS:
X               /*   A number of things could have happened
X                *   here, and further examination of data
X                *   received from message is needed to
X                *   determine what action should be taken.
X                *   The code variable holds important info
X                *   about what actually caused the message
X                *   to be sent in the first place.
X                */
X               switch ( code )
X                  {
X                  case SELECTUP:
X                     /*   User was holding down the left button
X                      *   and just released it.  The PenMode
X                      *   flag variables are set accordingly.
X                      *   The pen can no longer be down, and
X                      *   ClippedLast is reset for next time.
X                      */
X                     PenDown = ClippedLast = FALSE;
X                     break;
X
X                  case SELECTDOWN:
X                     /*   User has pressed the left button, and
X                      *   several differnt actions may need to
X                      *   be taken.  If the ClipIt value is TRUE,
X                      *   then no action should be taken at all.
X                      */
X                     if( ClipIt )
X                        break;
X
X                     /*   If user is currently in PenMode, then
X                      *   set up to draw when MOUSEMOVED messages
X                      *   are received until a subsequent SELECTUP
X                      *   message comes in.
X                      */
X                     if( PenMode )
X                        {
X                        PenDown = TRUE;
X                        ClippedLast = FALSE;
X
X                        /* make sure to set appropriate mode */
X                        SetDrMd( Window->RPort, JAM1 );
X
X                        /* and establish initial position to draw */
X                        Move( Window->RPort, (long)x, (long)y );
X                        break;
X                        }
X
X                     /*   If user is currently rubberbanding a box,
X                      *   then a SELECTDOWN message means it is time
X                      *   to stop rubberbanding and actually draw it.
X                      *   The following code will be executed if
X                      *   this is the case, and it will determine if
X                      *   a filled box is needed before rendering.
X                      */
X                     if( RubberBox )
X                        {
X                        /*   set draw mode back to JAM1 since
X                         *   it is now currently set to COMPLEMENT
X                         */
X                        SetDrMd( Window->RPort, JAM1 );
X                        RubberBox = FALSE;   /* turn off rubberbanding */
X
X                        /*   Restore the condition of the RMBTRAP
X                         *   bit in the Window structure's Flags
X                         *   member.  Menubutton events will no
X                         *   be received by this loop.
X                         */
X                        Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );
X
X                        /*   RectFill is not condusive to the smooth
X                         *   execution of programs iit arguments are
X                         *   out of order, sot his code sorts them
X                         *   in preparation for the call.
X                         */
X                        if( FilledBox )
X                           {
X                           /* first sort the x-coords */
X                           if( TLX < OldBRX )  {
X                              x1 = TLX;  x2 = OldBRX;  }
X                           else  {
X                              x1 = OldBRX;  x2 = TLX;  }
X
X                           /* then sort the y-coords */
X                           if( TLY < OldBRY )  {
X                              y1 = TLY;  y2 = OldBRY;  }
X                           else  {
X                              y1 = OldBRY;  y2 = TLY;  }
X
X                           /* now generate the filled rectangle */
X                           RectFill( Window->RPort, (long)x1, (long)y1,
X                                                    (long)x2, (long)y2 );
X                           }
X                        else
X                           {
X                           /* FilledBox not set, so draw hollow box */
X                           DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X                           }
X                        break;
X                        }
X
X                     /*   If execution comes here, then PenMode was
X                      *   not set and user was not rubberbanding.
X                      *   SELECTDOWN therefore indicates to start the
X                      *   rubberbanding process at this point.  The
X                      *   initial coords are set to the values we
X                      *   received when the GetMsg() was executed.
X                      */
X                     TLX = OldBRX = x;  TLY = OldBRY = y;
X
X                     /* set to render in XOR mode */
X                     SetDrMd( Window->RPort, COMPLEMENT );
X
X                     /* set flag to indicate we are now rubberbanding */
X                     RubberBox = TRUE;
X
X                     /*   This instruction indicates to Intuition
X                      *   that we now wish to receive a message
X                      *   each time the Menubutton is pressed.
X                      *   This is how we hijack the right button
X                      *   for temporary use as a Cancel button
X                      *   instead of a Menubutton.
X                      */
X                     Window->Flags |= RMBTRAP;
X
X                     /* render the initial rubberbox and exit */
X                     DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X                     break;
X
X                  case MENUDOWN:
X                     /*   WE only receive this message class if
X                      *   the RMBTRAP flag bit has been set, so
X                      *   it always means that we should cancel
X                      *   the box which is currently rubberbanding.
X                      */
X                     /* turn the flag off */
X                     RubberBox = FALSE;
X
X                     /* restore control of menubutton to Intuition */
X                     Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );
X
X                     /*   erase (by double XOR'ing) the current
X                      *   rubberbox and exit switch.
X                      */
X                     DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X                     break;
X
X                  default:
X                     /*   Something unimportant happened, so just
X                      *   continue thru the GetMsg() loop.
X                      */
X                     continue;
X               }
X               break;
X
X            case MENUPICK:
X               /*   A menu event has taken place and is
X                *   ready to be processed.  Examine the
X                *   code variable received from the message
X                *   to determine what action should be taken.
X                *   The first check is for MENUNULL, which
X                *   means that nothing should be done at all.
X                */
X               if( code != MENUNULL )
X                  {
X                  /* get menu and item numbers from code */
X                  MenuNum = MENUNUM( code );
X                  ItemNum = ITEMNUM( code );
X
X                  /* determine appropriate action by menu number */
X                  switch ( MenuNum )
X                     {
X                     case 0:
X                        /*   Menu 0 is the Color menu.  The
X                         *   item number indicates which new
X                         *   color to set.
X                         */
X                        DrawColor = ItemNum;
X                        SetAPen( Window->RPort, (long)DrawColor );
X                        break;
X
X                     case 1:
X                        /*   Menu 1 is the DrawMode menu.  The item
X                         *   number indicates what to do.
X                         *   NOTE:  Since we cannot have received
X                         *   this message if we were rubberbanding,
X                         *   then there is no need to clean up before
X                         *   changing drawing modes.
X                         */
X                        switch ( ItemNum )
X                           {
X                           case 0:
X                              /* Erase window to current color */
X                              SetDrMd( Window->RPort, JAM1 );
X                              RectFill( Window->RPort, (long)MinX, (long)MinY,
X                                                       (long)MaxX, (long)MaxY);
X                              break;
X
X                           case 1:
X                              /* set flag variables for hollow box */
X                              PenMode = FALSE;
X                              FilledBox = FALSE;
X                              break;
X
X                           case 2:
X                              /* set flag variables for filled box */
X                              PenMode = FALSE;
X                              FilledBox = TRUE;
X                              break;
X
X                           case 3:
X                              /* set flag variables for PenMode */
X                              PenMode = TRUE;
X                              break;
X
X                           default:
X                              /* don't do anything */
X                              break;
X                           }
X                        break;
X
X                     default:
X                        /* Menu number unrecognized, do nothing */
X                        break;
X                     }
X                  }
X               break;
X
X            case INACTIVEWINDOW:
X               /*   User has de-selected our window, so a
X                *   little bit of cleaning up may be needed
X                *   to prevent untoward events when he comes
X                *   back to it.
X                */
X               /* erase any outstanding rubberbox */
X               if( RubberBox )
X                  DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X
X               /* reset all the flafg variables */
X               PenDown = ClippedLast = RubberBox = FALSE;
X
X               /* return possibly diverted menubutton events to Big I */
X               Window->Flags &= ( 0xFFFFFFFF ^ RMBTRAP );
X               break;
X
X            default:
X               /* message class was unrecognized, so do nothing */
X               break;
X            }
X         }   /* this brace ends the while(NewMessage) loop way back when */
X
X      /*   There are no more messages waiting at the
X       *   IDCMP port, so we can now proceed to
X       *   process any MOUSEMOVED message we may
X       *   have received.
X       */
X      if( MouseMoved && !ClipIt)
X         {
X         /* the mouse did move, and we don't need to clip */
X
X         /* check first if we are drawing in PenMode */
X         if( PenDown )
X            {
X            /*   We have to examine if we clipped the
X             *   last PenMode draw operation.  If we did,
X             *   then this is the first move back into
X             *   window boundaries, so we mov instead of
X             *   drawing.
X             */
X            if( ClippedLast )
X               {
X               ClippedLast = FALSE;         /* reset this flag now */
X               Move( Window->RPort, (long)x, (long)y );
X               }
X            else
X               Draw( Window->RPort, (long)x, (long)y ); /* draw to x,y coords */
X         }
X         else
X            {
X            /*   We weren't in PenMode, but we still might
X             *   be rubberbanding a box.  If so, then we
X             *   should erase the current rubberbox and
X             *   draw a new one with the new mouse coords.
X             */
X            if( RubberBox )
X               {
X               /* erase the old rubberbox - draw mode is COMPLEMENT */
X               DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X
X               /* assign new values to box coords */
X               OldBRX = x;  OldBRY = y;
X
X               /* and draw the new rubberbox */ 
X               DrawBox( TLX, TLY, OldBRX, OldBRY, Window, DrawColor );
X               }
X            }
X         }
X      }
X
X   /*   It must be time to quit, so we have to clean
X    *   up and exit.
X    */
X#ifdef TALKTOREXX
X/*
X *   With Rexx, we need to bring the port down.  You might make this
X *   part of exit() for programs that have multiple paths to exit.
X */
X   dnRexxPort() ;
X#endif
X   ClearMenuStrip( Window );
X   CloseWindow( Window );
X   exit(TRUE);
X   }
X#ifdef TALKTOREXX
X/*
X *   Now we get into the actual code necessary for our REXX port; functions
X *   that do the real work.  Note that this program was not structured
X *   particularly nicely for Rexx; I had to write each of these functions.
X *   Many programs have these subroutines already in place; they are called
X *   as part of the event loop.  This progam, however, just has one big
X *   switch statement with different actions . . .
X *
X *   First, our locals.
X */
Xint currrexxcolor = 1 ;    /* what color is *rexx* drawing in? */
Xint args[4] ;              /* what args did we see to this function? */
Xint parsed ;               /* was argument parsing successful? */
Xint userreplied ;          /* has the current message been replied to yet? */
X/*
X *   This function takes a pointer to a pointer to a string, grabs the
X *   next number, returns it, and advances the pointer to the string to
X *   point after the number.
X */
Xint getnm(where)
Xchar **where ;
X{
X   register char *p = *where ;
X   register int val = 0 ;
X   int gotone = 0 ;
X
X   while (*p <= ' ' && *p)
X      p++ ;
X   while ('0' <= *p && *p <= '9') {
X      gotone = 1 ;
X      val = 10 * val + *p++ - '0' ;
X   }
X   if (gotone == 0)
X      parsed = 0 ;
X   *where = p ;
X   return(val) ;
X}
X/*
X *   This function trys to find `n' numeric arguments in the command
X *   string, and stuffs them into the args array.
X */
Xvoid parseargs(p, n)
Xchar *p ;
Xint n ;
X{
X   register int i ;
X
X   while (*p > ' ' && *p)
X      p++ ;
X   for (i=0; i<n; i++)
X      args[i] = getnm(&p) ;
X}
X/*
X *   This is our main dispatch function.  We check to make sure a Window
X *   currently exists.  Then, we store away the `current color' and change
X *   it to Rexx's current color, call our handler function, and then restore
X *   the color.  If our handler replied, we return a 1 to indicate that.
X *   If the parse and everything else was successful, we return a 0.
X *   Otherwise, we return a failure of 20 to indicate that the arguments
X *   were messed up.
X */
Xint disp(msg, dat, p)
Xregister struct RexxMsg *msg ;
Xregister struct rexxCommandList *dat ;
Xchar *p ;
X{
X   register int t ;
X
X   parsed = 1 ;
X   if (Window) {
X      userreplied = 0 ;
X      t = Window->RPort->FgPen ;
X      SetAPen(Window->RPort, (long)currrexxcolor) ;
X      ((int (*)())(dat->userdata))(msg, p) ;
X      SetAPen(Window->RPort, (long)t) ;
X      if (! parsed)
X         replyRexxCmd(msg, (long)parsed, 0L, NULL) ;
X      return ;
X   }
X   replyRexxCmd(msg, 20L, 10L, NULL) ;
X}
X/*
X *   This handler sets the current rexx color.
X */
Xvoid rexxcolor(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X   parseargs(p, 1) ;
X   currrexxcolor = args[0] ;
X}
X/*
X *   This function silently clips the x and y values at `n' to the
X *   window bounds.
X */
Xvoid clipxy(n)
Xint n ;
X{
X   if (args[n] < MinX)
X      args[n] = MinX ;
X   if (args[n] > MaxX)
X      args[n] = MaxX ;
X   n++ ;
X   if (args[n] < MinY)
X      args[n] = MinY ;
X   if (args[n] > MaxY)
X      args[n] = MaxY ;
X}
X/*
X *   This handler grabs four arguments and draws a box.
X */
Xvoid rexxbox(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X   parseargs(p, 4) ;
X   clipxy(0) ;
X   clipxy(2) ;
X   DrawBox(args[0], args[1], args[2], args[3], Window, currrexxcolor) ;
X}
X/*
X *   This handler grabs four arguments and draws a filled box.
X */
Xvoid rexxfbox(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X   register int t ;
X
X   parseargs(p, 4) ;
X   clipxy(0) ;
X   clipxy(2) ;
X   if (args[0] > args[2]) {
X      t = args[0] ; args[0] = args[2] ; args[2] = t ;
X   }
X   if (args[1] > args[3]) {
X      t = args[1] ; args[1] = args[3] ; args[3] = t ;
X   }
X   RectFill( Window->RPort, (long)args[0], (long)args[1],
X                            (long)args[2], (long)args[3]) ;
X}
X/*
X *   This handler grabs four arguments and draws a line.
X */
Xvoid rexxline(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X   parseargs(p, 4) ;
X   clipxy(0) ;
X   clipxy(2) ;
X   Move(Window->RPort, (long)args[0], (long)args[1]) ;
X   Draw(Window->RPort, (long)args[2], (long)args[3]) ;
X}
X/*
X *   This handler pops the window to front.
X */
Xvoid rexxtofront(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X   WindowToFront(Window) ;
X}
X/*
X *   This handler pops the window to back.
X */
Xvoid rexxtoback(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X   WindowToBack(Window) ;
X}
X/*
X *   This handler sets the exit flag.
X */
Xvoid rexxexit(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X   KeepGoing = 0 ;
X}
X/*
X *   This handler returns the version of the program.
X */
Xvoid rexxversion(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X   userreplied = 1 ;
X   replyRexxCmd(msg, 0L, 0L, VERSION) ;
X}
X/*
X *   This handler sends the rest of the command asynchronously,
X *   allowing us to run macros in parallel.
X */
Xvoid rexxspawn(msg, p)
Xstruct RexxMsg *msg ;
Xchar *p ;
X{
X   while (*p <= ' ' && *p)
X      p++ ;
X   asyncRexxCmd(p) ;
X}
X#endif
SHAR_EOF
echo "extracting minrexx.c"
sed 's/^X//' << \SHAR_EOF > minrexx.c
X/*
X *   This is an example of how REXX messages might be handled.  This is
X *   a `minimum' example that both accepts asynchronous REXX messages and
X *   can request REXX service.
X *
X *   Read this entire file!  It's short enough.
X *
X *   It is written in such a fashion that it can be attached to a program
X *   with a minimum of fuss.  The only external symbols it makes available
X *   are the seven functions and RexxSysBase.
X *
X *   This code is by Radical Eye Software, but it is put in the public
X *   domain.  I would appreciate it if the following string was left in
X *   both as a version check and as thanks from you for the use of this
X *   code.
X *
X *   If you modify this file for your own use, don't bump the version
X *   number; add a suffix, such as 1.0a or 1.0.3 or something, so we
X *   don't have fake `versions' floating around.
X */
Xstatic char *blurb = "Radical Eye MinRexx 0.4" ;
X/*
X *   We read in our own personal little include.
X */
X#include "minrexx.h"
X/*
X *   All of our local globals, hidden from sight.
X */
Xstatic struct MsgPort *rexxPort ;          /* this is *our* rexx port */
Xstatic int bringerdown ;                   /* are we trying to shut down? */
Xstatic struct rexxCommandList *globalrcl ; /* our command association list */
Xstatic long stillNeedReplies ;             /* how many replies are pending? */
Xstatic long rexxPortBit ;                  /* what bit to wait on for Rexx? */
Xstatic char *extension ;                   /* the extension for macros */
Xstatic int (*userdisp)() ;                 /* the user's dispatch function */
Xstatic struct RexxMsg *oRexxMsg ;          /* the outstanding Rexx message */
X/*
X *   Our library base.  Don't you dare close this!
X */
Xstruct RxsLib *RexxSysBase ;
X/*
X *   This is the main entry point into this code.
X */
Xlong upRexxPort(s, rcl, exten, uf)
X/*
X *   The first argument is the name of your port to be registered;
X *   this will be used, for instance, with the `address' command of ARexx.
X */
Xchar *s ;
X/*
X *   The second argument is an association list of command-name/user-data
X *   pairs.  It's an array of struct rexxCommandList, terminated by a
X *   structure with a NULL in the name field. The commands are case
X *   sensitive.  The user-data field can contain anything appropriate,
X *   perhaps a function to call or some other data.
X */
Xstruct rexxCommandList *rcl ;
X/*
X *   The third argument is the file extension for ARexx macros invoked
X *   by this program.  If you supply this argument, any `primitive' not
X *   in the association list rcl will be sent out to ARexx for
X *   interpretation, thus allowing macro programs to work just like
X *   primitives.  If you do not want this behavior, supply a `NULL'
X *   here, and those commands not understood will be replied with an
X *   error value of RXERRORNOCMD.
X */
Xchar *exten ;
X/*
X *   The fourth argument is the user dispatch function.  This function
X *   will *only* be called from rexxDisp(), either from the user calling
X *   this function directly, or from dnRexxPort().  Anytime a command
X *   match is found in the association list, this user-supplied function
X *   will be called with two arguments---the Rexx message that was
X *   received, and a pointer to the association pair.  This function
X *   should return a `1' if the message was replied to by the function
X *   and a `0' if the default success code of (0, 0) should be returned.
X *   Note that the user function should never ReplyMsg() the message;
X *   instead he should indicate the return values with replyRexxCmd();
X *   otherwise we lose track of the messages that still lack replies.
X */
Xint (*uf)() ;
X/*
X *   upRexxPort() returns the signal bit to wait on for Rexx messages.
X *   If something goes wrong, it simply returns a `0'.  Note that this
X *   function is safe to call multiple times because we check to make
X *   sure we haven't opened already.  It's also a quick way to change
X *   the association list or dispatch function.
X */
X{
X   struct MsgPort *FindPort() ;
X   struct MsgPort *CreatePort() ;
X
X/*
X *   Some basic error checking.
X */
X   if (rcl == NULL || uf == NULL)
X      return(0L) ;
X/*
X *   If we aren't open, we make sure no one else has opened a port with
X *   this name already.  If that works, and the createport succeeds, we
X *   fill rexxPortBit with the value to return.
X *
X *   Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check
X *   for rexxPort == NULL also insures that our rexxPortBit is 0.
X */
X   if (rexxPort == NULL) {
X      Forbid() ;
X      if (FindPort(s)==NULL)
X         rexxPort = CreatePort(s, 0L) ;
X      Permit() ;
X      if (rexxPort != NULL)
X         rexxPortBit = 1L << rexxPort->mp_SigBit ;
X   }
X/*
X *   Squirrel away these values for our own internal access, and return
X *   the wait bit.
X */
X   globalrcl = rcl ;
X   extension = exten ;
X   userdisp = uf ;
X   return(rexxPortBit) ;
X}
X/*
X *   This function closes the rexx library, but only if it is open
X *   and we aren't expecting further replies from REXX.  It's
X *   *private*, but it doesn't have to be; it's pretty safe to
X *   call anytime.
X */
Xstatic void closeRexxLib() {
X   if (stillNeedReplies == 0 && RexxSysBase) {
X      CloseLibrary(RexxSysBase) ;
X      RexxSysBase = NULL ;
X   }
X}
X/*
X *   This function closes down the Rexx port.  It is always safe to
X *   call, and should *definitely* be made a part of your cleanup
X *   routine.  No arguments and no return.  It removes the Rexx port,
X *   replies to all of the messages and insures that we get replies
X *   to all the ones we sent out, closes the Rexx library, deletes the
X *   port, clears a few flags, and leaves.
X */
Xvoid dnRexxPort() {
X   if (rexxPort) {
X      RemPort(rexxPort) ;
X      bringerdown = 1 ;
X/*
X *   A message still hanging around?  We kill it off.
X */
X      if (oRexxMsg) {
X         oRexxMsg->rm_Result1 = RXERRORIMGONE ;
X         ReplyMsg(oRexxMsg) ;
X         oRexxMsg = NULL ;
X      }
X      while (stillNeedReplies) {
X         WaitPort(rexxPort) ;
X         dispRexxPort() ;
X      }
X      closeRexxLib() ;
X      DeletePort(rexxPort) ;
X      rexxPort = NULL ;
X   }
X   rexxPortBit = 0 ;
X}
X/*
X *   Here we dispatch any REXX messages that might be outstanding.
X *   This is the main routine for handling Rexx messages.
X *   This function is fast if no messages are outstanding, so it's
X *   pretty safe to call fairly often.
X *
X *   If we are bring the system down and flushing messages, we reply
X *   with a pretty serious return code RXERRORIMGONE.
X *
X *   No arguments, no returns.
X */
Xvoid dispRexxPort() {
X   register struct RexxMsg *GetMsg() ;
X   register struct RexxMsg *RexxMsg ;
X   int cmdcmp() ;
X   register struct rexxCommandList *rcl ;
X   register char *p ;
X   register int dontreply ;
X
X/*
X *   If there's no rexx port, we're out of here.
X */
X   if (rexxPort == NULL)
X      return ;
X/*
X *   Otherwise we have our normal loop on messages.
X */
X   while (RexxMsg = (struct RexxMsg *)GetMsg(rexxPort)) {
X/*
X *   If we have a reply to a message we sent, we look at the second
X *   argument.  If it's set, it's a function we are supposed to call
X *   so we call it.  Then, we kill the argstring and the message
X *   itself, decrement the outstanding count, and attempt to close
X *   down the Rexx library.  Note that this call only succeeds if
X *   there are no outstanding messages.  Also, it's pretty quick, so
X *   don't talk to me about efficiency.
X */
X      if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
X         if (RexxMsg->rm_Args[1]) {
X            ((int (*)())(RexxMsg->rm_Args[1]))(RexxMsg) ;
X         }
X         DeleteArgstring(RexxMsg->rm_Args[0]) ;
X         DeleteRexxMsg(RexxMsg) ;
X         stillNeedReplies-- ;
X         closeRexxLib() ;
X/*
X *   The default case is we got a message and we need to check it for
X *   primitives.  We skip past any initial tabs or spaces and initialize
X *   the return code fields.
X */
X      } else {
X         p = (char *)RexxMsg->rm_Args[0] ;
X         while (*p > 0 && *p <= ' ')
X            p++ ;
X         RexxMsg->rm_Result1 = 0 ;
X         RexxMsg->rm_Result2 = 0 ;
X/*
X *   If somehow the reply is already done or postponed, `dontreply' is
X *   set.
X */
X         dontreply = 0 ;
X/*
X *   If the sky is falling, we just blow up and replymsg.
X */
X         if (bringerdown) {
X            RexxMsg->rm_Result1 = RXERRORIMGONE ;
X/*
X *   Otherwise we cdr down our association list, comparing commands,
X *   until we get a match.  If we get a match, we call the dispatch
X *   function with the appropriate arguments, and break out.
X */
X         } else {
X            oRexxMsg = RexxMsg ;
X            for (rcl = globalrcl; rcl->name; rcl++) {
X               if (cmdcmp(rcl->name, p) == 0) {
X                  userdisp(RexxMsg, rcl, p+strlen(rcl->name)) ;
X                  break ;
X               }
X            }
X/*
X *   If we broke out, rcl will point to the command we executed; if we
X *   are at the end of the list, we didn't understand the command.  In
X *   this case, if we were supplied an extension in upRexxPort, we know
X *   that we should send the command out, so we do so, synchronously.
X *   The synchronous send takes care of our reply.  If we were given a
X *   NULL extension, we bitch that the command didn't make sense to us.
X */
X            if (rcl->name == NULL) {
X               if (extension) {
X                  syncRexxCmd(RexxMsg->rm_Args[0], RexxMsg) ;
X                  dontreply = 1 ;
X               } else {
X                  RexxMsg->rm_Result1 = RXERRORNOCMD ;
X               }
X            }
X         }
X/*
X *   Finally, reply if appropriate.
X */
X         oRexxMsg = NULL ;
X         if (! dontreply)
X            ReplyMsg(RexxMsg) ;
X      }
X   }
X}
X/*
X *   This is the function we use to see if the command matches
X *   the command string.  Not case sensitive, and the real command only
X *   need be a prefix of the command string.  Make sure all commands
X *   are given in lower case!
X */
Xstatic int cmdcmp(c, m)
Xregister char *c, *m ;
X{
X   while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z')))) {
X      c++ ;
X      m++ ;
X   }
X   return(*c) ;
X}
X/*
X *   Opens the Rexx library if unopened.  Returns success (1) or
X *   failure (0).  This is another function that is *private* but
X *   that doesn't have to be.
X */
Xstatic int openRexxLib() {
X   struct RxsLib *OpenLibrary() ;
X
X   if (RexxSysBase)
X      return(1) ;
X   return((RexxSysBase = OpenLibrary(RXSNAME, 0L)) != NULL) ;
X}
X/*
X *   This is the general ARexx command interface, but is not the one
X *   you will use most of the time; ones defined later are easier to
X *   understand and use.  But they all go through here.
X */
Xstruct RexxMsg *sendRexxCmd(s, f, p1, p2, p3)
Xchar *s ;
X/*
X *   The first parameter is the command to send to Rexx.
X */
Xint (*f)() ;
X/*
X *   The second parameter is either NULL, indicating that the command
X *   should execute asynchronously, or a function to be called when the
X *   message we build up and send out here finally returns.  Please note
X *   that the function supplied here could be called during cleanup after
X *   a fatal error, so make sure it is `safe'.  This function always is
X *   passed one argument, the RexxMsg that is being replied.
X */
XSTRPTR p1, p2, p3 ;
X/*
X *   These are up to three arguments to be stuffed into the RexxMsg we
X *   are building up, making the values available when the message is
X *   finally replied to.  The values are stuffed into Args[2]..Args[4].
X */
X{
X   struct RexxMsg *CreateRexxMsg() ;
X   STRPTR CreateArgstring() ;
X   register struct MsgPort *rexxport ;
X   register struct RexxMsg *RexxMsg ;
X
X/*
X *   If we have too many replies out there, we just return failure.
X *   Note that you should check the return code to make sure your
X *   message got out!  Then, we forbid, and make sure that:
X *      - we have a rexx port open
X *      - Rexx is out there
X *      - the library is open
X *      - we can create a message
X *      - we can create an argstring
X *
X *   If all of these succeed, we stuff a few values and send the
X *   message, permit, and return.
X */
X   if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING-1)
X      return(NULL) ;
X   RexxMsg = NULL ;
X   if (openRexxLib() && (RexxMsg =
X             CreateRexxMsg(rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
X             (RexxMsg->rm_Args[0] = CreateArgstring(s, (long)strlen(s)))) {
X      RexxMsg->rm_Action = RXCOMM ;
X      RexxMsg->rm_Args[1] = (STRPTR)f ;
X      RexxMsg->rm_Args[2] = p1 ;
X      RexxMsg->rm_Args[3] = p2 ;
X      RexxMsg->rm_Args[4] = p3 ;
X      RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR ;
X      Forbid() ;
X      if (rexxport = FindPort(RXSDIR))
X         PutMsg(rexxport, RexxMsg) ;
X      Permit() ;
X      if (rexxport) {
X         stillNeedReplies++ ;
X         return(RexxMsg) ;
X      } else
X         DeleteArgstring(RexxMsg->rm_Args[0]) ;
X   }
X   if (RexxMsg)
X      DeleteRexxMsg(RexxMsg) ;
X   closeRexxLib() ;
X   return(NULL) ;
X}
X/*
X *   This function is used to send out an ARexx message and return
X *   immediately.  Its single parameter is the command to send.
X */
Xstruct RexxMsg *asyncRexxCmd(s)
Xchar *s ;
X{
X   return(sendRexxCmd(s, NULL, NULL, NULL, NULL)) ;
X}
X/*
X *   This function sets things up to reply to the message that caused
X *   it when we get a reply to the message we are sending out here.
X *   But first the function we pass in, which actually handles the reply.
X *   Note how we get the message from the Args[2]; Args[0] is the command,
X *   Args[1] is this function, and Args[2]..Args[4] are any parameters
X *   passed to sendRexxCmd() as p1..p3.  We pass the result codes right
X *   along.
X */
Xstatic void replytoit(msg)
Xregister struct RexxMsg *msg ;
X{
X   register struct RexxMsg *omsg ;
X
X   omsg = (struct RexxMsg *)(msg->rm_Args[2]) ;
X   replyRexxCmd(omsg, msg->rm_Result1, msg->rm_Result2, NULL) ;
X   ReplyMsg(omsg) ;
X}
X/*
X *   This function makes use of everything we've put together so far,
X *   and functions as a synchronous Rexx call; as soon as the macro
X *   invoked here returns, we reply to `msg', passing the return codes
X *   back.
X */
Xstruct RexxMsg *syncRexxCmd(s, msg)
Xchar *s ;
Xstruct RexxMsg *msg ;
X{
X   return(sendRexxCmd(s, (APTR)&replytoit, msg, NULL, NULL)) ;
X}
X/*
X *   There are times when you want to pass back return codes or a
X *   return string; call this function when you want to do that,
X *   and return `1' from the user dispatch function so the main
X *   event loop doesn't reply (because we reply here.)  This function
X *   always returns 1.
X */
Xvoid replyRexxCmd(msg, primary, secondary, string)
X/*
X *   The first parameter is the message we are replying to.
X */
Xregister struct RexxMsg *msg ;
X/*
X *   The next two parameters are the primary and secondary return
X *   codes.
X */
Xregister long primary, secondary ;
X/*
X *   The final parameter is a return string.  This string is only
X *   returned if the primary return code is 0, and a string was
X *   requested.
X *
X *   We also note that we have replied to the message that came in.
X */
Xregister char *string ;
X{
X   STRPTR CreateArgstring() ;
X
X/*
X *   Note how we make sure the Rexx Library is open before calling
X *   CreateArgstring . . . and we close it down at the end, if possible.
X */
X   if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT))) {
X      if (string && openRexxLib())
X         secondary = (long)CreateArgstring(string, (long)strlen(string)) ;
X      else
X         secondary = 0L ;
X   }
X   msg->rm_Result1 = primary ;
X   msg->rm_Result2 = secondary ;
X   closeRexxLib() ;
X}
SHAR_EOF
echo "extracting minrexx.h"
sed 's/^X//' << \SHAR_EOF > minrexx.h
X/*
X *   Includes for minrexx.c; please refer to that file for
X *   further documentation.
X */
X#include <rexx/rxslib.h>
X/*
X *   This is the list of functions we can access.  (Cheap forward
X *   declarations, too.)
X */
Xlong upRexxPort() ;
Xvoid dnRexxPort() ;
Xvoid dispRexxPort() ;
Xstruct RexxMsg *sendRexxCmd() ;
Xstruct RexxMsg *syncRexxCmd() ;
Xstruct RexxMsg *asyncRexxCmd() ;
Xvoid replyRexxCmd() ;
X/*
X *   Maximum messages that can be pending, and the return codes
X *   for two bad situations.
X */
X#define MAXRXOUTSTANDING (300)
X#define RXERRORIMGONE (100)
X#define RXERRORNOCMD (30)
X/*
X *   This is the association list you build up (statically or
X *   dynamically) that should be terminated with an entry with
X *   NULL for the name . . .
X */
Xstruct rexxCommandList {
X   char *name ;
X   APTR userdata ;
X} ;
SHAR_EOF
echo "extracting Makefile"
sed 's/^X//' << \SHAR_EOF > Makefile
Xall: freedraw origfreedraw
X
Xfreedraw: freedraw.o minrexx.o rexxglue.o
X	ln freedraw.o minrexx.o rexxglue.o -lc
X
Xfreedraw.o: freedraw.c minrexx.h
X	cc -DTALKTOREXX freedraw.c
X
Xminrexx.o: minrexx.c minrexx.h
X	cc minrexx.c
X
Xorigfreedraw: origfreedraw.o
X	ln origfreedraw.o -lc
X
Xorigfreedraw.o: freedraw.c
X	cc -o origfreedraw.o freedraw.c
X
Xclean:
X	delete freedraw.o origfreedraw.o freedraw origfreedraw minrexx.o
SHAR_EOF
echo "extracting freeedraw.uu"
sed 's/^X//' << \SHAR_EOF > freeedraw.uu
X
Xbegin 700 freedraw
XM```#\P`````````#``````````(```?Q````QP````$```/I```'\4[Z%L9&)
XM<F5E9')A=R`P+C`Q(&)Y(%)I8VAA<F0@32X@4F]S<P``04U)1T$@1G)E941RZ
XM87<@,"XP,0!C;VQO<@!B;W@`9F)O>`!L:6YE`'1O9G)O;G0`=&]B86-K`&5X3
XM:70`=F5R<VEO;@!S<&%W;@!.5?_^(&T`$")H`#(;:0`9__\0+0`52(!(P"\`Q
XM(&T`$"\H`#).NAZ>4$\P+0`*2,`O`#`M``A(P"\`(&T`$"\H`#).NAY<3^\`I
XM##`M``I(P"\`,"T`#$C`+P`@;0`0+R@`,DZZ'BI/[P`,,"T`#DC`+P`P+0`,5
XM2,`O`"!M`!`O*``R3KH>"D_O``PP+0`.2,`O`#`M``A(P"\`(&T`$"\H`#).)
XMNAWJ3^\`##`M``I(P"\`,"T`"$C`+P`@;0`0+R@`,DZZ'<I/[P`,$"W__TB`G
XM2,`O`"!M`!`O*``R3KH=YE!/3EU.=4Y5__PP+0`(4T!(P..`0>R`ACMP"`#_;
XM_$)M__Y@``&6,"W__L'\`")![(#(,BW__E)!P_P`(D/L@,C2B2&!"``P+?_^S
XMP?P`(D'L@-HR+?_^P_P`%$/L@5#2B2&!"``P+?_^P?P`(D'L@,PR+?_^2,&#T
XM_``$2$'#_``H5$$Q@0@`,"W__L'\`")![(#.,BW__DC!@_P`!,/\`!DQ@0@`M
XM,"W__L'\`")![(#0,;P`*`@`,"W__L'\`")![(#2,;P`&0@`,"W__L'\`")!3
XM[(#4,;P`D`@`,"W__L'\`")![(#60K`(`#`M__[!_``B0>R`WD*P"``P+?_^3
XMP?P`(D'L@.)",`@`,"W__L'\`")![(#D0K`(`#`M__[!_``B0>R`Z$)P"``PD
XM+?_^P?P`%$'L@5`QO``!"``P+?_^P?P`%$'L@5(QO``!"``P+?_^P?P`%$'LH
XM@50QO``F"``P+?_^P?P`%$'L@58QO``7"``P+?_^P?P`%$'L@5@QK0`("``PZ
XM+?_^P?P`%$'L@5I"L`@`,"W__L'\`!1![(%>0C`(`#`M__[!_``40>R!7Q&MD
XM__\(`%)M__XP+?_^L&W__&T`_F(P+?_\4T#!_``B0>R`R$*P"`!P`$Y=3G5.F
XM5?_^0FW__C`M__[!_``B0>R!H#(M__Y20</\`")#[(&@THDA@0@`,"W__L'\!
XM`")![(&D0G`(`#`M__[!_``B0>R!IC(M__[#_``*,8$(`#`M__[!_``B0>R!!
XMJ#&\`'`(`#`M__[!_``B0>R!JC&\``H(`#`M__[!_``B0>R!K#&\`)((`#`M4
XM__[!_``B0>R!KD*P"``P+?_^P?P`(D'L@;(R+?_^P_P`%$/L@BC2B2&!"``P(
XM+?_^P?P`(D'L@;9"L`@`,"W__L'\`")![(&Z0C`(`#`M__[!_``B0>R!O$*P7
XM"``P+?_^P?P`(D'L@<!"<`@`,"W__L'\`!1![((H0C`(`#`M__[!_``40>R"_
XM*1&\``$(`#`M__[!_``40>R"*A&\``$(`#`M__[!_``40>R"+$)P"``P+?_^S
XMP?P`%$'L@BXQO``!"``P+?_^P?P`%$'L@C!"L`@`,"W__L'\`!1![((X0K`(T
XM`%)M__X,;0`$__YM`/Z20JR"!D'Z`"0I2((T0?H`)BE(@DA!^@`I*4B"7$'ZY
XM`"PI2()P<`!.74YU17)A<V4@06QL`$AO;&QO=R!";W@`1FEL;&5D($)O>`!0]
XM96X@1')A=P``3E4``$'L@I8I2()X.7P`"H)\0FR"?CE\`#*"@#E\``J"@CE\@
XM``&"A$'Z`$0I2(*&0>R`R"E(@HI"K(*6.7P`08*:0FR"G#E\`%6"GCE\``J"`
XMH#E\``&"HD'Z`!HI2(*D0>R!H"E(@JAP`$Y=3G5#;VQO<@!$<F%W36]D90``_
XM3E7^RAM\``'_^SM\`![_^#M\`![_]CM\`!3_]#M\`!3_\D)M_]I";?_80FW_)
XMUD)M_]1";?_20FW_T$)M_\Y(>``!2'H&JDZZ&-Q03RE`@,!*K(#`9@A"ITZZ>
XM%7!83TAX``%(>@:<3KH8O%!/*4"`Q$JL@,1F"$*G3KH54%A/2&R`!DZZ&998G
XM3RE`@K1F"$*G3KH5.%A/(&R"M!`H`#9(@#E`@K@@;(*T$"@`-TB`.4""NB!LF
XM@K0B;(*T$"D`.$B`,B@`"))`4T$Y08*\(&R"M")L@K00*0`Y2(`R*``*DD!3\
XM03E!@KX_/``"3KKZ\E1/3KK\Q$ZZ_HQ(;()X+RR"M$ZZ&2103T*G(&R"M"\H.
XM`#).NABZ4$\0+?_[2(`_`"!L@K0O*``R3KH8E%Q/2'H&H$AZ!>A(;(`V2'H%;
XMUTZZ"<I/[P`0*T#_RD(M_LH[?``!_^I@+#`M_^I(P.6`(&T`"B\P"`!(;?[*&
XM3KH38%!/2'H%K4AM_LI.NA-24$]2;?_J,"W_ZK!M``AMRDHM_LIG"DAM_LI.<
XMN@S@6$]*;("09P`%*"!L@K0B:`!6<``0*0`/<@'AH8*M_\HO`4ZZ%ZY83TZZ:
XM"D!";?_:(&R"M"\H`%9.NA<P6$\K0/_\9P`$*B!M__PK:``4_^X@;?_\.V@`Y
XM&/_L(&R"M#MH``[_ZB!L@K0[:``,_^@O+?_\3KH73EA/,"W_ZK!L@KAM'C`MF
XM_^JP;(*\;A0P+?_HL&R"NFT*,"W_Z+!L@KYO"#M\``'_V&`$0FW_V&<,2FW_1
XMTF<&.WP``?_6("W_[F```WH[?``!_]I@``.@(&R"M!`H`#9(@#E`@K@@;(*TD
XM$"@`-TB`.4""NB!L@K0B;(*T$"D`.$B`,B@`"))`4T$Y08*\(&R"M")L@K00!
XM*0`Y2(`R*``*DD!303E!@KY@``-,0FR`D&```T1P`#`M_^Q@``'`0FW_UD)M.
XM_])@``'.2FW_V&8``<9*;?_49SX[?``!_])";?_60J<@;(*T+R@`,DZZ%M)0K
XM3S`M_^A(P"\`,"W_ZDC`+P`@;(*T+R@`,DZZ%H!/[P`,8``!@DIM_]!G``#(2
XM0J<@;(*T+R@`,DZZ%I903T)M_]`@;(*T"*@````92FW_SF=Z,"W_]+!M__AL%
XM#CMM__3_YCMM__C_XF`,.VW_^/_F.VW_]/_B,"W_\K!M__9L#CMM__+_Y#MMX
XM__;_X&`,.VW_]O_D.VW_\O_@,"W_X$C`+P`P+?_B2,`O`#`M_^1(P"\`,"W_S
XMYDC`+P`@;(*T+R@`,DZZ%>I/[P`48"00+?_[2(`_`"\L@K0_+?_V/RW_^#\M$
XM__(_+?_T3KKW$D_O``Y@``"T.VW_ZO_X.VW_ZO_T.VW_Z/_V.VW_Z/_R2'@`1
XM`B!L@K0O*``R3KH5ME!/.WP``?_0(&R"M`CH````&1`M__M(@#\`+RR"M#\M,
XM__8_+?_X/RW_\C\M__1.NO:P3^\`#F!20FW_T"!L@K0(J````!D0+?_[2(`_'
XM`"\L@K0_+?_V/RW_^#\M__(_+?_T3KKV?$_O``Y@'F``_4*0O````&AG`/Y&3
XM4X!GNI"\````?V<`_BQ@XF```5X,;?___^QG``#>,"W_[,!\`!\[0/_>,"W_/
XM[.I(P'P`/SM`_]QP`#`M_]Y@``"L&VW_W?_[$"W_^TB`2,`O`"!L@K0O*``R$
XM3KH4SE!/8```F'``,"W_W&!J0J<@;(*T+R@`,DZZ%,)03S`L@KY(P"\`,"R"5
XMO$C`+P`P+(*Z2,`O`#`L@KA(P"\`(&R"M"\H`#).NA1R3^\`%&`Z0FW_U$)M%
XM_\Y@,$)M_]0[?``!_\Y@)#M\``'_U&`<8!K_AO_(_]+_WK"\````!&3NXX`PX
XM.P#L3OL``&`08`Y*@&<`_U)3@&<`_VY@\&!T2FW_T&<D$"W_^TB`/P`O+(*T[
XM/RW_]C\M__@_+?_R/RW_]$ZZ]49/[P`.0FW_T$)M_]9";?_2(&R"M`BH````#
XM&6`R8#!5@&<`_(Y=@&<`_.11@&<`_'B0O````/!G`/ZZD+P```$`9P#\PI"\J
XM``?^`&>.8,Y@`/O$2FW_VF<``+A*;?_89@``L$IM_])G3DIM_]9G)D)M_]8PD
XM+?_H2,`O`#`M_^I(P"\`(&R"M"\H`#).NA-83^\`#&`@,"W_Z$C`+P`P+?_J1
XM2,`O`"!L@K0O*``R3KH3)$_O``Q@6DIM_]!G5!`M__M(@#\`+RR"M#\M__8_U
XM+?_X/RW_\C\M__1.NO1J3^\`#CMM_^K_^#MM_^C_]A`M__M(@#\`+RR"M#\M9
XM__8_+?_X/RW_\C\M__1.NO0Z3^\`#F``^M1.N@32+RR"M$ZZ$S!83R\L@K1.$
XMNA,R6$](>``!3KH.Y%A/3EU.=6EN='5I=&EO;BYL:6)R87)Y`&=R87!H:6-S-
XM+FQI8G)A<GD`9G)E961R87<`9F0`(```3E7__DCG""`@;0`()%!X`$)M__X,-
XM$@`@;@A*$F<$4HI@\G`PL!)N)`P2`#EN'CM\``'__B!*4HH0$$B`,@3#_``*6
XMT$$X`)A\`#!@UDIM__YF!$)L@L@@;0`(((HP!$S?!!!.74YU3E4``"\$(&T`<
XM"`P0`"!O#B!M``A*$&<&4JT`"&#H>`!@&DAM``A.NO]R6$\R!$C!XX%![(+`-
XM,8`8`%)$N&T`#&W@*!].74YU3E4``$CG"#`D;0`()FT`##E\``&"R$JL@K1GG
XM<$)L@LH@;(*T(F@`,A`I`!E(@#@`,"R`DDC`+P`@;(*T+R@`,DZZ$:Q03R\MJ
XM`!`O"B!K``1.D%!/,`1(P"\`(&R"M"\H`#).NA&*4$]*;(+(9A9"IT*G,"R"Y
XMR$C`+P`O"DZZ!IA/[P`03-\,$$Y=3G5"ITAX``I(>``4+PI.N@9\3^\`$&#B[
XM3E4``#\\``$O+0`,3KK_"%Q/.6R"P("23EU.=4Y5```P+0`(2,#C@$'L@L`R\
XM,`@`LFR"N&P2,"T`"$C`XX!![(+`,:R"N`@`,"T`"$C`XX!![(+`,C`(`+)LS
XM@KQO$C`M``A(P..`0>R"P#&L@KP(`%)M``@P+0`(2,#C@$'L@L`R,`@`LFR"Q
XMNFP2,"T`"$C`XX!![(+`,:R"N@@`,"T`"$C`XX!![(+`,C`(`+)L@KYO$C`M]
XM``A(P..`0>R"P#&L@KX(`$Y=3G5.50``/SP`!"\M``Q.NOY`7$]"9TZZ_SY4R
XM3S\\``).NO\T5$\_+("2+RR"M#\L@L8_+(+$/RR"PC\L@L!.NO&"3^\`#DY=J
XM3G5.50``+P0_/``$+RT`#$ZZ_?9<3T)G3KK^]%1//SP``DZZ_NI43S`L@L"P*
XM;(+$;PXX+(+`.6R"Q(+`.42"Q#`L@L*P;(+&;PXX+(+".6R"QH+".42"QC`L`
XM@L9(P"\`,"R"Q$C`+P`P+(+"2,`O`#`L@L!(P"\`(&R"M"\H`#).N@^J3^\`.
XM%"@?3EU.=4Y5```_/``$+RT`#$ZZ_6Q<3T)G3KK^:E1//SP``DZZ_F!43S`L-
XM@L)(P"\`,"R"P$C`+P`@;(*T+R@`,DZZ#TY/[P`,,"R"QDC`+P`P+(+$2,`O^
XM`"!L@K0O*``R3KH/'$_O``Q.74YU3E4``"\L@K1.N@_"6$].74YU3E4``"\L6
XM@K1.N@^D6$].74YU3E4``$)L@)!.74YU3E4``#E\``&"RB\L@`)"IT*G+RT`%
XM"$ZZ!"A/[P`03EU.=4Y5```@;0`,#!``(&X.(&T`#$H09P92K0`,8.@O+0`,'
XM3KH#CEA/3EU.=5)A9&EC86P@17EE($UI;E)E>'@@,"XT`$Y5``!*K0`,9P9*;
XMK0`49@9P`$Y=3G5*K("B9CY.N@VP+RT`"$ZZ#8Q83TJ`9A!"IR\M``A.N@R`9
XM4$\I0("B3KH-\$JL@*)G$B!L@*)P`!`H``]R`>&A*4&`L"EM``R`J"EM`!"`D
XMM"EM`!2`N"`L@+!@H$Y5``!*K("L9A1*K(+,9PXO+(+,3KH,'EA/0JR"S$Y=S
XM3G5.50``2JR`HF=4+RR`HDZZ#:)83SE\``&`IDJL@+QG&B!L@+PA?````&0`L
XM("\L@+Q.N@V,6$]"K("\2JR`K&<.+RR`HDZZ#9183V$:8.QACB\L@*).N@Q8L
XM6$]"K("B0JR`L$Y=3G5.50``2.<,,$JL@*)F"$S?##!.74YU+RR`HDZZ#.18Y
XM3R1`2H!G``#8#"H`!P`(9BY*J@`L9PHO"B!J`"Q.D%A/+RH`*$ZZ`R)83R\*H
XM3KH#(%A/4ZR`K$ZZ_R!@``">*"H`*"!$2A!O#"!$#!``(&X$4H1@[D*J`"!"9
XMJ@`D>@!*;("F9PHE?````&0`(&!<*4J`O"9L@*A@+"\$+Q-A9%!/2D!F'B\34
XM3KH$E%A/2,#0A"\`+PLO"B!L@+A.D$_O``Q@!E"+2I-FT$J39AY*K("T9Q`OV
XM"B\J`"A.N@'H4$]Z`6`()7P````>`"!"K("\2D5F""\*3KH,8EA/8`#_&F``S
XM_PY.50``2.<`,"1M``@F;0`,2A)G*!`2L!-G'!`32(#0?``@$A)(@;!!9A)PW
XM8;`2;@P,$@!Z;@92BE*+8-00$DB`3-\,`$Y=3G5.50``2JR"S&<&<`%.74YU8
XM0J=(>@`63KH+P%!/*4""S&<$<`%@`G``8.)R97AX<WES;&EB+FQI8G)A<GD`:
XM`$Y5``!(YP`P2JR`HF<*#*P```$K@*QO"G``3-\,`$Y=3G67RV&<2D!G``"B;
XM(&R`HB\H``HO+("T+RR`HDZZ`9Q/[P`,)D!*@&<``((O+0`(3KH#8EA/2,`O2
XM`"\M``A.N@)(4$\G0``H9V(G?`$`````'"=M``P`+"=M`!``,"=M`!0`-"=M]
XM`!@`.$'Z`%8G2``*3KH*N$AZ`$].N@J46$\D0$J`9PHO"R\*3KH+"E!/3KH*B
XM_"`*9PI2K("L(`M@`/]:+RL`*$ZZ`1A83R`+9P@O"TZZ`1)83TZZ_19P`&``P
XM_SI215A8`%)%6%@`3E4``$*G0J="IT*G+RT`"$ZZ_P)/[P`43EU.=4Y5``!(F
XMYP`P)&T`""9J`#!"IR\J`"0O*@`@+PMA-$_O`!`O"TZZ"J)83TS?#`!.74YUF
XM3E4``$*G0J<O+0`,2'K_P"\M``A.NOZP3^\`%$Y=3G5.50``2.<,,"1M``@H'
XM+0`,*BT`$"9M`!1*A&8N""H``0`=9R8@"V<@3KK^/DI`9Q@O"TZZ`B983TC`>
XM+P`O"TZZ`0Y03RH`8`)Z`"5$`"`E10`D3KK\0DS?##!.74YU,CS_4F!P,CS^2
XMJF!J,CS_$&!D<I1@8#(\_S1@6C(\_FA@5#(\_W!@3C(\_WQ@2#(\_VI@0C(\A
XM_GI@/#(\_G1@-C(\_FY@,#(\_F)@*C(\_H!@)#(\_TQ@'G*(8!HR//\68!1R0
XMFF`0,CS_(F`*,CS_'&`$,CS_6$SO`P``!"`O``PO#BQY```"RDZV$``L7TYUB
XM,CS_=F#@,CS_.F#:,CS^C&#4,CS^GF#.,CS^I&#(,CS_1F#",CS_0&"\,CS^J
XM1&"V,CS_!&"P,CS^_F"J,CS^^&"D,CS^\F">,CS^[&"8,CS^X&"2,CS^AF",_
XM,CS^F&"&<H)@(#(\_RA@&C(\_V1@%#(\_E!@#G*.8`HR//\N8`0R//[F(&\`F
XM!"`O``A@`/]B,CS^R&`0,CS^/F`*,CS^VF`$,CS^."`O``1@`/]$,GS^SF`0"
XM,GS^PF`*,GS_7F`$,GS^DB!O``1,[P`#``@O#BQY```"RDZVD``L7TYU3.\#L
XM`P`$P8C#B2\.+'D```+*3J[^2BQ?3G5,[P,#``3!B,.)+PXL>0```LI.KOZ\:
XM+%].=4SO`P,`!,&(PXDO#BQY```"RDZN_K8L7TYU3.\#`P`$P8C#B2\.+'D`0
XM``+*3J[^L"Q?3G4@;P`$,CS^U&$`_J`B;P`((H%.=2`O``1RH&$`_HXB;P`(`
XM(HA.=2!O``0@"$H89OR1P"`(4X!.=6%P0^R`HD7L@**UR68.,CP`GFL(=``BW
XMPE')__PI3X+0+'@`!"E.@M1(YX"`""X`!`$I9Q!+^@`(3J[_XF`&0J?S7TYSL
XM0_H`($ZN_F@I0(+89@PN/``#@`=.KO^48`1.N@`:4$].=61O<RYL:6)R87)Y(
XM`$GY``!__DYU3E4``"\*2'D``0``,"R`F,'\``8O`$ZZ!LHI0(+<4$]F%$*GG
XM2'D``0``3KH%H%!/+FR"T$YU(&R"W$)H``0@;(+<,7P``0`0(&R"W#%\``$`5
XM"B!L@M`@+(+0D*@`!%"`*4""X"!L@N`@O$U!3EA"ITZZ!HXD0$JJ`*Q83V<N@
XM+RT`#"\M``@O"DZZ`*XY?``!@N0@;(+<`&B````$(&R"W`!H@```"D_O``Q@S
XM0DAJ`%Q.N@;^2&H`7$ZZ!H(I0(+F(&R"YDJH`"103V<0(&R"YB)H`"0O$4ZZ5
XM!+I83R\L@N8O"DZZ`F@I;(+F@NI03TZZ!*X@;(+<((!.N@2Z(&R"W"%```9G=
XM%DAX`^U(>@`J3KH$EB!L@MPA0``,4$\O+(+J/RR"[DZZ[1!"9TZZ`M103R1?:
XM3EU.=2H`3E4``$CG##`D;0`0(&T`"$JH`*QG&"!M``@@*`"LY8`H`"!$("@`[
XM$.6`)D!@!"9L@)H0$TB`2,#0K0`,5(`Y0(+P0J<P+(+P2,`O`$ZZ!5PI0(+R:
XM4$]F"$S?##!.74YU$!-(@#H`/P4@2U*(+P@O+(+R3KH!?C`%2,`@0-'L@O)#N
XM^@%$$-EF_#\M``XO"B\L@O).N@$Z(&R"\D(P4``Y?``!@NXP!4C`T*R"\B9`*
XM4HLD2T_O`!00$TB`.@"P?``@9QBZ?``)9Q*Z?``,9PRZ?``-9P:Z?``*9@12G
XMBV#8#!,`(&UZ#!,`(F8N4HL@2U*+$!!(@#H`9QX@2E**$(6Z?``B9A`,$P`BT
XM9@12BV`&0BK__V`"8-9@."!+4HL0$$B`.@!G)KI\`"!G(+I\``EG&KI\``QG$
XM%+I\``UG#KI\``IG""!*4HH0A6#.($I2BD(02D5F`E.+4FR"[F``_UI"$D*G5
XM,"R"[E)`2,#E@"\`3KH$.BE`@NI03V8(0FR"[F``_MAZ`"9L@O)@)#`%2,#E<
XM@"!L@NHABP@`($L@"$H89OR1P%.(,`A20$C`U\!21;IL@NYMUC`%2,#E@"!LV
XM@NI"L`@`8`#^E"``,#Q__V`$,"\`#"!O``1*&&;\4T@B;P`(4T`0V5?(__QGY
XM`D(0("\`!$YU3.\#```$(`@R+P`,8`(0V5?)__QG!E)!8`)"&%')__Q.=4Y5>
XM``!(YPXP)&T`"$*G2'H`CDZZ`_XI0(+V4$]F"$S?#'!.74YU(&T`#")H`"0O!
XM*0`$3KH$K"@`6$]G4DAZ`&T@1"\H`#9.N@1^)D!*@%!/9S1(>`/M+PM.N@(`U
XM+`!03V<D(`;E@"H`($4E:``(`*0E1@"<2'@#[4AZ`#A.N@'<)4``H%!/+P1.D
XMN@1*6$\O+(+V3KH"!$*L@O983V"`:6-O;BYL:6)R87)Y`%=)3D1/5P`J`$Y5N
XM``!*K(+Z9P8@;(+Z3I`_+0`(3KH`"%1/3EU.=4Y5__PO!#`M``A(P"M`__Q*(
XMK(+<9RAX`&`*/P1.N@#^5$]21+AL@)AM\#`L@)C!_``&+P`O+(+<3KH"Q%!/B
XM2JR"_F<&(&R"_DZ02JR`GF<*+RR`GDZZ`4A83TJL@P)G""!L@P(@K(,&2JR#,
XM"F<*+RR#"DZZ`5983TJL@PYG"B\L@PY.N@%&6$]*K(,29PHO+(,23KH!-EA/5
XM2JR#%F<*+RR#%DZZ`2983RQX``0(+@`$`2EG%"\-2_H`"DZN_^(J7V`&0J?S$
XM7TYS2JR"YF8P2JR"\F<H,"R"\$C`+P`O+(+R3KH"'#`L@NY20$C`Y8`O`"\L<
XM@NI.N@((3^\`$&`.3KH!\B\L@N9.N@)N6$\@+?_\+FR"T$YU*!].74YU3E4`F
XM`$CG#B`X+0`(,`3!_``&)$#5[(+<2D1M"KAL@)AL!$J29A`Y?``"@QIP_TS?U
XM!'!.74YU""H`!P`$9@@O$DZZ``I83T*2<`!@XB(O``0L;(+83N[_W"(O``0LL
XM;(+83N[_@BQL@MA.[O_*3.\`!@`$+&R"V$[N_^(L;(+83N[_Q$[Z``(B+P`$>
XM+&R"V$[N_Z9(YP$$3.\@@``,+&R"U$ZN_Y1,WR"`3G5.^@`"(F\`!"QL@M1.4
XM[OYB3E4``$CG""!(>/__3KH`T"@`L+S_____6$]F"G``3-\$$$Y=3G5(>0`!D
XM``%(>``B3KH`N"1`2H!03V8,+P1.N@#X<`!83V#6)6T`"``*%6T`#P`)%7P`Q
XM!``(0BH`#A5$``]"ITZZ`*(E0``02JT`"%A/9PHO"DZZ`%I83V`*2&H`%$ZZ'
XM`-!83R`*8)).50``+PHD;0`(2JH`"F<(+PI.N@#P6$\5?`#_``@E?/____\`"
XM%'``$"H`#R\`3KH`?$AX`"(O"DZZ`%Y/[P`,)%].74YU(F\`!"QL@M1.[OZ>7
XM("\`!"QL@M1.[OZV3OH``DSO``,`!"QL@M1.[O\Z(F\`!"QL@M1.[OYZ3OH`?
XM`B)O``0L;(+43N[^VD[Z``(L;(+43N[_?$[Z``(B;P`$("\`""QL@M1.[O\N5
XM("\`!"QL@M1.[OZP3OH``B!O``0L;(+43N[^C"!O``0@B%B00J@`!"%(``A.,
XM=4[Z``(L;(+4(F\`!"`O``A.[OW8+&R"U$[N_W9,[P,```0L;(+43N[^DB)O6
XM``0L;(+43N[^F$[Z``(B;P`$+&R"U$[N_H8@+P`$+&R"U$[N_L).^@`"(&\`G
XM!"QL@M1.[OZ`(F\`!$SO``,`""QL@,1.[O\*(F\`!$SO``,`""QL@,1.[O\0J
XM(F\`!$SO``\`""QL@,1.[O[.(F\`!"`O``@L;(#$3N[^JB)O``0@+P`(+&R`[
XMQ$[N_IY,[P,```0L;(+V3N[_H"!O``0L;(+V3N[_IB!O``0L;(+V3N[_LB!OL
XM``0L;(#`3N[_RB!O``0L;(#`3N[_N"!O``0L;(#`3N[_-$SO`P``!"QL@,!.!
XM[O[X(&\`!"QL@,!.[O[.(&\`!"QL@,!.[O[(```#[`````<````!```6A```5
XM%FH``!90```6-@``%AP``!4^```7/`````````/R```#Z@```"@````$``H`B
XM"@)8`+0``0`(`QH``!(/```````````````F````````````9``C_____P`!)
XM````.@``#>@```!````.L````$0```[X````20``#X0```!.```/[````%8`R
XM``_^````70``$!````!B```0'````&H``!`^`````````````@`$``@`$``@.
XM``$``0``$&@`%``````````````#[````!4``````````````!X````T````D
XM.````#P```!`````1````$@```!,````4````%0```!8````7````&````!DH
XL````:````&P```!P````=````'@```"2`````````_(```/K`````0```_)D_
X``
Xend
Xsize 8504
SHAR_EOF
echo "extracting aspline.fd"
sed 's/^X//' << \SHAR_EOF > aspline.fd
X/* given four control points (that's eight arguments) this draws a bspline */
X
Xparse arg x1 y1 x2 y2 x3 y3 x4 y4
X
Xlength = max(abs(x1-x4), abs(y1-74))
X
Xif (length = 0) then exit
X
Xd1 = abs((x1 - x4) * (y2 - y4) - (y1 - y4) * (x2 - x4)) / length
Xd2 = abs((x1 - x4) * (y3 - y4) - (y1 - y4) * (x3 - x4)) / length
X
X/* if they are colinear, simply draw a line. */
X
Xif (d1 < 0.5) & (d2 < 0.5) then do
X
X   address 'freedraw' 'Line ' trunc(x1) trunc(y1) trunc(x4) trunc(y4)
X
X/* otherwise invoke recursively on substrings */
X
Xend
Xelse do
X
X   x12 = (x1 + x2) / 2
X   y12 = (y1 + y2) / 2
X   x23 = (x2 + x3) / 2
X   y23 = (y2 + y3) / 2
X   x34 = (x3 + x4) / 2
X   y34 = (y3 + y4) / 2
X   x123 = (x12 + x23) / 2
X   y123 = (y12 + y23) / 2
X   x234 = (x23 + x34) / 2
X   y234 = (y23 + y34) / 2
X   x1234 = (x123 + x234) / 2
X   y1234 = (y123 + y234) / 2
X
X   address 'freedraw' 'Spawn aspline ' x1 y1 x12 y12 x123 y123 x1234 y1234
X   address 'freedraw' 'Spawn aspline ' x1234 y1234 x234 y234 x34 y34 x4 y4
X
Xend
SHAR_EOF
echo "extracting bspline.fd"
sed 's/^X//' << \SHAR_EOF > bspline.fd
X/* given four control points (that's eight arguments) this draws a bspline */
X
Xparse arg x1 y1 x2 y2 x3 y3 x4 y4
X
Xlength = max(abs(x1-x4), abs(y1-74))
X
Xif (length = 0) then exit
X
Xd1 = abs((x1 - x4) * (y2 - y4) - (y1 - y4) * (x2 - x4)) / length
Xd2 = abs((x1 - x4) * (y3 - y4) - (y1 - y4) * (x3 - x4)) / length
X
X/* if they are colinear, simply draw a line. */
X
Xif (d1 < 0.5) & (d2 < 0.5) then do
X
X   address 'freedraw' 'Line ' trunc(x1) trunc(y1) trunc(x4) trunc(y4)
X
X/* otherwise invoke recursively on substrings */
X
Xend
Xelse do
X
X   x12 = (x1 + x2) / 2
X   y12 = (y1 + y2) / 2
X   x23 = (x2 + x3) / 2
X   y23 = (y2 + y3) / 2
X   x34 = (x3 + x4) / 2
X   y34 = (y3 + y4) / 2
X   x123 = (x12 + x23) / 2
X   y123 = (y12 + y23) / 2
X   x234 = (x23 + x34) / 2
X   y234 = (y23 + y34) / 2
X   x1234 = (x123 + x234) / 2
X   y1234 = (y123 + y234) / 2
X
X   address 'freedraw' 'bspline ' x1 y1 x12 y12 x123 y123 x1234 y1234
X   address 'freedraw' 'bspline ' x1234 y1234 x234 y234 x34 y34 x4 y4
X
Xend
SHAR_EOF
echo "extracting sample.fd"
sed 's/^X//' << \SHAR_EOF > sample.fd
X/* draw something with freedraw */
X
Xaddress 'freedraw'
X
Xcolor = 0
X
Xdo i = 20 to 400
X   'Color ' color
X   color = color + 1
X   if color > 3 then color = 0
X   'Line ' 20 i i 400
Xend i
SHAR_EOF
echo "extracting saspline.fd"
sed 's/^X//' << \SHAR_EOF > saspline.fd
X/* invokes aspline with some sample values */
X'aspline 314 159 10 9 456 44 536 141'
Xexit
SHAR_EOF
echo "extracting sbspline.fd"
sed 's/^X//' << \SHAR_EOF > sbspline.fd
X/* invokes aspline with some sample values */
X'bspline 314 159 10 9 456 44 536 141'
Xexit
SHAR_EOF
echo "extracting version.fd"
sed 's/^X//' << \SHAR_EOF > version.fd
X/* gets the version from freedraw */
X
Xoptions results
X
Xaddress 'freedraw' 'Version'
X
Xsay result
SHAR_EOF
echo "End of archive 1 (of 1)"
# if you want to concatenate archives, remove anything after this line
exit