Projekt Multishow (Deutsch)

Neid

Wie bereits anderswo beschrieben, wurde mit AmigaOS Versionen ab OS 3.0 ein DATATYPES-Konzept implementiert. Augenfälligster Unterschied zu den vorhergehenden OS-Versionen war die Anwendung Multiview, die das objektorientierte Dateiklassenkonzept nutzt, um den Inhalt jeder beliebigen Ton-, Bild-, Animations-, usw.-Datei anzuzeigen (sofern ein entsprechender DATATYPE installiert war). Nutzer des Betriebssystems AmigaOS 2.1 schauten neidisch auf diesen kleinen ‚Alleskönner‘. Schnell war die Idee geboren, ein ähnliches universelles Anzeigeprogramm zu programmieren.

Das Konzept

Da AmigaOS 2.1 und Vorgänger keine datatypes.library kannte, mußte die Definition von Datentypen auf andere Art und Weise erfolgen. Auch die Neuimplementierung eines komplett neuen Anzeigeprogrammes erschien ineffizient, da für fast alles, was an Multimediadatentypen vorhanden war, ein propietäres Programm vorlag.

Das Programmpaket Multishow mit seinen Teilen ms.rexx (Multishow - Host) und msp.rexx (MultishowPrefs - Voreinsteller)

Die Idee war Folgende: Es sollte ein möglichst kleines System eingesetzt werden, daß die vom Benutzer ausgesuchten Dateien analysiert, zuordnet und automatisch das passende Programm zur jeweiligen Datei aufruft. Das Ganze sollte aufgrund naheliegender Gründe (Interprozesskommunikation, siehe oben) in ARexx realisiert werden. Zudem sollte ein eigenes, leicht zu bedienendes, GUI entwickelt werden, daß es dem Benutzer erlaubt, schnell neue Dateitypen zu definieren und vorzugeben, welches Programm für die Anzeige zu verwenden ist. Es sollte daher modular und leicht zu erweitern sein.

Der ‚Host‘

Das eigentliche Hauptprogramm ‚Multishow‘ wurde als speicherresidenter ARexx-Host programmiert, der selbst keine (grafische) Oberfläche besitzt (ähnlich den UNIX-oiden daemons), sondern nur über seinen ARexx-Port mit der Umgebung kommuniziert. Der Zugriff auf die internen Funktionen erfolgt daher immer über diesen Port und kann auf vielfältige Weise geschehen. Neben einem Aufruf über die Kommandozeile (CLI) in der Form

rx "ADDRESS MULTISHOW.1 'Pfad:Beispieldatei'"

(MULITSHOW.1 ist der Name des ARexx-Ports) kann der Port quasi von ‚überall‘ angesprochen werden. So läßt sich z.B. Drag&Drop-Funktionalität über ein entsprechendes Tool-Dock einbringen oder man bindet den Aufruf in einen Dateimanager ein.

Im unteren Ausschnitt erkennt man die Hauptroutine ‚main:‘, die auf Eingaben von Schlüsselwörtern (wie ‚QUIT‘) oder die Übergabe eines Dateinamens/-pfades (‚filename‘) durch den ARexx-Port wartet und entsprechend reagiert. Wurde eine Datei übergeben, werden die Prozeduren ‚getdatatype:‘ und ‚test:‘ aufgerufen, die tatsächlich die ersten Bytes der Datei extrahieren und anscannen, um herauszufinden, um welchen Dateityp es sich handelt (und sich eben NICHT nur auf etwaige Dateiendungen verläßt…).

/*******************************************************/
/* MAIN: Hauptroutine, Abfragen des Ports "MULTI-      */
/*       SHOW.1"; Steuerung in Abhängigkeit von        */
/*       eingetroffenen Argumenten > Subroutinen       */
/* packet         - Message packet                     */
/* nft            - Gesamtanzahl der def. Filetypes    */
/* inkey          - Statusvariable für main() >        */
/*                  0=Kommandozeilenargument  >        */
/*                  1=Arg. über WaitPkt() abwarten     */
/* filename       - übergeb. Argument                  */
/* datatype       - <= 128 Byte des angescannten Files */
/* dtl            - Länge von datatype (<= 128)        */
/*******************************************************/

main:
    DO FOREVER
        IF inkey = 1 THEN DO
            port     = WaitPkt('MULTISHOW.1')
            DO FOREVER
                packet    = GetPkt('MULTISHOW.1')
                IF packet = '0000 0000'x THEN LEAVE
                filename  = GetArg(packet,0)
                CALL Reply(packet,0)
            END
            END
        SELECT
            WHEN UPPER(filename) = 'QUIT' THEN CALL dasende(0)
            WHEN
(...)
            OTHERWISE NOP
        END
        IF ~(Exists(filename)) | filename = "" THEN CALL resetvar()
        datatype = getdatatype(filename)
        dtl      = Length(datatype)
        CALL test(nft)
        CALL resetvar()
        inkey = 1
    END

Ausschnitt aus ‚main:‘ des Projektes Multishow

Das eigentliche Erkennen eines Dateityps erfolgt in mehreren Schritten. Die ersten Bytes der zu untersuchenden Datei (Extraktion in der Funktion ‚getdatatype:‘) werden nacheinander allen zur Verfügung stehenden ‚filetypes‘ siehe unten) vorgelegt. Die in den ‚filetypes‘ abgelegten Routinen liefern in der Funktion ‚test:‘ einen Match (oder eben nicht). Ist ein Dateityp erkannt, wird das zugehörige (durch den Benutzer festgelegte) ‚command‘ (also der Aufruf eines passenden Anzeigeprogramms) ausgeführt.

Der ‚filetype‘: Interpretierte Interpretation

Da ARexx eine interpretierte, prozedurale und typenlose Sprache darstellt, die zudem über rekursive Mechanismen verfügt, wurde folgender Ansatz gewählt:

Der ‚filetype‘ selbst ist eine kleine Datei, die den Namen des Dateityps trägt (z.B. JPEG.filetype, GIF.filetype etc.). Im filetype selbst steht einfach ein (ARexx-)Prozeduraufruf, der zur Erkennung eines Dateityps definiert wurde. Als Beispiel hier der GIF.filetype:

CHAR(1,'GIF8')

Der GIF.filetype

Der obige String entspricht genau dem (ARexx-)Aufruf einer im Hauptprogramm ‚Multishow‘ befindlichen Routine ‚char:‘ (siehe unten). Der so im GIF.filetype abgelegte Prozeduraufruf wird im Programm einfach durch den Befehl ‚INTERPRET‘ interpretiert. Diese Art der Interpretation von Code innerhalb eines interpretierten Codes führt zu außerordentlich einfacher und schneller Einarbeitung neuer Funktionen und Module.

Als Beispiel für die im Host ‚Multishow‘ aufgerufenen Funktionen, die dafür zuständig sind, die ersten Bytes der zu untersuchenden Datei auf bestimmte Schlüsselreize (z.B. Byteabfolgen, Offsets) zu untersuchen, sei hier die oben verwendete Prozedur ‚char:‘ gezeigt:

/******************************************************/
/* CHAR: Überprüfung auf ASCII-String-Matches         */
/* VAR:                                               */
/* a             - Startposition für die Überpr. (0=  */
/*                 relative Suche, d.h. globaloffset  */
/*                 benutzen;                          */
/* b             - Argumentstring (ASCII)             */
/* ret           - Rückgabe/Status 1=Überein-         */
/*                 stimmung, 0=keine Übereinst.       */
/* globaloffset  - Einsprungpunkt für relative Tests  */
/* SUBARG: a , b - (s.o.)                             */
/* SUBRET: ret                                        */
/******************************************************/
CHAR:
    ARG a,b
    ret = 0
    IF a = 0 THEN DO
        a = globaloffset
        globaloffset = globaloffset + Length(b)
        END
    IF UPPER(SubStr(datatype,a,Length(b))) = b THEN ret = 1
RETURN ret

Die matching-Routine ‚char:‘

Hier wird also auf das Vorhandensein einer bestimmten, vorher definierten, ASCII-Abfolge an einer relativen oder absoluten Position innerhalb der ersten (128) Bytes gesucht und bei Erfolg ‚1‘, bei Mißerfolg ‚0‘ zurückgegeben. Im obigen Beispiel wird also zur Erkennung eines GIF-Bildes in der Prozedur ‚char:‘ ab der Position 1 nach der Zeichenkette ‚GIF8‘ gefahndet.

Das Preferences-Programm: MultishowPrefs

Wie bringt man nun dem Host Multishow weitere und neue Dateitypen bei? Grundsätzlich muß ’nur‘ eine Textdatei mit dem Namen des zu definierenden Dateityps erstellt werden. Dem obigen Beispiel folgend wird also z.B. eine Datei namens GIF.filetype erzeugt und dort der passende Prozeduraufruf mit sinnvollen Argumenten eingetragen.

Aufgrund der Tatsache, daß viele Leute lieber mit der Maus als mit dem Texteditor arbeiten und zweitens für diesen Zweck die Einbindung externer Funktionsbibliotheken (insbesondere zu Erstellung eines GUIs) in ARexx getestet werden sollte, wurde also ein eigenständiges Programm zur leichteren Definition der Dateitypen erstellt: MultishowPrefs.

Hauptfenster 'MultishowPrefs'

Oben sieht man das Hauptfenster mit den bis dahin definierten Dateitypen. Die Anlage einer neuen Dateitypdefinition geschieht in einem zweiteiligen Listenfenster (siehe unten); rechts sind die möglichen Testprozeduren aufgeführt, links werden die für den gerade bearbeiteten Typ zu verwendenden aufgelistet. Als ‚Inspiration‘ für die dort gezeigten Funktionen und deren Anwendung diente der einschlägig bekannte Dateimanager DirectoryOPUS (Versionen 4.x bzw. 5.x). Für jeden Dateityp können mehrere Testverfahren – und damit (intern im Host ‚Multishow‘) Ansprünge der dazugehörigen Programmprozedur – angegeben werden. Die Reihenfolge der Routinenaufrufe entspricht zudem der Priorität. Im gezeigten Beispiel soll nun zusätzlich zu den schon weiter oben erklärten Test auf die Zeichenfolge ‚GIF8‘ am Anfang (IN der Datei) die Dateiendung ‚.gif‘ als zusätzliches Merkmal definiert werden. Verschiebt man danach diesen Prozeduraufruf für die Routine ‚extension:‘ in der Liste vor den MatchCHARFrom()-Eintrag (-> interne Routine ‚char:‘), wird zuerst auf die Dateiendung ‚.gif‘ und danach innerhalb der Datei auf die Abfolge ‚GIF8‘ geprüft. Dies ist zwar für den Bilddateityp GIF nicht wirklich notwending, es gibt allerdings tatsächlich Multimediadatentypen, die erst durch mehrere sukzessive Überprüfungsroutinen wirklich eindeutig zu identifizieren sind.

Fenster zur Dateitypdefinition in 'MultishowPrefs' (hier: gif.filetype)

Technisches

Das ARexx-Programm ‚MultishowPrefs‘ macht massiven Gebrauch von der ARexx-Funktionsbibliothek apig.library (ARexx programmers intuition and graphics library). Daher besteht der Hauptteil des Programms aus Routinen zur Erstellung der graphischen Benutzerschnittstelle. Bei der apig.library handelt es sich eigentlich nur um einen library wrapper, der die Betriebssystemfunktionen der intuition.-  und graphics.library des AmigaOS auf das ARexx-übliche API abbildet; der Programmcode unterscheidet sich daher kaum von den üblichen Strukturen, die man dazu normalerweise in (z.B.) C anlegen würde.

newgadx0    = MAKENEWGADGET(screenvinfo,screenfont,25,150,195,50,
                                 ,"Copyright © 1995 Frank Ruthe",
                                 ,PLACETEXT_IN,
                                 ,1,NULL())
prevgad0    = CreateGadget(TEXT_KIND,prevgad0,newgadx0,
                             ,GTTX_TEXT,"",
                             ,GTTX_CLIPPED,1,
                             ,GTTX_BORDER,1,
                             ,TAG_DONE,0)
CALL SETNEWGADGET(newgadx0,screenvinfo,screenfont,25,90,195,50,
                       ,"MultiShowPrefs v0.943",
                       ,PLACETEXT_IN,
                       ,2,NULL())

Ausschnitt aus ‚MultishowPrefs‘: Beispiel für die anzulegenden Strukturen und Tags für den Aufruf der zugehörigen OS-GUI-Funktionen mittels apig.libary

Mittlerweile existieren wesentlich mächtigere und programmierfreundlicher angelegte Bibliotheken, die das Programm bzw. die Anlage eines GUI kürzer, leichter wartbar und wesentlich einfacher implementierbar machen würden (z.B. die AWNPipe oder die rxmui.library).

Schlußbemerkung

Beide Teile des gezeigten Projektes ‚Multishow‘, also Host und Voreinsteller, sind absolut unoptimiert. Desweiteren glänzt der Sourcecode durchaus mit mitunter nicht besonders eleganten Routinen, die sicherlich verbessert (und beschleunigt) werden könnten. Nichtsdestotrotz haben Host und Preferences-Programm zwei Jahre lang außerordentlich zuverlässig ihren (unauffälligen) Dienst getan und waren aufrund einer zwar rudimentären aber funktionstüchtigen Fehler-/Exception-Behandlung auch in der Lage Laufzeitfehler o.ä. abzufangen und zu handhaben (z.B. durch eine erneute Selbstinitiation). Zumindest als proof of concept bzw. proof of function für (1.) eine Datentyp-abhängige Dateitypbehandlung und (2.) die Erstellung und Nutzung eines GUI mittels Skriptsprache ARexx taugte dieses Projekt allemal.