home *** CD-ROM | disk | FTP | other *** search
- # Rexx-Interface für CLISP
- # Jörg Höhle 10.2.1994
-
-
- #include "lispbibl.c"
-
- #ifdef AMIGAOS
-
- #include "amiga2.c"
-
- # ARexx 'library base' pointer:
- # (Muß global sichtbar sein und diesen Namen tragen, damit's der Linker findet!)
- global struct RxsLib * RexxSysBase = NULL;
-
-
- #ifdef DEBUG_REXX
- #define debug_asciz_out asciz_out
- #define debug_dez_out dez_out
- #define debug_hex_out hex_out
- #else
- #define debug_asciz_out(x)
- #define debug_dez_out(x)
- #define debug_hex_out(x)
- #endif
-
-
- # Fehlermeldung wenn kein Rexx möglich
- # fehler_norexx();
- # > subr_self: Aufrufer (ein SUBR)
- nonreturning_function(local, fehler_norexx, (void));
- local void fehler_norexx()
- {
- //: DEUTSCH "Keine Kommunikation mit ARexx möglich."
- //: ENGLISH "Communication with ARexx isn't possible."
- //: FRANCAIS "La communication avec ARexx n'est pas possible."
- fehler(error, GETTEXT("Communication with ARexx isn't possible."));
- }
-
- # Speicher freigeben, der wegen Fehler nicht freigegeben wurde.
- local UBYTE* rexxLostArgstr = NULL;
- local void handle_lost_argstr (void);
- local void handle_lost_argstr()
- { if (rexxLostArgstr)
- { begin_system_call();
- DeleteArgstring(rexxLostArgstr);
- end_system_call();
- rexxLostArgstr = NULL;
- } }
-
- # Die Anzahl der auf Antwort (durch andere Prozesse) wartenden Messages:
- local uintC rexxNeededReplies = 0;
-
- # O(rexx_inmsg_list) ist eine Liste von Foreigns, die jeweils die ein-
- # gegangenen und auf Antwort (durch CLISP) wartenden Messages repräsentieren.
-
- # Sucht eine gegebene Message in O(rexx_inmsg_list):
- local object find_inmsg (FOREIGN pointer);
- local object find_inmsg(pointer)
- var reg2 FOREIGN pointer;
- { var reg1 object current;
- for (current = O(rexx_inmsg_list); consp(current); current = Cdr(current))
- { if (TheFpointer(Car(current))->fp_pointer == pointer) { return Car(current); } }
- return NIL;
- }
-
- # Der Message Port, auf dem wir arbeiten:
- local struct MsgPort * rexxPort = NULL;
- # Sein Name:
- local UBYTE rexxPortName[] = {'C','L','I','S','P','1','\0','\0'};
- # Position der Ziffer darin:
- #define NRPOSITION 5
- # Default-Extension für ARexx-Kommandofiles:
- local UBYTE rexxExtension[] = "cl";
- # Signalnummer, mit der wir auf Ereignisse an diesem Port warten können:
- local ULONG rexxPortBit = 0UL;
-
- LISPFUN(rexx_put,1,0,norest,key,6,\
- (kw(result),kw(string),kw(token),kw(async),kw(io),kw(return)) )
- { # Stackaufbau: string/array, resultp, stringp, tokenp, asyncp, iop, returnp.
- # > string/array: String für Kommando inklusive Argumente oder
- # Array von Strings für Funktion und Argumente
- # > resultp: Flag: Antwort merken?
- # > stringp: Flag: ARexx Argument als Befehle oder
- # erstes Token als Dateiname verstehen?
- # > tokenp: Flag: Soll ARexx Tokens erzeugen?
- # > asyncp: Flag: Nachricht asynchron bearbeiten?
- # > iop: Flag: E/A Kanäle übernehmen?
- # > returnp: Flag: Auf Antwort warten?!?
- # Es sind nicht alle Kombinationen sinvoll.
- var reg4 uintL vargs; # 1 + Zahl Funktionsargumente
- var reg5 boolean functionp; # Funktions- statt Kommandoaufruf
- if (rexxPort == NULL) { fehler_norexx(); }
- # vorsorglich ein Foreign allozieren:
- pushSTACK(allocate_fpointer(NULL));
- {var reg6 object* args_pointer = args_end_pointer;
- # Erstes Argument verarbeiten:
- if (mstringp(STACK_(6+1)))
- { # String
- functionp = FALSE;
- STACK_(6+1) = coerce_ss(STACK_(6+1));
- }
- else
- { functionp = TRUE;
- # sollte (Simple-)Vector sein:
- # evtl.: STACK_(6+1) = coerce_sequence(STACK_(6+1),S(simple_vector));
- if (!m_simple_vector_p(STACK_(6+1)))
- { pushSTACK(STACK_(6+1)); # Wert für Slot DATUM von TYPE-ERROR
- pushSTACK(S(simple_vector)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
- pushSTACK(STACK_1);
- //: DEUTSCH "~ muß ein String für Kommandos oder ein Vektor von Strings für eine Funktion sein."
- //: ENGLISH "~ must be a string for commands or a vector of strings for a function"
- //: FRANCAIS "~ doit être une chaîne pour une commande ou un vecteur de chaînes pour une fonction."
- fehler(type_error, GETTEXT("~ must be a string for commands or a vector of strings for a function"));
- }
- vargs = TheSvector(STACK_(6+1))->length;
- if (!( /* vargs > 0 && */ vargs-1 <= MAXRMARG))
- { pushSTACK(STACK_(6+1));
- pushSTACK(fixnum(MAXRMARG));
- pushSTACK(S(rexx_put));
- //: DEUTSCH "~: ARexx Funktion muß 0 bis ~ Argumente haben: ~"
- //: ENGLISH "~: an ARexx function must have 0 to ~ arguments: ~"
- //: FRANCAIS "~ : Une fonction ARexx a de 0 à ~ arguments : ~"
- fehler(error, GETTEXT("~: an ARexx function must have 0 to ~ arguments: ~"));
- }
- # Alle Argumentstrings aus dem Vektor auf dem Stack ablegen:
- {var reg3 object* vptr = &STACK_(6+1);
- var reg2 uintL index;
- for (index = 0; index < vargs; index++)
- { var reg1 object arg = TheSvector(*vptr)->data[index];
- if (!stringp(arg))
- { pushSTACK(arg); # Wert für Slot DATUM von TYPE-ERROR
- pushSTACK(S(string)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
- pushSTACK(arg);
- pushSTACK(S(rexx_put));
- //: DEUTSCH "~: Muß für ARexx ein String sein: ~"
- //: ENGLISH "~: must be a string for ARexx: ~"
- //: FRANCAIS "~ : Doit être une chaîne pour ARexx : ~"
- fehler(type_error, GETTEXT("~: must be a string for ARexx: ~"));
- }
- # Argument in Simple-String umwandeln:
- pushSTACK(coerce_ss(arg));
- }} }
- # Stackaufbau: ... string/vector ..(6).. foreign ..(vargs).. .
- # Ab hier für eine Weile keine GC mehr
- { var reg3 struct RexxMsg * rexxmsg;
- debug_asciz_out("%REXX-PUT: ");
- begin_system_call();
- rexxmsg = CreateRexxMsg(rexxPort,rexxExtension,rexxPortName);
- end_system_call();
- if (!(rexxmsg == NULL))
- # vorerst erfolgreich
- { var reg4 boolean success;
- if (functionp)
- { # ARexx Funktionsaufruf
- debug_asciz_out("function ");
- # Argumente einfüllen:
- { var reg2 uintL i;
- var reg2 object* argptr = args_pointer;
- success = TRUE;
- begin_system_call();
- for (i=0; i<vargs; i++)
- { var reg1 object s = NEXT(argptr);
- if ((rexxmsg->rm_Args[i] = CreateArgstring(&TheSstring(s)->data[0],TheSstring(s)->length)) == NULL)
- { if (i>0) ClearRexxMsg(rexxmsg,i);
- success = FALSE;
- break;
- } }
- end_system_call();
- }
- set_args_end_pointer(args_pointer); # Stack aufräumen
- }
- else
- { # ARexx Kommando
- debug_asciz_out("command ");
- begin_system_call();
- if (rexxmsg->rm_Args[0] = CreateArgstring(&TheSstring(STACK_(6+1))->data[0],TheSstring(STACK_(6+1))->length))
- { success = TRUE; }
- else
- { success = FALSE; }
- end_system_call();
- }
- # Stackaufbau: ... string/vector ..(6).. foreign.
- if (success)
- # vorerst immer noch erfolgreich
- { rexxmsg->rm_Action = (functionp ? (RXFUNC | (vargs-1)) : RXCOMM);
- # Keyword-Argumente verarbeiten:
- #define is_set(obj) (!(eq(obj,unbound) || nullp(obj)))
- # :RESULT-Argument:
- if (is_set(STACK_(5+1))) { rexxmsg->rm_Action |= RXFF_RESULT; }
- # :STRING-Argument:
- if (is_set(STACK_(4+1))) { rexxmsg->rm_Action |= RXFF_STRING; }
- # :TOKEN-Argument:
- if (is_set(STACK_(3+1))) { rexxmsg->rm_Action |= RXFF_TOKEN; }
- # :IO-Argument:
- if (!is_set(STACK_(1+1))) { rexxmsg->rm_Action |= RXFF_NOIO; }
- # :RETURN-Argument:
- if (!is_set(STACK_(0+1))) { rexxmsg->rm_Action |= RXFF_NONRET; }
- rexxmsg->rm_Node.mn_Node.ln_Name = RXSDIR;
- { var reg2 boolean asyncp = is_set(STACK_(2+1)); # :ASYNCP-Argument
- var reg1 struct MsgPort* arexxport;
- begin_system_call();
- Forbid();
- arexxport = FindPort(asyncp ? RXADIR : RXSDIR);
- if (!(arexxport==NULL))
- # Message abschicken:
- { PutMsg(arexxport,(struct Message *)rexxmsg); }
- else
- { success = FALSE; }
- Permit();
- end_system_call();
- }
- #undef is_set
- if (success)
- # erfolgreich -> mitzählen:
- { rexxNeededReplies++;
- TheFpointer(STACK_0)->fp_pointer = rexxmsg;
- debug_hex_out(rexxmsg);
- }
- else
- # nicht erfolgreich -> aufräumen:
- { begin_system_call();
- if (functionp)
- { ClearRexxMsg(rexxmsg,vargs); }
- else
- { DeleteArgstring(rexxmsg->rm_Args[0]); }
- end_system_call();
- } }
- if (success)
- { value1 = STACK_0; } # Wert ist das Foreign zu rexxmsg
- else
- # Nachricht konnte nicht erfolgreich abgeschickt werden, also löschen
- { begin_system_call();
- DeleteRexxMsg(rexxmsg);
- end_system_call();
- value1 = NIL;
- } }
- else
- { set_args_end_pointer(args_pointer); # Stack aufräumen
- value1 = NIL;
- }
- debug_asciz_out(CRLFstring);
- }
- mv_count=1; skipSTACK(7+1);
- }}
-
- # Warten, bis am Port eine Message eintrifft oder Ctrl-C.
- # Ergebnis ist ein Flag, das angibt, ob eine Message eintraf.
- local boolean rexx_wait (void);
- local boolean rexx_wait()
- { start:
- begin_system_call();
- {var LONG wait_erg = Wait(rexxPortBit | SIGBREAKF_CTRL_C);
- end_system_call();
- #if 0 # spätere asynchrone DOS-Packet-Bearbeitung ??
- if (wait_erg & ioPortBit)
- { flush_io_queue(); }
- #endif
- if (wait_erg & SIGBREAKF_CTRL_C)
- { # Bearbeitung einer evtl. Message verschieben:
- if (wait_erg & rexxPortBit)
- { begin_system_call(); SetSignal(wait_erg,rexxPortBit); end_system_call(); }
- # Ctrl-C behandeln:
- pushSTACK(S(rexx_wait_input)); tast_break();
- goto start;
- }
- else
- { if (wait_erg & rexxPortBit)
- return TRUE;
- else
- return FALSE; # eigentlich nicht möglich
- }
- }}
-
- # (SYSTEM::REXX-WAIT-INPUT) wartet bis am AREXX-Port etwas anliegt,
- # und liefert dann T.
- LISPFUNN(rexx_wait_input,0)
- { if (!(rexxPort == NULL))
- { if (rexx_wait())
- { value1 = T; mv_count=1; return; }
- }
- value1 = NIL; mv_count=1;
- }
-
- # Flag, ob sich das ARexx-Interface gerade in der Endphase befindet und
- # deswegen keine neuen Nachrichten entgegennimmt:
- local boolean rexxShutdown = TRUE;
-
- # Empfängt ARexx Nachrichten.
- # Liefert eine Liste (MsgId ...) oder T, wenn eine Nachricht empfangen wurde.
- # Falls rexxShutdown gesetzt ist, werden keine neuen Nachrichten, nur noch
- # Antworten, angenommen.
- # Kann GC auslösen, falls nicht im rexxShutdown Modus.
- local object rexx_getmsg(void);
- local object rexx_getmsg()
- { if (rexxPort == NULL)
- { return NIL; }
- else
- { var reg1 struct RexxMsg * rexxmsg;
- handle_lost_argstr();
- # Resource-tracking für einkommende Nachrichten
- # (Benutzt eine globale Variable O(rexx_prefetch_inmsg),
- # um nicht jedes Mal ein neues Cons erzeugen zu müssen.)
- if (!rexxShutdown
- && matomp(O(rexx_prefetch_inmsg)))
- { pushSTACK(allocate_fpointer(NULL));
- {var reg1 object new_cons = allocate_cons();
- Car(new_cons) = popSTACK();
- O(rexx_prefetch_inmsg) = new_cons;
- }}
- # O(rexx_prefetch_inmsg) ist nun garantiert ein brauchbares Cons.
- # Bereich gegen GC geschützt.
- begin_system_call();
- rexxmsg = (struct RexxMsg *)GetMsg(rexxPort);
- end_system_call();
- if (rexxmsg == NULL) # keine Nachricht vorhanden?
- { return NIL; }
- else
- { debug_asciz_out("rexx_getmsg: ");
- debug_hex_out(rexxmsg->rm_Action);
- if (rexxmsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
- # Antwort auf eine von uns geschickte Message
- { var reg3 LONG result1 = rexxmsg->rm_Result1;
- begin_system_call();
- if (rexxmsg->rm_Action & RXCOMM)
- { DeleteArgstring(rexxmsg->rm_Args[0]); }
- else
- { ClearRexxMsg(rexxmsg,1+(rexxmsg->rm_Action & RXARGMASK)); }
- if ((rexxmsg->rm_Action & RXFF_RESULT)
- && (rexxmsg->rm_Result1 == 0)
- && rexxmsg->rm_Result2
- && !rexxShutdown
- )
- { # DeleteArgstring(rexxmsg->rm_Result2); kommt später
- rexxLostArgstr = (UBYTE*)(rexxmsg->rm_Result2);
- }
- DeleteRexxMsg(rexxmsg);
- end_system_call();
- rexxNeededReplies--;
- debug_asciz_out(" a reply ");
- # ab hier GC wieder möglich
- if (rexxShutdown)
- { handle_lost_argstr(); return T; }
- else
- # Ergebnis ist eine 2- oder 3-elementige Liste (Msg-ID RC [RESULT]),
- { pushSTACK(allocate_fpointer(rexxmsg));
- pushSTACK(L_to_I(result1));
- if (rexxLostArgstr)
- { pushSTACK(make_string(rexxLostArgstr,LengthArgstring(rexxLostArgstr)));
- handle_lost_argstr();
- return listof(3);
- }
- else
- { /* handle_lost_argstr(); */ # hier unnötig
- return listof(2);
- } }
- }
- else
- { rexxmsg->rm_Result2 = 0;
- debug_asciz_out(" incoming is " CRLFstring);
- debug_asciz_out(rexxmsg->rm_Args[0]);
- # Eingehender Befehl
- if (rexxShutdown)
- { # Schluß, nichts läuft mehr
- rexxmsg->rm_Result1 = RXERRORIMGONE;
- begin_system_call();
- ReplyMsg((struct Message *)rexxmsg);
- end_system_call();
- return T;
- }
- else
- { var reg2 object new_cons = O(rexx_prefetch_inmsg);
- # Resource-tracking, bis dahin keine GC.
- TheFpointer(Car(new_cons))->fp_pointer = rexxmsg;
- Cdr(new_cons) = O(rexx_inmsg_list);
- O(rexx_inmsg_list) = new_cons;
- O(rexx_prefetch_inmsg) = NIL;
- # Resource-tracking beendet, ab hier wieder GC möglich
- # Ergebnis ist zweielementige Liste (Msg-ID "Msg-string")
- pushSTACK(Car(new_cons));
- pushSTACK(make_string(rexxmsg->rm_Args[0],LengthArgstring(rexxmsg->rm_Args[0])));
- return listof(2);
- }
- }
- }
- }
- }
-
- # (SYSTEM::%REXX-GET) empfängt eine Nachricht und liefert sie im
- # Format (MsgId ...). Ergebnis NIL falls keine Nachricht vorliegt.
- LISPFUNN(rexx_get,0)
- { if (rexxPort == NULL) { fehler_norexx(); }
- value1 = rexx_getmsg(); mv_count=1;
- }
-
- # Antwortet auf eine eingegangene Nachricht.
- # > foreign: Foreign mit der Message-Adresse
- # > rc, result,result_length: Return-Code und Ergebnis-String
- local void rexx_replymsg (object foreign, LONG rc, UBYTE* result, ULONG result_length);
- local void rexx_replymsg(foreign,rc,result,result_length)
- var reg4 object foreign;
- var reg2 LONG rc;
- var reg3 UBYTE* result;
- var reg5 ULONG result_length;
- { var reg1 struct RexxMsg* rexxmsg = TheFpointer(foreign)->fp_pointer;
- debug_asciz_out("rexx_replymsg: ");
- debug_hex_out(rexxmsg);
- rexxmsg->rm_Result1 = rc;
- begin_system_call();
- rexxmsg->rm_Result2 = (ULONG)
- (((rc == 0) && (rexxmsg->rm_Action & RXFF_RESULT))
- ? CreateArgstring(result,result_length)
- : NULL
- );
- ReplyMsg((struct Message *)rexxmsg);
- end_system_call();
- # Die Message foreign ist nun beantwortet.
- O(rexx_inmsg_list) = deleteq(O(rexx_inmsg_list),foreign);
- mark_fp_invalid(TheFpointer(foreign)); # prohibit further use
- debug_asciz_out(CRLFstring);
- }
-
- # (SYS::%REXX-REPLY message-id return-code return-string)
- # antwortet auf eine Message.
- LISPFUNN(rexx_reply,3)
- { # Stackaufbau: ..., message-id, return-code, return-string.
- if (rexxPort == NULL) { fehler_norexx(); }
- # Argumente überprüfen:
- # return-code sollte ein Fixnum sein:
- if (!mfixnump(STACK_1))
- { pushSTACK(STACK_1); # Wert für Slot DATUM von TYPE-ERROR
- pushSTACK(S(fixnum)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
- pushSTACK(STACK_(1+2));
- pushSTACK(S(rexx_reply));
- //: DEUTSCH "~: Kein Fixnum: ~"
- //: ENGLISH "~: Not a Fixnum: ~"
- //: FRANCAIS "~ : ~ n'est pas de type FIXNUM"
- fehler(type_error, GETTEXT("~: Not a Fixnum: ~"));
- }
- # return-string sollte ein String oder NIL sein:
- if (mstringp(STACK_0))
- { STACK_0 = coerce_ss(STACK_0); } # in Simple-String umwandeln
- # message-id sollte ein Foreign sein:
- {var reg3 object foreign;
- if (!(fpointerp(STACK_2) && !nullp(foreign = find_inmsg(TheFpointer(STACK_2)->fp_pointer))))
- { pushSTACK(STACK_2);
- pushSTACK(S(rexx_reply));
- //: DEUTSCH "~: Keine eingehende Rexx Nachricht: ~"
- //: ENGLISH "~: Not an incoming Rexx message: ~"
- //: FRANCAIS "~ : ~ n'est pas un message Rexx entrant"
- fehler(error, GETTEXT("~: Not an incoming Rexx message: ~"));
- }
- # Beantworten:
- { var reg2 object retcode = STACK_1;
- var reg4 LONG result1 = fixnum_to_L(retcode);
- var reg1 object retstring = STACK_0;
- if (simple_string_p(retstring))
- { rexx_replymsg(foreign,result1,&TheSstring(retstring)->data[0],TheSstring(retstring)->length); }
- else
- { rexx_replymsg(foreign,result1,NULL,0); }
- }
- skipSTACK(3);
- value1 = NIL; mv_count=0;
- }}
-
- # Initialisiert das REXX-Interface.
- # < ergebnis: Flag, ob erfolgreich initialisiert.
- # Kann mehrfach aufgerufen werden.
- global boolean init_rexx(void)
- { if (rexxPort == NULL) # noch was zu tun?
- { if (RexxSysBase == NULL)
- { begin_system_call();
- RexxSysBase = (struct RxsLib *) OpenLibrary(RXSNAME,0L);
- end_system_call();
- if (RexxSysBase == NULL) { return FALSE; }
- }
- {var reg1 uintC nr = 1; # wir probieren verschiedene Ports
- loop
- { if (!(rexxPort == NULL)) break;
- {var reg2 boolean existent;
- rexxPortName[NRPOSITION] = '0' + nr;
- { begin_system_call();
- Forbid();
- if (FindPort(rexxPortName) == NULL)
- # Port existiert noch nicht, wir machen einen (öffentlichen):
- { rexxPort = CreatePort(rexxPortName,0L);
- existent = FALSE;
- }
- else
- { existent = TRUE; }
- Permit();
- end_system_call();
- }
- if (!existent)
- # Wir haben's wenigstens probiert...
- { if (rexxPort == NULL) { return FALSE; }
- rexxPortBit = bit(rexxPort->mp_SigBit);
- rexxNeededReplies = 0;
- rexxShutdown = FALSE;
- break;
- }
- # Wir versuchen es mit einem anderem Namen erneut.
- nr++; if (nr==10) { return FALSE; }
- }}
- }}
- return TRUE;
- }
-
- # Schließt das REXX-Interface.
- # Kann nur einmal aufgerufen werden.
- global void close_rexx(void)
- { rexxShutdown = TRUE;
- debug_asciz_out("close_rexx: ");
- debug_dez_out(rexxNeededReplies); debug_asciz_out(" messages waiting." CRLFstring);
- if (!(rexxPort == NULL))
- { # Port unbekannt machen (abmelden):
- begin_system_call();
- RemPort(rexxPort);
- end_system_call();
- handle_lost_argstr();
- # Ausstehende Nachrichten mit Fehler zurückschicken:
- while (mconsp(O(rexx_inmsg_list)))
- { rexx_replymsg(Car(O(rexx_inmsg_list)),RXERRORIMGONE,NULL,0L); }
- # Eingegangene Nachrichten mit Fehler zurückschicken:
- loop
- { until (nullp(rexx_getmsg())) { /* loop until empty */ }
- if (rexxNeededReplies == 0) break;
- begin_system_call();
- Wait(rexxPortBit);
- end_system_call();
- debug_asciz_out("Looping" CRLFstring);
- }
- begin_system_call();
- rexxPort->mp_Node.ln_Name = NULL;
- DeletePort(rexxPort);
- end_system_call();
- rexxPort = NULL;
- rexxPortBit = 0;
- }
- if (!(RexxSysBase == NULL))
- { begin_system_call();
- CloseLibrary((struct Library *)RexxSysBase);
- end_system_call();
- RexxSysBase = NULL;
- }
- }
-
- #endif # AMIGAOS
-
-