MODULE CrossRef;
(*
  -------------------------------------------------------------------------
  CrossRef V1.0a, 27.07.1988, Written by J.R. Gutzke in M2Amiga Modula-2.
  -------------------------------------------------------------------------
  
  CrossRef V1.0a ist PUBLIC DOMAIN solange die Copyright-Meldung nicht
  entfernt oder verändert wird und solange der Source und die Anleitung
  von CrossRef V1.0a nicht in veränderter Form, außer vom Autor selbst,
  erneut als Public Domain oder in jeder anderen Art und Weise wieder
  veröffentlicht wird!
  CrossRef V1.0a darf ohne besondere Genehmigung des Autors als Source
  sowie als lauffähige compilierte Version weitergegeben und kopiert werden.
  
  Das Programm kann mit oder ohne Parameter aufgerufen werden. Wenn es sich
  bei dem aufrufenden Parameter um ein "?" (Fragezeichen) handelt, so wird
  eine Kurzanleitung für das Programm angezeigt und anschließend nach einem
  Filenamen gefragt.
  Bei einem Aufruf ohne Parameter wird direkt nach dem Programmstart ein
  Filename verlangt. Falls das Programm mit Parameter aufgerufen wurde, so
  wird von der Datei von welcher der Namen als Parameter übergeben worden ist,
  die Cross-Reference-Liste erstellt.
  Hat man es sich anders überlegt, so kann man mit "#" (Doppelkreuz) das
  Programm vorzeitig beenden und verlassen (auch als Übergabeparameter
  möglich! Häääh ???).
  
  Im Moment sind mir keine Fehler dieses Programmes bekannt! Falls irgend
  jemand einen solchen finden sollte so bitte ich um Zusendung einer Fehler-
  beschreibung, wenn möglich mit einer nachvollziehbarer Befehlsfolge, welche
  den Fehler hervorgerufen hat.
  Sollte das Programm während des Programmlaufs nicht genügend Speicher vor-
  finden, so wird mit einem Requester der Programmlauf absturzsicher abge-
  brochen (Dank dem Super-Laufzeitsystem von M2Amiga!) !
  In diesem Fall sollte man z.B. beim nächsten Versuch die Workbench
  oder andere Speicherintensive Programme weglassen.
  
  Kommentare, Bemerkungen, Ideen, Fehlerbeschreibungen, etc. und natürlich
  Kritik bitte an:
  
        Jörg Gutzke
        Dessauer Straße 54
        4050 Mönchengladbach 1
        West Germany
  *)
        
  FROM Arguments    IMPORT NumArgs, GetArg;
  FROM Arts         IMPORT Assert, detectCtrlC;
  FROM FileHandling IMPORT OpenFileRead, CloseFile, Done, Files,
                           EndOfFile, ReadCharacter;
  FROM InOut        IMPORT WriteCard, WriteString, WriteLn, Write,
                           ReadString;
  FROM Storage      IMPORT ALLOCATE, Available;
  FROM SYSTEM       IMPORT ADR;
  

  CONST
    BufferLaenge  = 10000;
    WortLaenge    = 16;
    OutOfMemory   = "   Nicht genug Speicher verfügbar!";
    CSI           = 233C;                    (* Control Sequence Introducer *)
    TextFett      = "1m";                    (* Fettschrift *)
    TextFettRot   = "1;33m";                 (* Fettschrift, Orange *)
    TextRekursiv  = "3m";                    (* Kursiv *)
    TextNormal    = "0;31m";                 (* Normalschrift, Weiß *)
              (* Die oben angegebenen Farbe weichen je nach Voreinstellung
                 entsprechend voneinander ab. *)


  TYPE
    String        = ARRAY [0..31] OF CHAR; (* maximale Namenslänge unter AMIGA-Dos *)
    WortPointer   = POINTER TO Wort;
    ObjektPointer = POINTER TO Objekt;
    Wort          = RECORD
                      Schluessel      : CARDINAL;
                      Erster, Letzter : ObjektPointer;
                      Links, Rechts   : WortPointer;
                    END;  (* RECORD *)
    Objekt        = RECORD
                      Zeilennummer : CARDINAL;
                      Naechster    : ObjektPointer;
                    END;  (* RECORD *)


  VAR
    Wurzel         : WortPointer;
    HilfsVariable0, HilfsVariable1, Zeile  : CARDINAL;
    Zeichen        : CHAR;
    Buffer         : ARRAY [0..BufferLaenge-1] OF CHAR;
    Input          : Files;
    ArgumentString : String;
    ArgumenteAnzahl, ArgumentLaenge, arg : INTEGER;



    PROCEDURE WortAusgeben(Zahl : CARDINAL);
    (* Schreibt ein Wort aus der Liste *)
    VAR
      Limit : CARDINAL;

    BEGIN
      Limit := Zahl + WortLaenge;
      WHILE Buffer[Zahl] > 0C DO
        Write(Buffer[Zahl]);
        INC(Zahl);
      END;  (* WHILE *)
      WHILE Zahl < Limit DO
        Write(' ');
        INC(Zahl);
      END;  (* WHILE *)
    END WortAusgeben;
    
    
    
    PROCEDURE ListeAusgeben(Zeiger : WortPointer);
    (* Ausgabe der Cross-Reference-Liste in alphabetischer Reihenfolge. *)
    VAR
      Zaehler, ZahlenProZeile : INTEGER;
      Objekt                  : ObjektPointer;

    BEGIN
      IF Zeiger # NIL THEN
        WITH Zeiger^ DO
          ListeAusgeben(Links);
          WortAusgeben(Schluessel);
          Objekt := Erster;
          ZahlenProZeile := 0;
          REPEAT
            IF ZahlenProZeile = 10 THEN         (* 10 Zeilennr. geschrieben? *)
              WriteLn();                        (* Ja, Zeilenvorschub *)
              ZahlenProZeile := 0;
              FOR Zaehler := 1 TO WortLaenge DO (* Zeilennr. in der nächsten *)
                Write(' ');                     (* Zeile einrücken. *)
              END;  (* FOR *)
            END;  (* IF *)
            INC(ZahlenProZeile);
            WriteCard(Objekt^.Zeilennummer, 6); (* Zeilennr. ausgeben *)
            Objekt := Objekt^.Naechster;
          UNTIL Objekt = NIL;
          WriteLn();
          ListeAusgeben(Rechts);
        END;  (* WITH *)
      END;  (* IF *)
    END ListeAusgeben;



    PROCEDURE DifferenzBestimmen(Zahl1, Zahl2 : CARDINAL) : INTEGER;
    (* Test ob das Verhältnis zweier zu vergleichender Worte gleich, kleiner
       oder größer zueinander ist. *)
    BEGIN
      LOOP
        IF Buffer[Zahl1] # Buffer[Zahl2] THEN
          RETURN INTEGER(ORD(Buffer[Zahl1]))-INTEGER(ORD(Buffer[Zahl2]));
        ELSIF Buffer[Zahl1] = 0C THEN
          RETURN 0;
        END;  (* IF *)
        INC(Zahl1);
        INC(Zahl2);
      END;  (* LOOP *)
    END DifferenzBestimmen;
    
    
    
    PROCEDURE InListeEinsortieren(VAR Zeiger : WortPointer);
    VAR
      Differenz : INTEGER;
      Objekt    : ObjektPointer;

    BEGIN
      IF Zeiger = NIL THEN
        Assert(Available(SIZE(Wort)), ADR(OutOfMemory));
        ALLOCATE(Zeiger, SIZE(Wort));
        Assert(Available(SIZE(Objekt)), ADR(OutOfMemory));
        ALLOCATE(Objekt, SIZE(Objekt));
        WITH Zeiger^ DO
          Schluessel := HilfsVariable0;
          Erster := Objekt;
          Letzter := Objekt;
          Links := NIL;
          Rechts := NIL;
        END;  (* WITH *)
        Objekt^.Zeilennummer := Zeile;
        Objekt^.Naechster := NIL;
        HilfsVariable0 := HilfsVariable1;
      ELSE
        Differenz := DifferenzBestimmen(HilfsVariable0, Zeiger^.Schluessel);
        IF Differenz < 0 THEN
          InListeEinsortieren(Zeiger^.Links);
        ELSIF Differenz > 0 THEN
          InListeEinsortieren(Zeiger^.Rechts);
        ELSE
          Assert(Available(SIZE(Objekt)), ADR(OutOfMemory));
          ALLOCATE(Objekt, SIZE(Objekt));
          Objekt^.Zeilennummer := Zeile;
          Objekt^.Naechster := NIL;
          Zeiger^.Letzter^.Naechster := Objekt;
          Zeiger^.Letzter := Objekt;
        END;  (* IF *)
      END;  (* IF *)
    END InListeEinsortieren;
    
    
    
    PROCEDURE LeseWort();
    BEGIN
      HilfsVariable1 := HilfsVariable0;
      REPEAT
        Write(Zeichen);
        Buffer[HilfsVariable1] := Zeichen;
        INC(HilfsVariable1);
        ReadCharacter(Input, Zeichen);
      UNTIL (Zeichen < '0') OR (Zeichen > '9') AND
            (CAP(Zeichen) < 'A') OR (CAP(Zeichen) > 'Z') AND
            (ORD(Zeichen) < 192) OR EndOfFile(Input);
      Buffer[HilfsVariable1] := 0C;
      INC(HilfsVariable1);
      InListeEinsortieren(Wurzel);
    END LeseWort;
    
    
    
    PROCEDURE ZeigeHilfe();
    BEGIN
      Write(CSI); WriteString(TextFett);
      WriteString('Gebrauch:');
      WriteLn();
      Write(CSI); WriteString(TextFettRot);
      WriteString('  CrossRef [Dateiname] / [?]');
      Write(CSI); WriteString(TextFett);
      WriteLn();
      WriteLn();
      Write(CSI); WriteString(TextNormal);
      Write(CSI); WriteString(TextRekursiv);
      WriteString('  Bitte benutzen Sie den ">"-Befehl um die Ausgabe des');
      WriteLn();
      WriteString('  Programms umzulenken (Siehe AmigaDOS-Handbuch).');
      WriteLn();
      WriteLn();
      WriteString('  Z.B.: ');
      Write(CSI); WriteString(TextNormal);
      Write(CSI); WriteString(TextFett);
      WriteString('CrossRef >PRT: CrossRef.mod');
      Write(CSI); WriteString(TextNormal);
      Write(CSI); WriteString(TextRekursiv);
      WriteString(' um eine Cross-Reference-Liste');
      WriteLn();
      WriteString('  von "CrossRef.mod" auf den Drucker ausgeben zu lassen.');
      Write(CSI); WriteString(TextNormal);
      WriteLn();
      WriteLn();
      WriteLn();
      ArgumenteAnzahl := 0;
    END ZeigeHilfe;



  BEGIN
(*    detectCtrlC := FALSE;              (* KEIN Benutzer-Abbruch möglich *)*)
    WriteLn();
    Write(CSI); WriteString(TextFett);
    WriteString('CrossRef V1.0a,  27.07.1988,  Written by J.R. Gutzke');
    WriteLn();
    Write(CSI); WriteString(TextNormal);
    Write(CSI); WriteString(TextRekursiv);
    WriteString('   This Program is PUBLIC DOMAIN! Copy it if you like it!');
    Write(CSI); WriteString(TextNormal);
    WriteLn();
    WriteLn();
    ArgumenteAnzahl := NumArgs();
    IF ArgumenteAnzahl = 1 THEN
      arg := 1;
      GetArg(arg, ArgumentString, ArgumentLaenge);
      IF ArgumentLaenge = 1 THEN
        IF ArgumentString[0] = '?' THEN
          ZeigeHilfe();
        END;  (* IF *)
      END;  (* IF *)
    END;  (* IF *)
    LOOP
      IF ArgumenteAnzahl = 0 THEN
        WriteString('Dateiname ("#" = Ende): ');
        ReadString(ArgumentString);
        WriteLn();
      END;  (* IF *)
      IF ArgumentString[0] = '#' THEN
        EXIT;
      END;  (* IF *)
      Input := OpenFileRead(ArgumentString);
      IF Done(Input) THEN
        EXIT;
      END;  (* IF *)
      ArgumenteAnzahl := 0;
      Write(CSI); WriteString(TextFettRot);
      WriteString('FEHLER: Datei konnte nicht geöffnet werden!');
      Write(CSI); WriteString(TextNormal);
      WriteLn();
      WriteLn();
    END;  (* LOOP *)
    IF ArgumentString[0] <> '#' THEN
      Wurzel := NIL;
      HilfsVariable0 := 0;
      Zeile := 0;
      WriteCard(0, 6);
      Write(' ');
      ReadCharacter(Input, Zeichen);
      WHILE NOT EndOfFile(Input) DO
        CASE Zeichen OF
           0C..11C   : ReadCharacter(Input, Zeichen);
        | 12C        : WriteLn();
                       ReadCharacter(Input, Zeichen);
                       INC(Zeile);
                       WriteCard(Zeile, 6);
                       Write(' ');
        | 13C..37C   : ReadCharacter(Input, Zeichen);
        | " ".."@"   : Write(Zeichen);
                       ReadCharacter(Input, Zeichen);
        | "A".."Z"   : LeseWort();
        | "[".."`"   : Write(Zeichen);
                       ReadCharacter(Input, Zeichen);
        | "a".."z"   : LeseWort();
        | "{".."~"   : Write(Zeichen);
                       ReadCharacter(Input, Zeichen);
        | 300C..377C : LeseWort();
        ELSE
          ReadCharacter(Input, Zeichen);
        END;  (* CASE *)
      END;  (* WHILE *)
      WriteLn();
      WriteLn();
      CloseFile(Input);
      ListeAusgeben(Wurzel);
    END;  (* IF *)
  END CrossRef.
