# datei-eio Specification ## Purpose Die Datei-E/A stellt die Dateiverwaltung des Dialekts bereit: sequenzieller, wahlfreier und binärer Zugriff über Dateinummern, Record-Puffer mit Festtextfeldern, Positions- und Statusfunktionen sowie die Dateisystem-Anweisungen — plattformübergreifend und mit den Laufzeitfehlern des Vorbilds. ## Requirements ### Requirement: Dateinummern und Öffnen in drei Zugriffsarten `OPEN` SHALL in beiden Syntaxen des Vorbilds (Klausel- und Kurzform) die Zugriffsarten sequenziell (`INPUT`, `OUTPUT`, `APPEND`), wahlfrei (`RANDOM`) und binär (`BINARY`) unterstützen und die Datei an eine Dateinummer binden. `FREEFILE` SHALL die kleinste freie Dateinummer liefern. `CLOSE` ohne Argument SHALL alle offenen Dateien schließen, `RESET` ebenso. Eine bereits belegte Dateinummer MUST Laufzeitfehler 55 „File already open" auslösen, eine nicht geöffnete Nummer Fehler 52 „Bad file name or number", eine fehlende Datei bei `INPUT` Fehler 53 „File not found". #### Scenario: Sequenzielles Schreiben und Lesen - **WHEN** eine Datei mit `OPEN "d.txt" FOR OUTPUT AS #1` beschrieben, geschlossen und mit `FOR INPUT` wieder geöffnet wird - **THEN** liefert das Lesen genau die geschriebenen Zeilen #### Scenario: Doppeltes Öffnen derselben Nummer - **WHEN** `OPEN` eine bereits offene Dateinummer verwendet - **THEN** tritt Laufzeitfehler 55 auf #### Scenario: Freie Dateinummer - **WHEN** `#1` offen ist und `FREEFILE` aufgerufen wird - **THEN** liefert `FREEFILE` eine Nummer, die noch nicht belegt ist ### Requirement: Sequenzielle Datenübertragung `PRINT #` SHALL wie `PRINT` formatieren (Druckzonen, Zahlendarstellung mit Vorzeichenspalte), `WRITE #` SHALL Elemente komma-getrennt mit Strings in Anführungszeichen schreiben. `INPUT #` SHALL komma-getrennte Felder lesen und dabei Anführungszeichen auswerten, `LINE INPUT #` die ganze Zeile ohne Zeilenende. `EOF` SHALL das Erreichen des Dateiendes melden; Lesen über das Dateiende hinaus MUST Laufzeitfehler 62 „Input past end of file" auslösen. #### Scenario: WRITE # und INPUT # sind gegenläufig - **WHEN** `WRITE #1, "a,b", 5` geschrieben und die Zeile mit `INPUT #1, s$, n` gelesen wird - **THEN** enthält `s$` den Text `a,b` und `n` den Wert 5 #### Scenario: Lesen über das Dateiende - **WHEN** `INPUT #1, x` nach dem letzten Datensatz ausgeführt wird - **THEN** tritt Laufzeitfehler 62 auf ### Requirement: Wahlfreier Zugriff mit Record-Puffern `OPEN … FOR RANDOM … LEN = n` SHALL Datensätze fester Länge bereitstellen. `GET` und `PUT` SHALL einen Datensatz an einer 1-basierten Recordnummer lesen bzw. schreiben; ohne Nummer gilt der Satz nach der letzten Operation. `FIELD` SHALL den Recordpuffer in benannte Festtextfelder aufteilen, `LSET`/`RSET` SHALL links- bzw. rechtsbündig in ein Feld schreiben und überlange Werte abschneiden. Alternativ SHALL `GET`/`PUT` mit einer Variablen eines benutzerdefinierten Typs arbeiten. #### Scenario: Datensatz schreiben und an gleicher Stelle lesen - **WHEN** ein Record an Position 3 geschrieben und anschließend von Position 3 gelesen wird - **THEN** stimmen alle Feldwerte mit den geschriebenen überein #### Scenario: RSET schneidet ab - **WHEN** in ein Feld der Länge 5 mit `RSET` der Text `ABCDEFG` geschrieben wird - **THEN** enthält das Feld genau 5 Zeichen ### Requirement: Record-Konvertierungsfunktionen Die Bibliothek SHALL die Konvertierungsfunktionen für Recordpuffer bereitstellen: `MKI$`, `MKL$`, `MKS$`, `MKD$` und `MKC$` wandeln einen numerischen Wert in seine Byte-Darstellung als String, `CVI`, `CVL`, `CVS`, `CVD` und `CVC` wandeln zurück. Jedes Paar MUST gegenläufig sein. Ein Argument, dessen Länge nicht zur Zielbreite passt, MUST Laufzeitfehler 5 „Illegal function call" auslösen. `MKSMBF$`, `MKDMBF$`, `CVSMBF` und `CVDMBF` SHALL dieselbe Rolle für das Microsoft-Binärformat älterer BASIC-Versionen erfüllen; ist dieses Format ein deklariertes Non-Feature, MUST die Ablehnung namentlich erfolgen und in der Sprachreferenz unter „Abweichungen" stehen. #### Scenario: MK und CV sind gegenläufig - **WHEN** `CVL(MKL$(123456))` ausgewertet wird - **THEN** ist das Ergebnis 123456 #### Scenario: Falsche Argumentlänge - **WHEN** `CVI("x")` mit einem ein Zeichen langen String ausgewertet wird - **THEN** tritt Laufzeitfehler 5 auf ### Requirement: Festtextfelder als UTF-32 mit fester Recordlänge Feste Strings (`STRING * n`) in Recordpuffern SHALL als UTF-32 gespeichert werden — 4 Bytes je Zeichen, damit `STRING * n` stets genau `4 * n` Bytes belegt und die Recordlänge unabhängig vom Zeicheninhalt konstant bleibt. Die daraus folgende Binär-Inkompatibilität zu Dateien des Vorbilds ist beschlossen (2026-09-02) und MUST in der Sprachreferenz unter „Abweichungen" dokumentiert sein. #### Scenario: Recordlänge unabhängig vom Inhalt - **WHEN** in ein Feld `STRING * 10` einmal `abc` und einmal `äöüßÄÖÜ` geschrieben wird - **THEN** belegt der Datensatz in beiden Fällen dieselbe Byteanzahl und der Folgesatz beginnt an derselben Position ### Requirement: Binärzugriff auf Byteebene `OPEN … FOR BINARY` SHALL byteweise Positionierung erlauben. `GET`/`PUT` SHALL an einer 1-basierten Byteposition lesen bzw. schreiben, wobei die übertragene Länge der Größe der Zielvariablen entspricht. `SEEK` als Anweisung SHALL die Position setzen, `SEEK` als Funktion sie liefern. #### Scenario: Positioniertes Byte überschreiben - **WHEN** in einer Binärdatei an Position 4 ein Byte geschrieben und anschließend gelesen wird - **THEN** liefert das Lesen den geschriebenen Wert und die übrigen Bytes bleiben unverändert ### Requirement: Positions- und Statusfunktionen `LOF` SHALL die Dateigröße in Bytes liefern, `LOC` die aktuelle Position in der Einheit der Zugriffsart (Datensatz bei `RANDOM`, Byte bei `BINARY`), `SEEK` die nächste Lese-/Schreibposition und `FILEATTR` den Modus bzw. das Betriebssystem-Handle. Alle Funktionen MUST bei einer nicht geöffneten Dateinummer Laufzeitfehler 52 auslösen. #### Scenario: Größe nach dem Schreiben - **WHEN** 100 Bytes geschrieben wurden und `LOF(1)` aufgerufen wird - **THEN** liefert `LOF(1)` den Wert 100 #### Scenario: Statusfunktion auf geschlossener Datei - **WHEN** `LOF(9)` ohne offene Datei 9 aufgerufen wird - **THEN** tritt Laufzeitfehler 52 auf ### Requirement: Satzsperren `LOCK` und `UNLOCK` SHALL einen Datensatz-, Byte- oder den gesamten Dateibereich sperren und wieder freigeben. Auf Plattformen ohne entsprechende Sperrmechanik MUST das Verhalten in der Sprachreferenz unter „Abweichungen" dokumentiert sein statt still abzuweichen. #### Scenario: Sperren und Freigeben - **WHEN** ein Datensatzbereich gesperrt und anschließend mit denselben Grenzen freigegeben wird - **THEN** läuft die Folgeoperation auf diesem Bereich ohne Fehler ### Requirement: Dateisystem-Anweisungen mit plattformübergreifender Pfadsemantik `KILL`, `NAME … AS`, `FILES`, `CHDIR`, `CHDRIVE`, `MKDIR`, `RMDIR`, `CURDIR$`, `DIR$` sowie `SHELL` als Anweisung **und** als Funktion SHALL verfügbar sein. `CURDIR$` SHALL das aktuelle Verzeichnis liefern, `DIR$` den ersten bzw. — bei leerem Argument — den nächsten Treffer eines Suchmusters und den leeren String, wenn keiner mehr folgt. Die `SHELL`-Funktion SHALL den Beendigungsstatus des aufgerufenen Prozesses liefern. `CHDRIVE` SHALL auf Plattformen ohne Laufwerksbuchstaben namentlich abgewiesen oder als Non-Feature dokumentiert werden. Pfade SHALL sowohl `\` als auch `/` als Trennzeichen akzeptieren und auf die Konvention der laufenden Plattform abgebildet werden; Laufwerksbuchstaben SHALL auf Nicht-Windows-Plattformen als gewöhnlicher Namensbestandteil behandelt oder mit benannter Ablehnung abgewiesen werden. Fehlende Dateien MUST Fehler 53 auslösen, fehlende Pfade Fehler 76 „Path not found". #### Scenario: Rückwärts-Schrägstrich auf Unix - **WHEN** `OPEN "unter\d.txt" FOR OUTPUT AS #1` auf einer Unix-Plattform ausgeführt wird - **THEN** entsteht die Datei `d.txt` im Verzeichnis `unter` #### Scenario: Verzeichnis durchlaufen - **WHEN** `DIR$("*.txt")` und danach `DIR$("")` wiederholt ausgewertet werden - **THEN** liefern sie nacheinander alle passenden Namen und zuletzt den leeren String #### Scenario: Fehlender Pfad - **WHEN** `MKDIR` in einem nicht existierenden Elternverzeichnis aufgerufen wird - **THEN** tritt Laufzeitfehler 76 auf ### Requirement: LPRINT als Druckausgabe `LPRINT` und `LPRINT USING` SHALL wie `PRINT` formatieren und in einen Druckerkanal schreiben. `LPOS` SHALL die aktuelle Spalte im Druckerpuffer liefern. Existiert auf der Plattform kein Druckerkanal, MUST das Ziel (Datei, Standardausgabe oder Ablehnung) in der Sprachreferenz unter „Abweichungen" dokumentiert sein. #### Scenario: LPRINT formatiert wie PRINT - **WHEN** `LPRINT 1; 2` ausgeführt wird - **THEN** entspricht der erzeugte Text der Ausgabe von `PRINT 1; 2` #### Scenario: Druckerspalte - **WHEN** nach `LPRINT "abc";` die Funktion `LPOS(0)` ausgewertet wird - **THEN** liefert sie die Spalte hinter dem ausgegebenen Text