#ifdef SNAPREXX /* * 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. * * 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 seven functions and RexxSysBase. * * 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. */ static char *blurb = "Radical Eye MinRexx 0.4" ; /* * We read in our own personal little include. */ #include "minrexx.h" /* * 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 int (*userdisp)() ; /* the user's dispatch function */ static struct RexxMsg *oRexxMsg ; /* the outstanding Rexx message */ /* * Our library base. Don't you dare close this! */ struct RxsLib *RexxSysBase ; /* * This is the main entry point into this code. */ long upRexxPort(s, rcl, exten, uf) /* * The first argument is the name of your port to be registered; * this will be used, for instance, with the `address' command of ARexx. */ char *s ; /* * 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. */ struct rexxCommandList *rcl ; /* * 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. */ char *exten ; /* * 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. */ int (*uf)() ; /* * 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. */ { struct MsgPort *FindPort() ; struct MsgPort *CreatePort() ; /* * 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 the rexx library, but only if it is open * and we aren't expecting further replies from REXX. It's * *private*, but it doesn't have to be; it's pretty safe to * call anytime. */ static void closeRexxLib() { if (stillNeedReplies == 0 && RexxSysBase) { CloseLibrary(RexxSysBase) ; RexxSysBase = NULL ; } } /* * 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() { if (rexxPort) { RemPort(rexxPort) ; bringerdown = 1 ; /* * A message still hanging around? We kill it off. */ if (oRexxMsg) { oRexxMsg->rm_Result1 = RXERRORIMGONE ; ReplyMsg(oRexxMsg) ; oRexxMsg = NULL ; } while (stillNeedReplies) { WaitPort(rexxPort) ; dispRexxPort() ; } closeRexxLib() ; 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() { register struct RexxMsg *RexxMsg ; int cmdcmp() ; 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 (*)())(RexxMsg->rm_Args[1]))(RexxMsg) ; } DeleteArgstring(RexxMsg->rm_Args[0]) ; DeleteRexxMsg(RexxMsg) ; stillNeedReplies-- ; closeRexxLib() ; /* * 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) { 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(RexxMsg) ; } } } /* * This is the function we use to see if the command matches * the command string. Not case sensitive, and the real command only * need be a prefix of the command string. Make sure all commands * are given in lower case! */ static int cmdcmp(c, m) register char *c, *m ; { while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z')))) { c++ ; m++ ; } return(*c) ; } /* * Opens the Rexx library if unopened. Returns success (1) or * failure (0). This is another function that is *private* but * that doesn't have to be. */ static int openRexxLib() { if (RexxSysBase) return(1) ; return((RexxSysBase = (struct RxsLib *)OpenLibrary(RXSNAME, 0L)) != NULL) ; } /* * 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(s, f, p1, p2, p3) char *s ; /* * The first parameter is the command to send to Rexx. */ int (*f)() ; /* * 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. */ STRPTR p1, p2, p3 ; /* * 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]. */ { struct RexxMsg *CreateRexxMsg() ; STRPTR CreateArgstring() ; 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) ; RexxMsg = NULL ; if (openRexxLib() && (RexxMsg = CreateRexxMsg(rexxPort, extension, rexxPort->mp_Node.ln_Name)) && (RexxMsg->rm_Args[0] = CreateArgstring(s, (long)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, RexxMsg) ; Permit() ; if (rexxport) { stillNeedReplies++ ; return(RexxMsg) ; } else DeleteArgstring(RexxMsg->rm_Args[0]) ; } if (RexxMsg) DeleteRexxMsg(RexxMsg) ; closeRexxLib() ; 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(s) 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(msg) register struct RexxMsg *msg ; { register struct RexxMsg *omsg ; omsg = (struct RexxMsg *)(msg->rm_Args[2]) ; replyRexxCmd(omsg, msg->rm_Result1, msg->rm_Result2, NULL) ; ReplyMsg(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(s, msg) char *s ; struct RexxMsg *msg ; { return(sendRexxCmd(s, (APTR)&replytoit, 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(msg, primary, secondary, string) /* * The first parameter is the message we are replying to. */ register struct RexxMsg *msg ; /* * The next two parameters are the primary and secondary return * codes. */ register long primary, secondary ; /* * 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. */ register char *string ; { STRPTR CreateArgstring() ; /* * 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 && openRexxLib()) secondary = (long)CreateArgstring(string, (long)strlen(string)) ; else secondary = 0L ; } msg->rm_Result1 = primary ; msg->rm_Result2 = secondary ; closeRexxLib() ; } #endif