EUMEL-Benutzerhandbuch TEIL 7: Dateien, Datei-Verwaltung und Datenräume 1. Übersicht Dateien dienen zur Aufnahme von Informationen, die auch über die Bearbei- tungszeit eines Programms erhalten bleiben (können). Dateien werden als Objekte in einer Task gehalten. Sie bleiben solange erhalten, bis sie expli- zit gelöscht ('forget'-Kommando) oder die Task beendet wird ('end'-Kommando). Dateien kann man an andere Tasks schicken ('save'-Kommando) oder von anderen Tasks holen ('fetch'-Kommando). Mit diesen (hier vorgestellten) Kommandos ist der Transport von Dateien jedoch nur in direkter Linie des Task-Baums möglich, also an einen "Vater" oder einen "Sohn". Damit ist automatisch eine Datei-Hierarchie im EUMEL-System realisiert. Alle Datei-Arten des EUMEL-Systems basieren auf Datenräumen. Ein Datenraum ist ein Speicherplatz, der 1 MByte Daten speichern kann und vom EUMEL-System verwaltet wird. Datenräume können gelöscht, kopiert usw. werden. Mit Hilfe Mit Hilfe von Datenräumen werden alle Datei-Arten des EUMEL-Systems reali- siert, indem einem Datenraum eine Struktur (Datentyp) aufgeprägt wird. Da- durch wird es Benutzern des EUMEL-Systems ermöglicht, auf einfache Weise neben den bereits vorhandenen Datei-Arten (FILEs für die Speicherung von Texten, PICFILEs für die Speicherung von Bildinformationen, u.a.m.) spezielle Dateien zu konstruieren. Die wichtigste Datei-Art, mit der ein Benutzer in Berührung kommt, ist die sequentielle Datei, genannt FILE. Ein FILE kann nur TEXTe aufnehmen und hat z.Zt. ein Fassungsvermögen von 4000 Zeilen (Sätze). Insgesamt darf ein FILE 1 MByte aufnehmen. Eine Datei-Zeile (Satz) kann bis zu 32 000 Zeichen auf- nehmen. FILEs können in einer von drei Betriebsrichtungen bearbeitet werden: - "input": nur Lesen. - "output": nur Schreiben. - "modify": Lesen/Schreiben und zusätzlich beliebiges Positionieren, Ein- fügen und Löschen von Sätzen. 'input'-Dateien sind also Eingabedateien, 'output'- sind Ausgabedateien und 'modify'-Dateien kann man verändern. Insbesondere die 'modify'-Dateien eig- nen sich, um andere Datei-Arten leicht zu realisieren (z.B. indexsequentiel- le Dateien). Eine Datei kann auf einen bestimmten Zeilenbereich eingeschränkt werden. Ein solches Segment kann selbst wie eine Datei von einem Programm behandelt werden. 2. Datei-Kommandos Dateien werden in der Regel in einer Benutzer-Task gehalten. Die Kommandos für die Behandlung von Dateien in einer Benutzer-Task werden hier vorge- stellt. Dann wird erklärt, wie Dateien zu einer übergeordneten Task ge- schickt oder geholt werden können. Datei-Kommandos für Benutzer-Tasks Im Monitor-Dialog kann man Dateien kopieren, löschen und einen anderen Namen geben. Mit dem Kommando edit ("dateiname") wird eine Text-Datei im Monitor-Dialog (implizit) eingerichtet, wenn es sich um einen noch nicht vorhandenen 'dateiname' handelt (vergl. auch die Editor- Beschreibung). Welche Dateien in der Benutzer-Task vorhanden sind, kann mit dem Kommando list erfragt werden. 'list' zeigt auf dem Bildschirm die Dateinamen der Benutzer- Task. Vor jedem Namen steht ein Datum, welches anzeigt, an welchem Tag die Datei zuletzt von einem Programm bearbeitet wurde. Ein 'a' hinter dem Datum kennzeichnet Dateien, die nach einer Archivierung nicht bearbeitet wurden. Ein Duplizieren einer Datei kann mit copy ("alte datei", "neue datei") erreicht werden, wobei eine Kopie von 'alter datei' angelegt wird, die den Namen 'neue datei' erhält. Man beachte, daß es sich um eine logische (und vorerst um keine physikalische) Kopie handelt. Erst bei Schreibzugriffen werden Seiten " entshared" ("copy-on-write"). Ein Umbenennen einer Datei kann mit rename ("alter datei name", "neuer datei name") erfolgen. Mit forget ("dateiname") kann eine Datei gelöscht werden. Wurde in einer Datei editiert, kann durch Löschen oder Einfügen von Zeilen der interne Verwaltungsaufwand hoch werden, so daß Positionierungen nicht mehr effizient vor sich gehen. Mit dem Kommando reorganize ("dateiname") wird die Datei so organisiert, als wenn sie neu erstellt wäre (gilt nur für Text-Dateien). Sie braucht dann in der Regel auch etwas weniger Speicher- platz. Merke: Das Kommando 'rename' gibt einer Datei einen anderen Namen, 'copy' dupliziert eine Datei. Mit 'list' kann man sich anzeigen lassen, welche Dateien in der Task vorhanden sind. Datei-Kommandos für Vater-Tasks Man kann Dateien von einer Vater-Task holen oder an eine Vater-Task schicken, um sie dort längerfristig zu speichern. Auch wenn die Benutzer-Task gelöscht wird, bleiben alle Dateien, die bei einer Vater-Task gespeichert (also "gerettet") sind, erhalten. Andere Sohn-Tasks können sich Dateien bei einer Vater-Task abholen. Auf Dateien einer Benutzer-Task können andere Nutzer aus anderen Tasks in der Regel nicht zugreifen. Manchmal ist es jedoch notwendig, daß mehrere Nutzer Dateien gemeinsam benutzen oder eine Datei eine Task "überleben" soll. In solchen Fällen muß man Dateien bei einem Vater aufbewahren. Mit der Prozedur global manager kann die Benutzer-Task zu einem Datei-Manager gemacht werden. Erst nach Aufruf dieser Prozedur können Söhne dieser Task eingerichtet werden. Von einer Benutzer-Task kann eine Datei mit save ("Buch 1") der direkten Vater-Task übergeben werden, wobei 'buch 1' in der Benutzer- Task erhalten bleibt. 'save' wirkt also wie ein Transport einer Kopie der angegebenen Datei in die Vater-Task. Achtung: ist eine Datei 'Buch 1' in der Vater-Task bereits vorhanden, wird diese ohne Warnung überschrieben. Analog zu der 'forget'-Prozedur für Dateien einer Benutzer-Task können Dateien einer unmittelbaren Vater-Task mit erase ("datei name") gelöscht werden. Soll eine Datei von einer Vater-Task in eine Benutzer-Task geholt werden, so benutzt man die Prozedur fetch ("datei") Nach Aufruf dieser Prozedur wird eine Kopie der angegebenen Datei in der Benutzer-Task angelegt. Ist bereits eine Datei 'datei' in der Benutzer-Task vorhanden, so erfolgt eine Fehlermeldung. Mit 'save', 'fetch' und 'erase' ist es möglich, daß zwei oder mehr Benutzer eine Datei alternierend bearbeiten. In diesem Fall müssen sich die Benutzer als Söhne einer gemeinsamen Vater-Task einrichten und die Datei mit 'fetch' jeweils in die Benutzer-Task holen. Um zu verhindern, daß ein anderer Be- nutzer zur gleichen Zeit mit der Datei arbeitet, wird sie jeweils mit 'erase' bei der Vater-Task gelöscht. Nach Beendigung der Arbeiten muß die Datei dann wieder mit 'save' in die Vater-Task transportiert werden. Dann steht sie wieder anderen Benutzern zur Verarbeitung zur Verfügung. Beispiel: fetch ("datei"); erase ("datei"); (* Datei vom Vater holen und dort loeschen *) edit ("datei"); (* Datei bearbeiten *) save ("datei"); (* Datei zur Vater-Task bringen *) forget ("datei") (* Datei in Benutzer-Task loeschen *) Mit den Prozeduren save all fetch all kann man alle Dateien einer Benutzer-Task in die direkte Vater-Task schicken oder von dort holen. Merke: * * * * * * * * * * * * * <-+ * Vater-Task * | | * * * * | * * | * * * | | fetch save | | * * * | * * | * * * * | | * * ->edit| * Benutzer-Task * * * * * * * * * * * * Ansprechen von anderen Tasks Sollen Dateien in andere als die direkte Vater-Task transportiert oder von anderen Tasks geholt werden, muß man den internen Task-Bezeichner angeben. Es gibt einige Prozeduren, mit denen man den internen Task-Bezeichner einer Task bekommt. Solch eine Prozedur ist father Prozeduren, mit denen man Dateien transportieren kann, liegen auch in einer Version vor, bei denen man den internen Task-Bezeichner angeben kann. Beispiel: save ("datei", father) (* wie: save ("datei") *) erase ("datei", father) (* wie: erase ("datei") *) fetch ("datei", father) (* wie: fetch ("datei") *) Weitere Prozeduren, die einen internen Task-Bezeichner liefern, sind: myself printer public archive Anmerkung: Zur Prozedur 'archive' siehe auch das nächste Kapitel; zu 'print' siehe auch SPOOLER. Dadurch kann man Dateien auch zu anderen als der Vater-Task transportieren, holen oder löschen. Beispiele: erase ("datei", public) (* Loescht 'datei' in Task 'PUBLIC' *) fetch ("datei", public) (* Holt 'datei' aus der Task "PUBLIC" *) save ("datei", public) (* Kopiert 'datei' in die Task 'PUBLIC' *) save ("datei", printer) (* Uebergibt 'datei' dem Spooler und druckt die Datei *) list (father) (* Listet Dateien der Vater-Task *) Bei komplizierten "Verwandschafts-Verhältnisse" von Tasks ist es einfacher, eine Task mit Hilfe des Task-Namens anzusprechen. Das erfolgt mit Hilfe der Prozedur 'task'. Beispiel: save ("datei", task ("hugo")) 'task' liefert den internen Task-Bezeichner von 'hugo'. Die Umkehr-Prozedur zu 'task' ist 'name': put (name (myself)) schreibt den Namen der Task, in der man sich gerade befindet, auf den Bild- schirm. Durch die Prozedur 'father', die einen internen Task-Bezeichner als Parameter erwartet, kann man an die Vater-Task einer Task gelangen. Beispiel: save ("datei", father (father)) Hier wird die Datei 'datei' an die Vater-Task der Vater-Task geschickt (also zur "Großvater-Task"). Mit dem Operator / kann man aus einem Tasknamen den internen Tasknamen erhalten. '/' kann über- all dort eingesetzt werden, wo ein interner Taskname verlangt wird. Beispiel: task status (/"meine task") Merke: Mit den internen Task-Bezeichner kann man mit anderen Tasks als der Vater-Task kommunizieren. Kommandos für mehrere Dateien Durch die vom EUMEL-System bereitgestellten Task-Variablen und Datei-Ver- zeichnisse (Thesaurus, Plural: Thesauren) ist es möglich, mehrere Dateien mit einem Kommando zu behandeln. Sollen alle Dateien einer Task zu einer anderen transportiert werden (mit 'save') oder alle Dateien geholt werden, kann man 'save all' und 'fetch all' auch mit einem internen Task-Bezeichner versehen. Beispiele: save all (public) fetch all (public) Damit werden alle Dateien der Benutzer-Task zu der Task 'PUBLIC' geschickt oder von dort geholt. Die internen Tasknamen wie z.B. 'myself', 'father', 'public' usw. werden auch bei dem Einsatz eines Thesaurus benutzt. Was ist ein "Thesaurus"? Das EUMEL-System hält sich die Dateinamen einer Task des Taskbaums in einer internen Liste. Die Namens-Liste wird Thesaurus (laut Duden: ein "systematisch geordnetes Verzeichnis") genannt. Der Operator ALL liefert den Thesaurus einer Task. Beispiel: ALL father ALL myself liefert jeweils den Thesaurus der Vater- oder der eigenen Task. Solche Thesauren kann man als Parameter der meisten der oben erwähnten Kommandos verwenden: save (ALL myself) (* kopiert alle Dateien in die Vater-Task; arbeitet wie 'save all' *) forget (ALL myself) (* loescht alle Dateien der Benutzer-Task *) fetch (ALL father) (* holt alle Dateien der Vater-Task; arbeitet wie 'fetch all' *) Der Operator SOME dagegen bietet einen Thesaurus vorher zum Editieren an, um Dateinamen zu streichen (HOP RUBOUT). Beispiele: fetch (SOME father) (* holt die nicht gestrichenen Dateien *) save (SOME myself) (* kopiert die nicht gestrichenen Dateien *) forget (SOME myself) (* loescht die nicht gestrichenen Dateien *) Es ist auch möglich, aus den Thesauren mehrerer Tasks einen neuen Thesaurus zu bilden: - (* Differenzmenge *) / (* Schnittmenge *) + (* Vereinigungsmenge *) Beispiel: fetch (ALL father - ALL myself) holt alle Dateien der Vater-Task, die nicht bereits in der Benutzer-Task sind. Merke: Mit den Operatoren 'ALL' und 'SOME' kann man mehrere Dateien mit einem Kommando behandeln. Datei-Schutz durch Paßworte und Verschlüsselung Dateien können vor unbefugtem Zugriff mit Hilfe von Paßworten und/oder der Verschlüsselung der Informationen geschützt werden. Paßworte Paßworte für Dateien dienen zur Verhinderung von unbefugtem Zugriff auf Dateien, die bei einer Vater-Task gespeichert werden. (Die Dateien der Benutzer-Task können durch ein Task-Paßwort geschützt werden). Dabei sollte man bedenken, daß Paßworte bekannt werden können. Der Paßwort-Schutz ist im EUMEL-System etwas sicherer als in anderen Betriebssystemen, weil Paßworte nur selten angegeben werden müssen und daher nicht so leicht bekannt werden können (z.B. durch "über die Schulter schauen"). Die Kommandozeile wird zudem bei der Angabe eines Paßworts nach dem Betätigen der RETURN-Taste gelöscht. Paßworte werden nur im Verkehr mit Vater-Tasks eingesetzt. Lokale Benutzer- Dateien brauchen nicht gesondert gesichert zu werden, da man ein Paßwort auf die Benutzer-Task legen kann oder die zu sichernden Dateien aus dem System nehmen kann. Ein Paßwort kann mit der Prozedur enter password ("schreibpasswort / lesepasswort") angegeben oder ein bereits eingestelltes Paßwort kann mit dieser Prozedur überschrieben werden. Voreingestellt ist kein Paßwort. Nachdem ein Benutzer die 'enter password'-Prozedur angegeben hat, wird jeder Datei-Verkehr mit einer Vater-Task mit Hilfe dieses Paßworts überprüft. Unzulässige Zugriffe durch Benutzer anderer Tasks auf Dateien einer Vater-Task werden abgewiesen. Ein Paßwort hat eine eigene Syntax. Es besteht aus zwei Teilen, die leer sein können und die durch ein "/"-Zeichen voneinander getrennt sind. Der erste Teil ist das Schreib-Paßwort, der zweite das Lese-Paßwort. Wird kein Lese-Paßwort angegeben, gilt das Schreib-Paßwort auch für das Lesen. Ist also kein "/"-Zeichen im Paßwort-String vorhanden, so wird das Schreib- Paßwort sozusagen gedoppelt. Beispiele: enter password ("") (* kein Paßwort *) enter password ("hugo") (* 'hugo' gilt fuer das Schreiben und Lesen *) enter password ("egon/meier") (* 'egon' fuers Schreiben, 'meier' fuers Lesen *) Zusätzlich kann das "-"-Zeichen in dem Teil des Paßworts angegeben werden, für den ein Zugriff nicht erlaubt sein soll. Beispiel: enter password ("-/nurlesen") (* Schreibzugriff nicht erlaubt, Lese-Passwort ist 'nurlesen' *) Das Lese-Paßwort gilt für die Prozedur fetch während das Schreib-Paßwort für die Prozeduren save erase gilt. Sofern die betreffende Datei durch ein Paßwort geschützt ist, ist folgendes zu beachten: a) fetch: Will ein Benutzer mit der Prozedur 'fetch' eine Datei in seine Benutzer- Task holen, so wird sein aktuell eingestelltes Paßwort (nur Lese-Teil) und das Paßwort der Datei überprüft. Stimmen diese nicht überein, so wird der 'fetch'-Zugriff auf eine Datei der Vater-Task abgewiesen. b) save: Bei einem Transport in eine Vater-Task mit Hilfe der Prozedur 'save' sind zwei Fälle zu unterscheiden: - Datei ist in der Vater-Task noch nicht vorhanden: Diese Datei wird in der Vater-Task mit dem aktuellen Paßwort des Benutzers einge- tragen. - Datei ist in der Vater-Task bereits vorhanden und soll durch die neue Datei ersetzt werden: Es wird überprüft, ob das aktuelle Schreib-Paßwort des Benutzers mit dem der gleichnamigen Datei in der Vater-Task übereinstimmt. Ist dies nicht der Fall, wird die Datei nicht in der Vater-Task eingetragen. c) erase: Soll eine Datei der Vater-Task gelöscht werden, wird überprüft, ob das aktuelle Schreib-Paßwort mit dem der zu löschenden Datei in der Vater- Task übereinstimmt. Ist dies nicht der Fall, wird die Lösch-Operation abgewiesen. Merke: Dateien können bei der Sicherung in einer Vater-Task vor unbefugtem Zugriff durch Paßworte geschützt werden. Dateien verschlüsseln Zusätzlich zu einem Paßwort kann man eine Datei vor unbefugtem Zugriff schützen, indem sie verschlüsselt wird. Mit den Prozeduren crypt decrypt kann eine Datei ver- oder entschlüsselt werden (Datenschutz für einzelne Dateien). Dabei ist es möglich, eine Datei mehrfach zu verschlüsseln (man muß die Verschlüsselung dann allerdings auch mehrfach rückgängig machen). Beispiel: crypt ("datei", "schluessel") verschlüsselt 'datei' mit 'schluessel'. Die Entschlüsselung erfolgt, indem man die Prozedur 'decrypt' auf 'datei' anwendet und den gleichen Schlüssel zur Entschlüsselung benutzt. Beispiel: decrypt ("datei", "schluessel") Den Text des Schlüssels (hier: 'schluessel') sollte man sich also unbedingt merken, sonst kann die Datei nicht mehr entschluesselt werden. Merke: Dateien werden durch 'crypt' und 'decrypt' ver- und entschlüsselt. 3. Das Archiv Dateien werden im EUMEL-System auf Systemträgern gespeichert. Mit dem Archiv können Dateien vom System auf externe Speichermedien (normalerweise Disketten) gebracht und von diesen ins System geholt werden. Das EUMEL-Archiv hat folgende Aufgaben: a) Das Archiv wird als Sicherheitsbereich benutzt, indem man von wichtigen Dateien Kopien anlegt und diese außerhalb des EUMEL-Systems speichert. b) Man kann das Archiv-System aber auch benutzen, um Dateien, die nur selten oder in größeren Zeitabständen benötigt werden, zu kopieren ("archi- vieren") und im EUMEL-System zu löschen. Falls diese Dateien wieder gebraucht werden, können sie wieder in das EUMEL-System geholt werden. Damit wird erreicht, daß im EUMEL-System nur die wirklich notwendigen Dateien stehen. c) Sollen Dateien von einer EUMEL-Installation auf eine andere übertragen werden, so kann man ebenfalls Archive verwenden. d) Bei Versionswechsel des EUMEL-Systems sind Archive die einzige Möglich- keit, Dateien in die neue Systemversion einzuspielen. Dabei wird garan- tiert, daß zumindest Archive der letzten Version gelesen werden können. Merke: Durch das Archiv können Dateien außerhalb des Systems gesichert werden. Das Archiv reservieren Soll archiviert werden, muß man das dem Archiv-System mitteilen, damit nicht ein anderer Benutzer zur gleichen Zeit auf das Archiv zugreift. Dieser Vorgang wird "reservieren" genannt. Archiv-Disketten haben aus Sicherheitsgründen einen Namen. Dieser Name muß bei der Anmeldung einer Archivierung angegeben werden. Die Anmeldung einer Archivierung und gleichzeitige Reservierung erfolgt mit archive ("name") Der Archiv-Name wird bei folgenden Archiv-Operationen zur Überprüfung mit dem eingelegten Archiv verwandt. Wichtig: Eine Diskette sollte erst nach dem 'archive'-Kommando in das Laufwerk geschoben werden, d.h. erst dann, wenn das Archiv-System reserviert ist. Sonst kann es geschehen, daß ein anderer Benutzer auf dieser Diskette archiviert. Archivierungen erfolgen durch die schon beschriebenen Transportkommandos 'save' und 'fetch'. Dabei wird als Zieltask 'archive' angegeben. Beispiel: save ("mein buch", archive) fetch ("kapitel 1", archive) save all (archive) fetch all (archive) Das Archiv bleibt für den Nutzer solange reserviert, wie er Archivierungs- operationen vollzieht. Dann sollte er das Archiv wieder für andere Benutzer mit der Prozedur release (archive) freigeben. Leitet ein Nutzer innerhalb von fünf Minuten nach der letzten Archiv- Operation keine neue ein und meldet sich ein anderer Nutzer mit 'archive' bei dem Archiv-System an, so wird dem ersten Nutzer die Archiv-Berechtigung entzogen, um Blockaden zu verhindern. Wenn der erste Nutzer eine erneute Archiv-Operation versucht, erhält er eine Fehlermeldung. Dadurch kann ein Nutzer das Archiv längere Zeit (ohne 'release') benutzen, ohne den Betrieb zu stören, da andere Nutzer bei Bedarf jederzeit eingeschoben werden. Wichtig: Muß die Archiv-Floppy gewechselt werden (weil etwa von einer Diskette eine Datei gelesen wurde und diese auf eine andere geschrieben werden soll), muß erneut das 'archive'-Kommando gegeben werden. Dies ist notwendig, um das Verzeichnis aller auf der Diskette befindlichen Dateien der neuen Diskette zu lesen. Merke: Mit 'archive' wird die Archiv-Verwaltung für einen Nutzer reserviert, mit 'release' wieder freigegeben. Mit 'archive' wird gleichzeitig ein Archiv-Name eingestellt, der bei Archiv-Operationen mit dem auf der Diskette gespeicherten Archiv-Namen verglichen wird. Archiv löschen und einen Namen geben Bevor ein Archiv-Diskette benutzt werden kann, muß sie den mit 'archive' eingestellten Namen bekommen. Bei der Erstbenutzung eines Archivs muß dieses mit einem Namen versehen werden. Als Archiv-Name wird der mit 'archive' eingestellte Name verwandt. Dies erfolgt mit der Prozedur clear (archive) Gleichzeitig werden alle Dateien, die sich eventuell vorher auf dem Archiv- Träger befanden, gelöscht. Somit wird 'clear' auch für das Löschen von Archiven verwendet. Merke: Mit 'clear' wird die Archiv-Diskette gelöscht und gleichzeitig der mit 'archive' eingestellte Name gegeben. Einfache Archiv-Operationen In diesem Abschnitt werden einfache Archiv-Operationen beschrieben. Mit den Prozeduren save ("datei", archive) fetch ("datei", archive) erase ("datei", archive) kann jeweils eine Datei auf die Archiv-Diskette ('save') und von dem Archiv in das EUMEL-System kopiert ('fetch') oder auf dem Archiv gelöscht ('erase') werden. Dabei bedeutet 'datei' die zu kopierende Datei und 'archive' der interne Task-Name für die Archiv-Verwaltung. Bei den ersten zwei Kommandos ist zu beachten, daß die Datei, die auf das Archiv geschrieben (bei 'save') oder die in die Benutzer-Task geholt werden soll (bei 'fetch'), immer kopiert wird. Dateien bleiben also im "Ursprung" immer erhalten. Ist eine Datei gleichen Namens bereits auf der Archiv-Diskette (bei 'save') oder in der Benutzer-Task (bei 'fetch') vorhanden, wird von der Archiv-Ver- waltung angefragt, ob diese Datei überschrieben werden darf. Es kann somit nicht zu einem unbeabsichtigten Löschen von Dateien kommen. Mit dem Kommando list (archive) erhält man (wie bei dem "normalen" 'list'-Kommando) ein Namens-Verzeichnis aller Dateien, die sich auf der eingelegten Archiv-Diskette befinden. Merke: Nachdem ein Archiv mit 'archive' reserviert wurde, kann mit 'save' eine Datei auf das Archiv geschrieben und mit 'fetch' eine Datei von dem Archiv in die Benutzer-Task kopiert werden. Archiv-Operationen für mehrere Dateien Hier wird beschrieben, wie man mehrere Dateien auf einmal auf ein Archiv schreibt oder von einem Archiv liest. Mit den Kommandos save all (archive) fetch all (archive) werden alle Dateien der Benutzer-Task auf das Archiv geschrieben bzw. von dort geholt. Zusätzlich ist es möglich, die Operatoren 'ALL' und 'SOME' auf ein Archiv anzuwenden. Wie bereits geschildert, liefern diese Operatoren einen Thesaurus der angegebenen Task: 'ALL' liefert alle Dateinamen, während man bei 'SOME' eine Auswahl treffen kann. Damit ist es möglich, alle oder einige Dateien der Benutzer-Task auf eine Archiv-Diskette zu kopieren oder von der Archiv-Diskette in die Benutzer-Task zu holen: fetch (SOME archive, archive) (* zeigt die Namen der Dateien der Archiv-Diskette. Die nicht ge- strichenen Dateien werden in die Benutzer-Task geholt *) save (SOME myself, archive) (* zeigt die Namen der Dateien der Benutzer-Task. Die nicht ge- strichenen Dateien werden auf die Archiv-Diskette kopiert *) save (ALL archive, archive) (* sichert alle Dateien der Benutzer- Task, die sich schon auf dem Archiv befinden (alte Version). Das Kommando ist für Sicherungs- Disketten gedacht, bei dem man immer die gleichen Dateien auf dem Archiv haben will *) Durch die Thesaurus-Operatoren '-' (Differenzmenge), '/' (Schnittmenge) und '+' (Vereinigungsmenge) kann man kompliziertere Wirkungen erzielen. Beispiele: save (ALL myself - ALL archive, archive) (* Schreibt alle Dateien der Benutzer-Task auf das Archiv, die nicht bereits auf dem Archiv stehen *) save (ALL myself - ALL father - ALL archive, archive) (* Schreibt alle Dateien der Benutzer-Task auf das Archiv, mit Ausnahme der Dateien, die längerfristig in der Vater- Task gespeichert sind und die nicht bereits auf dem Archiv stehen *) fetch (ALL archive - ALL myself) (* Holt alle Dateien vom Archiv, die sich nicht bereits in der Benutzer-Task befinden *) Werden mehrere Dateien mittels eines Thesaurus bearbeitet, kann man nach einer Unterbrechung der Bearbeitung (d.h. Fehlermeldung) die Operation wieder aufsetzen. Dazu liefert das Kommando remainder den "Rest"-Thesaurus. Er enthält alle nicht bearbeiteten Dateinamen. Beispiel: save (SOME myself, archive) (* Unterbrechung, z.B. durch einen vom Programm erzeugten 'errorstop' oder SV und 'halt' *) save (remainder, archive) (* bearbeitet die restlichen Dateien *) Merke: Mit 'save all' bzw. 'fetch all' kann man alle Dateien einer Benutzer- Task archivieren bzw. alle Dateien eines Archivs in die Benutzer-Task holen. Mit den Operatoren SOME und ALL sind weitere Operationen möglich. Fehlermeldungen des Archivs Bei Archiv-Operationen kann es zu Fehlersituationen kommen. Versucht man eine Datei vom Archiv zu lesen, kann es vorkommen, daß das Archiv-System Lese-Fehler (Archiv) meldet und den Lese-Vorgang abbricht. Dies kann auftreten, wenn die Floppy beschädigt oder aus anderen Gründen nicht lesbar ist (z.B. nicht justierte Disketten-Geräte). In einem solchen Fall vermerkt das Archiv-System intern, daß die Datei nicht korrekt gelesen werden kann. Das sieht man z.B. beim 'list (archive)'. Dort ist der betreffende Datei-Name mit dem Zusatz 'mit Lese-Fehler' gekennzeichnet. Um diese Datei trotzdem zu lesen, muß man sie im Datei-Namen mit dem Zusatz 'mit Lese-Fehler' versehen. Beispiel: fetch ("dateiname mit Lese-Fehler") Die Datei wird in diesem Fall trotz Lese-Fehler (Informationsverlust!) vom Archiv gelesen. Um solche Fälle möglichst zu vermeiden, sieht das EUMEL-System die Möglichkeit vor, Archive bzw. Archiv-Dateien nach beschreiben zu prüfen. Das erfolgt mit dem Kommando check ("dateiname", archive) (* oder *) check (ALL archive, archive) (* fuer alle Archiv-Dateien *) Durch dieses Kommando werden eventuelle Lese-Fehler gemeldet. Weitere Fehlermeldungen des Archivs: * Lesen unmöglich (Archiv) Archiv-Floppy nicht eingelegt oder die Tür des Laufwerks ist nicht geschlossen. * Schreiben unmöglich (Archiv) Floppy ist schreibgeschützt. * Archiv nicht angemeldet Archiv wurde nicht angemeldet ('archive ("name")' geben). * Lese-Fehler (Archiv) Siehe obige Beschreibung. * Schreibfehler (Archiv) Die Floppy kann nicht (mehr) beschrieben werden. Andere Floppy verwenden. * Speicherengpass Im System ist nicht mehr genügend Platz, um eine Datei vom Archiv zu laden. Ggf. Dateien löschen. * RERUN beim Archiv-Zugriff Das System wurde bei einer Archiv-Operation durch Ausschalten bzw. Reset unterbrochen. * ... gibt es nicht Die Datei ... gibt es nicht auf dem Archiv. * Archiv heißt ... Die eingelegte Floppy hat einen anderen als den eingestellten Archiv-Namen. * Archiv wird von Task ... benutzt Das Archiv wurde von einem anderen Benutzer reserviert. * ... kann nicht geschrieben werden (Archive voll) Das Archiv ist voll. Neue Archiv-Floppy benutzen. * Archiv inkonsistent Die eingelegte Floppy hat nicht die Struk- tur einer Archiv-Floppy ('clear (archive)' vergessen). * save/erase wegen Lese-Fehler verboten Bei Archiven mit Lese-Fehler sind Schreiboperationen verboten, weil ein Erfolg nicht garantiert werden kann. 4. FILEs in Programmen Die bisher geschilderten Kommandos gelten für alle Datei-Arten. In diesem Kapitel wird der Standard Datei-Typ FILE beschrieben. Eine Datei von Daten- typ FILE ist eine sequentielle Datei, die das Lesen und Schreiben von Daten in strikter Aufeinanderfolge von Sätzen erlaubt (Betriebsrichtungen 'input' und 'output'). Die Betriebsrichtung 'modify' erlaubt das gleichzeitige Lesen und Schreiben sowie beliebiges Positionieren. Deklaration, Assoziierung und Betriebsrichtungen Für ELAN-Programme gibt es standardmäßig den Datentyp FILE. FILEs müssen deklariert werden. Die Kopplung einer FILE VAR im Programm mit einer Datei erfolgt durch eine Assoziierungsprozedur. Bei der Assoziierung muß man an- geben, wie die Datei bearbeitet werden soll. Dateien müssen in einem ELAN-Programm - wie alle anderen Objekte auch - deklariert werden. Beispiel: FILE VAR f :: ... Mit der Deklaration wird dem ELAN-Compiler der Name der Datei-Variablen bekannt gemacht. Dabei ist zu beachten, daß im EUMEL-System alle FILEs mit VAR deklariert werden müssen, denn jede Lese/Schreib-Operation verändert einen FILE. FILE CONST Objekte sind also nicht erlaubt. Ähnlich wie bei anderen Datenobjekten werden FILEs initialisiert. In der Regel erfolgt dies mit einer Assoziierungsprozedur. Beispiele: FILE VAR meine datei :: sequential file (output, "daten") ... (* oder: *) TEXT VAR datei name; put ("Dateiname bitte:"); get (datei name); FILE VAR f :: sequential file (input, datei name); Die Assoziierungsprozedur 'sequential file' hat die Aufgabe, eine in einem Programm deklarierte FILE VAR mit einer bereits vorhandenen oder noch einzu- richtenden Datei (abhängig von der Betriebsrichtung, siehe unten) des EUMEL-Systems zu koppeln. Den Dateinamen gibt man als zweiten Parameter an. Dadurch können ELAN-Programme geschrieben werden, die Dateien bearbeiten, deren Namen man beim Erstellen des Programms noch nicht kennt. Der erste Parameter gibt die sog. Betriebsrichtung an. Sie bestimmt, in welcher Weise die assoziierte Datei bearbeitet wird. Es gibt folgende drei Betriebsarten: a) input: Die Datei kann vom Programm nur gelesen werden. Durch 'input' wird bei der Assoziierung automatisch auf den ersten Satz der Datei positioniert. Ist die zu lesende Datei nicht vorhanden, wird ein Fehler gemeldet. b) output: Die Datei kann vom Programm nur beschrieben werden. Durch 'output' wird bei der Assoziierung automatisch hinter den letzten Satz der Datei positioniert (bei einer leeren Datei also auf den ersten Satz). Ist die Datei vor der Assoziierung nicht vorhanden, wird sie automatisch einge- richtet. c) modify: Die Datei kann vom Programm in beliebiger Weise gelesen und beschrieben werden. Im Gegensatz zu den Betriebsrichtungen 'input' und 'output', bei denen ausschließlich ein rein sequentielles Lesen oder Schreiben erlaubt ist, kann bei 'modify' beliebig positioniert, gelöscht, eingefügt und neu geschrieben werden. Hier ist nicht definiert, auf welchen Satz der Datei man nach erfolgter Assoziierung steht. Die Datei wird automatisch einge- richtet, wenn sie vor der Assoziierung nicht vorhanden war. Anmerkung: In jeder Betriebsrichtung sind nur bestimmte Operationen zugelassen. Z.B. sind bei 'input' keine Schreib-Prozeduren erlaubt. Vergl. dazu die nächsten Abschnitte. Neben der Betriebsrichtung 'input', 'output' oder 'modify' muß bei 'sequential file' als zweiter Parameter der Name der zu bearbeitenden Datei angegeben werden. Beispiel: TEXT VAR datei name, zeile; put ("Bitte Name der zu bearbeitenden Datei eingeben:"); get (datei name); FILE VAR f :: sequential file (input, dateiname); ... getline (f, zeile); ... Hier wird der Name der zu bearbeitenden Datei zuerst eingelesen. Diese Datei wird mit 'f' in der Betriebsrichtung 'input' assoziiert, d.h. würde man z.B. die Prozedur putline (f, irgendein text) an einer Stelle im Programm verwenden, wird ein Fehler gemeldet. Die Be- triebsrichtung hilft also, Fehler bei der Programmierung zu vermeiden. Mit den Prozeduren modify (f) input (f) output (f) kann die Betriebsrichtung von Dateien geändert werden. Dateien brauchen im EUMEL-System vor Programmende nicht geschlossen zu werden, da sie vom System immer konsistent gehalten werden. Merke: Die Betriebsrichtung gibt an, wie man eine Datei bearbeiten will. 'input' steht für Lesen, 'output' für Schreiben und 'modify' für Verändern. Jede Datei muß vor einer Benutzung mit 'sequential file' assoziiert werden. Die Assoziierungsprozedur stellt die Kopplung zwischen dem Programm und dem EUMEL-Betriebssystem her. Operationen der Betriebsrichtung 'output' In der Betriebsrichtung 'output' sind nur Schreib-Operationen gestattet. 'output' ist also für das Bearbeiten von Ausgabedateien vorgesehen. Mit den Prozeduren put können INT-, REAL- und TEXT-Werte in eine Datei geschrieben werden. Die Prozedur line sorgt dafür, daß eine neue Zeile in der Datei begonnen wird. Beispiel: FILE VAR f :: sequential file (output, "daten"); put (f, "Daten:"); line (f); schreibe daten. schreibe daten: INT VAR intwert; REAL VAR textwert; put ("bitte Daten eingeben (INT, REAL):"); REP get (intwert); put (f, intwert); get (realwert); put (f, realwert); line (f); UNTIL yes ("fertig") END REP. Durch die Assoziierungsprozedur mit der Betriebsrichtung 'output' wird hinter den letzten Satz der Datei positioniert, bei einer leeren Datei also auf den ersten Satz. In dem ersten Satz in unserem Beispiel wird also 'Daten:' geschrieben. Durch die Prozedur 'line' geht man auf den nächsten Satz. Dann werden jeweils ein INT- und ein REAL-Wert in eine Datei-Zeile geschrieben, bis die Frage 'fertig' mit 'j' beantwortet wird. Die 'put'-Prozeduren schreiben die Werte jeweils in die aktuelle Datei-Zeile. Zwischen den Werten wird von den 'put'-Prozeduren jeweils ein Leerzeichen eingefügt. Im Gegensatz zu den 'put'-Prozeduren schreibt die Prozedur 'write' einen TEXT ohne ein anschließendes Leerzeichen in die aktuelle Datei-Zeile. Beispiel: ... write (f, "meine Daten:"); write (f, " "); write (f, text (17 + 4)); (* das Gleiche wie: put (f, "meine Daten:"); put (f, 17 + 4) *) Auf eine neue Datei-Zeile kann auf zwei verschiedene Arten positioniert werden: a) Prozedur 'line': Diese darf auch mit einem Parameter angegeben werden, der die Anzahl Zeilen angibt. Beispiel: line (f); (* positioniert auf die naechste Zeile *) line (f, 1) (* das Gleiche *) line (f, 4) (* Vier Zeilen weiter; dazwischen sind 3 Leerzeilen *) b) Überschreiten der Zeilengrenze: Eine Datei-Zeile kann (voreingestellt) 77 Zeichen aufnehmen. Wird bei einer Schreib-Operation diese Grenze überschritten, wird der auszugebende Wert auf die nächste Zeile plaziert. Mit der Prozedur 'max line length' kann die eingestellte Datei-Zeilenlänge gelesen oder verändert werden. Beispiel: FILE VAR f :: sequential file (output, "meine daten"); put (max line length (f)); (* ergibt 77 *) max line length (f, 132); put (max line length (f)) (* ergibt 132 *) Ist die Länge des auszugebenden Textes größer als die verbleibende Zeilenbreite, wird der Text auf der nächsten Zeile ausgegeben. Die 'putline'-Prozedur bietet die Möglichkeit, eine ganze Datei-Zeile auf einmal auszugeben. Eine Positionierung auf die nächste Datei-Zeile ('line') braucht dabei nicht vorgenommen werden. Beispiel: TEXT VAR zeile :: ""; ... zeile := ...; putline (f, zeile); ... Merke: In der Betriebsrichtung 'output' kann in eine Datei geschrieben werden. Dazu stehen die Prozeduren 'put', 'write' und 'putline' zur Verfü- gung. Die Prozedur 'line' positioniert auf die nächste Zeile der Ausgabe- datei. Mit 'max line length' kann die Länge einer Datei-Zeile verändert oder erfragt werden. Operationen der Betriebsrichtung 'input' In der Betriebsrichtung 'input' sind nur Lese-Operationen gestattet. 'input' ist also für das Bearbeiten von Eingabedateien vorgesehen. Analog der Betriebsrichtung 'output' sind bei 'input' 'get'-Prozeduren vor- handen, die INT-, REAL- oder TEXT-Werte aus einer Datei lesen. Beispiel: FILE VAR f :: sequential file (input, "Betriebszeiten"); REAL VAR zeiten; zeiten einlesen und berechnen. zeiten einlesen und berechnen: REP get (f, zeiten); IF zeiten = 0.0 THEN LEAVE zeiten einlesen und berechnen FI; (* siehe auch 'eof'-Prozedur *) berechne END REP. berechne: ... Die 'get'-Prozeduren positionieren automatisch auf die nächste Zeile, sofern keine Werte mehr in der aktuellen Zeile vorhanden sind. Mit der Prozedur 'line' kann explizit auf die nächste Zeile positioniert werden. Damit können die restlichen Daten einer Zeile überschlagen werden. Für die 'get'-Proze- duren gilt, daß jeder zu lesende Wert entweder beim nächsten Leerzeichen oder beim Zeilenende aufhört. Beispiel: FILE VAR f :: sequential file (input, "text"); TEXT VAR wort; lese worte. lese worte: REP get (f, wort); (* Lesen eines Worts ohne Leerzeichen *) put (wort); (* Schreiben eines Worts mit Leerzeichen *) IF wort = "Ende" THEN LEAVE lese worte FI END REP. Trennzeichen ("separator") zwischen den Worten sind also Leerzeichen, welche nicht eingelesen werden. Manchmal sollen jedoch Daten eingelesen werden, die durch andere Zeichen als dem Leerzeichen voneinander getrennt sind. Eine Möglichkeit, solche Daten zu behandeln, bietet die 'getline'-Prozedur. Sie liest (analog 'putline') eine ganze Zeile und positioniert auf die nächste Zeile. Dann kann man mit Hilfe von TEXT-Prozeduren solche Zeilen 'per Hand' auseinandernehmen. Als Beispiel zeigen wir ein Programm, welches den zweiten Wert einer Zeile lesen soll. Die Werte werden durch Kommata voneinander getrennt: FILE VAR eingabe datei :: sequential file (input, "daten"); TEXT VAR zeile, wert; lese jeweils zweiten wert; verarbeite wert. lese jeweils zweiten wert: REP getline (f, zeile); IF zeile = "Ende" THEN LEAVE lese jeweils zweiten wert FI; extrahiere zweiten wert END REP. extrahiere zweiten wert: wert := subtext (zeile, anfang, ende). anfang: pos (zeile, ",") + 1. ende: pos (zeile, ",", anfang) + 1. verarbeite wert: ... Diese (etwas umständliche) Methode ist immer dann angebracht, wenn Zeilen unterschiedlich untersucht werden müssen. Eine einfachere Möglichkeit, die in vielen Fällen angewandt werden kann, bietet eine andere Form der 'get'- Prozedur, bei der man das oder die Trennzeichen angeben kann. Beispiel: ... extrahiere zweiten wert: get (f, wert, ","); (* ersten Wert der Zeile ueberlesen *) get (f, wert, ","). (* hier der richtige zweite Wert *) Hier wird also das Trennzeichen mit angegeben (dritter Parameter). Eine andere Methode müssen wir anwenden, wenn Daten nicht durch ein Trennzeichen unterschieden werden, sondern nur durch ihre Länge definiert sind. Beispiel: ... lese fuenfstellige werte: REP get (f, wort, 5); verarbeite wert ... END REP. Bei dieser 'get'-Prozedur wird die Länge des einzulesenden Textes als dritter Parameter angegeben. Man beachte, daß die letzten zwei 'get'-Proze- duren nur TEXTe einlesen. Entsprechende Typwandlungen hat der Programmierer vorzunehmen. Merke: Die Betriebsrichtung 'input' erlaubt nur Lesen aus einer Eingabedatei. Für diesen Zweck gibt es die Prozeduren 'get', 'getline' und 'line'. Operationen der Betriebsrichtung 'modify' Die Betriebsrichtung 'modify' erlaubt das Lesen und Schreiben von Informa- tionen auf Dateien. Zusätzlich ist beliebiges Positionieren auf Dateien erlaubt. 'modify' ist also für Dateien gedacht, die man gleichzeitig als Ausgabe- und Eingabedateien behandeln will. Die Betriebsrichtung 'modify' erlaubt ein Ändern einer Datei ("updating"), wobei die sequentielle Natur der Datei erhalten bleibt. Eine Datei der Betriebsrichtung 'modify' muß ebenso mit 'sequential file' assoziiert werden, wie bei den zwei anderen Betriebsrichtungen. Während bei 'input' auf den ersten bzw. bei 'output' auf den letzten Satz der Datei positioniert wird, ist bei 'modify' nicht definiert, auf welchem Satz der Datei nach erfolgter Assoziierung positioniert wird. Man muß also die erste Positionierung explizit vornehmen. Für die Zwecke der Positionierung gibt es die Prozeduren to line (* auf eine bestimmte Zeile *) col (* auf eine Spalte innerhalb der Zeile *) down (* eine Zeile vorwaerts *) up (* eine Zeile zurueck *) Neben diesen Positionierungsprozeduren gibt es die Informationsprozeduren: lines (* Anzahl Zeilen in der Datei *) line no (* aktuelle Zeilennummer *) eof (* Dateiende? *) Beispiele: down (f); (* wie: to line (f, line no (f) + 1) *) (* Nicht über eof *) up (f); (* wie: to line (f, line no (f) - 1) *) (* Nicht über Zeile 1 *) Mit der Prozedur read record kann der Satz, auf den aktuell positioniert wurde, gelesen werden. Mit write record kann sein Inhalt geschrieben werden (also auch "überschreiben"). Mit den Prozeduren insert record delete record kann eine Zeile vor der aktuellen eingefügt (Position ist dann die einge- fügte Zeile) oder der aktuelle Satz gelöscht werden (Position ist dann der nächste Satz). Beispiele: FILE VAR f :: sequential file (modify, "meine daten") TEXT VAR zeile, neue zeile; to line (f, 1); read record (f, zeile); (* erste Zeile lesen *) ... insert record (f); (* neue erste Zeile *) write record (f, neue zeile); down (f); (* auf die 2. Zeile, die vorher die 1. war *) delete record (f); (* diese loeschen, so dass die Zeilenzahl wieder stimmt *) Das nächste Beispiel zeigt, wie hinter den letzten Satz einer Datei eine Zeile eingefügt werden kann (hier wird ausgenutzt, daß man in der Betriebs- richtung 'modify' hinter den letzten Satz der Datei positioniert werden kann): FILE VAR f :: sequential file (modify, "test"); to line (f, lines (f)); (* auf die letzte Zeile *) down (f); insert record (f); write record (f, "neue letzte Zeile"); ... Mit 'down' bzw. 'up' kann man auch um einige Zeilen auf einmal vorwärts oder rückwärts positionieren. Beispiele: down (f, 17) (* 17 Zeilen vorwaerts *) up (f, 13) (* 13 zeilen rueckwaerts *) Merke: In der Betriebsrichtung 'modify' können Dateien gelesen und/oder ge- schrieben werden ('read record' oder 'write record'). Positionierungen kön- nen mit 'down', 'up' oder 'to line' vorgenommen werden. Manipulationen von FILEs FILEs können im EUMEL-System auch als Einheiten behandelt werden. Dazu stehen die bereits erläuterten Prozeduren zur Verfügung, die wir hier der Vollständigkeit halber nochmals aufführen. Mit der Prozedur exists kann erfragt werden, ob eine Datei bereits existiert. Beispiel: TEXT VAR name; REP erfrage name; UNTIL exists (name) END REP; ... erfrage name: put ("Dateiname bitte:"); get (name); line. Weitere Prozeduren: forget (* Datei löschen *) rename (* umbenennen *) copy (* kopieren *) Für Programmierer ist eine Version der 'forget'-Prozedur interessant, die eine Datei ohne Kontroll-Anfrage löscht. Beispiel: forget ("meine scratch datei", quiet) Merke: Mit 'exists' kann erfragt werden, ob ein FILE bereits existiert. Texte Suchen Mit den Prozeduren 'down' und 'up' kann man (ebenso wie im Editor) auch nach Texten suchen. Die Prozeduren 'down' bzw. 'up' suchen einen Text in der Datei. Beispiele: down (f, "dieser text") up (f, "noch'n text") Diese Prozeduren suchen direkt auf der Dateistruktur. Wird der gesuchte Text gefunden, steht man direkt auf dem gesuchten Text. Wird der Text nicht gefunden, steht man auf dem ersten (bei 'up') oder hinter dem letzten (bei 'down') Zeichen der Datei. Die Position innerhalb der Zeile nach einer Suche kann mit col (f) abgefragt werden. Um die Suche auf einen Bereich zu beschränken, kann man 'down' bzw. 'up' mit einem weiterem Parameter versehen, der die max. Anzahl von Zeilen angibt. Beispiel: FILE VAR f :: ... ... INT VAR akt zeilennr :: line no (f); down (f, "pattern", 100); (* sucht in den naechsten 100 Zeilen nach 'pattern' *) IF line no (f) <> akt zeilen nr + 100 THEN gefunden ELSE nicht gefunden FI; ... Achtung: 'down' bzw. 'up' beginnen die Suche immer mit dem nächsten Zeichen in Suchrichtung, so daß man mehrmals hintereinander suchen kann, ohne in eine Endlosschleife zu geraten (Erinnerung: wird ein Text gefunden, ist die Position innerhalb der Zeile das erste Zeichen des gesuchten Begriffs). Mit den Prozeduren (gleiche Parameterversorgung wie 'down' und 'up') downety uppety beginnt man mit der Suche immer auf der aktuellen Position. Darum sollte man diese Prozeduren mit Vorsicht verwenden. Mit der Prozedur pattern found kann man anfragen, ob die letzte Suchoperation erfolgreich war oder nicht. Beispiel: FILE VAR f :: ... ... INT VAR akt zeilennr :: line no (f); down (f, "pattern", 100); (* sucht in den naechsten 100 Zeilen nach 'pattern' *) IF pattern found THEN gefunden ELSE nicht gefunden FI; ... Mit der Prozedur at kann man anfragen, ob man auf einem gewünschten Wort steht. Beispiel: IF at (f, "pattern") THEN .. FI Die Prozedur word liefert das aktuelle Wort der aktuellen Position einer Zeile. Beispiele: TEXT VAR dieses wort :: word (f); (* Zeichenkette von der aktuellen Position bis zum nächsten Blank oder Zeilenende *) dieses wort := word (f, "<"); (* Zeichenkette (Wort) von der aktuellen Position bis zum Zeichen '<' oder Zeilenende *) dieses wort := word (f, 13); (* Zeichenkette (Wort) mit der Laenge 13 *) Merke: Die Prozeduren 'down' und 'up' suchen einen Text innerhalb einer Datei. Mit 'at' kann man anfragen, ob man auf dem gesuchten Begriff steht. 'word' liefert das aktuelle Wort. FILE-Ausschnitte Hier wird erklärt, wie man mehrere Zeilen aus einer Datei auf einmal löschen und/oder verschieben und wie man nur einen Ausschnitt einer Datei zugänglich machen kann. Den einfachsten Zugang zu Datei-Abschnitten erhält ein Programmierer durch einige Anwendungsprozeduren, die u.a. auch im Editor verwandt werden. Dort gibt es die Möglichkeit, einen markierten Bereich "vorsichtig" zu löschen und u.U. an anderer Stelle wieder einzufügen (ESC RUBOUT und ESC RUBIN). Solche Prozeduren stehen auch einem Programmierer zur Verfügung. Beispiel: FILE VAR f :: .... .... remove (f, 100); (* entfernt 100 Zeilen von der aktuellen Position rueckwaerts (!) aus der Datei 'f' *) to line (27); (* zum Beispiel *) re insert (f) (* fuegt die "vorsichtig" geloeschten Zeilen vor (!) die Zeile 27 ein *) Die Prozedur remove löscht also eine angebbare Anzahl von Zeilen in der Datei (rückwärts von der aktuellen Zeilennummer ab) und schreibt diesen Datei-Abschnitt in einen internen Puffer. Man beachte, daß sich dabei natürlich die Zeilennummer der Datei ändert. Die entfernten Zeilen können aus dem internen Puffer an einer anderen Stelle durch reinsert genau einmal wieder eingefügt werden. Sollen jedoch die mit 'remove' ent- fernten Zeilen wirklich gelöscht und nicht mehr an anderer Stelle eingesetzt werden, dann kann man die Prozedur clear removed verwenden. Beispiel: ... remove (f, 50); (* loescht vorsichtig *) clear removed (f); (* und endgueltig *) ... Durch solche Löschungen oder Einfügungen entstehen Datei-Segmente. Innerhalb eines Segments kann direkt positioniert werden. Werden jedoch Löschungen oder Einfügungen vorgenommen (Sätze werden ein- oder ausgekettet), muß erst zu einem entsprechenden Segment positioniert und dann innerhalb des Segments auf den entsprechenden Satz positioniert werden. Das kann - je nach Anzahl der Segmente - zeitaufwendig sein. Deshalb existiert die Prozedur segments mit der man feststellen kann, wieviel Datei-Segmente in der Datei existieren. (Sind es "zu viele", kann man die Datei "reorganisieren"). Diese und die folgenden Prozeduren nutzen eine vom EUMEL-System bereitge- stellte Möglichkeit, Ausschnitte aus Dateien wie eigenständige Dateien zu behandeln. Beispiel: FILE VAR f :: ... ... FRANGE VAR alter bereich; set range (f, 200, 1, alter bereich); (* Datei mit 200 Zeilen von der Spalte 1 der aktuellen Zeile *) edit (f); (* Zeilen 1-200 editieren *) set range (f, alter bereich); (* Datei zuruecksetzen *) ... Von dem Beispiel-Programm wird die Datei 'f' bearbeitet. Die FRANGE-Variable dient hier dazu, sich den ursprünglichen Bereich der Datei 'f' (der auch schon eingeschränkt sein kann), zu merken. Mit der Prozedur 'set range' wird die Datei 'f' auf 200 Zeilen eingeschränkt (von der aktuellen Zeile 200 Zeilen rückwärts). Mit der Prozedur 'edit' kann nun der Benutzer unseres Programms die (eingeschränkte) Datei beliebig editieren. Ihm steht am Anfang Zeilen 1 bis 200 zur Verfügung; die "ausgeblendeten" Datei-Teile kann er nicht verändern. Mit dem zweiten Aufruf von 'set range' wird der einge- schränkte (und u.U. veränderte) Datei-Bereich aufgehoben, so daß hier wieder alle ursprünglichen Datei-Zeilen zur Verfügung stehen. Solche Beschränkungen können natürlich mehrmals geschachtelt vorgenommen werden. Um nach Ablauf solcher Programmteile sicher wieder die ursprüngliche Datei (mit allen ihren Zeilen) zur Verfügung zu haben, gibt es die Prozedur reset range (f) Sie setzt die Datei 'f' auf den größtmöglichen Bereich zurück. Merke: Mit 'remove' und 'reinsert' können Zeilen gelöscht und/oder ver- schoben werden. Mit dem Datentyp FRANGE und den Prozeduren 'set range' können Dateien eingeschränkt werden. 5. Datenräume Die bis jetzt behandelten Dateien können nur TEXTe aufnehmen (bei einigen Schreib-/Lese-Operationen werden Daten in Texte umgewandelt, z.B. bei 'get' und 'put'). Damit ist gewährleistet, daß alle Programme im EUMEL-System (Editor, Drucker, Compiler, Benutzer-Programme usw.) auf gleiche Art und Weise auf Dateien zugreifen können, unabhängig davon, welche Daten wirklich gespeichert sind. Der folgende Abschnitt zeigt, wie man mit Dateien umgeht, die nicht vom Standardtyp FILE sind. Konzept des Datenraums Standarddateien (FILEs) können nur Texte aufnehmen, da sie ja hauptsächlich für die Kommunikation mit dem Menschen (vorwiegend mit Hilfe des Editors bzw. Ein-/Ausgabe) gedacht sind. Will man Zahlen in einen FILE ausgeben, so müssen diese zuvor in Texte umgewandelt werden. Hierfür stehen Standard- prozeduren zur Verfügung (z.B. 'put (f, 17)'). Will man aber Dateien zur Kommunikation zwischen Programmen verwenden, die große Zahlenmengen austauschen, verursachen die Umwandlungen von Zahlen in TEXTe und umgekehrt unnötigen Rechenaufwand. Daher wurden in EUMEL� die Datenräume eingef�hrt, die es gestatten, beliebige Strukturen (Typen) in Dateien zu speichern. Solche Datenräume kann man weder mit dem Editor noch mit dem Standarddruckprogramm (print) bearbeiten, da diese ja den Typ des in der Datei gespeicherten Objektes nicht kennen. Einen Datenraum kann man sich als eine Sammlung von Daten vorstellen (u.U. leer), die ausschließlich von einem Programm her behandelt wird. Man kann einem Datenraum durch ein Programm einen Datentyp "aufprägen". Meist handelt es sich um Reihungen, weil die Benutzung von Datenräumen erst bei größeren Datenmengen lohnt. Nach einem solchen "Aufpräge"-Vorgang kann der Datenraum wie ein "normaler" Datentyp behandelt werden, mit dem Unterschied, daß die Daten in einem Datenraum (d.h. Datei) gespeichert werden. Somit können nach- folgende Programme auf die im Datenraum gespeicherten Daten zugreifen, so- fern sie den gleichen Datentyp auf den Datenraum aufprägen. Merke: Ein Datenraum ist eine Sammlung von Daten. Er kann ausschließlich durch ein Programm (und z.B. nicht durch den Editor) behandelt werden. Programme können Datenräumen Datentypen aufprägen und sie dann mit den ver- fügbaren Operationen dieses Datentyps manipulieren. Ein Beispiel Diesen etwas komplizierten Vorgang wollen wir an Hand eines Beispiels Schritt für Schritt erklären. Angenommen, Programmierer Meier hat ein Gehaltsprogramm zu erstellen. Er überlegt sich, das Programm in (mindestens) zwei Moduln (PACKETs) zu er- stellen: a) Berechnung der Gehaltssumme aus den Arbeitszeiten (also Bruttogehalt) und dann b) Endgültige Berechnung der Gehälter durch Abzug von Steuern usw. Das Programm aus a) erstellt also eine Gehaltsliste für alle Beschäftigten des Betriebs. Die Gehaltsliste soll ebenfalls von Modul b) genutzt werden. Meier entschließt sich, die Gehaltsliste in einem Datenraum zu speichern. Das hat neben der effizienteren Bearbeitung noch den Vorteil, daß man die Gehaltsliste - ohne Programmierkenntnisse zu besitzen - nicht mit dem Editor bearbeiten kann (Datenschutz). Dem ELAN-Compiler muß Meier also mitteilen, daß die Reihung für die Gehaltsliste (oder irgendein anderer Datentyp) nicht im Speicherbereich des Programms, sondern in einem Datenraum gespeichert werden soll. Dies erfolgt mit dem Schlüsselwort BOUND, welches dem Datentyp bei der Deklaration vorangestellt wird. Beispiel: BOUND ROW 1000 REAL VAR gehaltsliste Dieses BOUND-Objekt muß Meier noch mit einer Datei verbinden, man spricht von "ankoppeln". Die Ankopplung erfolgt durch den Operator ':='. Dies kann man gleich bei der Initialisierung vornehmen. Beispiel: BOUND ROW 1000 REAL VAR gehaltsliste := new ("hugo") Die Prozedur 'new' kreiert dabei einen leeren Datenraum (hier mit dem Namen 'hugo'), der mit Hilfe der Zuweisung (hier: Initialisierung) an die Variable 'gehaltsliste' gekoppelt wird. Nun kann Meier mit der 'gehaltsliste' arbeiten wie mit allen anderen Feldern auch, mit dem Unterschied, daß die Daten, die er in 'gehaltsliste' speichert, eigentlich im Datenraum 'hugo' gespeichert sind. Beispiele: gehaltsliste [5] := 10 000.0; (* Traumgehalt *) gehaltsliste [index] INCR 200.0; (* usw. *) Meier kann auch Prozeduren schreiben, die auf der Gehaltsliste arbeiten. Beispiel: PROC sort (ROW 1000 REAL VAR liste): ... END PROC sort; ... sort (gehaltsliste); ... Man beachte, daß der formale Parameter der Prozedur 'sort' nicht mit BOUND spezifiziert werden darf (BOUND wird nur bei der Deklaration des Objekts angegeben). Das ist übrigens ein weiterer wichtiger Vorteil von BOUND-Objek- ten: man kann alle Prozeduren des EUMEL-Systems auch für BOUND-Objekte verwenden, nur die Datentypen müssen natürlich übereinstimmen. Nach der Bearbeitung des Moduls a) ist Meier nun sicher, daß seine Brutto- daten in dem Datenraum 'hugo' stehen. Meier braucht (genauso wie bei FILEs) den Datenraum nicht zu schließen. Im zweiten Modul muß Meier nun erneut ein BOUND-Objekt deklarieren. Deshalb deklariert Meier nun BOUND ROW 1000 REAL VAR nettoliste :: old ("hugo"); Hier muß Meier nun die Prozedur 'old' verwenden, weil der Datenraum bereits aus dem ersten Modul existiert. Nun kann Meier weiter programmieren, bis er letztendlich den Datenraum löscht: forget ("hugo") Merke: Ein Datenobjekt eines beliebigen Datentyps kann mit einem vorange- stellten BOUND deklariert werden und an einen Datenraum gekoppelt werden. Der Datentyp ist dann auf den Datenraum aufgeprägt und man kann mit ihm arbeiten wie mit allen anderen Objekten dieses Datentyps. Datenräume als Datentyp Datenräume können auch als eigener Datentyp (DATASPACE) in einem Programm behandelt werden. Somit können Datenräume (als Ganzes) ohne Kenntnis eines eventuell (vorher oder später) aufgeprägten Typs verwandt werden. Als Operationen auf DATASPACE-Objekten sind nur Transporte, Löschen und Initialisieren zugelassen. DATASPACE VAR ds :: old ("daten"); Der Zuweisungsoperator bewirkt eine Kopie des Datenraums vom rechten auf den linken Operanden. Des weiteren gibt es eine DATASPACE Konstante 'nil- space', die eine leere Datenraum repräsentiert. Mit diesem Wert initialisiert der Datei-Manager Dateien, die neu kreiert werden. Eine neuer Datenraum kann durch new ("name") eingerichtet werden. 'new' liefert gleichzeitig einen Datenraum und wird deshalb für Initialisierungen verwandt. Beispiel: DATASPACE VAR datenraum :: new ("name1"); (* Kopie ! *) Ein bereits vorhandener Datenraum in der Benutzer-Task kann mit old ("datei") erneut benutzt werden. 'old' liefert (wie 'new') einen DATASPACE, so daß 'old' ebenfalls zur Initialisierung benutzt werden kann. Die Prozedur nilspace liefert einen leeren Datenraum. Für Datenräume gelten zusätzlich einige der Prozeduren wie für FILEs, u.a.: forget fetch save rename Ausgenommen davon sind Prozeduren, die einen TEXT-File voraussetzen, wie z.B. 'crypt' und 'decrypt', 'put', 'putline' usw. Abschließend soll hier noch auf häufig gemachte Fehler hingewiesen werden: Wenn man an ein DATASPACE-Objekt zuweist (z.B.: DATASPACE VAR ds := new ("mein datenraum")) so erhält man, wie oben erwähnt, eine Kopie des Datenraums in 'ds'. Koppelt man jetzt 'ds' an ein BOUND-Objekt an und führt Änderungen durch, so wirken diese nur auf die Kopie und nicht auf die Quelle (d.h. im Beispiel, daß die Datenraum 'hugo' nicht verändert wird, hier also leer bleibt). Für Änderungen in den vom Datei-Manager verwalteten Dateien ist also stets direkt anzukoppeln, wie es im Beispiel gezeigt wurde. Wenn man ein DATASPACE-Objekt benutzt, ohne den Datei-Manager zu verwenden, so muß man selbst dafür sorgen, daß dieses Objekt nach seiner Benutzung wieder gelöscht wird. Das Löschen geschieht durch die Prozedur 'forget'. Ferner ist zu beachten, daß vor der Ankopplung an ein BOUND-Objekt das DATASPACE-Objekt initialisiert wird (im Normalfall mit 'nilspace'). Beispiel: DATASPACE VAR ds := nilspace; BOUND ROW 1000 REAL VAR real feld := ds; .... real feld [index] := wert; .... forget (ds) (* Datei löschen, damit der Platz wieder verwendet wird *) Ein automatisches Löschen von DATASPACE-Objekten erfolgt auch nicht bei Programmende (sonst könnten sie ihre Funktion als Datei nicht erfüllen). Erst beim Löschen einer Task werden alle ihr gehörenden DATASPACE-Objekte freigegeben. Verboten ist weiterhin folgendes: BOUND X ...; wobei 'X' mit BOUND deklariert wurde oder ein DATASPACE ist. Merke: Datenräume können durch 'new' erschaffen werden. Mit 'old' kann ein bereits vorhandener Datenraum angesprochen werden. Im Übrigen gelten auch einige der für FILEs vorhandenen Operationen. Datei-Typen definieren Durch die Datenräume und die Datentyp-Definition von ELAN ist es für Programmierer relativ einfach, neue Datei-Datentypen zu definieren. In der Regel reicht der Datentyp FILE für "normale" Anwendungen aus, jedoch kann es manchmal sinnvoll und notwendig sein, neue Datei-Typen für spezielle Aufgaben zu definieren. In diesem Abschnitt zeigen wir an dem Beispiel DIRFILE (welcher zwar im ELAN-Standard definiert, aber nicht im EUMEL-System realisiert ist), wie ein neuer Datei-Datentyp definiert wird: PACKET dirfiles DEFINES DIRFILE, :=, dirfile, getline, ...: LET maxsize = 1000; TYPE DIRFILE = BOUND ROW maxsize TEXT; (* DIRFILE besteht aus TEXTen; Zugriff erfolgt ueber einen Schluessel, der den Index auf die Reihung darstellt *) OP := (DIRFILE VAR dest, DATASPACE CONST space): CONCR (dest) := space END OP :=; DATASPACE PROC dirfile (TEXT CONST name): IF exists (name) THEN old (name) ELSE new (name) FI END PROC dirfile; PROC getline (DIRFILE CONST df, INT CONST index, TEXT VAR record): IF index <= 0 THEN errorstop ("access before first record") ELIF index > maxsize THEN errorstop ("access after last record") ELSE record := df [index] FI END PROC getline; PROC putline (DIRFILE CONST df, INT CONST index, TEXT VAR record): ... END PROC putline; ... END PACKET dirfiles; Die Prozedur 'dirfile' ist die Assoziierungsprozedur für DIRFILEs (analog 'sequential file' bei FILEs). 'dirfile' liefert entweder einen bereits vor- handenen Datenraum oder richtet einen neuen ein. Um eine Initialisierung mit der 'dirfile'-Prozedur vornehmen zu können, braucht man auch einen Zu- weisungsoperator, der den Datenraum an den DIRFILE-Datentyp koppelt. Zugriffe auf einen DIRFILE sind nun relativ einfach zu schreiben. Im obigen Beispiel wird nur die Prozedur 'getline' gezeigt. Nun ist es möglich, Programme zu schreiben, die den DIRFILE-Datentyp benutzen. Beispiel: DIRFILE VAR laeufer :: dirfile ("Nacht von Borgholzhausen"); INT VAR nummer; TEXT VAR name; REP put ("Startnummer bitte:"); get (nummer); line; put ("Name des Laeufers:"); get (name); putline (laeufer, nummer, name); line UNTIL no ("weiter") END REP; ... Merke: Neue Datei-Typen für spezielle Anwendungen kann man leicht selbst programmieren. 6. Beschreibung der Prozeduren In diesem Abschnitt werden alle Operationen, die für Dateien zur Verfügung stehen, aufgeführt. Dabei werden die Operationen für FILEs und Datenräume mit (F) gekennzeichnet. + THESAURUS OP + (THESAURUS CONST left, right) Zweck: Vereinigungsmenge von 'left' und 'right'. THESAURUS OP + (THESAURUS VAR left, TEXT CONST name) Zweck: Nimmt den TEXT 'name' in den Thesaurus 'left' auf. Beispiel: save (SOME father + "hugo", archive) - THESAURUS OP - (THESAURUS CONST left, right) Zweck: Differenzmenge von 'left' und 'right'. THESAURUS OP - (THESAURUS VAR left, TEXT CONST name) Zweck: Liefert einen Thesaurus aus left, aber ohne den Eintrag 'name'. Beispiel: save (ALL myself - "hugo", archive) / THESAURUS OP / (THESAURUS CONST left, right) Zweck: Schnittmenge von 'left' und 'right'. TASK OP / (TEXT CONST task name) Zweck: Liefert aus einem Tasknamen den internen Tasknamen. '/' kann über- all dort eingesetzt werden, wo ein interner Taskname verlangt wird. ALL THESAURUS OP ALL (TASK CONST task) Zweck: Liefert einen Thesaurus, der alle Dateinamen der angegebenen Task enthält (auch der Benutzer-Task: 'myself'). THESAURUS OP ALL (TEXT CONST file name) Zweck: Liefert einen Thesaurus, der die in 'file name' vorhandenen Datei- namen (jede Zeile ein Name) enthält. at (F) BOOL PROC at (FILE VAR f, TEXT CONST word) Zweck: Abfrage, ob man auf 'word' in der Datei 'f' positioniert ist. Beispiel: ... down (f, "muster") IF at (f, "muster") THEN gefunden ELSE nicht gefunden FI; ... archive PROC archive (TEXT CONST archive name) Zweck: Anmeldung von Archiv-Operationen. 'archive name' wird zur Über- prüfung für alle folgenden Archiv-Operationen verwandt, um die unberechtigte Benutzung eines Archivs zu verhindern. Die Anmeldung wird abgelehnt, wenn ein anderer Nutzer das Archiv belegt hat. TASK PROC archive Zweck: Liefert den internen Task-Bezeichner für die Verwendung in Datei-Kommandos. Beispiel: save ("datei", archive) brother TASK PROC brother (TASK CONST task) Zweck: Liefert den internen Task-Bezeichner der angegebenen "Bruder"- Task. check PROC check (TEXT CONST dateiname, TASK CONST task) Zweck: Überprüft, ob die Datei 'dateiname' auf dem Archiv lesbar ist. Beispiel: check ("meine datei", archive) PROC check (THESAURUS CONST t, TASK CONST task) Zweck: Überprüft, ob die in dem Thesaurus 't' enthaltenen Dateien auf dem Archiv lesbar sind. Beispiel: check (ALL archive, archive) clear PROC clear (TASK CONST task) Zweck: Löscht alle Dateien der Task 'task'. Ist z.Z. nur für die Task 'ARCHIVE' implementiert. clear removed (F) PROC clear removed (FILE VAR f) Zweck: Löscht die mit 'remove' "vorsichtig" gelöschten Zeilen aus der Datei 'f' endgültig. close (F) PROC close (FILE VAR file) Zweck: Schließen der Datei 'file'. Im EUMEL-System ist der Aufruf von 'close' nicht notwendig. 'close' wurde nur aufgenommen, um die Kompatibilität zu Standard zu wahren. col (F) PROC col (FILE VAR f, INT CONST position) Zweck: Positionierung auf die Spalte 'position' innerhalb der aktuellen Zeile. INT PROC col (FILE CONST f) Zweck: Liefert die aktuelle Position innerhalb der aktuellen Zeile. copy (F) PROC copy (TEXT CONST source, destination) Zweck: Kopiert die Datei 'source' in eine neue Datei mit dem Namen 'destination' in der Benutzer-Task. Fehlerfälle: * destination file already exists Eine Datei mit dem Namen 'destination' existiert bereits. * source file does not exist Die Ursprungsdatei mit dem Namen 'source' ist nicht vorhanden. * directory overflow Die Anzahl der zulässigen Dateien der Benutzer-Task ist über- schritten. PROC copy (DATASPACE CONST ds, TEXT CONST destination) Zweck: Eintragen eines unbenannten DATASPACE in die Datei-Verwaltung. Fehlerfälle: * destination file already exists Eine Datei mit dem Namen 'destination' existiert bereits. * directory overflow Die Anzahl der zulässigen Dateien der Benutzer-Task ist über- schritten. create PROC create (TEXT CONST name) Zweck: Erschafft einen neuen Datenraum in der Benutzer-Task. Fehlerfälle: * file already exists Eine Datei mit dem Namen 'name' existiert bereits in der Benutzer- Task. * directory overflow Die Anzahl der zulässigen Dateien der Benutzer-Task ist über- schritten. crypt (F) PROC crypt (TEXT CONST name, parole) Zweck: Verschlüsseln des Inhaltes der Datei 'name' mit Hilfe des Textes 'parole' für Zwecke des Datenschutzes. Die Verschlüsselung ist umso besser (bzw. umso schwieriger zu "knacken"), je länger der Text 'parole' ist. Die Datei kann mit der Prozedur 'decrypt' wieder entschlüsselt werden. Eine Datei kann mehrfach verschlüsselt werden. Dabei gilt bei einer Entschlüsselung das Klammerungsprinzip. Es muß also genau so oft entschlüsselt werden, wie anfangs verschlüsselt wurde. Dabei ist auf die richtige Angabe der 'parole'n zu achten. Beispiel: crypt ("hugo", "verschluesselung1"); crypt ("hugo", "verschluesselung2"); ... decrypt ("hugo", "verschluesselung2"); decrypt ("hugo", "verschluesselung1") Achtung: 'crypt' und 'decrypt' sind nicht standardmäßig insertiert. decrypt (F) PROC decrypt (TEXT CONST name, parole) Zweck: Entschlüsselt die Datei 'name' mit Hilfe der angegebenen 'parole'. Dabei ist darauf zu achten, daß die gleiche 'parole' anzugeben ist, die verwendet wurde, um die Datei zu verschlüsseln (sonst wirkt 'decrypt' wie ein erneuter Aufruf von 'crypt').Beim mehr- fachen Ver- und Entschlüsseln ist das Klammerungsprinzip zu be- achten (dazu vergl. 'crypt'). delete record (F) PROC delete record (FILE VAR file) Zweck: Der aktuelle Satz der Datei 'file' wird gelöscht. Der folgende Satz wird der aktuelle Satz. Die Datei 'file' muß mit der Verar- beitungsart 'modify' assoziiert worden sein. do PROC do (PROC (TEXT CONST) operate, THESAURUS CONST thesaurus) Zweck: Ruft 'operate' mit allen im 'thesaurus' enthaltenen Dateinamen nacheinander auf. Man beachte, daß bei Prozedur-Parametern der Name der Prozedur hinter dessen Parametern geschrieben wird. Beispiel: do (PROC (TEXT CONST) reorganize, ALL myself) PROC do (PROC (TEXT CONST, TASK CONST) operate, THESAURUS CONST thesaurus, TASK CONST task) Zweck: S.o.. Dabei ist zu beachten, daß 'task' als zweiter Parameter in der Prozedur 'operate' eingesetzt wird. Beispiel: do (PROC (TEXT CONST, TASK CONST) save, SOME myself, father) (* enspricht: *) save (SOME myself, father) down (F) PROC down (FILE VAR f) Zweck: Positionieren um eine Zeile vorwärts in der Datei 'f'. PROC down (FILE VAR f, INT CONST number) Zweck: Positionieren um 'number' Zeilen vorwärts in der Datei 'f'. PROC down (FILE VAR f, TEXT CONST pattern) Zweck: Suche nach 'pattern' in der Datei 'f'. Wird 'pattern' gefunden, ist die Position das erste Zeichen von 'pattern'. Andernfalls steht man hinter dem letzten Zeichen der Datei. Achtung: 'down' sucht vom nächsten Zeichen rechts ab, so daß wiederholtes Suchen keine Endlosschleife ergibt. PROC down (FILE VAR f, TEXT CONST pattern, INT CONST number) Zweck: Wie obiges 'down', aber maximal nur 'number'-Zeilen weit. downety (F) PROC downety (FILE VAR f, TEXT CONST pattern) Zweck: Suche nach 'pattern' in der Datei 'f'. Wird 'pattern' gefunden, ist die Position das erste Zeichen von 'pattern'. Andernfalls steht man auf dem letzten Zeichen der Datei. Achtung: 'downety' sucht (im Gegensatz zu 'down') vom aktuellen Zeichen. PROC downety (FILE VAR f, TEXT CONST pattern, INT CONST number) Zweck: Wie obiges 'downety', aber maximal nur 'number'-Zeilen weit. enter password PROC enter password (TEXT CONST password) Zweck: Einstellen eines Paßwortes in der Benutzer-Task für den Datei- Verkehr mit einer Vater-Task. Der Parameter 'password' kann dabei aus zwei Teilen bestehen, die durch ein "/"-Zeichen getrennt werden müssen. Der erste Teil bedeutet das schreib-Passwort, während der TEXT nach dem "/"-Zeichen das Lese-Paßwort beinhaltet. Enthält der Parameter 'password' kein "/"-Zeichen, gilt der ange- gebene TEXT sowohl für das Schreib- wie auch für das Lese-Paßwort. Im Schreib- bzw. Lese-Teil des Paßworts kann man das "-"-Zeichen angeben, um eine Datei vor überschreibendem oder lesendem Zugriff zu schützen. Die Paßwort-Überprüfung findet statt bei - fetch (Überprüfung der Lese-Berechtigung) - save (Überprüfung der Schreib-Berechtigung) - erase (Überprüfung der Schreib-Berechtigung) eof (F) BOOL PROC eof (FILE CONST file) Zweck: Informationsprozedur auf das Ende eines FILEs. Liefert den Wert TRUE, sofern hinter den letzten Satz eines FILEs positioniert wurde. erase PROC erase (TEXT CONST name) Zweck: Löscht eine Datei mit dem Namen 'name' in der unmittelbaren Vater-Task. Fehlerfälle: * ... gibt es nicht Eine Datei mit dem Namen 'name' existiert in der unmittelbaren Vater-Task nicht. * wrong password Es wurde mit der Prozedur 'enter password' nicht das richtige Paßwort angegeben. PROC erase (TEXT CONST name, TASK CONST task) Zweck: Löscht eine Datei mit dem Namen 'name' in der Task 'task'. Bei- spiel: erase ("meine datei", father) PROC erase (THESAURUS CONST thesaurus) Zweck: Löscht die im 'thesaurus' angegebenen Dateien in der Vater-Task. Beispiel (löscht alle Dateien in der Vater-Task, die in der Benut- zer-Task vorhanden sind): erase (ALL myself) PROC erase (THESAURUS CONST thesaurus, TASK CONST manager) Zweck: S.o.. exists (F) BOOL PROC exists (TEXT CONST name) Zweck: Informationsprozedur zur Abfrage der Existenz einer Datei in der Benutzer-Task. Beispiel: IF exists ("dateiname") THEN FILE VAR f :: sequential file ...; ELSE errorstop ("Datei existiert nicht") FI father TASK PROC father Zweck: Liefert den internen Task-Bezeichner der Vater-Task der Benutzer- Task. Beispiel: save ("datei", father) TASK PROC father (TASK CONST task) Zweck: Liefert den internen Task-Bezeichner von 'task'. Beispiel: save ("datei", father (father)) (* Kopiert 'datei' zum "Opa" *) fetch PROC fetch (TEXT CONST name) Zweck: Einbringen einer Datei in die Benutzer-Task von dem "direkten" Vater im Taskbaum. Fehlerfälle: * ... gibt es nicht Die Datei existiert bei dem Vater nicht. * directory overflow Die Anzahl der zulässigen Dateien der Benutzer-Task ist über- schritten. * wrong password Es wurde mit der Prozedur 'enter password' nicht das richtige Paßwort angegeben. PROC fetch (TEXT CONST name, TASK CONST task) Zweck: Kopieren einer Datei in die Benutzer-Task von 'task'. Beispiel: fetch ("datei", public) PROC fetch (THESAURUS CONST thesaurus) Zweck: Holt alle im 'thesaurus' enthaltenen Dateien von der Vater-Task. PROC fetch (THESAURUS CONST thesaurus, TASK CONST manager) Zweck: Holt alle im 'thesaurus' enthaltenen Dateien von der 'manager'- Task. forget (F) PROC forget (TEXT CONST name) Zweck: Löschen einer Datei mit dem Namen 'name' in der Benutzer-Task. Fehlerfälle: * ... gibt es nicht Die Datei mit dem Namen 'name' existiert nicht in der Benutzer- Task. PROC forget (DATASPACE VAR ds) Zweck: Löschen des Datenraums 'ds'. PROC forget (THESAURUS CONST thesaurus) Zweck: Löscht die im 'thesaurus' enthaltenen Dateinamen in der Benutzer- Task. Beispiel: forget (SOME myself) PROC forget (TEXT CONST file name, QUIET CONST q) Zweck: Löschen der Datei 'file name' ohne Anfrage. Als zweiter Parameter muß 'quiet' übergeben werden. Beispiel: forget ("hugo", quiet) get (F) PROC get (FILE VAR f, INT VAR number) Zweck: Lesen eines INT-Wertes 'number' von der Datei 'f'. PROC get (FILE VAR f, REAL VAR number) Zweck: Lesen eines REAL-Wertes 'number' von der Datei 'f'. PROC get (FILE VAR f, TEXT VAR text) Zweck: Lesen eines TEXT-Wertes 'text' von der Datei 'f'. PROC get (FILE VAR f, TEXT VAR text, TEXT CONST delimiter) Zweck: Lesen eines TEXT-Wertes 'text' von der Datei 'f', bis das Zeichen 'delimiter' angetroffen wird. Ein eventueller Zeilenwechsel in der Datei wird dabei nicht übergangen. PROC get (FILE VAR f, TEXT VAR text, INT CONST maxlength) Zweck: Lesen eines TEXT-Wertes 'text' von der Datei 'f' mit 'maxlength' Zeichen. Ein eventueller Zeilenwechsel in der Datei wird dabei nicht übergangen. getline (F) PROC get line (FILE VAR file, TEXT VAR record) Zweck: Lesen einer Zeile 'record' von einer sequentiellen Datei 'file'. Die Datei muß mit 'input' assoziiert sein (vergl. 'sequential file'). Fehlerfälle: * file not open Die Datei 'file' ist gegenwärtig nicht assoziiert. * input after end of file Es wurde versucht, über die letzte Zeile einer Datei zu lesen. * input access to output file Es wurde versucht, von einem mit 'output' assoziierten FILE zu lesen. global manager PROC global manager Zweck: Durch den Aufruf der Prozedur wird die Benutzer-Task zu einem Datei-Manager. Danach können Söhne dieser Task eingerichtet werden. input (F) PROC input (FILE VAR f) Zweck: Ändern der Verarbeitungsart von 'modify' oder 'output' in 'input'. Dabei wird auf den ersten Satz der Datei positioniert. TRANSPUTDIRECTION CONST input Zweck: Assoziierung in Zusammenhang mit der Prozedur 'sequential file' einer sequentiellen Datei mit der 'TRANSPUTDIRECTION' 'input' (nur lesen). insert record (F) PROC insert record (FILE VAR file) Zweck: Es wird ein leerer Satz in die Datei 'file' vor die aktuelle Position eingefügt. Dieser Satz kann anschließend mit 'write record' beschrieben werden (d.h. der neue Satz ist jetzt der aktuelle Satz). Die Datei 'file' muß mit der Verarbeitungsart 'modify' assoziiert worden sein. line no (F) INT PROC line no (FILE CONST file) Zweck: Liefert die aktuelle Zeilennummer. line (F) PROC line (FILE VAR file) Zweck: Positionierung auf die nähhste Zeile der Datei 'file'. Die Datei 'file' darf mit 'output' oder 'input' assoziiert sein. Wird ver- sucht, über das Ende eines mit 'input' assoziierten FILEs zu positionieren, wird keine Aktion vorgenommen. PROC line (FILE VAR file, INT CONST lines) Zweck: Positionierung auf 'lines' nächste Zeilen der Datei 'file'. Die Datei 'file' darf mit 'output' oder 'input' assoziiert sein. Wird versucht, über das Ende eines mit 'input' assoziierten FILEs zu positionieren, wird keine Aktion vorgenommen. Ist 'lines' <= 0, wird keine Aktion durchgeführt. lines (F) PROC lines (FILE VAR f) Zweck: Liefert die Anzahl der Zeilen der Datei 'f'. list PROC list Zweck: Listet alle Dateien der Benutzer-Task mit Namen und Datum des letzten Zugriffs auf dem Terminal auf. PROC list (FILE VAR f) Zweck: Schreibt alle Dateien der Benutzer-Task mit Namen und Datum der letzten Änderung in die Datei 'f'. PROC list (TASK CONST task) Zweck: Listet alle Dateien der angegebenen 'task' mit Namen und Datum der letzten Änderung auf dem Terminal auf. Beispiel: list (father) max line length (F) INT PROC max line length (FILE CONST file) Zweck: Informationsprozedur über die Anzahl von Zeichen in einer Zeile der Datei 'file'. Standardmäßig sind die Anzahl der Zeichen einer Zeile wie beim Editor 77 Zeichen. PROC max line length (FILE VAR file, INT CONST number) Zweck: Setzen der Anzahl von Zeichen einer Zeile in dem FILE 'file'. modify (F) TRANSPUTDIRECTION CONST modify Zweck: Diese Betriebsrichtung erlaubt das Vorwärts- und Rückwärts-Posi- tionieren und das beliebige Einfügen und Löschen von Sätzen. 'modify' wird für die Assoziierungsprozedur 'sequential file' benötigt. PROC modify (FILE VAR f) Zweck: Ändern der Betriebsrichtung von 'input' oder 'output' in die Be- triebsrichtung 'modify'. myself TASK PROC myself Zweck: Liefert den internen Task-Bezeichner der Benutzer-Task. Beispiel: save (ALL myself, father) name TEXT PROC name (TASK CONST task) Zweck: Liefert den TEXT-Namen von 'task'. Beispiel: put (name (myself)) new (F) DATASPACE PROC new (TEXT CONST name) Zweck: Richtet eine neue Datei in der Benutzer-Task ein. Fehlerfälle: * file already exists Die Datei mit dem Namen 'name' existiert bereits in der Benutzer- Task. * directory overflow Die Anzahl der zulässigen Dateien in der Benutzer-Task ist über- schritten. nilspace (F) DATASPACE CONST nilspace Zweck: Liefert einen leeren Datenraum. old (F) DATASPACE PROC old (TEXT CONST name) Zweck: Eine bereits vorhandene Datei der Benutzer-Task wird erneut zur Bearbeitung angemeldet. Fehlerfälle: * ... gibt es nicht Die Datei mit dem Namen 'name' ist nicht in der Benutzer-Task vorhanden. output (F) PROC output (FILE VAR file) Zweck: Ändern der Verarbeitungsart von 'input' oder 'modify' in 'output'. Dabei wird hinter den letzten Satz der Datei positioniert. TRANSPUTDIRECTION CONST output Zweck: In Verbindung mit der Prozedur 'sequential file' kann eine Datei assoziiert werden mit der Betriebsrichtung 'output' (nur schreiben). pattern found BOOL PROC pattern found Zweck: Liefert TRUE, sofern die letzte Suchoperation (siehe 'down' und 'up') erfolgreich war, sonst FALSE. printer TASK PROC printer Zweck: Liefert den internen TASK-Bezeichner der SPOOLer-Task für den Drucker. Beispiel: save ("datei", printer) public TASK PROC public Zweck: Liefert den internen Task-Bezeichner von "PUBLIC". Beispiel: fetch ("datei", public) put (F) PROC put (FILE VAR f, INT CONST number) Zweck: Ausgabe eines INT-Wertes 'number' in die Datei 'f'. Dabei wird ein Leerzeichen an die Ausgabe angefügt. PROC put (FILE VAR f, REAL CONST number) Zweck: Ausgabe eines REAL-Wertes 'number' in die Datei 'f'. Dabei wird ein Leerzeichen an die Ausgabe angefügt. PROC put (FILE VAR f, TEXT CONST text) Zweck: Ausgabe eines TEXT-Wertes 'text' in die Datei 'f'. Dabei wird ein Leerzeichen an die Ausgabe angefügt. putline (F) PROC putline (FILE VAR file, TEXT CONST record) Zweck: Ausgabe eines TEXTes 'record' in die Datei 'file'. 'file' muß mit 'output' assoziiert sein. Fehlerfälle: * file not open Die Datei 'file' ist gegenwärtig nicht assoziiert. * output access to input file Es wurde versucht, auf einen mit 'input' assoziierten FILE zu schreiben. read record (F) PROC read record (FILE CONST file, TEXT VAR record) Zweck: Liest den aktuellen Satz der Datei 'file' in den TEXT 'record'. Die Position wird dabei nicht verändert. Die Datei 'file' muß mit der Verarbeitungsart 'modify' assoziiert worden sein. reinsert (F) PROC reinsert (FILE VAR f) Zweck: Einfügen von "vorsichtig gelöschten" Zeilen (vergl. 'remove') an der aktuellen Position. release PROC release (TASK CONST task) Zweck: Aufgabe der Reservierung des Archivs. Ein implizites 'release' wird automatisch fünf Minuten nach der letzten Archiv-Operation gegeben, sofern ein 'archive' eines anderen Nutzers vorliegt. Beispiel: release (archive) rename (F) PROC rename (TEXT CONST oldname, newname) Zweck: Umbenennen einer Datei von 'oldname' in 'newname'. remainder THESAURUS PROC remainder Zweck: Liefert nach der Unterbrechung einer Thesaurus-Operation den "Rest"-Thesaurus. reorganize (F) PROC reorganize (TEXT CONST filename) Zweck: Reorganisiert eine Datei. Die durch eventuelles Einfügen und Löschen entstandene Lücken werden eliminiert und die Anordung der Sätze der Datei wird linearisiert. reset (F) PROC reset (FILE VAR file) Zweck: Positionieren in einem FILE auf den Anfang (bei mit 'input' asso- ziierten FILEs) oder auf das Ende (bei mit 'output' assoziierten FILEs). reset range (F) PROC reset range (FILE VAR file) Zweck: Setzt alle Einschränkungen auf 'file' zurück. Siehe auch 'set range'. remove (F) PROC remove (FILE VAR f, INT CONST anzahl) Zweck: Löscht eine 'anzahl' von Zeilen "vorsichtig" aus der Datei 'f', die mit 'reinsert' an anderer Stelle wieder eingesetzt werden können. save PROC save (TEXT CONST datei) Zweck: Datei 'datei' wird an die unmittelbare Vater-Task übertragen. Fehlerfälle: * ... gibt es nicht Eine Datei mit dem Namen 'datei' existiert nicht in der Benutzer- Task. * directory overflow Die Anzahl der zulässigen Dateien in 'task' ist überschritten. * wrong password Es wurde mit der Prozedur 'enter password' nicht das richtige Paßwort angegeben. PROC save (TEXT CONST name, TASK CONST task) Zweck: Datei mit dem Namen 'name' in Task 'task' kopieren. Beispiel: save ("meine datei", father) Fehlerfälle: * ... gibt es nicht Eine Datei mit dem Namen 'name' existiert nicht in der Benutzer- Task. * directory overflow Die Anzahl der zulässigen Dateien in der angegebenen task ist überschritten. * wrong password Es wurde mit der Prozedur 'enter password' nicht das richtige Paßwort angegeben. PROC save (THESAURUS CONST thesaurus) Zweck: Kopiert die Dateien, die in 'thesaurus' enthalten sind, in die Vater-Task. Beispiel: save (SOME myself) PROC save (THESAURUS CONST thesaurus, TASK CONST manager) Zweck: Kopiert die Dateien, die in 'thesaurus' enthalten sind, in Task 'manager'. segments (F) INT PROC segments (FILE CONST f) Zweck: Liefert die Anzahl der Datei-Segmente von 'f'. Nach 'reorganize' besteht 'f' aus einem Segment. Einfügungen oder Löschungen erhöhen die Segmentanzahl. sequential file (F) FILE PROC sequential file (TRANSPUTDIRECTION CONST mode, DATASPACE VAR ds) Zweck: Assoziierung einer sequentiellen Datei mit dem Dataspace 'ds' und der Betriebsrichtung 'TRANSPUTDIRECTION' (vergl. 'modify', 'input' bzw. 'output'). Diese Prozedur dient zur Assoziierung eines tempo- rären Datenraums in der Benutzer-Task, der nach der Beendigung des Programmlaufs nicht mehr zugriffsfähig ist (weil der Name des Datenraums nicht mehr ansprechbar ist). Somit muß der Datenraum explizit vom Programm gelöscht werden. FILE PROC sequential file (TRANSPUTDIRECTION CONST mode, TEXT CONST name) Zweck: Assoziierung einer sequentiellen Datei mit dem Namen 'name' und der Betriebsrichtung 'TRANSPUTDIRECTION' (vergl. 'input' bzw. 'output'). Existiert der FILE bereits, dann wird mit 'input' auf den Anfang des FILEs, bei 'output' hinter den letzten Satz der Datei positioniert. Existiert dagegen der FILE noch nicht und ist die TRANSPUTDIRECTION 'output', wird ein neuer FILE eingerichtet. Fehlerfall: * input file not existing Es wurde versucht, einen nicht vorhandenen FILE mit 'input' zu assoziieren. set range (F) PROC set range (FILE VAR f, INT CONST anz, INT CONST column, FRANGE VAR b) Zweck: Schränkt die Datei 'f' auf 'anz' Zeilen beginnend bei der Position 'column' der aktuellen Zeile. Der "alte" Datei-Bereich wird in 'b' gespeichert. PROC set range (FILE VAR f, FRANGE VAR b) Zweck: Setzt die Datei 'f' auf die in 'b' gespeicherten Bereich zurück. son TASK PROC son (TASK CONST task) Zweck: Liefert den internen Task-Bezeichner der Sohn-Task. Beispiel: put (name (son (myself))) SOME THESAURUS OP SOME (THESAURUS CONST thesaurus) Zweck: Bietet den angegebenen 'thesaurus' zum Editieren an. Dabei können nicht erwünschte Namen gestrichen werden. THESAURUS OP SOME (TASK CONST task) Zweck: Bietet einen THESAURUS von 'task' zum Editieren an. THESAURUS OP SOME (TEXT CONST file name) Zweck: Bietet einen 'thesaurus', der aus 'file name' gebildet wird, zum editieren an. to line (F) PROC to line (FILE VAR f, INT CONST number) Zweck: Positionierung auf die Zeile 'number'. Nur erlaubt in der Betriebsrichtung 'modify'. task TASK PROC task (TEXT CONST task name) Zweck: Liefert den internen Task-Bezeichner von 'task name'. Beispiel: save ("datei", task ("PUBLIC")) (* das gleiche wie: *) save ("datei", public) type (F) INT PROC type (DATASPACE CONST ds) Zweck: Liefert den frei wählbaren (INT-) Schlüssel des Datenraums 'ds'. Wurde der Datenraum noch nie angekoppelt, so liefert die Prozedur 'type' einen Wert < 0, erfolgte eine Ankopplung und hat ein Programmierer für den Datenraum 'ds' noch keinen anderen Schlüssel festgelegt, so liefert 'type' den Wert '0'. PROC type (DATASPACE CONST ds, INT CONST type) Zweck: Setzt den frei wählbaren Schlüssel 'type' für den Datenraum 'ds' (vergl. obige Prozedur 'type'). up (F) PROC up (FILE VAR f) Zweck: Positionieren um eine Zeile rückwärts in der Datei 'f'. PROC up (FILE VAR f, INT CONST number) Zweck: Positionieren um 'number' Zeilen rückwärts in der Datei 'f'. PROC up (FILE VAR f, TEXT CONST pattern) Zweck: Suche nach 'pattern' rückwärts in der Datei 'f'. Wird 'pattern' gefunden, ist die Position das erste Zeichen von 'pattern'. Andernfalls steht man auf dem ersten Zeichen der Datei. Achtung: 'down' sucht vom nächsten Zeichen links ab, so daß wiederholtes Suchen keine Endlosschleife ergibt. PROC up (FILE VAR f, TEXT CONST pattern, INT CONST number) Zweck: Wie obiges 'up', aber maximal nur 'number'-Zeilen weit. uppety (F) PROC uppety (FILE VAR f, TEXT CONST pattern) Zweck: Suche nach 'pattern' rückwärts in der Datei 'f'. Wird 'pattern' gefunden, ist die Position das erste Zeichen von 'pattern'. Andernfalls steht man auf dem ersten Zeichen der Datei. Achtung: 'uppety' sucht (im Gegensatz zu 'up') vom aktuellen Zeichen. PROC uppety (FILE VAR f, TEXT CONST pattern, INT CONST number) Zweck: Wie obiges 'uppety', aber maximal nur 'number'-Zeilen weit. word (F) TEXT PROC word (FILE CONST f) Zweck: Liefert das aktuelle Wort (bis zum nächsten Leerzeichen oder Zeilenende). TEXT PROC word (FILE CONST f, TEXT CONST sep) Zweck: Liefert einen Text von der aktuellen Position bis zum nächsten 'sep-Zeichen oder Zeilenende. TEXT CONST word (FILE CONST f, INT CONST len) Zweck: Liefert einen Text von der aktuellen Position mit der Länge 'len' bzw. bis zum Zeilenende. write (F) PROC write (FILE VAR f, TEXT CONST text) Zweck: Schreibt 'text' in die Datei 'f' (analog 'put (f, text)'), aber ohne Trennblank. write record (F) PROC write record (FILE VAR file, TEXT CONST record) Zweck: Schreibt einen Satz in die Datei 'file' an die aktuelle Position. Dieser Satz muß bereits vorhanden sein, d.h. mit 'write record' kann keine leere Datei beschrieben werden, sondern es wird der Satz an der aktuellen Position überschrieben. Die Position in der Datei wird nicht verändert. Die Datei 'file' muß mit der Verar- beitungsart 'modify' assoziiert worden sein.