(**************************************************************************


:Program.       Dup.mod

:Contents.      dupliziert einen Ausgabestrom

:Usage.         [Run] Dup [3mSource Dest1 Dest2 BufSize[0m

:Bugs.          Dup sollte nur von der Shell gestartet werden. Ein
:Bugs.          Workbench-Start ist zu unterlassen und wird auch nicht
:Bugs.          abgefangen!

:Copyright.     © 1991 by

:Author.        Thomas Ansorge

:Address.       Dinkelackerring 55, W-6730 Neustadt, Deutschland


:Language.      Modula-2

:Translator.    M2Amiga V4.0 (deutsch)


:Version.       1.0 vom 27.12.1991

:History.       1.0 vom 27.12.1991: Es läuft.


:Remark.        Es soll möglichst kurz werden.

:Remark.        Darunter soll aber die Bequemlichkeit nicht leiden.

:Remark.        PIPE: darf zum Lesen nur DosD.oldFile und zum Schreiben
:Remark.        nur DosD.newFile geöffnet werden, sonst Fehlermeldung!


**************************************************************************)


(* Der Sinn der folgenden Compiler-Anweisungen ist, Code zu sparen:      *)

(*$ StackChk := FALSE  RangeChk := FALSE  OverflowChk := FALSE
    NilChk := FALSE  EntryClear := FALSE  CaseChk := FALSE
    ReturnChk := FALSE  Volatile := FALSE  LargeVars := FALSE
    StackParms := FALSE                                                  *)

(* Die folgende Variable macht bei TRUE 2 Sicherheitsabfragen beim *)
(* Schreiben und 64 Bytes mehr Code. *)

(*$ DEFINE Sicher := TRUE *) 

MODULE Dup;

FROM Arts IMPORT dosCmdBuf, dosCmdLen, returnVal, Terminate;

FROM DosD IMPORT FileHandlePtr, newFile, oldFile;

FROM DosL IMPORT Close, Open, Output, Read, Write;

FROM SYSTEM IMPORT ADR;

(* --------------------------------------------------------------------- *)

(* die Ausgaben im Fehlerfalle: *)

CONST BufText    = "Buffergröße!\x0A";
      Dest1Text  = "Dest 1!\x0A";
      Dest2Text  = "Dest 2!\x0A";
      SourceText = "Source!\x0A";
      Usage1     = " Dup © 1991 Thomas Ansorge\x0A";
      Usage2     = " Usage: " +
         "[Run] Dup [3mSource Dest1 Dest2 BufSize[0m\x0A";

(* Die Länge des gelesenen Strings. PIPE: sorgt dafür, daß dieser String *)
(* immer erst gefüllt wird (bis 4 kByte oder Dateiende) bevor es weiter- *)
(* geht!                                                                 *)

CONST BufferStringMax = 4096;

TYPE String          = ARRAY [0..80] OF CHAR;
     StringPtr       = POINTER TO String;
     BufferString    = ARRAY [0..BufferStringMax - 1] OF CHAR;
     BufferStringPtr = POINTER TO BufferString;

VAR (* die 3 Dateien: *)
    Dest1,
    Dest2,
    Source: FileHandlePtr;

VAR Outputfile: FileHandlePtr;

VAR File1,
    File2,
    File3  : String;

VAR Buffer : BufferString;
    BufSize: LONGINT;

VAR Gelesen   : LONGINT;
    i         : INTEGER;
    Laenge    : LONGINT;

(* --------------------------------------------------------------------- *)

(*$ CopyDyn := FALSE *)

PROCEDURE Error (Bed: BOOLEAN;
                 Str: StringPtr;
                 Len: INTEGER);

   (* globale Variable: Outputfile *)

   CONST ErrorMsg = " Fehler bei ";

   (* ------------------------------------------------------------------ *)

   BEGIN (* Prozedur Error *)

   IF Bed THEN
      Laenge := Write (Outputfile, ADR (ErrorMsg), SIZE (ErrorMsg));
      Laenge := Write (Outputfile, Str, Len);

      returnVal := 20; (* Mißerfolg *)
      Terminate ();
   END (* IF Bed *);
END Error (* Prozedur *);

(* --------------------------------------------------------------------- *)

(*$ CopyDyn := FALSE *)

PROCEDURE GetArg (ArgNummer   : INTEGER;
                  VAR Argument: ARRAY OF CHAR;
                  VAR Len     : INTEGER);

   (* Ersetzt Arguments.GetArg der Code-Länge wegen. *)

   VAR AString  : BufferStringPtr;
       Hochkomma: BOOLEAN;
       i        : INTEGER;
       Nummer   : INTEGER;

   (* ------------------------------------------------------------------ *)

   BEGIN (* Prozedur GetArg *);

   Argument [0] := "\o"; (* Leerstring *)
   AString := dosCmdBuf;
   Hochkomma := FALSE;
   Len := 0;
   Nummer := 1;

   FOR i := 0 TO dosCmdLen - 2 DO
      IF (AString^ [i] = " ") AND NOT Hochkomma THEN (* nächstes Arg *)
         INC (Nummer);

      ELSE
         IF AString^ [i] = "\"" THEN
            Hochkomma := NOT Hochkomma;

         ELSE (* kein Space außerhalb String, kein Hochkomma *)
            IF Nummer = ArgNummer THEN
               Argument [Len] := AString^ [i];
               INC (Len);
               Argument [Len] := "\o";
            END (* IF Nummer *);
         END (* IF AString^ *);
      END (* IF (AString^ *);
   END (* FOR i *);
END GetArg (* Prozedur *);

(* --------------------------------------------------------------------- *)

(*$ CopyDyn := FALSE *)

PROCEDURE ZehnHoch (i: INTEGER): LONGINT;

   VAR Zahl: LONGINT;
       j   : INTEGER;

   (* ------------------------------------------------------------------ *)

   BEGIN (* Funktion ZehnHoch *)

   Zahl := 1;

   FOR j := 1 TO i DO
      Zahl := Zahl * 10;
   END (* FOR j *);

   RETURN Zahl;
END ZehnHoch (* Prozedur *);

(* --------------------------------------------------------------------- *)
(* --------------------------------------------------------------------- *)

BEGIN (* Modul Dup *)

(* initialisieren... *)

Outputfile := Output ();

(* die Bufferlänge *)

GetArg (4, File1, i);

(* Die folgende Abfrage ist hier zulässig, weil das aktuelle Verzeichnis *)
(* als Argument unzulässig ist!                                          *)

IF i = 0 THEN
   Laenge := Write (Outputfile, ADR (Usage1), SIZE (Usage1));
   Laenge := Write (Outputfile, ADR (Usage2), SIZE (Usage2));

   Terminate ();
END; (* IF i = 0 *)

(* Wenn das 4. Argument existiert, existieren die Argumente 1 - 3 auch! *)

BufSize := 0;

IF i > 4 THEN (* maximal 4 Ziffern *)
   i := 4;
END (* IF i *);

i := i - 1; (* Rationalisierung für die FOR-Schleife *)

FOR Laenge := 0 TO i DO
   IF (File1 [Laenge] >= "0") AND (File1 [Laenge] <= "9") THEN
      BufSize := BufSize + (ORD (File1 [Laenge]) - ORD ("0")) *
         ZehnHoch (i - Laenge);

   ELSE (* Fehler passiert *)
      Error (TRUE, ADR (BufText), SIZE (BufText));
   END (* IF (Str *);
END (* FOR i *);

Error (BufSize = 0, ADR (BufText), SIZE (BufText));

IF BufSize > BufferStringMax THEN
   BufSize := BufferStringMax;
END (* IF BufSize *);

(* die 3 Files öffnen *)

GetArg (1, File1, i);
GetArg (2, File2, i);
GetArg (3, File3, i);

Source := Open (ADR (File1), oldFile);
Error (Source = NIL, ADR (SourceText), SIZE (SourceText));

Dest1 := Open (ADR (File2), newFile);
Error (Dest1 = NIL, ADR (Dest1Text), SIZE (Dest1Text));

Dest2 := Open (ADR (File3), newFile);
Error (Dest2 = NIL, ADR (Dest2Text), SIZE (Dest2Text));

REPEAT
   Gelesen := Read (Source, ADR (Buffer), BufSize);

   IF Gelesen # 0 THEN
      Laenge := Write (Dest1, ADR (Buffer), Gelesen);

      (*$ IF Sicher *)
         Error (Laenge # Gelesen, ADR (Dest1Text), SIZE (Dest1Text));
      (*$ ENDIF *)

      Laenge := Write (Dest2, ADR (Buffer), Gelesen);

      (*$ IF Sicher *)
         Error (Laenge # Gelesen, ADR (Dest2Text), SIZE (Dest2Text));
      (*$ ENDIF *)

   END (* IF Gelesen *);
   (* Ein ASCII.eof kommt üblicherweise nicht! *)
UNTIL Gelesen = 0;

CLOSE; (* am Programmende aufräumen *)

IF Dest1 # NIL THEN
   Close (Dest1);
   Dest1 := NIL;
END (* IF Dest1 *);

IF Dest2 # NIL THEN
   Close (Dest2);
   Dest2 := NIL;
END (* IF Dest2 *);

IF Source # NIL THEN
   Close (Source);
   Source := NIL;
END (* IF Source *);

END Dup (* Modul *).