home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD2.iso / Programming / C / OTL-MC7.DMS / in.adf / cppdemo.lha / C++ / LoadEd / LoadEd.C < prev   
Encoding:
C/C++ Source or Header  |  1994-10-30  |  20.7 KB  |  821 lines

  1. #pragma +
  2. // *******************  LoadEd  *******************
  3. //
  4. // Geschrieben von Jens Gelhar  15.10. - 18.10.1992
  5. //
  6. // (c) by Jens N. Gelhar 1992 - all rights reserved
  7.  
  8. /*
  9.  
  10.   Dieses Programm soll die Einbindung des Editors Edward in selbstgeschriebene
  11.   Programme demonstrieren. Es lädt den Edward, startet ihn als Prozeß und tut
  12.   dann so, als wäre es die Maxon C++-Entwicklungsumgebung. Wenn Edward die
  13.   Aufforderung "run" oder "compile" abschickt, liest es den aktuellen Text
  14.   und gibt ihn aus.
  15.  
  16.   LoadEd wurde unter konsequenter Benutzung objektorientierter Konzepte
  17.   geschrieben und ist deshalb (bis auf das Hauptprogramm) in Klassen
  18.   gegliedert, was (vorausgesetzt natürlich, Sie kennen sich ein wenig mit C++
  19.   aus) die Lesbarkeit des Listings enorm steigert. Zusätzlich wurde jede Klasse
  20.   mit einer Falte umschlossen - Sie sollten also das originale Icon auf jeden
  21.   Fall aufbewahren.
  22.  
  23.   Dieser Quelltext liegt dem Maxon C++-Paket als Service bei und ist nicht PD.
  24.   Sie dürfen Klassen und Funktionen dieses Programms in Ihren eigenen Projekten
  25.   verwenden, wenn Sie in der Dokumentation darauf hinweisen.
  26.  
  27.   LoadEd benötigt wenigstens die Compiler-Version 1.01.2 und läuft nur ab
  28.   Kickstart 2.0, da die DOS-Funktion "CreateNewProc" verwendet wird.
  29.  
  30. */
  31.  
  32.  
  33.  
  34. // *****               Includes               *****
  35.  
  36. #include <pragma/dos_lib.h>
  37. #include <pragma/exec_lib.h>
  38. #include <new.h>
  39. #include <string.h>
  40. #include <stdlib.h>
  41. #include <math.h>
  42.  
  43.  
  44.  
  45.  
  46. // **********        MsgPortClass        **********
  47.  
  48. struct MsgPort;
  49. struct Message;
  50.  
  51. // Die Klasse repräsentiert eine Pointer auf einen Message-Port und erleichtert
  52. // den Umgang damit. Außerdem ersparen eigene Routinen, die "CreatePort" und
  53. // "DeletePort" ersetzen, das Einlinken der "amiga.lib" (die ja inzwischen
  54. // ganz schön groß geworden ist, weshalb das eine Zeit dauert) oder die
  55. // Benutzung der Exec-Funktionen "CreateMsgPort"/"DeleteMsgPort" (die es erst
  56. // ab Kick 2.0 gibt).
  57.  
  58. class MsgPortClass
  59. {
  60.    MsgPort *mp;
  61.  public:
  62.    // Konstruktoren: wahlweise Pointer auf NULL setzen...
  63.    MsgPortClass()
  64.       : mp(0) { }
  65.    // ... oder gleich einen Port einrichen:
  66.    MsgPortClass(char *name, int pri = 0);
  67.  
  68.    // Port einrichten:
  69.    MsgPort *Create(char *name, int pri = 0);
  70.  
  71.     void Delete();                 // Port löschen, "mp" auf NULL setzen
  72.    ~MsgPortClass();               // Destruktor macht auch Resource Tracking
  73.  
  74.    // Zwei Funktionen, mit denen man den Pointer lesen kann:
  75.    operator MsgPort*()
  76.      { return mp; }
  77.    struct MsgPort *Port()
  78.      { return mp; }
  79.  
  80.    unsigned char SigBit();        // liefert das Signalbit des Ports
  81.  
  82.    // Verschönert das Exec-Message-Handling:
  83.    Message *Get();
  84.    Message *Wait();
  85.    void Put(Message*);
  86. };
  87.  
  88.  
  89. #include <exec/ports.h>
  90. #include <exec/memory.h>
  91. #include <clib/exec_protos.h>
  92.  
  93.  
  94. MsgPortClass::MsgPortClass(char *name, int pri)
  95.  { Create(name,pri); }
  96.  
  97.  
  98. MsgPortClass::~MsgPortClass()
  99. { if(mp)
  100.     Delete();
  101. }
  102.  
  103.  
  104. MsgPort *MsgPortClass::Create(char *name, int pri)
  105. {
  106.   BYTE sigbit;
  107.   mp = 0;
  108.   if ((sigbit = AllocSignal(-1)) == -1)
  109.     return 0;
  110.   mp = (MsgPort*) AllocMem(sizeof(MsgPort), MEMF_CLEAR|MEMF_PUBLIC);
  111.  
  112.   if (!mp)
  113.     { FreeSignal(sigbit);
  114.       return 0; }
  115.  
  116.   mp->mp_Node.ln_Name = name;
  117.   mp->mp_Node.ln_Pri = pri;
  118.   mp->mp_Node.ln_Type = NT_MSGPORT;
  119.   mp->mp_Flags = PA_SIGNAL;
  120.   mp->mp_SigBit = sigbit;
  121.   mp->mp_SigTask = FindTask(0);
  122.  
  123.   AddPort(mp);
  124.   return mp;
  125. }
  126.  
  127.  
  128. void MsgPortClass::Delete()
  129. {
  130.   if(mp)
  131.   { RemPort(mp);
  132.     mp->mp_Node.ln_Type = -42;
  133.     mp->mp_MsgList.lh_Head = (Node*) -1;
  134.     FreeSignal(mp->mp_SigBit);
  135.     FreeMem(mp, sizeof(MsgPort));
  136.     mp = 0;
  137.   }
  138. }
  139.  
  140.  
  141. unsigned char MsgPortClass::SigBit()
  142.   { return mp->mp_SigBit; }
  143.  
  144.  
  145. Message *MsgPortClass::Get()
  146.  { return GetMsg(mp); }
  147.  
  148.  
  149. Message *MsgPortClass::Wait()
  150.  { return WaitPort(mp); }
  151.  
  152.  
  153. void MsgPortClass::Put(Message *m)
  154.  { PutMsg (mp, m); }
  155.  
  156.  
  157.  
  158. // **********        RexxArgClass        **********
  159.  
  160. #include <rexx/storage.h>
  161.  
  162. class RexxArgClass : public RexxArg
  163.  // OO-Überbau zur Struct "RexxArg"
  164. { public:
  165.    // Speicherverwaltung muß auch schon Stringlänge kennen
  166.    void *operator new(unsigned size, const char *str);
  167.    void *operator new(unsigned size, unsigned strlen);
  168.    void operator delete(void *obj);
  169.  
  170.    // Konstruktor braucht ebenfalls einen Command-String
  171.    RexxArgClass(const char *str);
  172.  
  173.    // 2mal Umwandlung RexxArgClass -> char* :
  174.    operator char *()
  175.      { return ra_Buff; }
  176.    char *String()
  177.      { return ra_Buff; }
  178.  
  179.    // Umwandlung char* -> RexxArgClass :
  180.    static RexxArgClass *Class (char *str)
  181.      { return (RexxArgClass*)(str-8) }
  182. };
  183.  
  184.  
  185. void *RexxArgClass::operator new(unsigned structsize, unsigned strlen)
  186. {
  187.   int size = structsize - 8 + strlen + 1;    // wahre Länge
  188.   struct RexxArg *ra = (RexxArg*) AllocMem(size, MEMF_PUBLIC | MEMF_CLEAR);
  189.   ra->ra_Size = size;   // jetzt schon setzen, wird von "delete" benutzt!
  190.   return ra;
  191. }
  192.  
  193.  
  194. void *RexxArgClass::operator new(unsigned structsize, const char *str)
  195. {
  196.   return operator new(structsize, strlen(str));
  197. }
  198.  
  199.  
  200. void RexxArgClass::operator delete (void *obj)
  201. {
  202.   FreeMem(obj, ((RexxArg*)obj)->ra_Size);  // diese Art, die Größe zu bestimmen,
  203. }                                          // ist gewagt, funktioniert aber sicher
  204.  
  205.  
  206. RexxArgClass::RexxArgClass(const char *str)
  207. {
  208.   ra_Length = strlen(str);
  209.   strcpy(ra_Buff, str);
  210. }
  211.  
  212.  
  213. // **********        RexxMsgClass        **********
  214.  
  215. class RexxMsgClass : public RexxMsg
  216. { public:
  217.    // Kon- und Destruktor:
  218.    RexxMsgClass(MsgPort *repport, const char *command = 0);
  219.    ~RexxMsgClass();
  220.  
  221.    // eigene Speicherverwlatung, wg. CHIP-RAM:
  222.    void *operator new(size_t size);
  223.    void operator delete(void *obj, size_t size);
  224.  
  225.    void Reply();       // verschönerter "ReplyMsg"-Aufruf
  226.  
  227.    // Es gibt "echte" Messages und Replys:
  228.    enum MsgType { message = NT_MESSAGE, reply = NT_REPLYMSG };
  229.    enum MsgType Type()
  230.      { return (enum MsgType) rm_Node.mn_Node.ln_Type; }
  231. };
  232.  
  233.  
  234. void *RexxMsgClass::operator new(size_t size)
  235.  { return AllocMem(size, MEMF_PUBLIC|MEMF_CLEAR); }
  236.  
  237.  
  238. void RexxMsgClass::operator delete(void *obj, size_t size)
  239.  { FreeMem(obj, size); }
  240.  
  241.  
  242. RexxMsgClass::RexxMsgClass(MsgPort *repport, const char *command)
  243. {
  244.   rm_Node.mn_Node.ln_Type = NT_MESSAGE;
  245.   rm_Node.mn_ReplyPort = repport;
  246.   rm_CommAddr = repport->mp_Node.ln_Name;
  247.  
  248.   // Argstring einrichten:
  249.   if (command)
  250.     rm_Args[0] = (new(command) RexxArgClass(command)) -> String();
  251.     // Mensch beachte die doppelte Parameterübergabe (an "new" und den Konstruktor)
  252. }
  253.  
  254.  
  255. RexxMsgClass::~RexxMsgClass()
  256. {
  257.   rm_Node.mn_Node.ln_Type = -42;         // TAG überschreiben
  258. /*
  259.   for (int i = 0; i<=MAXRMARG; ++i)      // Arg-String(s) löschen
  260.     if (rm_Args[i])
  261.       delete RexxArgClass::Class(rm_Args[i]);
  262.   if (rm_Result2)                        // Result-String löschen:
  263.       delete RexxArgClass::Class((char*)rm_Result2);
  264. */
  265. }
  266.  
  267.  
  268. void RexxMsgClass::Reply()
  269.  { ReplyMsg(&rm_Node); }
  270.  
  271.  
  272.  
  273. // **********         EdRequest          **********
  274.  
  275. class EdRequest
  276.   // Managed Anfragen an Edward
  277.   // Beim Erzeugen eines Objekts muß der später zu benutzende Reply-Port schon
  278.   // feststehen, während der Zeiger auf den Edward-Port später gesetzt werden darf
  279. { private:
  280.     MsgPort      *edport;    // Port für Anfragen (wird erst später gesetzt)
  281.     MsgPortClass *repport;   // Eigener Reply-Port (wird vom Konstruktor gesetzt)
  282.   protected:
  283.     RexxMsgClass *mess;      // letzte beantwortete Anfrage
  284.   public:
  285.     EdRequest(MsgPortClass &p)   // Konstruktor (Parameter: Replyport)
  286.       : mess(0), 
  287.         edport(0),
  288.         repport(&p)
  289.       { }
  290.  
  291.     ~EdRequest()                 // Destruktor löscht evtl. noch
  292.       { Drop(); }                // vorhandene Message
  293.  
  294.     RexxMsgClass *Get(const char *command);  // schickt Anfrage "command" an Edward
  295.                                              // bei Erfolg ist nachher "mess" != 0
  296.     // Funktionen zum Abfragen des Ergebnisses:
  297.     int Result1();
  298.     const char *Result2();
  299.  
  300.     void Drop();                 // löscht Message-Struktur
  301.  
  302.     void SetPort(MsgPort *m)     // zum Nachträglichen Setzen des Edward-Ports
  303.       { edport = m; }
  304. };
  305.  
  306. int EdRequest::Result1()
  307. { if (mess)
  308.     return mess->rm_Result1;
  309.   else
  310.     return 0;
  311. }
  312.  
  313. const char *EdRequest::Result2()
  314. { if (mess)
  315.     return (char*) mess->rm_Result2;
  316.   else
  317.     return 0;
  318. }
  319.  
  320. void EdRequest::Drop()
  321. { if (mess)
  322.     { delete mess;
  323.       mess = 0; }
  324. }
  325.  
  326.  
  327. RexxMsgClass *EdRequest::Get(const char *command)
  328. {
  329.   if (edport)
  330.    { Drop();              // bisherigen Status löschen
  331.      // Neue Message allozieren:
  332.      mess = new RexxMsgClass(repport->Port(), command);
  333.      if (mess)
  334.        { // Request abschicken:
  335.          PutMsg(edport, &mess->rm_Node );
  336.  
  337.          // Auf Antwort warten, aber:
  338.          // Zwischendurch könnte auch andere Message angekommen sein
  339.          if (repport->Wait() != (Message*)mess 
  340.            || mess->rm_Node.mn_Node.ln_Type != NT_REPLYMSG)
  341.             { mess = 0; }  // Dann gilt Request als fehlgeschlagen
  342.          else
  343.             { Message *hilf = repport->Get(); }
  344.        }
  345.    }
  346.   return mess;
  347. }
  348.  
  349.  
  350.  
  351. // **********         ListParser         **********
  352.  
  353. class ListParser
  354.  // Diese Klasse stellt Routinen zur Verfügung, die das Parsen von Listen, wie
  355.  // sie Edward liefert, erleichtern
  356. {
  357.   const char *st;      // Lesezeiger für String
  358.  public:
  359.   int level;           // aktuelle Klammerebene
  360.   unsigned long val;   // Wert des zuletzt gelesenen ¡nt's bzw. letzte Stringlänge
  361.   const char *sval;    // Anfang des zuletzt gelesenen Strings
  362.  
  363.   enum Token { bra, ket, str, num, end, unk };   // Token, die in Listen auftreten können
  364.  
  365.   void Reset(const char *s)
  366.    { st = s; level = 0; }
  367.  
  368.   char Skip();
  369.   Token Scan();
  370. };
  371.  
  372.  
  373. char ListParser::Skip()
  374. { for (char c = *st; c==' '; c = *++st)
  375.    ;
  376.   return c;
  377. }
  378.  
  379.  
  380. ListParser::Token ListParser::Scan()
  381. { switch(Skip())
  382.   { case '\0': return end;
  383.     case '(' : ++st; ++level; return bra;
  384.     case ')' : ++st; --level; return ket;
  385.     case '"' : sval = ++st;
  386.                for (val=0; *st++ != '"'; ++val) ;
  387.                return str;
  388.     case '0' : case '1': case '2':
  389.     case '3' : case '4': case '5':
  390.     case '6' : case '7': case '8':
  391.     case '9' : val = 0;
  392.                while (*st >= '0' && *st <= '9')
  393.                  val = 10*val + (*st++ - '0');
  394.                return num;
  395.     default  : ++st; return unk;
  396.   }
  397. }
  398.  
  399.  
  400.  
  401.  
  402. // **********          EdState           **********
  403.  
  404. struct EdText
  405. { const char *name;
  406.   unsigned len;
  407.   unsigned char id, notupdated, changed, transferred;
  408. };
  409.  
  410. class EdState : public EdRequest, private ListParser
  411.  // Spezialklasse zum Abfragen des Edward-Status
  412. { private:
  413.    void Drop();                 // löscht vorhandene Texttabelle
  414.   public:
  415.    short textanz,               // Anzahl der Texteinträge
  416.          currentID;             // ID des aktuellen Texts
  417.    struct EdText * texts,       // Text-Tabelle
  418.                  * current;     // Tabellenenitrag des akt. Texts
  419.  
  420.    EdState (MsgPortClass &mp)   // Konstruktor: Initialisierung wie bei "EdRequest"
  421.      : EdRequest(mp), textanz(0), texts(0)
  422.     { }
  423.    ~EdState();
  424.  
  425.    operator int()               // TRUE, wenn vollst. Information vorliegt
  426.      { return (Result2() && texts && current); }
  427.  
  428.    RexxMsgClass *Get();         // Stellt "GetTextList"-Anfrage
  429. };
  430.  
  431.  
  432. void EdState::Drop()
  433. { if (texts)
  434.     { delete [] texts;
  435.       texts = 0;
  436.       textanz = 0;
  437.       current = 0;
  438.       currentID = 0;
  439.     }
  440. }
  441.  
  442.  
  443. EdState::~EdState()
  444.  { Drop(); }
  445.  
  446.  
  447. RexxMsgClass *EdState::Get()
  448. { Drop();
  449.  
  450.    if ( EdRequest::Get("GetTextList") )
  451.     { // Liste durchparsen:
  452.       Reset(Result2());
  453.  
  454.       if (Scan() != bra || Scan() != num) goto err;
  455.       currentID = val;
  456.  
  457.       // Texte zählen:
  458.       textanz = 0;
  459.       Token t;
  460.       while ((t=Scan()) == bra)
  461.        { 
  462.          if (Scan() != str) goto err;
  463.          for (int i=0; i<4; ++i)
  464.            if (Scan() != num) goto err;
  465.          do
  466.            t = Scan();
  467.          while (t != end && t != unk && (t != ket || level > 1));
  468.          if (t!=ket) goto err;
  469.          ++textanz;
  470.        }
  471.       if (t!=ket || level != 0 || Scan() != end) goto err;
  472.       // Alles klar.
  473.  
  474.       if ((texts = new EdText[textanz]) == 0) goto err;
  475.  
  476.       // Alles noch mal lesen:
  477.       Reset(Result2());
  478.       Scan(); Scan();
  479.       for(EdText *ep = texts; (t=Scan()) == bra; ++ep)
  480.        { 
  481.          if (Scan() != str) goto err;                // Name
  482.          ep->name = sval; ep->len = val;
  483.  
  484.          Scan(); ep->id = val;                       // ID
  485.          if (val == currentID) current = ep;
  486.  
  487.          Scan(); ep->notupdated = val;               // übrige Flags
  488.          Scan(); ep->changed = val;
  489.          Scan(); ep->transferred = val;
  490.  
  491.          do
  492.            t = Scan();
  493.          while (t != end && t != unk && (t != ket || level > 1));
  494.        }
  495.     }
  496.    else
  497.     {
  498.      err:  // Fehler beim Parsen aufgetreten:
  499.       Drop();
  500.       return 0;
  501.       return mess;
  502.     }
  503. }
  504.  
  505.  
  506.  
  507. // **********         EdCommPort         **********
  508.  
  509. enum Commands {            // Befehlscodes
  510.  com_none = 0,
  511.  
  512.  com_helloapp = 1,
  513.  com_quit,
  514.  com_shutdown,
  515.  com_reopen,
  516.  com_run,
  517.  com_compile };
  518.  
  519.  
  520. struct Message;
  521. struct MsgPort;
  522.  
  523. class EdCommPort : public MsgPortClass
  524.  
  525.  // Klasse für die Kommunikation Edward -> Applikation
  526.  // Wenn die Applikation beschäftigt ist, ignoriert sie einfach alle Messages,
  527.  // die sie vom Edward erhält (d. h. der Empfang wird mit "ReplyMsg" bestätigt,
  528.  // aber nichts geschieht) - mit Ausnahme von "quit" und "shutdown", die
  529.  // unbedingt sofort bearbeitet und dann bestätigt werden müssen. Eine solche
  530.  // Message kann zwischengespeichert werden, um später (möglichst bald!) bearbeitet
  531.  // zu werden.
  532.  // Außerdem gibt's Routinen für die Ermittlung der Anweisungsnummer
  533. {
  534. private:
  535.   static char *commandstrs[];
  536.   RexxMsgClass *killmess,      // Empfangene, aber noch nicht beantwortete
  537.                                // "quit"- oder "shutdown"-Nachricht
  538.                *last;          // zuletzt von "Get()" empfangene Message
  539. public:
  540.   MsgPort *edwardport;    // Port, an den Edward-Anweisungen geschickt werden
  541.                           // können
  542.   EdCommPort()
  543.     : killmess(0), last(0) { }
  544.  
  545.   ~EdCommPort()
  546.     { ReplyKillmsg(); }
  547.  
  548.   // Kommunikation findet auf dieser Ebene ausschließlich über Rexx-Msg's statt.
  549.   RexxMsgClass *Get();   // liefert die zurückgestellte Message "killmess" oder
  550.                          // ruft "MsgPortClass::Get" auf. Setzt "last".
  551.                          // Message-Typ "NT_REPLYMSG" wird unterdrückt und gelöscht (Reste aus fehlgeschlagener Kommunikation)
  552.   RexxMsgClass *Last()
  553.     { return last; }
  554.  
  555.   void Reply();           // Macht "ReplyMsg(last)" und setzt ggf. "killmess" auf 0
  556.  
  557.   // Behandlung von "Kill"-Messages:
  558.   void IgnoreMessages();  // Schmeißt alle eingegangenen Nachrichten weg, aber nur
  559.                           // bis zur ersten "Kill"-Message - die wird aufbewahrt.
  560.   Commands TestKillmsg(); // Macht "IgnoreMessages" und liefert dann den empfangenen
  561.                           // "Kill"-Befehlscode bzw. com_none.
  562.   void ReplyKillmsg() ;   // "Reply" auf der gemerkten "killmess", falls vorhanden
  563.  
  564.   // Auswerten von Messages:
  565.   static Commands WhichCommand(const RexxMsg&);  // liefert Anweisungsnummer der Message, oder -1
  566.   Commands LastCommand();              // Anweisungsnummer der "last"-Message
  567. };
  568.  
  569.  
  570. RexxMsgClass *EdCommPort::Get()
  571. { if (killmess)
  572.     return killmess;
  573.   else
  574.   if ((last=(RexxMsgClass*)MsgPortClass::Get()) == 0)
  575.     return 0;
  576.   else
  577.     if (last->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
  578.       { delete last;           // übriggebliebene Reply löschen
  579.         last = 0;
  580.         return Get(); }
  581.     else
  582.       { switch(LastCommand())
  583.         { case com_quit:
  584.           case com_shutdown:
  585.             killmess = last;
  586.         }
  587.         edwardport = last->rm_Node.mn_ReplyPort;   // Port für spätere Anfragen an Edward merken
  588.         return last;
  589.       }                
  590. }
  591.  
  592.  
  593. void EdCommPort::Reply()
  594. {
  595.   if (last==killmess)
  596.     killmess = 0;
  597.   ReplyMsg(&last->rm_Node);
  598. }
  599.  
  600.  
  601. char *EdCommPort::commandstrs[] = { 
  602.  "helloapp",
  603.  "quit",
  604.  "shutdown",
  605.  "reopen",
  606.  "run",
  607.  "compile",
  608.  0 };
  609.  
  610.  
  611. Commands EdCommPort::LastCommand()
  612. { if (last)
  613.      return WhichCommand(*last)
  614.   else
  615.      return com_none;
  616. }
  617.  
  618.  
  619. Commands EdCommPort::WhichCommand(const RexxMsg &m)
  620. { for(int i=com_none; commandstrs[i]; ++i)
  621.     if (!stricmp(m.rm_Args[0], commandstrs[i]))
  622.       return (Commands)(i+1);
  623.   return com_none;
  624. }
  625.  
  626.  
  627. void EdCommPort::IgnoreMessages()
  628. {
  629.   while (killmess==0 && Get() != 0) ;
  630. }
  631.  
  632.  
  633. Commands EdCommPort::TestKillmsg()
  634. {
  635.   IgnoreMessages();
  636.   if (killmess) 
  637.     return LastCommand();
  638.   else
  639.     return com_none;
  640. }
  641.  
  642.  
  643. void EdCommPort::ReplyKillmsg()
  644. { if (killmess)
  645.     Reply();
  646. }
  647.  
  648.  
  649.  
  650.  
  651. // **********        TransferText        **********
  652.  
  653. class TransferText : private EdRequest
  654.  // liest Datei aus Edward (bzw. versucht es wenigstens)
  655. {
  656.   private:
  657.     unsigned handle;
  658.   public:
  659.  
  660.     TransferText (EdCommPort &p)          // Konstruktor setzt gleich Ports
  661.       : EdRequest(p) { SetPort(p.edwardport); }
  662.     ~TransferText()
  663.       { Close(); }
  664.  
  665.     // zwei Varianten für "Open": entweder mit normalem String-Dateinamen oder
  666.     // mit Zeiger+Länge
  667.     unsigned Open(const char *name);
  668.     unsigned Open(const char *name, int namelen);
  669.  
  670.     int Read(char *buf, unsigned max);     // führt "TextRead" aus
  671.  
  672.     void Close();
  673. };
  674.  
  675.  
  676. unsigned TransferText::Open(const char *name)
  677.  { return Open (name, strlen(name)); }
  678.  
  679.  
  680. unsigned TransferText::Open(const char *name, int namelen)
  681. {
  682.   handle = 0;
  683.   char command[150] = "InitTextRead \"";
  684.   strncat (command, name, namelen);
  685.   strcat  (command, "\"");
  686.  
  687.   if(Get(command))
  688.     {
  689.       handle = strtoul(Result2(),0,0);
  690.     }
  691.   return handle;
  692. }
  693.  
  694.  
  695. void TransferText::Close()
  696. {
  697.   if(handle)
  698.   { char command[150] = "EndTextRead ";
  699.     uinttostr(handle, command+strlen(command));
  700.     Get(command);    
  701.     handle = 0;
  702.   }
  703. }
  704.  
  705.  
  706. int TransferText::Read(char *buf, unsigned max)
  707. {
  708.   char command[150] = "TextRead ";
  709.   uinttostr(        handle, command+strlen(command));
  710.   strcat(command," ");
  711.   uinttostr((unsigned) buf, command+strlen(command));
  712.   strcat(command," ");
  713.   uinttostr(           max, command+strlen(command));
  714.   
  715.   if (Get(command))
  716.     return strtoul(Result2(),0,0);
  717.   else
  718.     return 0;
  719. }
  720.  
  721.  
  722.  
  723.  
  724. // **********       Hauptprogramm        **********
  725.  
  726. #include <stream.h>
  727. #include <dos/dostags.h>
  728.  
  729. #define PORTNAME "MppEdwardPort"
  730. #define EDNAME "Edward:Edward"
  731.  
  732. char argstring[80];
  733.  
  734. EdCommPort commport;
  735. EdState edstate(commport);
  736.  
  737. void main(int argc, char *argv[])
  738. {
  739.   // Edward laden:
  740.   BPTR EdSeg = LoadSeg(EDNAME);
  741.   
  742.   if (EdSeg)
  743.     {
  744.         char portname[] = PORTNAME "0";
  745.  
  746.         // Eindeutigen Namen sicherstellen:
  747.         Forbid();
  748.          while(FindPort(portname) && portname[sizeof(PORTNAME)] < '\x7f')
  749.            portname[sizeof(PORTNAME)]++;
  750.         Permit();
  751.  
  752.       // Argumente parsen:
  753.       for(int i = 1; i<argc && strlen(argstring)+strlen(argv[i]) < 78; ++i)
  754.         { strcat(argstring, argv[i]);
  755.           strcat(argstring, " "); }
  756.       // "-cPortname" anhängen:
  757.       strcat(argstring,"-c");
  758.         strcat(argstring, portname);
  759.  
  760.       // Ports + Tags erzeugen:
  761.       if (!commport.Create(portname)) goto ende;
  762.       TagItem EdTags[] = { NP_Seglist, EdSeg,
  763.                              NP_FreeSeglist, 1,
  764.                            NP_Name, (ULONG)"Maxon C++ editor process",
  765.                            NP_Arguments, (ULONG)argstring,
  766.                            NP_Cli, -1 };
  767.  
  768.       // Edward-Prozeß anstarten:
  769.         struct Process *EdProc = CreateNewProc(EdTags);
  770.       if (!EdProc)
  771.        { UnLoadSeg(EdSeg);
  772.          goto ende; }
  773.  
  774.       Commands commandcode;
  775.         
  776.       do {
  777.         // Schleife: Auf Edward's Messages warten
  778.         while (!commport.Get() )
  779.           commport.Wait();
  780.  
  781.         // Nummer feststellen + beantworten:
  782.         commandcode = commport.LastCommand();
  783.         commport.Reply();   // ACHTUNG: Wenn eine Applikation Windows auf den
  784.                             // Edward-Screen geöffnet hat, darf sie ein "Shutdown"
  785.                             // erst "Reply"-en, nachdem sie diese Windows
  786.                             // geschlossen hat.
  787.  
  788.         // Eine Ausgabe, damit man auch was sieht:
  789.         cout << "*** Anweisung #" << commandcode << " empfangen.\n";
  790.  
  791.         switch (commandcode)
  792.          { case com_compile:       // Zur Demonstration: aktuellen Text aus 
  793.            case com_run:           // Editor übernehmen und ausgeben
  794.              // Textliste anfordern:
  795.              edstate.SetPort(commport.edwardport);    // Reply-Port ist zugleich Port für Anfragen.
  796.              edstate.Get();
  797.  
  798.              if(edstate)            // Kann durchaus fehlgeschlagen sein...
  799.                 {
  800.                   char b[1000];              // Puffer
  801.                   TransferText t(commport);  // Objekt für Übertragung einrichten
  802.  
  803.                   if (t.Open(edstate.current->name, edstate.current->len))
  804.                     { int i;
  805.                       while ((i = t.Read(b,999)) > 0 && !commport.TestKillmsg())
  806.                         { 
  807.                           b[i] = '\0'; cout << b;
  808.                         }
  809.                       t.Close();
  810.                     }
  811.                  }
  812.              break;
  813.          }
  814.       }
  815.       while (commandcode != com_quit);
  816.      }
  817.  
  818.   ende:
  819. }
  820.  
  821.