home *** CD-ROM | disk | FTP | other *** search
/ Gold Fish 3 / goldfish_volume_3.bin / files / text / tex / pastex / source / driver / show / amiga / minrexx.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-06  |  16.1 KB  |  508 lines

  1. /*
  2.  *   This is an example of how REXX messages might be handled.  This is
  3.  *   a `minimum' example that both accepts asynchronous REXX messages and
  4.  *   can request REXX service.
  5.  *
  6.  *   Read this entire file!  It's short enough.
  7.  *
  8.  *   It is written in such a fashion that it can be attached to a program
  9.  *   with a minimum of fuss.  The only external symbols it makes available
  10.  *   are the seven functions and RexxSysBase.
  11.  *
  12.  *   This code is by Radical Eye Software, but it is put in the public
  13.  *   domain.  I would appreciate it if the following string was left in
  14.  *   both as a version check and as thanks from you for the use of this
  15.  *   code.
  16.  *
  17.  *   If you modify this file for your own use, don't bump the version
  18.  *   number; add a suffix, such as 1.0a or 1.0.3 or something, so we
  19.  *   don't have fake `versions' floating around.
  20.  */
  21. static char *blurb = "Radical Eye MinRexx 0.4.1" ;
  22. /*
  23.  *   We read in our own personal little include.
  24.  */
  25.  
  26. #include <stdio.h>
  27.  
  28. #include "defines.h"
  29. #include "globals.h"
  30.  
  31. #include "minrexx.h"
  32. #include "amscreen.h"
  33.  
  34. #include "globals.i"
  35. #include "am_requ.i"
  36.  
  37. #include <stdio.h>
  38. #include <string.h>
  39.  
  40. #include <clib/exec_protos.h>
  41. #include <clib/alib_protos.h>
  42. #include <pragmas/exec_pragmas.h>
  43.  
  44.  
  45.  
  46. /*
  47.  * Fuer die locale-Library:
  48.  *
  49.  * Hier duerfen *nur* die MSG_#? Nummern eingebunden werden!
  50.  * Achtung:
  51.  * Es muss/sollte 'multiple-include' erlaubt sein!
  52.  */
  53. #include "local.i"
  54.  
  55. #undef  CATCOMP_ARRAY
  56. #undef  CATCOMP_BLOCK
  57. #undef  CATCOMP_STRINGS
  58. #define CATCOMP_NUMBERS
  59. #include "localstr.h"
  60.  
  61.  
  62.  
  63. /*
  64.  *   All of our local globals, hidden from sight.
  65.  */
  66. static struct MsgPort *rexxPort ;          /* this is *our* rexx port */
  67. static int bringerdown ;                   /* are we trying to shut down? */
  68. static struct rexxCommandList *globalrcl ; /* our command association list */
  69. static long stillNeedReplies ;             /* how many replies are pending? */
  70. static long rexxPortBit ;                  /* what bit to wait on for Rexx? */
  71. static char *extension ;                   /* the extension for macros */
  72. static long (*userdisp)(struct RexxMsg *, struct rexxCommandList *, char *) ;
  73.                                /* the user's dispatch function */
  74. static struct RexxMsg *oRexxMsg ;          /* the outstanding Rexx message */
  75. /*
  76.  *   Our library base.  Don't you dare close this!
  77.  */
  78. struct RxsLib *RexxSysBase = NULL;
  79.  
  80.  
  81. /* prototypes */
  82. static void closeRexxLib(void);
  83. static int  cmdcmp(register char *c, register char *m);
  84. static int  openRexxLib(void);
  85. static void replytoit(struct RexxMsg *msg);
  86.  
  87.  
  88. /*
  89.  *   This is the main entry point into this code.
  90.  */
  91. long upRexxPort(s, rcl, exten, uf)
  92. /*
  93.  *   The first argument is the name of your port to be registered;
  94.  *   this will be used, for instance, with the `address' command of ARexx.
  95.  */
  96. char *s ;
  97. /*
  98.  *   The second argument is an association list of command-name/user-data
  99.  *   pairs.  It's an array of struct rexxCommandList, terminated by a
  100.  *   structure with a NULL in the name field. The commands are case
  101.  *   sensitive.  The user-data field can contain anything appropriate,
  102.  *   perhaps a function to call or some other data.
  103.  */
  104. struct rexxCommandList *rcl ;
  105. /*
  106.  *   The third argument is the file extension for ARexx macros invoked
  107.  *   by this program.  If you supply this argument, any `primitive' not
  108.  *   in the association list rcl will be sent out to ARexx for
  109.  *   interpretation, thus allowing macro programs to work just like
  110.  *   primitives.  If you do not want this behavior, supply a `NULL'
  111.  *   here, and those commands not understood will be replied with an
  112.  *   error value of RXERRORNOCMD.
  113.  */
  114. char *exten ;
  115. /*
  116.  *   The fourth argument is the user dispatch function.  This function
  117.  *   will *only* be called from rexxDisp(), either from the user calling
  118.  *   this function directly, or from dnRexxPort().  Anytime a command
  119.  *   match is found in the association list, this user-supplied function
  120.  *   will be called with two arguments---the Rexx message that was
  121.  *   received, and a pointer to the association pair.  This function
  122.  *   should return a `1' if the message was replied to by the function
  123.  *   and a `0' if the default success code of (0, 0) should be returned.
  124.  *   Note that the user function should never ReplyMsg() the message;
  125.  *   instead he should indicate the return values with replyRexxCmd();
  126.  *   otherwise we lose track of the messages that still lack replies.
  127.  */
  128. long (*uf)(struct RexxMsg *, struct rexxCommandList *, char *) ;
  129. /*
  130.  *   upRexxPort() returns the signal bit to wait on for Rexx messages.
  131.  *   If something goes wrong, it simply returns a `0'.  Note that this
  132.  *   function is safe to call multiple times because we check to make
  133.  *   sure we haven't opened already.  It's also a quick way to change
  134.  *   the association list or dispatch function.
  135.  */
  136. {
  137.    struct MsgPort *FindPort() ;
  138.    struct MsgPort *CreatePort() ;
  139.  
  140. /*
  141.  *   Some basic error checking.
  142.  */
  143.    if (rcl == NULL || uf == NULL)
  144.       return(0L) ;
  145. /*
  146.  *   If we aren't open, we make sure no one else has opened a port with
  147.  *   this name already.  If that works, and the createport succeeds, we
  148.  *   fill rexxPortBit with the value to return.
  149.  *
  150.  *   Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check
  151.  *   for rexxPort == NULL also insures that our rexxPortBit is 0.
  152.  */
  153.    if (rexxPort == NULL) {
  154.       Forbid() ;
  155.       if (FindPort(s)==NULL)
  156.          rexxPort = CreatePort(s, 0L) ;
  157.       Permit() ;
  158.       if (rexxPort != NULL)
  159.          rexxPortBit = 1L << rexxPort->mp_SigBit ;
  160.    }
  161. /*
  162.  *   Squirrel away these values for our own internal access, and return
  163.  *   the wait bit.
  164.  */
  165.    globalrcl = rcl ;
  166.    extension = exten ;
  167.    userdisp = uf ;
  168.    return(rexxPortBit) ;
  169. }
  170.  
  171. /*
  172.  *   This function closes the rexx library, but only if it is open
  173.  *   and we aren't expecting further replies from REXX.  It's
  174.  *   *private*, but it doesn't have to be; it's pretty safe to
  175.  *   call anytime.
  176.  */
  177. static void closeRexxLib() {
  178.    if (stillNeedReplies == 0 && RexxSysBase) {
  179.       CloseLibrary(&(RexxSysBase->rl_Node)) ;
  180.       RexxSysBase = NULL ;
  181.    }
  182. }
  183.  
  184. /*
  185.  *   This function closes down the Rexx port.  It is always safe to
  186.  *   call, and should *definitely* be made a part of your cleanup
  187.  *   routine.  No arguments and no return.  It removes the Rexx port,
  188.  *   replies to all of the messages and insures that we get replies
  189.  *   to all the ones we sent out, closes the Rexx library, deletes the
  190.  *   port, clears a few flags, and leaves.
  191.  */
  192. void dnRexxPort() {
  193.    if (rexxPort) {
  194.       RemPort(rexxPort) ;
  195.       bringerdown = 1 ;
  196. /*
  197.  *   A message still hanging around?  We kill it off.
  198.  */
  199.       if (oRexxMsg) {
  200.          oRexxMsg->rm_Result1 = RXERRORIMGONE ;
  201.          ReplyMsg(&(oRexxMsg->rm_Node)) ;
  202.          oRexxMsg = NULL ;
  203.       }
  204.       while (stillNeedReplies) {
  205.          WaitPort(rexxPort) ;
  206.          dispRexxPort() ;
  207.       }
  208.       closeRexxLib() ;
  209.       DeletePort(rexxPort) ;
  210.       rexxPort = NULL ;
  211.    }
  212.    rexxPortBit = 0 ;
  213. }
  214.  
  215. /*
  216.  *   Here we dispatch any REXX messages that might be outstanding.
  217.  *   This is the main routine for handling Rexx messages.
  218.  *   This function is fast if no messages are outstanding, so it's
  219.  *   pretty safe to call fairly often.
  220.  *
  221.  *   If we are bring the system down and flushing messages, we reply
  222.  *   with a pretty serious return code RXERRORIMGONE.
  223.  *
  224.  *   No arguments, no returns.
  225.  */
  226. long dispRexxPort() {
  227.    register struct RexxMsg *RexxMsg ;
  228.    register struct rexxCommandList *rcl ;
  229.    register char *p ;
  230.    register int dontreply ;
  231.    long ret = 0L;
  232.  
  233. /*
  234.  *   If there's no rexx port, we're out of here.
  235.  */
  236.    if (rexxPort == NULL)
  237.       return ret;
  238. /*
  239.  *   Otherwise we have our normal loop on messages.
  240.  */
  241.    while ((bringerdown || ret == 0) && (RexxMsg = (struct RexxMsg *)GetMsg(rexxPort))) {
  242. /*
  243.  *   If we have a reply to a message we sent, we look at the second
  244.  *   argument.  If it's set, it's a function we are supposed to call
  245.  *   so we call it.  Then, we kill the argstring and the message
  246.  *   itself, decrement the outstanding count, and attempt to close
  247.  *   down the Rexx library.  Note that this call only succeeds if
  248.  *   there are no outstanding messages.  Also, it's pretty quick, so
  249.  *   don't talk to me about efficiency.
  250.  */
  251.       if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
  252.          if (RexxMsg->rm_Result1 != 0) {
  253.        Message(MSG_AREXX_REPLY_CODE, RexxMsg->rm_Result1);
  254.      }
  255.          if (RexxMsg->rm_Args[1]) {
  256.             /* prototype only for replytoit !! */
  257.             ((int (*)(struct RexxMsg *msg))(RexxMsg->rm_Args[1]))(RexxMsg);
  258.          }
  259.          DeleteArgstring(RexxMsg->rm_Args[0]) ;
  260.          DeleteRexxMsg(RexxMsg) ;
  261.          stillNeedReplies-- ;
  262.          closeRexxLib() ;
  263. /*
  264.  *   The default case is we got a message and we need to check it for
  265.  *   primitives.  We skip past any initial tabs or spaces and initialize
  266.  *   the return code fields.
  267.  */
  268.       } else {
  269.          p = (char *)RexxMsg->rm_Args[0] ;
  270.          while (*p > 0 && *p <= ' ')
  271.             p++ ;
  272.          RexxMsg->rm_Result1 = 0 ;
  273.          RexxMsg->rm_Result2 = 0 ;
  274. /*
  275.  *   If somehow the reply is already done or postponed, `dontreply' is
  276.  *   set.
  277.  */
  278.          dontreply = 0 ;
  279. /*
  280.  *   If the sky is falling, we just blow up and replymsg.
  281.  */
  282.          if (bringerdown) {
  283.             RexxMsg->rm_Result1 = RXERRORIMGONE ;
  284. /*
  285.  *   Otherwise we cdr down our association list, comparing commands,
  286.  *   until we get a match.  If we get a match, we call the dispatch
  287.  *   function with the appropriate arguments, and break out.
  288.  */
  289.          } else {
  290.             oRexxMsg = RexxMsg ;
  291.             for (rcl = globalrcl; rcl->name; rcl++) {
  292.                if (cmdcmp(rcl->name, p) == 0) {
  293.                   ret = userdisp(RexxMsg, rcl, p+strlen(rcl->name)) ;
  294.                   break ;
  295.                }
  296.             }
  297. /*
  298.  *   If we broke out, rcl will point to the command we executed; if we
  299.  *   are at the end of the list, we didn't understand the command.  In
  300.  *   this case, if we were supplied an extension in upRexxPort, we know
  301.  *   that we should send the command out, so we do so, synchronously.
  302.  *   The synchronous send takes care of our reply.  If we were given a
  303.  *   NULL extension, we bitch that the command didn't make sense to us.
  304.  */
  305.             if (rcl->name == NULL) {
  306.                if (extension) {
  307.                   syncRexxCmd(RexxMsg->rm_Args[0], RexxMsg) ;
  308.                   dontreply = 1 ;
  309.                } else {
  310.                   RexxMsg->rm_Result1 = RXERRORNOCMD ;
  311.                }
  312.             }
  313.          }
  314. /*
  315.  *   Finally, reply if appropriate.
  316.  */
  317.          oRexxMsg = NULL ;
  318.          if (! dontreply)
  319.             ReplyMsg(&(RexxMsg->rm_Node)) ;
  320.       }
  321.    }
  322.  
  323.    return ret;
  324. }
  325.  
  326. /*
  327.  *   This is the function we use to see if the command matches
  328.  *   the command string.  Not case sensitive, and the real command only
  329.  *   need be a prefix of the command string.  Make sure all commands
  330.  *   are given in lower case!
  331.  */
  332. static int cmdcmp(c, m)
  333. register char *c, *m ;
  334. {
  335.    while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z')))) {
  336.       c++ ;
  337.       m++ ;
  338.    }
  339.    return (int)(*c) ;
  340. }
  341.  
  342. /*
  343.  *   Opens the Rexx library if unopened.  Returns success (1) or
  344.  *   failure (0).  This is another function that is *private* but
  345.  *   that doesn't have to be.
  346.  */
  347. static int openRexxLib() {
  348.    if (RexxSysBase)
  349.       return(1) ;
  350.    return((RexxSysBase = (struct RxsLib *)OpenLibrary(RXSNAME, 0L)) != NULL) ;
  351. }
  352.  
  353. /*
  354.  *   This is the general ARexx command interface, but is not the one
  355.  *   you will use most of the time; ones defined later are easier to
  356.  *   understand and use.  But they all go through here.
  357.  */
  358. struct RexxMsg *sendRexxCmd(s, f, p1, p2, p3)
  359. char *s ;
  360. /*
  361.  *   The first parameter is the command to send to Rexx.
  362.  */
  363. /* int (*f)() ; */
  364. APTR f;
  365. /*
  366.  *   The second parameter is either NULL, indicating that the command
  367.  *   should execute asynchronously, or a function to be called when the
  368.  *   message we build up and send out here finally returns.  Please note
  369.  *   that the function supplied here could be called during cleanup after
  370.  *   a fatal error, so make sure it is `safe'.  This function always is
  371.  *   passed one argument, the RexxMsg that is being replied.
  372.  */
  373. STRPTR p1, p2, p3 ;
  374. /*
  375.  *   These are up to three arguments to be stuffed into the RexxMsg we
  376.  *   are building up, making the values available when the message is
  377.  *   finally replied to.  The values are stuffed into Args[2]..Args[4].
  378.  */
  379. {
  380.    register struct MsgPort *rexxport ;
  381.    register struct RexxMsg *RexxMsg ;
  382.  
  383. /*
  384.  *   If we have too many replies out there, we just return failure.
  385.  *   Note that you should check the return code to make sure your
  386.  *   message got out!  Then, we forbid, and make sure that:
  387.  *      - we have a rexx port open
  388.  *      - Rexx is out there
  389.  *      - the library is open
  390.  *      - we can create a message
  391.  *      - we can create an argstring
  392.  *
  393.  *   If all of these succeed, we stuff a few values and send the
  394.  *   message, permit, and return.
  395.  */
  396.    if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING-1)
  397.       return(NULL) ;
  398.    RexxMsg = NULL ;
  399.    if (openRexxLib() && (RexxMsg =
  400.              CreateRexxMsg(rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
  401.              (RexxMsg->rm_Args[0] = CreateArgstring(s, (long)strlen(s)))) {
  402.       RexxMsg->rm_Action  = RXCOMM | RXFF_NOIO;
  403.       RexxMsg->rm_Args[1] = (STRPTR)f ;
  404.       RexxMsg->rm_Args[2] = p1 ;
  405.       RexxMsg->rm_Args[3] = p2 ;
  406.       RexxMsg->rm_Args[4] = p3 ;
  407.       RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR ;
  408.       Forbid() ;
  409.       if (rexxport = FindPort(RXSDIR))
  410.          PutMsg(rexxport, &(RexxMsg->rm_Node)) ;
  411.       Permit() ;
  412.       if (rexxport) {
  413.          stillNeedReplies++ ;
  414.          return(RexxMsg) ;
  415.       } else
  416.          DeleteArgstring(RexxMsg->rm_Args[0]) ;
  417.    }
  418.    if (RexxMsg)
  419.       DeleteRexxMsg(RexxMsg) ;
  420.    closeRexxLib() ;
  421.    return(NULL) ;
  422. }
  423.  
  424. /*
  425.  *   This function is used to send out an ARexx message and return
  426.  *   immediately.  Its single parameter is the command to send.
  427.  */
  428. struct RexxMsg *asyncRexxCmd(s)
  429. char *s ;
  430. {
  431.    return(sendRexxCmd(s, NULL, NULL, NULL, NULL)) ;
  432. }
  433.  
  434. /*
  435.  *   This function sets things up to reply to the message that caused
  436.  *   it when we get a reply to the message we are sending out here.
  437.  *   But first the function we pass in, which actually handles the reply.
  438.  *   Note how we get the message from the Args[2]; Args[0] is the command,
  439.  *   Args[1] is this function, and Args[2]..Args[4] are any parameters
  440.  *   passed to sendRexxCmd() as p1..p3.  We pass the result codes right
  441.  *   along.
  442.  */
  443. static void replytoit(msg)
  444. struct RexxMsg *msg ;
  445. {
  446.    register struct RexxMsg *omsg ;
  447.  
  448.    if (msg != NULL) {
  449.      omsg = (struct RexxMsg *)(msg->rm_Args[2]) ;
  450.      replyRexxCmd(omsg, msg->rm_Result1, msg->rm_Result2, NULL) ;
  451.      ReplyMsg(&(omsg->rm_Node)) ;
  452.    }
  453. }
  454.  
  455. /*
  456.  *   This function makes use of everything we've put together so far,
  457.  *   and functions as a synchronous Rexx call; as soon as the macro
  458.  *   invoked here returns, we reply to `msg', passing the return codes
  459.  *   back.
  460.  */
  461. struct RexxMsg *syncRexxCmd(s, msg)
  462. char *s ;
  463. struct RexxMsg *msg ;
  464. {
  465.    return(sendRexxCmd(s, (APTR)&replytoit, (STRPTR)msg, NULL, NULL)) ;
  466. }
  467.  
  468. /*
  469.  *   There are times when you want to pass back return codes or a
  470.  *   return string; call this function when you want to do that,
  471.  *   and return `1' from the user dispatch function so the main
  472.  *   event loop doesn't reply (because we reply here.)  This function
  473.  *   always returns 1.
  474.  */
  475. void replyRexxCmd(msg, primary, secondary, string)
  476. /*
  477.  *   The first parameter is the message we are replying to.
  478.  */
  479. struct RexxMsg *msg ;
  480. /*
  481.  *   The next two parameters are the primary and secondary return
  482.  *   codes.
  483.  */
  484. long primary, secondary ;
  485. /*
  486.  *   The final parameter is a return string.  This string is only
  487.  *   returned if the primary return code is 0, and a string was
  488.  *   requested.
  489.  *
  490.  *   We also note that we have replied to the message that came in.
  491.  */
  492. char *string ;
  493. {
  494. /*
  495.  *   Note how we make sure the Rexx Library is open before calling
  496.  *   CreateArgstring . . . and we close it down at the end, if possible.
  497.  */
  498.    if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT))) {
  499.       if (string && openRexxLib())
  500.          secondary = (long)CreateArgstring(string, (long)strlen(string)) ;
  501.       else
  502.          secondary = 0L ;
  503.    }
  504.    msg->rm_Result1 = primary ;
  505.    msg->rm_Result2 = secondary ;
  506.    closeRexxLib() ;
  507. }
  508.