home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / emacs-18.59-src.tgz / emacs-18.59-src.tar / fsf / emacs18 / src / amiga_rexx.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  14KB  |  461 lines

  1. /* low level ARexx code for use in amiga version of Emacs.
  2.    Copyright (C) 1993 Christian E. Hopps.
  3.  
  4. This file is part of GNU Emacs.
  5.  
  6. GNU Emacs is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU Emacs is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Emacs; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. #include "SimpleRexx.h"
  21. #include <proto/exec.h>
  22. #undef NULL
  23. #include "config.h"
  24. #include "lisp.h"
  25.  
  26. #include "amiga.h"
  27.  
  28. #define XLRXMSG(x) ((struct LispRexxMsg *) XPNTR((x)))
  29. #define XSETLRXMSG(x,v) (XSET((x), Lisp_Int, v))
  30.  
  31. static AREXXCONTEXT far handle;
  32. static int amiga_arexx_initialized;
  33.  
  34. /* This structure should be allocated with emacs_malloc() its pointer will be */
  35. /* used as an msgid. (emacs XUINT())*/
  36. struct LispRexxMsg {
  37.   struct MinNode  lrm_Node;              /* A node for tracking messages. */
  38.   struct RexxMsg *lrm_Msg;              /* The actual Rexx Msg. */
  39.   ULONG  lrm_Flags;
  40. };
  41. /* Flags for LispRexxMessage indicating what to do with it. */
  42. #define LRMF_SENTCMD (1L << 0)              /* this msg originated here. */
  43. #define LRMF_DOERRORS (1L << 1)              /* handle error replies */
  44. #define LRMF_DORESULTS (1L << 2)          /* handle result strings */
  45.  
  46. struct LispRexxList {
  47.     struct MinList lrl_List;
  48.     int    lrl_Count;
  49. };
  50.  
  51. struct LispRexxList pending;              /* The list of pending */
  52.                           /* (outgoing) Rexx Messages. */
  53. struct LispRexxList returned;              /* The list of pending */
  54.                           /* (outgoing) Rexx Messages */
  55.                           /* that have been received. */
  56. struct LispRexxList incoming;              /* The message that are */
  57.                           /* incoming to Emacs (sent */
  58.                           /* from some other rexx host. */
  59.  
  60. /* allocate a rexx message properly */
  61. Lisp_Object alloc_rexx_msg(ULONG flags)
  62. {
  63.     Lisp_Object rm = Qnil;
  64.     struct LispRexxMsg *lrm = (struct LispRexxMsg *)malloc(sizeof(*lrm));
  65.     if(lrm) {
  66.     lrm->lrm_Flags = flags;
  67.     XSETLRXMSG(rm,lrm);
  68.     return(rm);
  69.     }
  70.     return(Qnil);
  71. }
  72.  
  73. /* free an arexx message allocated with alloc_arexx_msg() */
  74. void free_rexx_msg (Lisp_Object rm)
  75. {
  76.     if(!NULL(rm)) {
  77.     void *mem = XLRXMSG(rm);
  78.     free(mem);
  79.     }
  80. }
  81.  
  82. /* The next 2 functions imlement FIFO lists. */
  83.  
  84. /* add LispRexxMsg to a LispRexxLisp Tail. */
  85. void add_rexx_msg_to_tail(struct LispRexxList *rl, Lisp_Object rm)
  86. {
  87.     AddTail((struct List *)rl,(struct Node *)XPNTR(rm));
  88.     rl->lrl_Count++;
  89. }
  90.  
  91. /* remove LispRexxMsg from head of a LispRexxLisp. */
  92. Lisp_Object remove_rexx_msg_from_head(struct LispRexxList *rl)
  93. {
  94.     Lisp_Object rm = (Lisp_Object)RemHead((struct List *)rl);
  95.     if(rm != 0) {
  96.     rl->lrl_Count--;
  97.     return(rm);
  98.     } else {
  99.         return Qnil;
  100.     }
  101. }
  102.  
  103. void remove_rexx_msg(struct LispRexxList *rl, Lisp_Object rm)
  104. {
  105.     Remove((struct Node *)XPNTR(rm));
  106.     rl->lrl_Count--;
  107. }
  108.  
  109.  
  110. /* find a rexx message on a list given an msgid (ptr) */
  111. int is_rexx_msgid_on_list(struct LispRexxList *rl,
  112.                     Lisp_Object id)
  113. {
  114.     struct MinNode *mn = rl->lrl_List.mlh_Head;
  115.     for(mn; mn->mln_Succ; mn = mn->mln_Succ) {
  116.     Lisp_Object cmpid = Qnil;
  117.     XSETLRXMSG(cmpid,mn);
  118.     if( EQ(cmpid,id)) {
  119.         return(1);
  120.     }
  121.     }
  122.     return(0);
  123. }
  124.  
  125. Lisp_Object find_rexx_msg_on_list(struct LispRexxList *rl,
  126.                   struct RexxMsg *msg)
  127. {
  128.     Lisp_Object pnt = Qnil;
  129.     struct MinNode *mn = rl->lrl_List.mlh_Head;
  130.     for(mn; mn->mln_Succ; mn = mn->mln_Succ) {
  131.     if( ((struct LispRexxMsg *)mn)->lrm_Msg == msg) {
  132.         XSETLRXMSG(pnt,mn);
  133.     }
  134.     }
  135.     return(pnt);
  136. }
  137.  
  138. /* This function is given a RexxMsg and it goes and find (or doesn't) the */
  139. /* corisponding pending message, removes it from the list and sets up the lisp */
  140. /* list for return values.  if it is not found nil is returned. (it deals with */
  141. /* the errors for incoming messages properly.  Some thought is needed on how to */
  142. /* handle errors from replied sent commands that were not asking for results. */
  143. Lisp_Object handle_rexx_msg_replied(struct RexxMsg *msg)
  144. {
  145.     Lisp_Object rm = find_rexx_msg_on_list(&pending, msg);
  146.     if(!NULL(rm)) {
  147.     /* Process the command.  If it was requesting results strings handle */
  148.     /* them otherwise just delete. */
  149.     struct LispRexxMsg *lrm = XLRXMSG(rm);
  150.     remove_rexx_msg(&pending, rm);
  151.  
  152.     if(msg->rm_Result1 == 0) {
  153.         if(lrm->lrm_Flags & LRMF_DORESULTS) {
  154.         /* add to returned so that result can be fetched. */
  155.         add_rexx_msg_to_tail(&returned,rm);
  156.         } else {
  157.         /* simply delete rexx message. */
  158.         DeleteARexxMsg(handle,msg);
  159.         free_rexx_msg(rm);
  160.         }
  161.     } else {
  162.         /* an error occured with our message. */
  163.         if(lrm->lrm_Flags & LRMF_DOERRORS) {
  164.         /* add to returned so that error can be fetched. */
  165.         add_rexx_msg_to_tail(&returned,rm);
  166.         } else {
  167.         /* simply delete rexx message. */
  168.         DeleteARexxMsg(handle,msg);
  169.         free_rexx_msg(rm);
  170.         }
  171.     }
  172.     } else {
  173.     /* This should never happen we received a rexx message reply */
  174.     /* that we never sent out. */
  175.     DeleteARexxMsg(handle,msg);
  176.     }
  177. }
  178.  
  179. /* This function takes incoming messages and place them on the incoming msg */
  180. /* list.  */
  181. Lisp_Object handle_rexx_msg_received(struct RexxMsg *msg)
  182. {
  183.     Lisp_Object rm = alloc_rexx_msg(LRMF_DORESULTS|LRMF_DOERRORS);
  184.     if(!NULL(rm)) {
  185.     /* Add message to incoming list. */
  186.     struct LispRexxMsg *lrm = XLRXMSG(rm);
  187.     lrm->lrm_Msg = msg;              /* set msg pointer. */
  188.     add_rexx_msg_to_tail(&incoming,rm);
  189.     } else {
  190.     /* This should never happen we received a rexx message but ran out of */
  191.     /* memory.  Set last error msg. and reply with fail. */
  192.     SetARexxLastError(handle, msg, "Out of emacs memory.");
  193.     ReplyARexxMsg(handle, msg, 0, 20);
  194.     }
  195. }
  196.  
  197. /* Almost the same as old one, but we now call handle_pending_arexx_reply() for */
  198. /* replied messages that we sent, so that we can setup result strings and such. */
  199. int check_arexx(int force, int kbd)
  200. {
  201.     struct RexxMsg *msg;
  202.     int msg_received = FALSE;
  203.     while (msg = GetARexxMsg(handle)) {
  204.     msg_received = TRUE;
  205.     if(msg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)    {
  206.         /* This is a reply to a rexx command we send out. */
  207.         handle_rexx_msg_replied(msg);
  208.     } else {
  209.         handle_rexx_msg_received(msg);
  210.     }
  211.     }
  212.     if ((kbd && amiga_arexx_initialized)) {
  213.     /* if we got a message or we have some out, or we have some waiting to */
  214.     /* be processes then enque the Key sequence that will call the rexx */
  215.     /* message handler.  We obviously don't do this for returned commands :^) */
  216.     if ((msg_received || force && incoming.lrl_Count > 0) &&
  217.         get_ttycount() == 0) {
  218.         enque(AMIGASEQ, FALSE); enque('X', FALSE);
  219.     }
  220.     }
  221.     return msg_received;
  222. }
  223.  
  224. DEFUN ("amiga-arexx-wait", Famiga_arexx_wait, Samiga_arexx_wait, 0, 0, 0,
  225.        "Wait for an ARexx event (command or reply) before proceeding.")
  226.     ()
  227. {
  228.     while (!check_arexx(FALSE, FALSE)) Wait(ARexxSignal(handle));
  229. }
  230.  
  231. DEFUN ("amiga-arexx-check-command",
  232.        Famiga_arexx_check_command, Samiga_arexx_check_command, 1, 1, 0,
  233.        "Return t if command ID has finished, nil otherwise.")
  234.     (id)
  235. Lisp_Object id;
  236. {
  237.     CHECK_NUMBER(id,0);
  238.  
  239.     if(is_rexx_msgid_on_list(&pending,id)) {
  240.     /* still on pending return false. */
  241.     return Qnil;
  242.     } else if(is_rexx_msgid_on_list(&returned,id)) {
  243.     /* is waiting to be processed return true. */
  244.     return Qt;
  245.     }
  246.  
  247.     /* is nowhere to be found. error. */
  248.     error("id not found.");
  249.     return Qnil;
  250. }
  251.  
  252. DEFUN ("amiga-arexx-get-next-msg", Famiga_arexx_get_next_msg,
  253.        Samiga_get_next_msg, 0, 0, 0,
  254. "Returns the oldest arexx msg sent to emacs rexx port.\n\
  255. When you are through with this message call (amiga-arexx-reply).\n\
  256. if the msg is not replied this function will continue to\n\
  257. return that msg until it has been replied to.")
  258.   ()
  259. {
  260.     struct RexxMsg *rmsg;
  261.  
  262.     check_arexx(FALSE, FALSE);
  263.     if (incoming.lrl_Count) {
  264.     struct RexxMsg *msg = ((struct LispRexxMsg *)
  265.                    incoming.lrl_List.mlh_Head)->lrm_Msg;
  266.     return build_string(ARG0(msg));
  267.     }
  268.     /* nothing to be gotten. */
  269.     return Qnil;
  270. }
  271.  
  272. DEFUN("amiga-arexx-get-msg-results", Famiga_arexx_get_msg_results,
  273.       Samiga_arexx_get_msg_results, 1,1,0,
  274. "Returns the results from MSGID. will be a list of the form:\n\
  275.   (msgid resultcode secondary)\n\n\
  276. If resultcode is 0 then secondary will be a string or nil.\n\
  277. else resulcode will be greater than 0 and secondary will be\n\
  278. an error-code (int).\n\n\
  279. If MSGID has not yet completed nil is returned.\n\
  280. if MSGID has been dealt with or is invalid and error will occur.")
  281.     (msgid)
  282. Lisp_Object msgid;
  283. {
  284.     CHECK_NUMBER(msgid,0);
  285.  
  286.     if(is_rexx_msgid_on_list(&returned,msgid)) {
  287.     /* msgid has completed build list and delete LispRexxMsg. */
  288.     struct LispRexxMsg *lrm = XLRXMSG(msgid);
  289.     Lisp_Object rc, error_or_string, ret;
  290.     struct RexxMsg *msg = lrm->lrm_Msg;
  291.  
  292.     remove_rexx_msg(&returned,msgid);
  293.  
  294.     rc = make_number(msg->rm_Result1);
  295.     if(msg->rm_Result1 == 0) {
  296.         error_or_string = msg->rm_Result2 ? build_string(msg->rm_Result2) : 0;
  297.     } else {
  298.         /* error occurred */
  299.         error_or_string = make_number(msg->rm_Result2); /* save error code. */
  300.     }
  301.     free_rexx_msg(msgid);              /* free our rexx msg. */
  302.     DeleteARexxMsg(handle,msg);          /* free ARexx msg proper */
  303.  
  304.     /* build lisp list. */
  305.     ret = Fcons( msgid, Fcons( rc, Fcons(error_or_string, Qnil)));
  306.     if(NULL(ret)) {
  307.         error("Couldn't get memory.");
  308.     }
  309.     return(ret);
  310.     } else if(is_rexx_msgid_on_list(&pending,msgid)) {
  311.     return Qnil;                  /* this msgid has not yet completed. */
  312.     } else {
  313.     error("Unknown MSGID.");
  314.     return Qnil;
  315.     }
  316. }
  317.  
  318. DEFUN ("amiga-arexx-reply", Famiga_arexx_reply, Samiga_arexx_reply,
  319.        2, 2, 0,
  320. "Replies to the first arexx message (the one got via amiga-arexx-get-event)\n\
  321. with RC as return code.\n\
  322. If RC=0, TEXT is the result, otherwise it is the error text. It can be nil.")
  323.     (rc, text)
  324. Lisp_Object rc, text;
  325. {
  326.     int retcode, ok = TRUE;
  327.     char *result;
  328.     struct RexxMsg *rmsg;
  329.     Lisp_Object rm = remove_rexx_msg_from_head(&incoming);
  330.     struct LispRexxMsg *lrm = XLRXMSG(rm);
  331.  
  332.     if (NULL(rm))
  333.     error("No ARexx message to reply to.");
  334.  
  335.     rmsg = lrm->lrm_Msg;
  336.  
  337.     CHECK_NUMBER(rc, 0);
  338.     retcode = XINT(rc);
  339.  
  340.     if (!NULL (text)) {
  341.     CHECK_STRING(text, 0);
  342.     result = XSTRING (text)->data;
  343.     } else {
  344.     result = 0;
  345.     }
  346.     if (retcode && result)
  347.     ok = SetARexxLastError(handle, rmsg, result);
  348.     ReplyARexxMsg(handle, rmsg, result, retcode);
  349.  
  350.     if (!ok)
  351.     error("Failed to set ARexx error message.");
  352.  
  353.     return Qnil;
  354. }
  355.  
  356. Lisp_Object send_rexx_command(Lisp_Object str, Lisp_Object as_file,
  357.                   ULONG flags)
  358. {
  359.     struct RexxMsg *rmsg;
  360.     int i;
  361.     Lisp_Object id, rm;
  362.     struct LispRexxMsg *lrm;
  363.  
  364.     rm = alloc_rexx_msg(flags);
  365.     if(NULL(rm)) {
  366.     error("Failed to send command to ARexx.");
  367.     return Qnil;
  368.     }
  369.  
  370.     CHECK_STRING (str, 0);
  371.     if (!(rmsg = SendARexxMsg(handle, XSTRING (str)->data,!NULL (as_file),
  372.                   (flags & LRMF_DORESULTS ? 1 : 0)))) {
  373.     free_rexx_msg(rm);
  374.     error("Failed to send command to ARexx.");
  375.     return Qnil;
  376.     }
  377.     lrm = XLRXMSG(rm);
  378.     lrm->lrm_Msg = rmsg;              /* set rexx message pointer. */
  379.     add_rexx_msg_to_tail(&pending,rm);          /* add to pending list. */
  380.  
  381.     return(rm);
  382. }
  383.  
  384. DEFUN ("amiga-arexx-send-command", Famiga_arexx_send_command,
  385.        Samiga_arexx_send_command, 1, 2, 0,
  386. "Sends a command to ARexx for execution.\n\
  387. If the second arg is non-nil, the command is directly interpreted.\n\
  388. Returns an integer that uniquely identifies this message.  This must\n\
  389. then be used to get the results from the command.\n\
  390. NOTE: this is very different from old way things worked.\n\
  391.       earlier versions of emacs discarded successful results\n\
  392.       and errors always got replied to becuase they caused failures\n\
  393.       Neither of these are true now.\
  394. This function is also no longer interactive.\n\
  395. Use (amiga-arexx-do-command)\n")
  396.     (str, as_file)
  397. Lisp_Object str, as_file;
  398. {
  399.     return(send_rexx_command(str,as_file,
  400.                  LRMF_DORESULTS|
  401.                  LRMF_DOERRORS|
  402.                  LRMF_SENTCMD));
  403. }
  404.  
  405. void init_amiga_rexx(void)
  406. {
  407.     extern ULONG inputsig;
  408.     int i;
  409.  
  410.     handle = InitARexx("Emacs", "elx");
  411.     inputsig |= ARexxSignal(handle);
  412.  
  413.     /* init exec lists. */
  414.     NewList((struct List *)&incoming.lrl_List);
  415.     incoming.lrl_Count = 0;
  416.  
  417.     NewList((struct List *)&pending.lrl_List);
  418.     pending.lrl_Count = 0;
  419.  
  420.     NewList((struct List *)&returned.lrl_List);
  421.     returned.lrl_Count = 0;
  422. }
  423.  
  424. void cleanup_amiga_rexx(void)
  425. {
  426.     /* Delete and reply all rexx messages we have gotten. */
  427.     Lisp_Object rm = remove_rexx_msg_from_head(&returned);
  428.     while(!NULL(rm)) {
  429.     struct LispRexxMsg *lrm = XLRXMSG(rm);
  430.     DeleteARexxMsg(handle,lrm->lrm_Msg);
  431.     free_rexx_msg(rm);
  432.     rm = remove_rexx_msg_from_head(&returned);
  433.     }
  434.  
  435.     rm = remove_rexx_msg_from_head(&incoming);
  436.     while(!NULL(rm)) {
  437.     struct LispRexxMsg *lrm = XLRXMSG(rm);
  438.     ReplyARexxMsg(handle, lrm->lrm_Msg, 0, 20);
  439.     free_rexx_msg(rm);
  440.     rm = remove_rexx_msg_from_head(&incoming);
  441.     }
  442.  
  443.     /* Free the rest of rexx, will wait for pending msgs to return */
  444.     FreeARexx(handle);
  445. }
  446.  
  447. void syms_of_amiga_rexx(void)
  448. {
  449.     DEFVAR_BOOL ("amiga-arexx-initialized", &amiga_arexx_initialized,
  450.          "Set this to t when Emacs is ready to respond to ARexx messages.\n"
  451.          "(ie C-\ X causes all pending ARexx messages to be answered)");
  452.     amiga_arexx_initialized = 0;
  453.  
  454.     defsubr(&Samiga_arexx_send_command);
  455.     defsubr(&Samiga_arexx_reply);
  456.     defsubr(&Samiga_get_next_msg);
  457.     defsubr(&Samiga_arexx_get_msg_results);
  458.     defsubr(&Samiga_arexx_check_command);
  459.     defsubr(&Samiga_arexx_wait);
  460. }
  461.