home *** CD-ROM | disk | FTP | other *** search
- #pragma +
- // ******************* LoadEd *******************
- //
- // Geschrieben von Jens Gelhar 15.10. - 18.10.1992
- //
- // (c) by Jens N. Gelhar 1992 - all rights reserved
-
- /*
-
- Dieses Programm soll die Einbindung des Editors Edward in selbstgeschriebene
- Programme demonstrieren. Es lädt den Edward, startet ihn als Prozeß und tut
- dann so, als wäre es die Maxon C++-Entwicklungsumgebung. Wenn Edward die
- Aufforderung "run" oder "compile" abschickt, liest es den aktuellen Text
- und gibt ihn aus.
-
- LoadEd wurde unter konsequenter Benutzung objektorientierter Konzepte
- geschrieben und ist deshalb (bis auf das Hauptprogramm) in Klassen
- gegliedert, was (vorausgesetzt natürlich, Sie kennen sich ein wenig mit C++
- aus) die Lesbarkeit des Listings enorm steigert. Zusätzlich wurde jede Klasse
- mit einer Falte umschlossen - Sie sollten also das originale Icon auf jeden
- Fall aufbewahren.
-
- Dieser Quelltext liegt dem Maxon C++-Paket als Service bei und ist nicht PD.
- Sie dürfen Klassen und Funktionen dieses Programms in Ihren eigenen Projekten
- verwenden, wenn Sie in der Dokumentation darauf hinweisen.
-
- LoadEd benötigt wenigstens die Compiler-Version 1.01.2 und läuft nur ab
- Kickstart 2.0, da die DOS-Funktion "CreateNewProc" verwendet wird.
-
- */
-
-
-
- // ***** Includes *****
-
- #include <pragma/dos_lib.h>
- #include <pragma/exec_lib.h>
- #include <new.h>
- #include <string.h>
- #include <stdlib.h>
- #include <math.h>
-
-
-
-
- // ********** MsgPortClass **********
-
- struct MsgPort;
- struct Message;
-
- // Die Klasse repräsentiert eine Pointer auf einen Message-Port und erleichtert
- // den Umgang damit. Außerdem ersparen eigene Routinen, die "CreatePort" und
- // "DeletePort" ersetzen, das Einlinken der "amiga.lib" (die ja inzwischen
- // ganz schön groß geworden ist, weshalb das eine Zeit dauert) oder die
- // Benutzung der Exec-Funktionen "CreateMsgPort"/"DeleteMsgPort" (die es erst
- // ab Kick 2.0 gibt).
-
- class MsgPortClass
- {
- MsgPort *mp;
- public:
- // Konstruktoren: wahlweise Pointer auf NULL setzen...
- MsgPortClass()
- : mp(0) { }
- // ... oder gleich einen Port einrichen:
- MsgPortClass(char *name, int pri = 0);
-
- // Port einrichten:
- MsgPort *Create(char *name, int pri = 0);
-
- void Delete(); // Port löschen, "mp" auf NULL setzen
- ~MsgPortClass(); // Destruktor macht auch Resource Tracking
-
- // Zwei Funktionen, mit denen man den Pointer lesen kann:
- operator MsgPort*()
- { return mp; }
- struct MsgPort *Port()
- { return mp; }
-
- unsigned char SigBit(); // liefert das Signalbit des Ports
-
- // Verschönert das Exec-Message-Handling:
- Message *Get();
- Message *Wait();
- void Put(Message*);
- };
-
-
- #include <exec/ports.h>
- #include <exec/memory.h>
- #include <clib/exec_protos.h>
-
-
- MsgPortClass::MsgPortClass(char *name, int pri)
- { Create(name,pri); }
-
-
- MsgPortClass::~MsgPortClass()
- { if(mp)
- Delete();
- }
-
-
- MsgPort *MsgPortClass::Create(char *name, int pri)
- {
- BYTE sigbit;
- mp = 0;
- if ((sigbit = AllocSignal(-1)) == -1)
- return 0;
- mp = (MsgPort*) AllocMem(sizeof(MsgPort), MEMF_CLEAR|MEMF_PUBLIC);
-
- if (!mp)
- { FreeSignal(sigbit);
- return 0; }
-
- mp->mp_Node.ln_Name = name;
- mp->mp_Node.ln_Pri = pri;
- mp->mp_Node.ln_Type = NT_MSGPORT;
- mp->mp_Flags = PA_SIGNAL;
- mp->mp_SigBit = sigbit;
- mp->mp_SigTask = FindTask(0);
-
- AddPort(mp);
- return mp;
- }
-
-
- void MsgPortClass::Delete()
- {
- if(mp)
- { RemPort(mp);
- mp->mp_Node.ln_Type = -42;
- mp->mp_MsgList.lh_Head = (Node*) -1;
- FreeSignal(mp->mp_SigBit);
- FreeMem(mp, sizeof(MsgPort));
- mp = 0;
- }
- }
-
-
- unsigned char MsgPortClass::SigBit()
- { return mp->mp_SigBit; }
-
-
- Message *MsgPortClass::Get()
- { return GetMsg(mp); }
-
-
- Message *MsgPortClass::Wait()
- { return WaitPort(mp); }
-
-
- void MsgPortClass::Put(Message *m)
- { PutMsg (mp, m); }
-
-
-
- // ********** RexxArgClass **********
-
- #include <rexx/storage.h>
-
- class RexxArgClass : public RexxArg
- // OO-Überbau zur Struct "RexxArg"
- { public:
- // Speicherverwaltung muß auch schon Stringlänge kennen
- void *operator new(unsigned size, const char *str);
- void *operator new(unsigned size, unsigned strlen);
- void operator delete(void *obj);
-
- // Konstruktor braucht ebenfalls einen Command-String
- RexxArgClass(const char *str);
-
- // 2mal Umwandlung RexxArgClass -> char* :
- operator char *()
- { return ra_Buff; }
- char *String()
- { return ra_Buff; }
-
- // Umwandlung char* -> RexxArgClass :
- static RexxArgClass *Class (char *str)
- { return (RexxArgClass*)(str-8) }
- };
-
-
- void *RexxArgClass::operator new(unsigned structsize, unsigned strlen)
- {
- int size = structsize - 8 + strlen + 1; // wahre Länge
- struct RexxArg *ra = (RexxArg*) AllocMem(size, MEMF_PUBLIC | MEMF_CLEAR);
- ra->ra_Size = size; // jetzt schon setzen, wird von "delete" benutzt!
- return ra;
- }
-
-
- void *RexxArgClass::operator new(unsigned structsize, const char *str)
- {
- return operator new(structsize, strlen(str));
- }
-
-
- void RexxArgClass::operator delete (void *obj)
- {
- FreeMem(obj, ((RexxArg*)obj)->ra_Size); // diese Art, die Größe zu bestimmen,
- } // ist gewagt, funktioniert aber sicher
-
-
- RexxArgClass::RexxArgClass(const char *str)
- {
- ra_Length = strlen(str);
- strcpy(ra_Buff, str);
- }
-
-
- // ********** RexxMsgClass **********
-
- class RexxMsgClass : public RexxMsg
- { public:
- // Kon- und Destruktor:
- RexxMsgClass(MsgPort *repport, const char *command = 0);
- ~RexxMsgClass();
-
- // eigene Speicherverwlatung, wg. CHIP-RAM:
- void *operator new(size_t size);
- void operator delete(void *obj, size_t size);
-
- void Reply(); // verschönerter "ReplyMsg"-Aufruf
-
- // Es gibt "echte" Messages und Replys:
- enum MsgType { message = NT_MESSAGE, reply = NT_REPLYMSG };
- enum MsgType Type()
- { return (enum MsgType) rm_Node.mn_Node.ln_Type; }
- };
-
-
- void *RexxMsgClass::operator new(size_t size)
- { return AllocMem(size, MEMF_PUBLIC|MEMF_CLEAR); }
-
-
- void RexxMsgClass::operator delete(void *obj, size_t size)
- { FreeMem(obj, size); }
-
-
- RexxMsgClass::RexxMsgClass(MsgPort *repport, const char *command)
- {
- rm_Node.mn_Node.ln_Type = NT_MESSAGE;
- rm_Node.mn_ReplyPort = repport;
- rm_CommAddr = repport->mp_Node.ln_Name;
-
- // Argstring einrichten:
- if (command)
- rm_Args[0] = (new(command) RexxArgClass(command)) -> String();
- // Mensch beachte die doppelte Parameterübergabe (an "new" und den Konstruktor)
- }
-
-
- RexxMsgClass::~RexxMsgClass()
- {
- rm_Node.mn_Node.ln_Type = -42; // TAG überschreiben
- /*
- for (int i = 0; i<=MAXRMARG; ++i) // Arg-String(s) löschen
- if (rm_Args[i])
- delete RexxArgClass::Class(rm_Args[i]);
- if (rm_Result2) // Result-String löschen:
- delete RexxArgClass::Class((char*)rm_Result2);
- */
- }
-
-
- void RexxMsgClass::Reply()
- { ReplyMsg(&rm_Node); }
-
-
-
- // ********** EdRequest **********
-
- class EdRequest
- // Managed Anfragen an Edward
- // Beim Erzeugen eines Objekts muß der später zu benutzende Reply-Port schon
- // feststehen, während der Zeiger auf den Edward-Port später gesetzt werden darf
- { private:
- MsgPort *edport; // Port für Anfragen (wird erst später gesetzt)
- MsgPortClass *repport; // Eigener Reply-Port (wird vom Konstruktor gesetzt)
- protected:
- RexxMsgClass *mess; // letzte beantwortete Anfrage
- public:
- EdRequest(MsgPortClass &p) // Konstruktor (Parameter: Replyport)
- : mess(0),
- edport(0),
- repport(&p)
- { }
-
- ~EdRequest() // Destruktor löscht evtl. noch
- { Drop(); } // vorhandene Message
-
- RexxMsgClass *Get(const char *command); // schickt Anfrage "command" an Edward
- // bei Erfolg ist nachher "mess" != 0
- // Funktionen zum Abfragen des Ergebnisses:
- int Result1();
- const char *Result2();
-
- void Drop(); // löscht Message-Struktur
-
- void SetPort(MsgPort *m) // zum Nachträglichen Setzen des Edward-Ports
- { edport = m; }
- };
-
- int EdRequest::Result1()
- { if (mess)
- return mess->rm_Result1;
- else
- return 0;
- }
-
- const char *EdRequest::Result2()
- { if (mess)
- return (char*) mess->rm_Result2;
- else
- return 0;
- }
-
- void EdRequest::Drop()
- { if (mess)
- { delete mess;
- mess = 0; }
- }
-
-
- RexxMsgClass *EdRequest::Get(const char *command)
- {
- if (edport)
- { Drop(); // bisherigen Status löschen
- // Neue Message allozieren:
- mess = new RexxMsgClass(repport->Port(), command);
- if (mess)
- { // Request abschicken:
- PutMsg(edport, &mess->rm_Node );
-
- // Auf Antwort warten, aber:
- // Zwischendurch könnte auch andere Message angekommen sein
- if (repport->Wait() != (Message*)mess
- || mess->rm_Node.mn_Node.ln_Type != NT_REPLYMSG)
- { mess = 0; } // Dann gilt Request als fehlgeschlagen
- else
- { Message *hilf = repport->Get(); }
- }
- }
- return mess;
- }
-
-
-
- // ********** ListParser **********
-
- class ListParser
- // Diese Klasse stellt Routinen zur Verfügung, die das Parsen von Listen, wie
- // sie Edward liefert, erleichtern
- {
- const char *st; // Lesezeiger für String
- public:
- int level; // aktuelle Klammerebene
- unsigned long val; // Wert des zuletzt gelesenen ¡nt's bzw. letzte Stringlänge
- const char *sval; // Anfang des zuletzt gelesenen Strings
-
- enum Token { bra, ket, str, num, end, unk }; // Token, die in Listen auftreten können
-
- void Reset(const char *s)
- { st = s; level = 0; }
-
- char Skip();
- Token Scan();
- };
-
-
- char ListParser::Skip()
- { for (char c = *st; c==' '; c = *++st)
- ;
- return c;
- }
-
-
- ListParser::Token ListParser::Scan()
- { switch(Skip())
- { case '\0': return end;
- case '(' : ++st; ++level; return bra;
- case ')' : ++st; --level; return ket;
- case '"' : sval = ++st;
- for (val=0; *st++ != '"'; ++val) ;
- return str;
- case '0' : case '1': case '2':
- case '3' : case '4': case '5':
- case '6' : case '7': case '8':
- case '9' : val = 0;
- while (*st >= '0' && *st <= '9')
- val = 10*val + (*st++ - '0');
- return num;
- default : ++st; return unk;
- }
- }
-
-
-
-
- // ********** EdState **********
-
- struct EdText
- { const char *name;
- unsigned len;
- unsigned char id, notupdated, changed, transferred;
- };
-
- class EdState : public EdRequest, private ListParser
- // Spezialklasse zum Abfragen des Edward-Status
- { private:
- void Drop(); // löscht vorhandene Texttabelle
- public:
- short textanz, // Anzahl der Texteinträge
- currentID; // ID des aktuellen Texts
- struct EdText * texts, // Text-Tabelle
- * current; // Tabellenenitrag des akt. Texts
-
- EdState (MsgPortClass &mp) // Konstruktor: Initialisierung wie bei "EdRequest"
- : EdRequest(mp), textanz(0), texts(0)
- { }
- ~EdState();
-
- operator int() // TRUE, wenn vollst. Information vorliegt
- { return (Result2() && texts && current); }
-
- RexxMsgClass *Get(); // Stellt "GetTextList"-Anfrage
- };
-
-
- void EdState::Drop()
- { if (texts)
- { delete [] texts;
- texts = 0;
- textanz = 0;
- current = 0;
- currentID = 0;
- }
- }
-
-
- EdState::~EdState()
- { Drop(); }
-
-
- RexxMsgClass *EdState::Get()
- { Drop();
-
- if ( EdRequest::Get("GetTextList") )
- { // Liste durchparsen:
- Reset(Result2());
-
- if (Scan() != bra || Scan() != num) goto err;
- currentID = val;
-
- // Texte zählen:
- textanz = 0;
- Token t;
- while ((t=Scan()) == bra)
- {
- if (Scan() != str) goto err;
- for (int i=0; i<4; ++i)
- if (Scan() != num) goto err;
- do
- t = Scan();
- while (t != end && t != unk && (t != ket || level > 1));
- if (t!=ket) goto err;
- ++textanz;
- }
- if (t!=ket || level != 0 || Scan() != end) goto err;
- // Alles klar.
-
- if ((texts = new EdText[textanz]) == 0) goto err;
-
- // Alles noch mal lesen:
- Reset(Result2());
- Scan(); Scan();
- for(EdText *ep = texts; (t=Scan()) == bra; ++ep)
- {
- if (Scan() != str) goto err; // Name
- ep->name = sval; ep->len = val;
-
- Scan(); ep->id = val; // ID
- if (val == currentID) current = ep;
-
- Scan(); ep->notupdated = val; // übrige Flags
- Scan(); ep->changed = val;
- Scan(); ep->transferred = val;
-
- do
- t = Scan();
- while (t != end && t != unk && (t != ket || level > 1));
- }
- }
- else
- {
- err: // Fehler beim Parsen aufgetreten:
- Drop();
- return 0;
- return mess;
- }
- }
-
-
-
- // ********** EdCommPort **********
-
- enum Commands { // Befehlscodes
- com_none = 0,
-
- com_helloapp = 1,
- com_quit,
- com_shutdown,
- com_reopen,
- com_run,
- com_compile };
-
-
- struct Message;
- struct MsgPort;
-
- class EdCommPort : public MsgPortClass
-
- // Klasse für die Kommunikation Edward -> Applikation
- // Wenn die Applikation beschäftigt ist, ignoriert sie einfach alle Messages,
- // die sie vom Edward erhält (d. h. der Empfang wird mit "ReplyMsg" bestätigt,
- // aber nichts geschieht) - mit Ausnahme von "quit" und "shutdown", die
- // unbedingt sofort bearbeitet und dann bestätigt werden müssen. Eine solche
- // Message kann zwischengespeichert werden, um später (möglichst bald!) bearbeitet
- // zu werden.
- // Außerdem gibt's Routinen für die Ermittlung der Anweisungsnummer
- {
- private:
- static char *commandstrs[];
- RexxMsgClass *killmess, // Empfangene, aber noch nicht beantwortete
- // "quit"- oder "shutdown"-Nachricht
- *last; // zuletzt von "Get()" empfangene Message
- public:
- MsgPort *edwardport; // Port, an den Edward-Anweisungen geschickt werden
- // können
- EdCommPort()
- : killmess(0), last(0) { }
-
- ~EdCommPort()
- { ReplyKillmsg(); }
-
- // Kommunikation findet auf dieser Ebene ausschließlich über Rexx-Msg's statt.
- RexxMsgClass *Get(); // liefert die zurückgestellte Message "killmess" oder
- // ruft "MsgPortClass::Get" auf. Setzt "last".
- // Message-Typ "NT_REPLYMSG" wird unterdrückt und gelöscht (Reste aus fehlgeschlagener Kommunikation)
- RexxMsgClass *Last()
- { return last; }
-
- void Reply(); // Macht "ReplyMsg(last)" und setzt ggf. "killmess" auf 0
-
- // Behandlung von "Kill"-Messages:
- void IgnoreMessages(); // Schmeißt alle eingegangenen Nachrichten weg, aber nur
- // bis zur ersten "Kill"-Message - die wird aufbewahrt.
- Commands TestKillmsg(); // Macht "IgnoreMessages" und liefert dann den empfangenen
- // "Kill"-Befehlscode bzw. com_none.
- void ReplyKillmsg() ; // "Reply" auf der gemerkten "killmess", falls vorhanden
-
- // Auswerten von Messages:
- static Commands WhichCommand(const RexxMsg&); // liefert Anweisungsnummer der Message, oder -1
- Commands LastCommand(); // Anweisungsnummer der "last"-Message
- };
-
-
- RexxMsgClass *EdCommPort::Get()
- { if (killmess)
- return killmess;
- else
- if ((last=(RexxMsgClass*)MsgPortClass::Get()) == 0)
- return 0;
- else
- if (last->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
- { delete last; // übriggebliebene Reply löschen
- last = 0;
- return Get(); }
- else
- { switch(LastCommand())
- { case com_quit:
- case com_shutdown:
- killmess = last;
- }
- edwardport = last->rm_Node.mn_ReplyPort; // Port für spätere Anfragen an Edward merken
- return last;
- }
- }
-
-
- void EdCommPort::Reply()
- {
- if (last==killmess)
- killmess = 0;
- ReplyMsg(&last->rm_Node);
- }
-
-
- char *EdCommPort::commandstrs[] = {
- "helloapp",
- "quit",
- "shutdown",
- "reopen",
- "run",
- "compile",
- 0 };
-
-
- Commands EdCommPort::LastCommand()
- { if (last)
- return WhichCommand(*last)
- else
- return com_none;
- }
-
-
- Commands EdCommPort::WhichCommand(const RexxMsg &m)
- { for(int i=com_none; commandstrs[i]; ++i)
- if (!stricmp(m.rm_Args[0], commandstrs[i]))
- return (Commands)(i+1);
- return com_none;
- }
-
-
- void EdCommPort::IgnoreMessages()
- {
- while (killmess==0 && Get() != 0) ;
- }
-
-
- Commands EdCommPort::TestKillmsg()
- {
- IgnoreMessages();
- if (killmess)
- return LastCommand();
- else
- return com_none;
- }
-
-
- void EdCommPort::ReplyKillmsg()
- { if (killmess)
- Reply();
- }
-
-
-
-
- // ********** TransferText **********
-
- class TransferText : private EdRequest
- // liest Datei aus Edward (bzw. versucht es wenigstens)
- {
- private:
- unsigned handle;
- public:
-
- TransferText (EdCommPort &p) // Konstruktor setzt gleich Ports
- : EdRequest(p) { SetPort(p.edwardport); }
- ~TransferText()
- { Close(); }
-
- // zwei Varianten für "Open": entweder mit normalem String-Dateinamen oder
- // mit Zeiger+Länge
- unsigned Open(const char *name);
- unsigned Open(const char *name, int namelen);
-
- int Read(char *buf, unsigned max); // führt "TextRead" aus
-
- void Close();
- };
-
-
- unsigned TransferText::Open(const char *name)
- { return Open (name, strlen(name)); }
-
-
- unsigned TransferText::Open(const char *name, int namelen)
- {
- handle = 0;
- char command[150] = "InitTextRead \"";
- strncat (command, name, namelen);
- strcat (command, "\"");
-
- if(Get(command))
- {
- handle = strtoul(Result2(),0,0);
- }
- return handle;
- }
-
-
- void TransferText::Close()
- {
- if(handle)
- { char command[150] = "EndTextRead ";
- uinttostr(handle, command+strlen(command));
- Get(command);
- handle = 0;
- }
- }
-
-
- int TransferText::Read(char *buf, unsigned max)
- {
- char command[150] = "TextRead ";
- uinttostr( handle, command+strlen(command));
- strcat(command," ");
- uinttostr((unsigned) buf, command+strlen(command));
- strcat(command," ");
- uinttostr( max, command+strlen(command));
-
- if (Get(command))
- return strtoul(Result2(),0,0);
- else
- return 0;
- }
-
-
-
-
- // ********** Hauptprogramm **********
-
- #include <stream.h>
- #include <dos/dostags.h>
-
- #define PORTNAME "MppEdwardPort"
- #define EDNAME "Edward:Edward"
-
- char argstring[80];
-
- EdCommPort commport;
- EdState edstate(commport);
-
- void main(int argc, char *argv[])
- {
- // Edward laden:
- BPTR EdSeg = LoadSeg(EDNAME);
-
- if (EdSeg)
- {
- char portname[] = PORTNAME "0";
-
- // Eindeutigen Namen sicherstellen:
- Forbid();
- while(FindPort(portname) && portname[sizeof(PORTNAME)] < '\x7f')
- portname[sizeof(PORTNAME)]++;
- Permit();
-
- // Argumente parsen:
- for(int i = 1; i<argc && strlen(argstring)+strlen(argv[i]) < 78; ++i)
- { strcat(argstring, argv[i]);
- strcat(argstring, " "); }
- // "-cPortname" anhängen:
- strcat(argstring,"-c");
- strcat(argstring, portname);
-
- // Ports + Tags erzeugen:
- if (!commport.Create(portname)) goto ende;
- TagItem EdTags[] = { NP_Seglist, EdSeg,
- NP_FreeSeglist, 1,
- NP_Name, (ULONG)"Maxon C++ editor process",
- NP_Arguments, (ULONG)argstring,
- NP_Cli, -1 };
-
- // Edward-Prozeß anstarten:
- struct Process *EdProc = CreateNewProc(EdTags);
- if (!EdProc)
- { UnLoadSeg(EdSeg);
- goto ende; }
-
- Commands commandcode;
-
- do {
- // Schleife: Auf Edward's Messages warten
- while (!commport.Get() )
- commport.Wait();
-
- // Nummer feststellen + beantworten:
- commandcode = commport.LastCommand();
- commport.Reply(); // ACHTUNG: Wenn eine Applikation Windows auf den
- // Edward-Screen geöffnet hat, darf sie ein "Shutdown"
- // erst "Reply"-en, nachdem sie diese Windows
- // geschlossen hat.
-
- // Eine Ausgabe, damit man auch was sieht:
- cout << "*** Anweisung #" << commandcode << " empfangen.\n";
-
- switch (commandcode)
- { case com_compile: // Zur Demonstration: aktuellen Text aus
- case com_run: // Editor übernehmen und ausgeben
- // Textliste anfordern:
- edstate.SetPort(commport.edwardport); // Reply-Port ist zugleich Port für Anfragen.
- edstate.Get();
-
- if(edstate) // Kann durchaus fehlgeschlagen sein...
- {
- char b[1000]; // Puffer
- TransferText t(commport); // Objekt für Übertragung einrichten
-
- if (t.Open(edstate.current->name, edstate.current->len))
- { int i;
- while ((i = t.Read(b,999)) > 0 && !commport.TestKillmsg())
- {
- b[i] = '\0'; cout << b;
- }
- t.Close();
- }
- }
- break;
- }
- }
- while (commandcode != com_quit);
- }
-
- ende:
- }
-
-