home *** CD-ROM | disk | FTP | other *** search
- /*
- * This is an example of how REXX messages might be handled. This is
- * a `minimum' example that both accepts asynchronous REXX messages and
- * can request REXX service [and can issue simple REXX commands to other
- * rexx servers -ljr].
- *
- * Read this entire file! It's short enough.
- *
- * It is written in such a fashion that it can be attached to a program
- * with a minimum of fuss. The only external symbols it makes available
- * are the [eight] functions.
- * [Note that the application must now declare, open, and close
- * RexxSysBase. -ljr]
- *
- * This code is by Radical Eye Software, but it is put in the public
- * domain. I would appreciate it if the following string was left in
- * both as a version check and as thanks from you for the use of this
- * code.
- *
- * If you modify this file for your own use, don't bump the version
- * number; add a suffix, such as 1.0a or 1.0.3 or something, so we
- * don't have fake `versions' floating around.
- *
- * Simple command issuing extensions by
- * Loren J. Rittle Thu Dec 05 01:33:46 1991
- */
- static char *blurb =
- "Radical Eye MinRexx 0.4ljr (with simple command issuing extensions)";
-
- /*
- * The application *must* declare, open (before calling any minrexx code),
- * and close (after calling any/all minrexx code) RexxSysBase. -ljr
- */
-
- /*
- * We read in our own personal little include.
- */
- #include <string.h>
- #pragma msg 148 ignore push
- #pragma msg 149 ignore push
- #pragma msg 61 ignore push
- #include <proto/exec.h>
- #pragma msg 149 pop
- #pragma msg 61 pop
- #include "libraries.h"
- #include "minrexx.h"
-
- static int cmdcmp (char *c, char *m);
- static void replytoit (struct RexxMsg *msg);
-
- /*
- * All of our local globals, hidden from sight.
- */
- static struct MsgPort *rexxPort;/* this is *our* rexx port */
- static int bringerdown; /* are we trying to shut down? */
- static struct rexxCommandList *globalrcl; /* our command association list */
- static long stillNeedReplies; /* how many replies are pending? */
- static long rexxPortBit; /* what bit to wait on for Rexx? */
- static char *extension; /* the extension for macros */
- static void (*userdisp) (struct RexxMsg *, struct rexxCommandList *, char *); /* the user's dispatch function */
- static struct RexxMsg *oRexxMsg;/* the outstanding Rexx message */
-
- /*
- * This is the main entry point into this code.
- */
- long upRexxPort (char *s, struct rexxCommandList *rcl, char *exten, void(*uf)(struct RexxMsg *, struct rexxCommandList *, char *))
- /*
- * The first argument is the name of your port to be registered;
- * this will be used, for instance, with the `address' command of ARexx.
- * The second argument is an association list of command-name/user-data
- * pairs. It's an array of struct rexxCommandList, terminated by a
- * structure with a NULL in the name field. The commands are case
- * sensitive. The user-data field can contain anything appropriate,
- * perhaps a function to call or some other data.
- * The third argument is the file extension for ARexx macros invoked
- * by this program. If you supply this argument, any `primitive' not
- * in the association list rcl will be sent out to ARexx for
- * interpretation, thus allowing macro programs to work just like
- * primitives. If you do not want this behavior, supply a `NULL'
- * here, and those commands not understood will be replied with an
- * error value of RXERRORNOCMD.
- * The fourth argument is the user dispatch function. This function
- * will *only* be called from rexxDisp(), either from the user calling
- * this function directly, or from dnRexxPort(). Anytime a command
- * match is found in the association list, this user-supplied function
- * will be called with two arguments---the Rexx message that was
- * received, and a pointer to the association pair. This function
- * should return a `1' if the message was replied to by the function
- * and a `0' if the default success code of (0, 0) should be returned.
- * Note that the user function should never ReplyMsg() the message;
- * instead he should indicate the return values with replyRexxCmd();
- * otherwise we lose track of the messages that still lack replies.
- * upRexxPort() returns the signal bit to wait on for Rexx messages.
- * If something goes wrong, it simply returns a `0'. Note that this
- * function is safe to call multiple times because we check to make
- * sure we haven't opened already. It's also a quick way to change
- * the association list or dispatch function.
- */
- {
-
- /*
- * Some basic error checking.
- */
- if (rcl == NULL || uf == NULL)
- return (0L);
- /*
- * If we aren't open, we make sure no one else has opened a port with
- * this name already. If that works, and the createport succeeds, we
- * fill rexxPortBit with the value to return.
- *
- * Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check
- * for rexxPort == NULL also insures that our rexxPortBit is 0.
- */
- if (rexxPort == NULL)
- {
- Forbid ();
- if (FindPort (s) == NULL)
- rexxPort = CreatePort (s, 0L);
- Permit ();
- if (rexxPort != NULL)
- rexxPortBit = 1L << rexxPort->mp_SigBit;
- }
- /*
- * Squirrel away these values for our own internal access, and return
- * the wait bit.
- */
- globalrcl = rcl;
- extension = exten;
- userdisp = uf;
- return (rexxPortBit);
- }
-
- /*
- * This function closes down the Rexx port. It is always safe to
- * call, and should *definitely* be made a part of your cleanup
- * routine. No arguments and no return. It removes the Rexx port,
- * replies to all of the messages and insures that we get replies
- * to all the ones we sent out, closes the Rexx library, deletes the
- * port, clears a few flags, and leaves.
- */
- void dnRexxPort (void)
- {
- if (rexxPort)
- {
- RemPort (rexxPort);
- bringerdown = 1;
- /*
- * A message still hanging around? We kill it off.
- */
- if (oRexxMsg)
- {
- oRexxMsg->rm_Result1 = RXERRORIMGONE;
- ReplyMsg ((struct Message *)oRexxMsg);
- oRexxMsg = NULL;
- }
- while (stillNeedReplies)
- {
- WaitPort (rexxPort);
- dispRexxPort ();
- }
- DeletePort (rexxPort);
- rexxPort = NULL;
- }
- rexxPortBit = 0;
- }
-
- /*
- * Here we dispatch any REXX messages that might be outstanding.
- * This is the main routine for handling Rexx messages.
- * This function is fast if no messages are outstanding, so it's
- * pretty safe to call fairly often.
- *
- * If we are bring the system down and flushing messages, we reply
- * with a pretty serious return code RXERRORIMGONE.
- *
- * No arguments, no returns.
- */
- void dispRexxPort (void)
- {
- register struct RexxMsg *RexxMsg;
- register struct rexxCommandList *rcl;
- register char *p;
- register int dontreply;
-
- /*
- * If there's no rexx port, we're out of here.
- */
- if (rexxPort == NULL)
- return;
- /*
- * Otherwise we have our normal loop on messages.
- */
- while (RexxMsg = (struct RexxMsg *) GetMsg (rexxPort))
- {
- /*
- * If we have a reply to a message we sent, we look at the second
- * argument. If it's set, it's a function we are supposed to call
- * so we call it. Then, we kill the argstring and the message
- * itself, decrement the outstanding count, and attempt to close
- * down the Rexx library. Note that this call only succeeds if
- * there are no outstanding messages. Also, it's pretty quick, so
- * don't talk to me about efficiency.
- */
- if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
- {
- if (RexxMsg->rm_Args[1])
- {
- (*((int (*) (struct RexxMsg *)) (RexxMsg->rm_Args[1]))) (RexxMsg);
- }
- DeleteArgstring (RexxMsg->rm_Args[0]);
- DeleteRexxMsg (RexxMsg);
- stillNeedReplies--;
- /*
- * The default case is we got a message and we need to check it for
- * primitives. We skip past any initial tabs or spaces and initialize
- * the return code fields.
- */
- }
- else
- {
- p = (char *) RexxMsg->rm_Args[0];
- while (*p > 0 && *p <= ' ')
- p++;
- RexxMsg->rm_Result1 = 0;
- RexxMsg->rm_Result2 = 0;
- /*
- * If somehow the reply is already done or postponed, `dontreply' is
- * set.
- */
- dontreply = 0;
- /*
- * If the sky is falling, we just blow up and replymsg.
- */
- if (bringerdown)
- {
- RexxMsg->rm_Result1 = RXERRORIMGONE;
- /*
- * Otherwise we cdr down our association list, comparing commands,
- * until we get a match. If we get a match, we call the dispatch
- * function with the appropriate arguments, and break out.
- */
- }
- else
- {
- oRexxMsg = RexxMsg;
- for (rcl = globalrcl; rcl->name; rcl++)
- {
- if (cmdcmp (rcl->name, p) == 0)
- {
- if (p[strlen (rcl->name)])
- (*userdisp) (RexxMsg, rcl, p + strlen (rcl->name) + 1);
- else
- (*userdisp) (RexxMsg, rcl, p + strlen (rcl->name));
- break;
- }
- }
- /*
- * If we broke out, rcl will point to the command we executed; if we
- * are at the end of the list, we didn't understand the command. In
- * this case, if we were supplied an extension in upRexxPort, we know
- * that we should send the command out, so we do so, synchronously.
- * The synchronous send takes care of our reply. If we were given a
- * NULL extension, we bitch that the command didn't make sense to us.
- */
- if (rcl->name == NULL)
- {
- if (extension)
- {
- syncRexxCmd (RexxMsg->rm_Args[0], RexxMsg);
- dontreply = 1;
- }
- else
- {
- RexxMsg->rm_Result1 = RXERRORNOCMD;
- }
- }
- }
- /*
- * Finally, reply if appropriate.
- */
- oRexxMsg = NULL;
- if (!dontreply)
- ReplyMsg ((struct Message *)RexxMsg);
- }
- }
- }
-
- /*
- * This is the function we use to see if the command matches
- * the command string. Not case sensitive. Make sure all commands
- * are given in lower case!
- */
- static int cmdcmp (char *c, char *m)
- {
- while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z'))))
- {
- c++;
- m++;
- }
- if (!(*c) && *m)
- return ((int) *m != ' ');
- return ((int) *c);
- }
-
- /*
- * This is the general ARexx command interface, but is not the one
- * you will use most of the time; ones defined later are easier to
- * understand and use. But they all go through here.
- */
- struct RexxMsg *sendRexxCmd (char *s, void (*f)(struct RexxMsg *), STRPTR p1, STRPTR p2, STRPTR p3)
- /*
- * The first parameter is the command to send to Rexx.
- * The second parameter is either NULL, indicating that the command
- * should execute asynchronously, or a function to be called when the
- * message we build up and send out here finally returns. Please note
- * that the function supplied here could be called during cleanup after
- * a fatal error, so make sure it is `safe'. This function always is
- * passed one argument, the RexxMsg that is being replied.
- * These are up to three arguments to be stuffed into the RexxMsg we
- * are building up, making the values available when the message is
- * finally replied to. The values are stuffed into Args[2]..Args[4].
- */
- {
- register struct MsgPort *rexxport;
- register struct RexxMsg *RexxMsg;
-
- /*
- * If we have too many replies out there, we just return failure.
- * Note that you should check the return code to make sure your
- * message got out! Then, we forbid, and make sure that:
- * - we have a rexx port open
- * - Rexx is out there
- * - the library is open
- * - we can create a message
- * - we can create an argstring
- *
- * If all of these succeed, we stuff a few values and send the
- * message, permit, and return.
- */
- if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING - 1)
- return (NULL);
- if ((RexxMsg = CreateRexxMsg (rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
- (RexxMsg->rm_Args[0] = CreateArgstring (s, strlen (s))))
- {
- RexxMsg->rm_Action = RXCOMM;
- RexxMsg->rm_Args[1] = (STRPTR) f;
- RexxMsg->rm_Args[2] = p1;
- RexxMsg->rm_Args[3] = p2;
- RexxMsg->rm_Args[4] = p3;
- RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR;
- Forbid ();
- if (rexxport = FindPort (RXSDIR))
- PutMsg (rexxport, (struct Message *)RexxMsg);
- Permit ();
- if (rexxport)
- {
- stillNeedReplies++;
- return (RexxMsg);
- }
- else
- DeleteArgstring (RexxMsg->rm_Args[0]);
- }
- if (RexxMsg)
- DeleteRexxMsg (RexxMsg);
- return (NULL);
- }
-
- /*
- * This function is used to send out an ARexx message and return
- * immediately. Its single parameter is the command to send.
- */
- struct RexxMsg *asyncRexxCmd (char *s)
- {
- return (sendRexxCmd (s, NULL, NULL, NULL, NULL));
- }
-
- /*
- * This function sets things up to reply to the message that caused
- * it when we get a reply to the message we are sending out here.
- * But first the function we pass in, which actually handles the reply.
- * Note how we get the message from the Args[2]; Args[0] is the command,
- * Args[1] is this function, and Args[2]..Args[4] are any parameters
- * passed to sendRexxCmd() as p1..p3. We pass the result codes right
- * along.
- */
- static void replytoit (struct RexxMsg *msg)
- {
- register struct RexxMsg *omsg;
-
- omsg = (struct RexxMsg *) (msg->rm_Args[2]);
- replyRexxCmd (omsg, msg->rm_Result1, msg->rm_Result2, NULL);
- ReplyMsg ((struct Message *)omsg);
- }
-
- /*
- * This function makes use of everything we've put together so far,
- * and functions as a synchronous Rexx call; as soon as the macro
- * invoked here returns, we reply to `msg', passing the return codes
- * back.
- */
- struct RexxMsg *syncRexxCmd (char *s, struct RexxMsg *msg)
- {
- return (sendRexxCmd (s, &replytoit, (STRPTR)msg, NULL, NULL));
- }
-
- /*
- * There are times when you want to pass back return codes or a
- * return string; call this function when you want to do that,
- * and return `1' from the user dispatch function so the main
- * event loop doesn't reply (because we reply here.) This function
- * always returns 1.
- */
- void replyRexxCmd (struct RexxMsg *msg, long primary, long secondary, char *string)
- /*
- * The first parameter is the message we are replying to.
- * The next two parameters are the primary and secondary return
- * codes.
- * The final parameter is a return string. This string is only
- * returned if the primary return code is 0, and a string was
- * requested.
- *
- * We also note that we have replied to the message that came in.
- */
- {
- /*
- * Note how we make sure the Rexx Library is open before calling
- * CreateArgstring . . . and we close it down at the end, if possible.
- */
- if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT)))
- {
- if (string)
- secondary = (long) CreateArgstring (string, strlen (string));
- else
- secondary = 0L;
- }
- msg->rm_Result1 = primary;
- msg->rm_Result2 = secondary;
- }
-
- /*
- * This is the general simple command interface. Added by Loren J. Rittle
- */
- struct RexxMsg *sendSimpleCmd (char *s, char *host, void (*f)(struct RexxMsg *), STRPTR p1, STRPTR p2, STRPTR p3)
- /*
- * The first parameter is the command to send to the host.
- * The second parameter is the name of the host to send to.
- * The thrid parameter is either NULL, indicating that the command
- * should execute asynchronously, or a function to be called when the
- * message we build up and send out here finally returns. Please note
- * that the function supplied here could be called during cleanup after
- * a fatal error, so make sure it is `safe'. This function always is
- * passed one argument, the RexxMsg that is being replied.
- * These are up to three arguments to be stuffed into the RexxMsg we
- * are building up, making the values available when the message is
- * finally replied to. The values are stuffed into Args[2]..Args[4].
- */
- {
- register struct MsgPort *rexxport;
- register struct RexxMsg *RexxMsg;
-
- /*
- * If we have too many replies out there, we just return failure.
- * Note that you should check the return code to make sure your
- * message got out! Then, we forbid, and make sure that:
- * - we have a rexx port open
- * - Rexx is out there
- * - the library is open
- * - we can create a message
- * - we can create an argstring
- *
- * If all of these succeed, we stuff a few values and send the
- * message, permit, and return.
- */
- if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING - 1)
- return (NULL);
- if ((RexxMsg = CreateRexxMsg (rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
- (RexxMsg->rm_Args[0] = CreateArgstring (s, strlen (s))))
- {
- RexxMsg->rm_Action = RXCOMM | RXFF_NOIO | RXFF_RESULT | RXFF_STRING;
- RexxMsg->rm_Args[1] = (STRPTR) f;
- RexxMsg->rm_Args[2] = p1;
- RexxMsg->rm_Args[3] = p2;
- RexxMsg->rm_Args[4] = p3;
- RexxMsg->rm_Node.mn_Node.ln_Name = host;
- Forbid ();
- if (rexxport = FindPort (host))
- PutMsg (rexxport, (struct Message *)RexxMsg);
- Permit ();
- if (rexxport)
- {
- stillNeedReplies++;
- return (RexxMsg);
- }
- else
- DeleteArgstring (RexxMsg->rm_Args[0]);
- }
- if (RexxMsg)
- DeleteRexxMsg (RexxMsg);
- return (NULL);
- }
-