# 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". FREEFILE SHALL die gemeinsame Belegung durch sequenzielle, wahlfreie, binäre und ISAM-Bindungen berücksichtigen. #### 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 #### Scenario: ISAM belegt eine Dateinummer - **WHEN** eine ISAM-Tabelle auf #1 und sonst keine Datei offen ist - **THEN** liefert FREEFILE den Wert 2 ### 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. INPUT # SHALL nicht konsumierte Felder einer Zeile für spätere Aufrufe bewahren und bei Bedarf über Zeilen hinweg weiterlesen. Fehlende geforderte Felder am Dateiende MUST Fehler 62 auslösen statt als leere Ersatzwerte zu gelten. #### 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 #### Scenario: Felder über zwei Aufrufe - **WHEN** WRITE #1, "a", "b" geschrieben und nach Wiederöffnung zweimal INPUT #1 mit je einer Stringvariablen ausgeführt wird - **THEN** liefern die Aufrufe a und b #### Scenario: Zu wenige Felder - **WHEN** nur ein Feld vorhanden ist und INPUT #1 zwei Felder verlangt - **THEN** tritt Fehler 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. Die Argumentlänge der CV-Funktionen SHALL exakt ihrer Zielbreite entsprechen, nicht nur einer Mindestbreite. #### 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 #### Scenario: Zu langes CVI-Argument - **WHEN** CVI("abc") 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 #### Scenario: OPEN mit fehlendem Elternpfad - **WHEN** OPEN FOR INPUT einen Dateinamen in einem nicht vorhandenen Elternverzeichnis verwendet - **THEN** tritt Fehler 76 auf; fehlt nur die Datei in einem vorhandenen Verzeichnis, tritt Fehler 53 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