Topic : Das ATOS-Magazin 3/98 Author : Das ATOS-Team Version : 18.9.1998 Subject : ATOS Diskettenmagazine Nodes : 294 Index Size : 8212 HCP-Version : 5 Compiled on : Atari @charset : atarist @lang : @default : @help : ATOS Hilfe @options : -i -s +zz -t2 @width : 70 View Ref-FileATOS ProgrammierpraxisGEMScript Teil II GS_COMMAND: Aye, Sir. Das ATOS-Magazin 3/98 Wenn GS_REQUEST und GS_REPLY zwischen den Applikationen ausgetauscht wurden, steht die GEMScript-Verbindung im Prinzip und es kann losge- scripted werden. Dreh- und Angelpunkt der Kommunikation ist hier die Message GS_COMMAND (0x1352), deren Verständnis seitens der Applikati- onen mittels Eintragen von GSM_COMMAND in msgs in der GS_INFO-Struktur ja bereits bestätigt wurde. Der Aufbau der GS_COMMAND-Message ist folgender: msg[0] GS_COMMAND (0x1352 (4946)) msg[1] die ID des Versenders msg[2] 0 msg[3] + Pointer auf Kommandozeile msg[4] msg[5] 0 msg[6] 0 msg[7] Gemscript-ID Diese Message enthält in ev_mgpbuff[3] und [4] grundsätzlich einen Zeiger auf eine Kommandozeile, deren Aufbau wir ja in Teil I schon kurz gezeigt hatten. Was tun wir nun, wenn wir eine solche Message er- halten? Fangen wir doch erst einmal damit an, uns das Kommando und die dazuge- hörigen Parameter aus der Kommandozeile zu extrahieren. Dazu schreiben wir uns, wie sollte es auch anders sein, eine Funktion, die ungefähr so aussehen könnte (ich beschränke mich jetzt mal auf zwei Kommandos): #define GS_OPEN 1 #define GS_CLOSE 2 PARLIST *extract(char *cmdline, int *command) { char *param; PARLIST *first, *curr; /* Kommando aus der cmdline holen */ if(stricmp(cmdline, "open")==0) *command=GS_OPEN; if(stricmp(cmdline, "close")==0) *command=GS_CLOSE; /* Ersten Parameter holen bzw. nachgucken, ob überhaupt einer drin ist */ param = cmdline + strlen(cmdline) + 1; if(*param==0) return(0); /* Und jetzt alle Parameter bis zum doppelten Nullbyte extrahieren */ curr = first; do { curr = malloc(sizeof(PARLIST)); curr->param = malloc(strlen(param)+1); strcpy(curr->param, param); curr = curr->next; param = param + strlen(param) +1; } while(*param!=0); curr->next=NULL; return(first); } Für die Parameter verwende ich hier eine einfach verkettete Liste, die eine Struktur typedef struct parlist { char *param; struct parlist *next; } PARLIST; benutzt. Das erscheint mir als der einfachste Weg, auch mit wirklich beliebig vielen Parametern fertig zu werden. Daß die einzelnen Bestandteile der Kommandozeile durch Nullzeichen getrennt sind, kommt uns hier natürlich sehr gelegen, da das Auflösen der Kommandozeile so einfach durch strcpy, strlen und stricmp (die Kommandos müssen ohne Unterscheidung von Groß- und Kleinschreibung differenziert werden) vorgenommen werden kann. extract() wird also vom Messagehandler der GS_COMMAND empfangenden Applikation mit der Kommandozeile und einem Zeiger auf eine Integer- Variable aufgerufen und gibt einen Zeiger auf das erste Element einer verketteten Liste PARLIST zurück. In PARLIST->next steht jeweils ein Zeiger auf den nächsten Parameter, der am Ende der Liste NULL ist. Wenn der Rückgabewert von extract() direkt NULL ist, enthält das Kom- mando keine Parameter. Das aus der Kommandozeile extrahierte Kommando steht nach dem Aufruf in der übergebenen Variable command, und zwar in Form der oben definierten Konstanten. Wenn die Kommandozeile derart aufgedröselt wurde und nicht mehr weiter benötigt wird, können wir GS_ACK (0x1353) an die Versenderapplikatio- nen verschicken, die daraufhin den Speicher für die Kommandozeile wieder freigeben: msg[0] GS_ACK (0x1353 (4947)) msg[1] ap_id msg[2] 0 msg[3] + exakt die Werte der empfangenen GS_COMMAND-Nachricht msg[4] msg[5] + Ergebnis bzw. Fehlermeldung als ASCIIZZ-Text (s.u.) oder NULL msg[6] msg[7] 0: (GSACK_OK) OK, Kommando wurde oder wird ausgeführt 1: (GSACK_UNKNOWN) Kommando unbekannt 2: (GSACK_ERROR) Fehler (Kommando nicht ausgeführt) In ev_mgpbuff[3] und [4] schicken wir also einfach einen Zeiger auf die empfangene Kommandozeile, bei Index 7 den entsprechenden Rückgabe- wert an die andere Applikation zurück. Für die weitere Verarbeitung der Kommandos gibt es nun mehrere Möglichkeiten. Empfehlenswert ist auf jeden Fall die Implementation eines Dispatchers, der nach dem Aufrufen von extract() aufgerufen wird und entsprechende Aktionen vornimmt. Nehmen wir z.B. einmal ein Pro- gramm an, das mittels einer Funktion FileOpen(char *path) eine Datei öffnet. Dann könnte der Dispatcher folgendermaßen aussehen: int gs_dispatch(int command, PARLIST *first) { PARLIST *curr_par = first; switch(command) { case GS_OPEN: do{ FileOpen(curr_par); curr_par = curr_par->next; } while(curr_par!=NULL) break; } return(0); } So würde also anhand des Kommandos command und des ersten Elements der Parameterliste first die Funktion FileOpen nacheinander mit den über- gebenen Dateipfaden aufgerufen. Abschließend brauchen wir eigentlich nur noch die PARLIST wieder freizugeben: void destroy_parlist(PARLIST *first) { PARLIST *curr = first, *next; do{ free(curr->command); next = curr->next; free(curr); curr = next; } while(curr!=NULL); } Und fertig ist die Interpretation des GEMScript-Open-Kommandos mit be- liebiger Parameterzahl. Die hier gezeigten Funktionen sollten leicht erweiterbar sein und können als Gerüst für die Implementation in eigene Programme dienen. Und jetzt wollen wir den Aufbau der weiteren Standard-GEMScript-Kom- mandos und eventuell auftretende Probleme erläutern. Close Das Gegenstück zu open. Parameter sind optional einer oder mehrere Dateinamen, wenn kein Parameter übergeben wurde ist die dem obersten Fenster entsprechende Datei zu schließen. Copy Parameter ist optional ein Dateiname. Beim Eintreffen dieses Kommandos soll die Selektion des dem Dateinamen ent- sprechenden (oder des obersten) Fensters auf das Clipboard kopiert werden. Cut Siehe copy. Nur eben nicht Kopieren, sondern Ausschneiden. ;-) Delete Wie copy oder cut, nur daß die Selektion nur gelöscht und nicht aufs Clipboard geschrieben werden soll. GetFront Bei GS_ACK soll der Dateiname der obersten Datei in ev_mgpbuff[5] und [6] zurückgegeben werden. New Das empfangende Programm soll eine neue Datei anlegen. Hierzu existieren keine Parameter, was es Grafikprogrammen erschweren wird, ohne weiteres etwas mit diesem Kommando anzufangen. Die beste Parameterfolge für Rastergrafikpro- gramme scheint mir hierbei "Breite in Pixeln\0Höhe in Pixeln\0Farbtiefe in Bit" zu sein. Das ist auch die in Smurf implementierte Parameterfolge. Hier sollte eine Einführung genauer spezifizierter Standards stattfinden. Paste Die Applikation soll den Inhalt des Klemmbretts in die Datei einfügen, die als Parameter übergeben wurde. Wenn kein Parameter enthalten ist, ist der Clipboardinhalt ins oberste Dateifenster einzufügen. Print Parameter sind hier eine oder mehrere Dateinamen. Die ange- gebenen Dateien sollen ausgedruckt werden. Auch hier existieren z. Zt. keine genaueren Spezifikationen über Ausgabeskalierung, Farbe oder s/w, zu verwendenden Drucker- treiber oder ähnliches. Quit Die Applikation soll sich beenden. Save Die als Parameter übergebene(n) Datei(en) (sofern geöffnet) speichern. SaveAs Die Applikation soll eine Datei unter einem neuen Namen speichern. Einer oder zwei Parameter: der erste ist der neue Dateiname, der zweite bezeichnet das zu speichernde Fenster. Wenn der zweite Parameter fehlt, ist die zuoberst liegende Datei zu speichern. Was an diesem Kommando vermißt werden könnte, ist die optionale Angabe eines Dateiformats. Es gibt hier nur die Möglichkeit, dieses anhand des neuen Dateinamens (bzw. dessen Extension) zu erkennen. SelectAll Als Parameter kann eine Datei übergeben werden. Die Appli- kation soll den gesamten Inhalt des dazugehörigen Fensters selektieren. Wenn der Parameter fehlt, ist der Inhalt der zuoberst liegenden Datei auszuwählen. ToFront Die Applikation soll ein Fenster in den Vordergrund brin- gen. Als Parameter wird auf jeden Fall der Name der zu toppenden Datei übergeben. Undo Die Applikation soll die letzte Aktion in der als Parameter übergebenen Datei rückgängig machen. Wenn kein Parameter übergeben wird, ist Undo auf die zuoberst liegende Datei anzuwenden. AppGetLongName Enthält keine Parameter. Die Applikation soll mit GS_ACK ihren Namen in langer Form zurückgeben. Die vom Be- triebssystem ermittelbaren Prozeßnamen von Programmen können maximal acht Zeichen lang sein. Da dies je nach Pro- gramm nicht unbedingt ausreichend oder aussagekräftig ist, kann hier ein längerer Name (hierbei soll auf Versionsin- formationen verzichtet werden!) zurückgegeben werden. In der nächsten Ausgabe schließlich werden wir uns ein wenig näher mit dem Feature der Aufnahmefähigkeit beschäftigen. Dahinter verbirgt sich die Möglichkeit, Scripte nicht durch manuelle Programmierung zu schreiben, sondern Aktionen innerhalb eines Programmes wie auf Band "aufzunehmen" und als GEMScript zu speichern, um sie später wieder abzurufen. Gerade für User, die keine Programmiererfahrung haben, er- leichtert ein solches Feature den Umgang mit GEMScript erheblich. Wie die Aufnahmefähigkeit zu realisieren ist, lesen Sie in der nächsten ATOS. Olaf Piesche