(*---------------------------------------------------------------------------
 :Program.      MuchMore.mod
 :Author.       Fridtjof Siebert
 :Address.      Nobileweg 67, D-7000 Stuttgart 40
 :Shortcut.     [fbs]
 :Copyright.    Freeware
 :Language.     Oberon-2
 :Translator.   Amiga Oberon Compiler v3.01
 :History. V1.0 summer-88: First very slow internal version              [fbs]
 :History. V1.1 24-Sep-88: First published version                       [fbs]
 :History. V1.2 26-Nov-88: Now displays Filelength & Percentage          [fbs]
 :History.      27-Nov-88: Mouse can be used instead of Space / BackSpace[fbs]
 :History. V1.3 29-Apr-89: Strong increase in speed, removed WarpText    [fbs]
 :History.      29-Apr-89: Now supports Numeric Keys (Home,PgUp etc.)    [fbs]
 :History.      29-Apr-89: Now opens Screen as big as gfx.normalDisplay  [fbs]
 :History. V1.4 29/30-Apr-89: Asynchronus loading / displaying. Very nice[fbs]
 :History.      30-Apr-89, 00:33: Removed bugs in Filelength & L-Command [fbs]
 :History.      30-Apr-89, 02:21: Added Find-Command                     [fbs]
 :History.      30-Apr-89, 10:30: Scrolling stops when window is inactive[fbs]
 :History.      01-May-89: Allocates no more unneeded memory for text    [fbs]
 :History.      07-May-89: Allocates even less memory now                [fbs]
 :History.      14-May-89: Removed deadlock-bug with Find-Window         [fbs]
 :History. V1.5 25-May-89: Added print feature                           [fbs]
 :History.      25-May-89: Removed all imports (apart from Arts)         [fbs]
 :History.      26-May-89: inspired by J. Kupfer, I added nk 5 to quit   [fbs]
 :History.      26-May-89: Now handle BS correctly                       [fbs]
 :History. V1.6 02-Jul-89: Now supports several fontstyles and colors    [fbs]
 :History. V1.7 03-Jul-89: Is again as fast as it was with 2 colors      [fbs]
 :History.      03-Jul-89: Now no more crashes when quitting while print [fbs]
 :History.      07-Jul-89: removed bug with texts of length 0            [fbs]
 :History. V1.8 10-Jul-89: small bug in find-command removed             [fbs]
 :History.      10-Jul-89: now found strings are highlighted             [fbs]
 :History.      14-Jul-89: nk0 to display fileinfo                       [fbs]
 :History. V2.0 06-Aug-89: Ported this to OBERON                         [fbs]
 :History.      06-Aug-89: Added ARP-FileRequester                       [fbs]
 :History.      07-Aug-89: Added L - (load new file) Command             [fbs]
 :History. V2.1 03-Sep-89: no more gurus if an r/w error occures         [fbs]
 :History.      03-Sep-89: MM used to execute CSI-Codes backwards. fixed [fbs]
 :History.      03-Sep-89: ping / pong with Shift+Fn / Fn                [fbs]
 :History.      03-Sep-89: new command: goto                             [fbs]
 :History. V2.2 05-Sep-89: will run with any keymapping now              [fbs]
 :History. V2.3 17-Sep-89: New command: sleep & Pop-Up feature           [fbs]
 :History.      17-Sep-89: "MuchMore -s" will go to sleep immediately    [fbs]
 :History.      17-Sep-89: Interprets <CSI>m as <CSI>0m now              [fbs]
 :History. V2.4 17-Sep-89: New command: write block "w"                  [fbs]
 :History.      17-Sep-89: rewritten argument parser to allow quotes     [fbs]
 :History. V2.5 18-Sep-89: now uses the 8x8 font set with SetFont        [fbs]
 :History.      19-Sep-89: no more scatters memory. Allocates 4K Chunks  [fbs]
 :History. V2.6 26-Jun-90: Made MuchMore reentrant                       [fbs]
 :History.      26-Jun-90: Opens 1-Plane Screen if memory is rare        [fbs]
 :History.      26-Jun-90: Asynchronus fast scrolling with Ctrl-Up/Down  [fbs]
 :History.      26-Jun-90: Now supports interlaced screens               [fbs]
 :History.      08-Aug-90: CLI-Option '-l' to toggle interlaced mode     [fbs]
 :History. V2.7 09-Aug-90: no more RethinkDisplay()s,looks good with 2.0 [fbs]
 :History.      10-Aug-90: Supports Kick2.0 ASL-FileRequester            [fbs]
 :History. V2.8 26-Dez-90: Leaves space between lines on interlaced scrns[fbs]
 :History. V3.0 04-Jul-91: Supports any non-proportional font now        [fbs]
 :History.      04-Jul-91: no more supports '-s' (sleep), was rarely used[fbs]
 :History.      04-Jul-91: new Options -f<font> and -s<size> for font    [fbs]
 :History.      09-Nov-91: Find works w/ dmouse (window may get inactive)[fbs]
 :History. V3.1 04-Sep-92: Uses Screenmode of Workbench screen           [fbs]
 :History. V3.2 02-Nov-92: Supports non-scrollable screens               [fbs]
 :History.      02-Nov-92: Complete redraw doesn't scroll anymore        [fbs]
 :History. V3.2.1 24-Dec-92: XPK Support                         (Chr. Stiens)
 :History.      24-Dec-92: New option -p for Password            (Chr. Stiens)
 :History.      24-Dec-92: New option -e for Extra Spacing       (Chr. Stiens)
 :History.      24-Dec-92: New option -c for Screen Colors       (Chr. Stiens)
 :History.      24-Dec-92: Tooltypes                             (Chr. Stiens)
 :History. V3.2.2 08-Jan-93: Doesn't use MyMakeScreen() no more  (Chr. Stiens)
 :History. V3.2.3 08-Feb-93: Now closes Console Device           (Chr. Stiens)
 :History.      08-Feb-93: Non-Scroll Mode didn't work always    (Chr. Stiens)
 :History.      10-Feb-93: ScreenMode Requester                  (Chr. Stiens)
 :History.      10-Feb-93: New Option -s for Scroll Mode         (Chr. Stiens)
 :History.      10-Feb-93: Busy Pointer                          (Chr. Stiens)
 :History.      14-Feb-93: Now evals Tooltypes also on CLI start (Chr. Stiens)
 :History. V3.2.4 15-Feb-93: Clears Idcmp while Busy             (Chr. Stiens)
 :History.      15-Feb-93: Bugs in GetString fixed               (Chr. Stiens)
 :History.      19-Feb-93: More Scrollmodes                      (Chr. Stiens)
 :History.      20-Feb-93: Bug in Type() fixed                   (Chr. Stiens)
 :History. V3.2.5 08-Mar-93: Removed Asynch Scrolling            (Chr. Stiens)
 :History.      08-Mar-93: New Option -t for Taskpri             (Chr. Stiens)
 :History.      10-Mar-93: Now uses Dos.ReadArgs if KS 2.04      (Chr. Stiens)
 :History. V3.2.6 19-Mar-93: scrollmode 3 now also scrolls soft  (Chr. Stiens)
 :History.      19-Mar-93: New Option -o for one plane           (Chr. Stiens)
 :History.      19-Mar-93: New Option -a for tab width           (Chr. Stiens)
 :History.      19-Mar-93: Removed QText                         (Chr. Stiens)
 :History.      21-Mar-93: Opens screen with full overscan width (Chr. Stiens)
 :History.      22-Mar-93: Filename can be on any pos at KS1.3   (Chr. Stiens)
 :History.      27-Mar-93: New Option N=NOOSCAN                  (Chr. Stiens)
 :History. V3.2.7 07-Apr-93: Dont pokes to bitmap no more        (Chr. Stiens)
 :Contents.     A Soft-Scrolling ASCII-File Printer.
 :Remark.       Compile: 'Oberon -[svbcrntz]dma MuchMore'
 :Remark.       Link:    'OLink -dma MuchMore'
---------------------------------------------------------------------------*)


MODULE MuchMore; (* $StackChk- *)

IMPORT d  := Dos,
       e  := Exec,
       g  := Graphics,
       gt := GadTools,
       I  := Intuition,
       ie := InputEvent,
       str:= Strings,
       u  := Utility,
       ol := OberonLib,
       SYS:= SYSTEM;

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

CONST
  oom = "Out of memory!";
  cos = "Can't open screen!";
  cow = "Can't open window!";
  cof = "Can't open file!";

  usage = "Usage: MuchMore [-a#|-c#{,#}|-e#|-f<name>/#|-l|-o|-p<name>|-s#|-t#|file]\n"
          "\ta = tab width\n"
          "\tc = colors\n"
          "\te = extra spacing\n"
          "\tf = font/size\n"
          "\tl = toggle lace\n"
          "\to = one plane\n"
          "\tp = password\n"
          "\ts = scroll mode\n"
          "\tt = toolpri";

  rwerr = "Read/Write Error";
  noarp = "Need arp for FileReq";

  conerr = "Console problem";
  MuchText = "MuchMore V3.2.7 © 1993 AMOK\o$VER: muchmore 3.27 (7.4.93)";
  w = TRUE;
  f = FALSE;
  MaxLen = 256;

  MyIdcmp = LONGSET{I.inactiveWindow,I.activeWindow,I.rawKey,I.mouseButtons};

(* Control codes: *)
  plain    = CHR(17);
  italic   = CHR(18);
  bold     = CHR(19);
  boldit   = CHR(20);
  ulineon  = CHR(21);
  ulineoff = CHR(22);

  Italic = 0;
  Bold   = 1;
  Ulin   = 2;
  Inv    = 3;

  saInterleaved = I.saDummy + 00022H;


TYPE
  String    = e.STRING;
  StringPtr = e.STRPTR;
  CharPtr   = UNTRACED POINTER TO CHAR;

  TextLinePtr = UNTRACED POINTER TO TextLine;
  TextLine = STRUCT
               prev,
               next : TextLinePtr;
               len  : INTEGER;
               size : INTEGER;
               text : String;
             END;

TYPE
  FileRequesterPtr = UNTRACED POINTER TO FileRequester;
  FileRequester = STRUCT
                    hail      : StringPtr;
                    ddef      : StringPtr;
                    ddir      : StringPtr;
                    wind      : I.WindowPtr;
                    funcFlags : SHORTSET;
                    flags2    : SHORTSET;
                    function  : PROCEDURE;
                    reserved2 : LONGINT;
                  END;

  WBStartupPtr = UNTRACED POINTER TO STRUCT (message : e.Message)
                   process    : d.ProcessId;
                   segment    : e.BPTR;
                   numArgs    : LONGINT;
                   toolWindow : StringPtr;
                   argList : UNTRACED POINTER TO ARRAY 256 OF STRUCT
                                 lock : d.FileLockPtr;
                                 name : StringPtr;
                             END;
                 END;

  DiskObjectPtr = UNTRACED POINTER TO STRUCT
                    magic      : INTEGER;
                    version    : INTEGER;
                    gadget     : I.Gadget;
                    type       : SHORTINT;
                    defaultTool: StringPtr;
                    toolTypes  : e.APTR;
                    currentX   : LONGINT;
                    currentY   : LONGINT;
                    drawerData : e.APTR;
                    toolWindow : StringPtr;
                    stackSize  : LONGINT;
                  END;

  LongPtr = UNTRACED POINTER TO LONGINT;

  Args = STRUCT
           a : LongPtr;
           c : StringPtr;
           d : StringPtr;
           e : LongPtr;
           f : StringPtr;
           n : StringPtr;
           o : StringPtr;
           p : StringPtr;
           s : LongPtr;
           t : LongPtr;
           file : StringPtr;
         END;

VAR
  pub: I.ScreenPtr;              (* default public screen             *)
  Screen: I.ScreenPtr;           (* Screen that contains the Text     *)
  rp: g.RastPortPtr;             (* Screen's RastPort                 *)
  id: LONGINT;                   (* Display ID                        *)
  BM: g.BitMapPtr;               (* Screen's BitMap                   *)
  Window: I.WindowPtr;           (* My window                         *)
  win: I.WindowPtr;              (* window for Find & Goto            *)
  MyFile: d.FileHandlePtr;       (* For loading Textfile              *)
  MyAttr: g.TextAttr;            (* The selected Font attributes      *)
  MyFont: g.TextFontPtr;         (* The selected Font                 *)
  FontName: String;              (* My Font Name or ""                *)
  FontSize: INTEGER;             (* My Font Size                      *)
  FirstLine: TextLinePtr;        (* the topmost Line                  *)
  TopLine: TextLinePtr;          (* the topmost Line                  *)
  BottomLine: TextLinePtr;       (* Last Line displayed on Screen     *)
  LoadLine: TextLinePtr;         (* currently loaded Line             *)
  LastLine: TextLinePtr;         (* Last element of LineList          *)
  Name,OldName: String;          (* Text's Name                       *)
  option: String;                (* CLI Option                        *)
  taskpri,oldpri: SHORTINT;      (* Muchmore's Task Priority          *)
  Pens: String;                  (* Screen colors                     *)
  Cols: ARRAY 4 OF INTEGER;      (* Color array for LoadRGB4          *)
  icon: DiskObjectPtr;           (* .info                             *)
  nameptr: StringPtr;            (* String Pointer                    *)
  chptr: CharPtr;                (* Char Pointer                      *)
  PStr: String;                  (* The command for Dos.Execute       *)
  Buffer: ARRAY 512 OF CHAR;     (* Buffer for Reading                *)
  RQPos: LONGINT;                (* Position within ReadBuffer        *)
  RQLen: LONGINT;                (* Number of CHARs in Buffer         *)
  scrollmode: INTEGER;           (* 0=MakeScreen/MrgCop 1=ScrollVPort *)
  NumLines: INTEGER;             (* Number of Lines on Screen         *)
  fontWidth,fontHeight: INTEGER; (* Font size                         *)
  fontBaseLine: INTEGER;         (* Font base line                    *)
  spacing: INTEGER;              (* Extra Line Spacing                *)
  NumColumns: INTEGER;           (* Number of Columns on Screen       *)
  PageSize: LONGINT;             (* fontHeight*NumLines*NumColumns    *)
  PageHeight: INTEGER;           (* fontHeight*NumLines               *)
  LineSize: LONGINT;             (* fontHeight*NumColumns             *)
  AnzLines: LONGINT;             (* Length of Text in Lines           *)
  MyLock,OldDir: d.FileLockPtr;  (* To Examine and Load File          *)
  oldcd: d.FileLockPtr;          (* To save old CD                    *)
  FileInfo: d.FileInfoBlock;     (* to get File's length              *)
  FileLength,TextLength: LONGINT;(* Length of File and Displayed Text *)
  ScreenPos: INTEGER;            (* actual position within bitmap     *)
  ShowTask: e.Task;              (* the task that displays the text   *)
  ShowStack: ARRAY 1024 OF LONGINT; (* it's stack                     *)
  ShowTaskRunning: BOOLEAN;      (* is Showtask activated?            *)
  SignalNewData: BOOLEAN;        (* Signal when new data is loaded    *)
  SignalAllRead: BOOLEAN;        (* send signal at end of text        *)
  Done: BOOLEAN;                 (* Quit                              *)
  print: BOOLEAN;                (* print text                        *)
  NewText: BOOLEAN;              (* load new text                     *)
  Info: BOOLEAN;                 (* is info currently displayed ?     *)
  modeReq: BOOLEAN;              (* Show Display Mode Requester?      *)
  onePlane: BOOLEAN;             (* Only one Bitplane?                *)
  Scroll: BOOLEAN;               (* scrolling or waiting?             *)
  Fast: BOOLEAN;                 (* scrollquick?                      *)
  Sync: BOOLEAN;                 (* scroll very quick?                *)
  lace,oldLace: BOOLEAN;         (* use interlaced screen?            *)
  Decrunched: BOOLEAN;           (* Is file decrunched?               *)
  Scrollable: BOOLEAN;           (* is screen able to scroll?         *)
  noOscan: BOOLEAN;              (* Open Text-overscan Screen?        *)
  overscanTag: LONGINT;          (* overscan tag                      *)
  mySigBit: INTEGER;             (* My SignalBit                      *)
  mySig: LONGSET;                (* My SignalSet = LONGSET{mySigBit}  *)
  Me: d.ProcessPtr;              (* my main task                      *)
  MyMsgPtr: I.IntuiMessagePtr;   (* for receiving Messages            *)
  i,j: INTEGER;                  (* count                             *)
  frame: INTEGER;                (* Frame Count                       *)
  in,out: d.FileHandlePtr;       (* i/o for TYPE xxx TO PRT:          *)
  fg,bg: INTEGER;                (* Text colors                       *)
  style: SHORTSET;               (* Text style                        *)
  CommLine: UNTRACED POINTER TO ARRAY 1000 OF CHAR;(* The CLI-commands*)
  tabw: INTEGER;                 (* Tabulator width                   *)
  rd: d.RDArgsPtr;               (* For ReadArgs                      *)
  args: Args;                    (* My CLI Args                       *)
  ArgPtr: StringPtr;              (* to get WBArg                      *)
  wbm: WBStartupPtr;             (* WBenchMessage                     *)
  ri: g.RasInfoPtr;              (* Screen's ViewPort's RasInfo       *)
  NuScreen: I.NewScreen;         (* to open screen                    *)
  dims: g.DimensionInfo;         (* Dims for KS2.0                    *)
  disp: g.DisplayInfo;           (* DisplayInfo for KS2.0             *)
  NuWindow: I.NewWindow;         (* to open window                    *)
  Prefs: I.Preferences;          (* Preferences (need wbLace)         *)
  StrGadget: I.Gadget;           (* Gadget for Find-Command           *)
  StrInfo: I.StringInfo;         (* its special info                  *)
  arp: e.LibraryPtr;             (* ArpBase                           *)
  asl: e.LibraryPtr;             (* ASL-librarybase                   *)
  diskFontBase : e.LibraryPtr;   (* DiskFont-LibraryBase              *)
  xpk: e.LibraryPtr;             (* XpkMaster-Librarybase             *)
  iconBase: e.LibraryPtr;        (* Icon-Librarybase                  *)
  body,text,ok: I.IntuiText;     (* IntuiTexts for AutoRequest        *)
  FR: FileRequester;             (* The Requester                     *)
  Filename: String;              (* The Filename (without path)       *)
  Dirname: String;               (* its path                          *)
  Pattern: ARRAY 80 OF CHAR;     (* The pattern for Filerequester     *)
  Password: String;              (* Password for encrypted texts      *)
  NewDisp: BOOLEAN;              (* need to rebuild Display ?         *)
  TextMarkers: ARRAY 10 OF TextLinePtr; (* Marked Positions in text   *)
  FindLine: TextLinePtr;         (* Last found line                   *)
  KeyMap: ARRAY 50H OF CHAR;     (* console's KeyMap                  *)
  wreq: e.IOStdReq;              (* Console IO-Request                *)
  console: e.DevicePtr;          (* the console.device                *)
  ievent: ie.InputEvent;         (* InputEvent to convert keycodes    *)
  WriteName: String;             (* File to write Block               *)
  savefrom,savesize: LONGINT;    (* How much to save?                 *)
  save: BOOLEAN;                 (* save block                        *)
  buffer: UNTRACED POINTER TO LONGINT; (* buffer to save file         *)
  c: CHAR;                       (* \ used by GetTextLine();          *)
  le: INTEGER;                   (* / global for speed                *)

(*------  Memory:  ------*)

CONST ChunkSize = 16384;          (* size of allocated chunks *)

TYPE
  MemChunkPtr = UNTRACED POINTER TO MemChunk;  (* chunklist *)
  MemChunk = STRUCT
               prev: MemChunkPtr;     (* link *)
               data: ARRAY ChunkSize OF SYS.BYTE; (* ChunkSize Bytes of memory *)
             END;

VAR
  MemIndex: INTEGER;      (* index in current Chunk *)
  CurChunk: MemChunkPtr;  (* current chunk          *)


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

(*------  Console Procedure:  ------*)

PROCEDURE RawKeyConvert{console,-48}(events{8}:ie.InputEventPtr;
                                     buffer{9}:LONGINT;
                                     length{1}:LONGINT;
                                     keyMap{10}:LONGINT);


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

(*------  DiskFont Procedure:  ------*)

PROCEDURE OpenDiskFont*{diskFontBase,-30}(VAR textAttr{8}: g.TextAttr): g.TextFontPtr;


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

(*------  Icon Procedures:  ------*)

PROCEDURE GetDiskObject {iconBase,- 78}(name{8}        : ARRAY OF CHAR): DiskObjectPtr;
PROCEDURE FreeDiskObject{iconBase,- 90}(diskobj{8}     : DiskObjectPtr);
PROCEDURE FindToolType  {iconBase,- 96}(toolTypes{8}   : e.APTR;
                                        typeName{9}    : ARRAY OF CHAR): StringPtr;
PROCEDURE MatchToolValue{iconBase,-102}(typeString{8}  : ARRAY OF CHAR;
                                        val{9}         : ARRAY OF CHAR): BOOLEAN;


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


PROCEDURE Append(VAR s1: String; s2: StringPtr);
(* appends s2 to s1 *)
VAR p,q: INTEGER;
BEGIN
  p := SHORT(str.Length(s1)); q := 0;
  WHILE (p<SIZE(s1)) & (s2^[q]#0X) & (p<NumColumns) DO
    s1[p] := s2^[q]; INC(p); INC(q)
  END;
  IF p<SIZE(s1) THEN s1[p] := 0X END;
END Append;


(*-----------------------------  Requester:  ------------------------------*)


PROCEDURE Request(Text: ARRAY OF CHAR); (* $CopyArrays- *)

VAR
  out: d.FileHandlePtr;
  c: CHAR;

BEGIN
  IF ol.wbStarted THEN
    body.frontPen := 0; body.backPen  := 1;  body.drawMode := g.jam2;
    body.leftEdge := 12; body.topEdge  := 8;
    text := body; ok := body;
    body.iText    := SYS.ADR(MuchText);
    body.nextText := SYS.ADR(text);
    text.iText    := SYS.ADR(Text); text.topEdge := 22;
    ok.leftEdge   := 6; ok.topEdge := 3; ok.iText := SYS.ADR("  OK  ");
    SYS.SETREG(0,I.AutoRequest(NIL,SYS.ADR(body),NIL,SYS.ADR(ok),LONGSET{I.rawKey},LONGSET{},320,65));
  ELSE
    out := d.Output();
    SYS.SETREG(0,d.Write(out,Text,str.Length(Text)));
    c := 0AX;
    SYS.SETREG(0,d.Write(out,c,1));
  END;
  HALT(0);
END Request;


PROCEDURE OutOfMemHandler;
CONST ES = I.EasyStruct(SIZE(I.EasyStruct),LONGSET{},
                        SYS.ADR(MuchText),
                        SYS.ADR(oom),
                        SYS.ADR("Retry|Abort"));
BEGIN
  IF I.int.libNode.version >= 37 THEN
    IF I.EasyRequest(NIL,SYS.ADR(ES),NIL,NIL)=0 THEN
      HALT(0)
    END;
  ELSE
    Request(oom);
  END;
END OutOfMemHandler;


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


PROCEDURE AllocLine(sz: INTEGER): TextLinePtr;

VAR
  a: TextLinePtr;
  newchunk: MemChunkPtr;

BEGIN
  INC(sz,SIZE(TextLine)-MaxLen); IF ODD(sz) THEN INC(sz) END;
  IF MemIndex+sz<=ChunkSize THEN     (* does mem fit into current chunk ? *)
    INC(MemIndex,sz);                (* increment index in current chunk  *)
  ELSE
    NEW(newchunk);                   (* allocate new chunk                *)
    newchunk.prev := CurChunk;       (* link chunk into list              *)
    CurChunk := newchunk;
    MemIndex := sz;
  END;
  RETURN SYS.ADR(CurChunk.data[MemIndex-sz]);
END AllocLine;


PROCEDURE DisposeLines();

VAR chunk: MemChunkPtr;

BEGIN
  WHILE CurChunk#NIL DO
    chunk := CurChunk.prev;
    DISPOSE(CurChunk);
    CurChunk := chunk;
  END;
  MemIndex := ChunkSize;
END DisposeLines;


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

(* $Debug- *)


PROCEDURE Busy;

(* $DataChip+ *)

TYPE Data = ARRAY 36 OF INTEGER;

CONST ClockData = Data(
  00000U,00000U,
  00400U,007C0U, 00000U,007C0U, 00100U,00380U, 00000U,007E0U,
  007C0U,01FF8U, 01FF0U,03FECU, 03FF8U,07FDEU, 03FF8U,07FBEU,
  07FFCU,0FF7FU, 07EFCU,0FFFFU, 07FFCU,0FFFFU, 03FF8U,07FFEU,
  03FF8U,07FFEU, 01FF0U,03FFCU, 007C0U,01FF8U, 00000U,007E0U,
  00000U,00000U);

BEGIN
  IF Window#NIL THEN
    I.OldModifyIDCMP(Window,MyIdcmp-LONGSET{I.rawKey,I.mouseButtons});
    I.SetPointer(Window,ClockData,16,16,-6,0);
  END;
END Busy;


PROCEDURE UnBusy;
BEGIN
  IF Window#NIL THEN
    I.ClearPointer(Window);
    I.OldModifyIDCMP(Window,MyIdcmp);
  END;
END UnBusy;


(*------  Scroll:  ------*)

PROCEDURE MakeThink(sync,fast,always: BOOLEAN);
VAR m: INTEGER;

BEGIN
  IF Scrollable THEN
    m := 1;
    IF ~always THEN
      IF lace & ~fast THEN m := 2 END;
      IF ~sync THEN INC(m,m*2) END;
    END;
    IF (m=1) OR (frame MOD m=0) THEN
      CASE scrollmode OF
      | 0: I.MakeScreen(Screen);
           e.Forbid; g.MrgCop(I.ViewAddress()); e.Permit;
           g.WaitTOF;
      | 1: g.ScrollVPort(SYS.ADR(Screen.viewPort));
           g.WaitTOF;
      ELSE
           I.MakeScreen(Screen);
           I.RethinkDisplay();
      END;
    END;
    INC(frame);
  END;
END MakeThink;


(*------  Clear Display:  ------*)


PROCEDURE ClearBitMaps;
VAR i: INTEGER;

BEGIN
  g.SetRast(rp,0);
  IF Scrollable THEN
    ri.ryOffset := 0;
    ScreenPos := 0;
    MakeThink(f,f,w);
  END;
END ClearBitMaps;

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

(*------  Read one TextLine into a Variable:  ------*)


PROCEDURE GetTextLine(): TextLinePtr;
(* returns NIL at EOF *)

VAR
  l: TextLinePtr;
  sz,wd,i,j: INTEGER;
  txt: ARRAY MaxLen+1 OF CHAR;
  num: ARRAY 10 OF INTEGER;
  newcol: BOOLEAN;
  oldstyle: SHORTSET;

  PROCEDURE GetCh();

  BEGIN
    IF RQPos=RQLen THEN
      RQLen := d.Read(MyFile,Buffer,SIZE(Buffer));
      IF RQLen<0 THEN Request(rwerr) END;
      RQPos := 0;
    END;
    IF RQLen=0 THEN c := 0X ELSE
      c := Buffer[RQPos]; IF c=0X THEN c:=1X END;
      INC(RQPos); INC(le);
    END;
  END GetCh;

BEGIN
  IF RQLen=0 THEN RETURN NIL END;
  sz := 0; wd := 0; le := 0;
  IF Italic IN style THEN IF Bold IN style THEN txt[sz] := boldit ELSE txt[sz] := italic END; INC(sz)
                     ELSE IF Bold IN style THEN txt[sz] := bold; INC(sz) END                      END;
  IF   Ulin IN style     THEN txt[sz] := ulineon; INC(sz) END;
  IF    Inv IN style     THEN txt[sz] := CHR(fg+4*bg+1); INC(sz)
  ELSIF (fg#1) OR (bg#0) THEN txt[sz] := CHR(bg+4*fg+1); INC(sz) END;
  LOOP
    LOOP
      GetCh;
      IF SYS.VAL(CHAR,SYS.VAL(SHORTSET,c)*SHORTSET{0..6})#1BX THEN EXIT END;
      i := -1;
      REPEAT
        GetCh;
        IF (c>=30X) & (c<=39X) THEN
          INC(i); num[i] := 0;
          REPEAT
            num[i] := 10*num[i]+ORD(c)-ORD(30X); GetCh;
          UNTIL (c<30X) OR (c>39X);
        END;
        c := CAP(c);
      UNTIL (c>=3FX(*"?"*)) & (c<=5AX) OR (c=0X) OR (i=9);
      IF c=4DX THEN
        newcol := f; oldstyle := style; j := 0;
        IF i=-1 THEN i:=0; num[0] := 0 END;
        WHILE (i>=j) & (sz<MaxLen-1) DO
          CASE num[j] OF
          0: style := SHORTSET{}; fg := 1; bg := 0; newcol := w |
          1: INCL(style,Bold) |
          2: fg := 2; IF onePlane THEN fg := 1 END; newcol := w (* I hope this is correct *) |
          3: INCL(style,Italic) |
          4: INCL(style,Ulin) |
          7: INCL(style,Inv); newcol := w |
          30..37: fg := (num[j]-30) MOD 4; IF onePlane & (fg>1) THEN fg:=1 END; newcol := w |
          40..47: bg := (num[j]-40) MOD 4; IF onePlane & (bg>1) THEN bg:=1 END; newcol := w |
          ELSE END;
          INC(j);
        END;
        IF (oldstyle#style) & (sz<MaxLen) THEN
          IF Italic IN style THEN IF Bold IN style THEN txt[sz] := boldit ELSE txt[sz] := italic END;
                             ELSE IF Bold IN style THEN txt[sz] := bold   ELSE txt[sz] := plain  END;
          END;
          INC(sz);
          IF (Ulin IN style) THEN
            IF ~((Ulin IN oldstyle)) & (sz<MaxLen) THEN
              txt[sz] := ulineon;
              INC(sz);
            END;
          ELSE
            IF (Ulin IN oldstyle) & (sz<MaxLen) THEN
              txt[sz] := ulineoff;
              INC(sz);
            END;
          END;
        END;
        IF newcol & (sz<MaxLen) THEN
          IF Inv IN style THEN txt[sz] := CHR(fg+4*bg+1)
                          ELSE txt[sz] := CHR(bg+4*fg+1) END;
          INC(sz);
        END;
      END;   (* IF c="m" THEN *)
    END;   (* LOOP *)
    CASE c OF
     20X.. 7FX:    txt[sz] := c; INC(sz); INC(wd) |
    0A1X..0FFX:    txt[sz] := c; INC(sz); INC(wd) |
    8X: (* BS *)   IF wd>0 THEN DEC(sz); DEC(wd); END |
    9X: (* TAB *)
      REPEAT
        txt[sz] := 20X; INC(sz); INC(wd)
      UNTIL (sz=MaxLen) OR (wd=NumColumns) OR (sz MOD tabw=0) |
    0A0X:                txt[sz] := 20X; INC(sz); INC(wd) |
    0AX,0X,0CX: EXIT |
    ELSE END;
    IF (wd>=NumColumns) OR (sz>=MaxLen) THEN EXIT END;
  END;
  txt[sz] := 0X; INC(sz);
  l := AllocLine(sz);
  l.len := le; l.size:= sz;
  WHILE sz>0 DO DEC(sz); l.text[sz]:=txt[sz] END;
  RETURN l;
END GetTextLine;


(*------  Write Line to Screen:  ------*)


PROCEDURE Type(pos: INTEGER; line: TextLinePtr);

VAR
  style: SHORTSET;
  front,back: SHORTINT;
  c: CHAR;
  last,i,x: INTEGER;
  strPtr: UNTRACED POINTER TO ARRAY 256 OF CHAR;

BEGIN
  g.SetDrMd(rp,g.jam2);
  IF Scrollable THEN
    i := NuScreen.depth;
    REPEAT
      DEC(i);
      g.BltClear(SYS.VAL(e.APTR,SYS.VAL(LONGINT,BM.planes[i])+pos*LineSize),LineSize,LONGSET{});
    UNTIL i=0;
  END;
  i := 0; x := 0; style := SHORTSET{}; front := 1; back := 0;
  LOOP
    WHILE line.text[i]<" " DO
      c := line.text[i];
      IF c=0X THEN EXIT END;
      CASE c OF
      plain   : style := style - SHORTSET{g.bold,g.italic} |
      italic  : EXCL(style,g.bold); INCL(style,g.italic)   |
      bold    : INCL(style,g.bold); EXCL(style,g.italic)   |
      boldit  : style := style + SHORTSET{g.bold,g.italic} |
      ulineon : INCL(style,g.underlined) |
      ulineoff: EXCL(style,g.underlined) |
      1X..10X : DEC(c);
                front := SHORT(ORD(c)) DIV 4;
                back  := SHORT(ORD(c)) MOD 4 |
      ELSE END;
      INC(i);
    END;
    strPtr := SYS.ADR(line.text[i]); last := i;
    REPEAT INC(i) UNTIL line.text[i]<" ";
    SYS.SETREG(0,g.SetSoftStyle(rp,style,-SHORTSET{}));
    g.SetAPen(rp,front);
    g.SetBPen(rp,back);
    g.Move(rp,fontWidth*x,fontHeight*pos+fontBaseLine);
    g.Text(rp,strPtr^,i-last);
    INC(x,i-last);
  END;
END Type;


PROCEDURE CopyScrollLine(pos: INTEGER);
(* Kopiert die an pos geschriebene Zeile auf den entsprechenden DoubleBuffer-Bereich *)
(*
 * ACHTUNG: Darf nur aufgerufen werden, wenn Scrollable TRUE ist!
 *)

VAR
  y,z: INTEGER;

BEGIN
  y := pos*fontHeight;
  z := PageHeight;
  IF pos>=NumLines THEN z := -z END;

  SYS.SETREG(0,g.BltBitMap(BM, 0,y, BM, 0,y+z, NuScreen.width,fontHeight, 0C0X, SHORTSET{0..7}, NIL));
END CopyScrollLine;


(*------  Write String to Screen (as bottom line):  ------*)


VAR writeText: TextLine;

PROCEDURE CopyToWriteText(String: StringPtr);
BEGIN
  writeText := FirstLine^;
  i := SHORT(str.Length(String^));
  IF i>=NumColumns THEN i := NumColumns-1 END;
  writeText.text[i+1] := 0X;
  writeText.size := i;
  REPEAT
    writeText.text[i] := String[i];
    DEC(i)
  UNTIL i<0;
END CopyToWriteText;


(*------  Write String to Screen (at any position):  ------*)


PROCEDURE TypeTo(VAR text: TextLine; pos: INTEGER);

BEGIN
  IF pos<NumLines THEN
    IF ~Scrollable THEN
      Type(pos,SYS.ADR(text));
    ELSE
      INC(pos,ScreenPos);
      Type(pos,SYS.ADR(text));
      CopyScrollLine(pos);
    END;
  END;
END TypeTo;


(*------  Write String to Screen (at any position):  ------*)


PROCEDURE WriteTo(String: StringPtr; pos: INTEGER);

BEGIN
  CopyToWriteText(String);
  TypeTo(writeText,pos);
END WriteTo;


(*------  Write Line at Bottom of Text:  ------*)


PROCEDURE AddBottomLine(Line: TextLinePtr; Fast: BOOLEAN);

VAR
  i,j: INTEGER;
  y: INTEGER;

BEGIN
  IF ~Scrollable THEN
    g.SetAPen(rp,0); g.SetBPen(rp,0);
    IF Fast THEN
      g.ScrollRaster(rp,0,fontHeight,0,0,NuScreen.width-1,NuScreen.height-1);
      IF Sync THEN g.WaitTOF END;
    ELSE
      i := fontHeight;
      REPEAT
        g.ScrollRaster(rp,0,1,0,0,NuScreen.width-1,NuScreen.height-1);
        IF Sync THEN g.WaitTOF END;
        DEC(i);
      UNTIL i=0;
    END;
    Type(NumLines-1,Line);
  ELSE
    Type(ScreenPos+NumLines,Line);
    y := ScreenPos*fontHeight;
    IF Fast THEN
      INC(ri.ryOffset,fontHeight);
      MakeThink(Sync,w,f);
      SYS.SETREG(0,g.BltBitMap(BM, 0,y+PageHeight, BM, 0,y, NuScreen.width,fontHeight, 0C0X, SHORTSET{0..7}, NIL));
    ELSE
      i := fontHeight;
      REPEAT
        INC(ri.ryOffset);
        MakeThink(Sync,f,f);
        SYS.SETREG(0,g.BltBitMap(BM, 0,y+PageHeight, BM, 0,y, NuScreen.width,1, 0C0X, SHORTSET{0..7}, NIL));
        INC(y);
        DEC(i);
      UNTIL i=0;
    END;
    INC(ScreenPos);
    IF ScreenPos=NumLines THEN
      ScreenPos := 0;
      ri.ryOffset := 0;
    END;
  END;
END AddBottomLine;


(*------  Check whether BottomLine.next is NIL or not:  ------*)


PROCEDURE TryBottomnext(): BOOLEAN;
(* returns TRUE if BottomLine.next#NIL END; *)

BEGIN
  IF (BottomLine.next=NIL) & (MyFile#NIL) THEN
    SignalNewData := w;
    SYS.SETREG(0,e.Wait(mySig));
    SignalNewData := f;
  END;
  RETURN BottomLine.next#NIL;
END TryBottomnext;


(*------  Scroll down one Line:  ------*)


PROCEDURE ScrollDown(Fast: BOOLEAN);

BEGIN
  IF TryBottomnext() THEN
    BottomLine := BottomLine.next;
    INC(AnzLines);
    INC(TextLength,BottomLine.len);
  ELSE RETURN END;
  IF AnzLines>=NumLines THEN TopLine := TopLine.next END;
  AddBottomLine(BottomLine,Fast);
END ScrollDown;


(*------  Scroll Up one Line:  ------*)


PROCEDURE ScrollUp(Fast: BOOLEAN);

VAR
  i,j: INTEGER;
  y: INTEGER;

BEGIN
  IF (TopLine.prev#NIL) & (TopLine.prev.prev#NIL) THEN
    TopLine := TopLine.prev;
    DEC(TextLength,BottomLine.len);
    DEC(AnzLines);
    BottomLine := BottomLine.prev;
    IF ~Scrollable THEN
      g.SetAPen(rp,0); g.SetBPen(rp,0);
      IF Fast THEN
        g.ScrollRaster(rp,0,-fontHeight,0,0,NuScreen.width-1,NuScreen.height-1);
        IF Sync THEN g.WaitTOF END;
      ELSE
        i := fontHeight;
        REPEAT
          g.ScrollRaster(rp,0,-1,0,0,NuScreen.width-1,NuScreen.height-1);
          IF Sync THEN g.WaitTOF END;
          DEC(i)
        UNTIL i=0;
      END;
      Type(0,TopLine.prev);
    ELSE
      IF ScreenPos=0 THEN
        ri.ryOffset  := NumLines*fontHeight;
        ScreenPos := NumLines-1;
      ELSE
        DEC(ScreenPos);
      END;
      Type(ScreenPos,TopLine.prev);
      y := ScreenPos*fontHeight;
      IF Fast THEN
        DEC(ri.ryOffset,fontHeight);
        MakeThink(Sync,w,f);
        SYS.SETREG(0,g.BltBitMap(BM, 0,y, BM, 0,y+PageHeight, NuScreen.width,fontHeight, 0C0X, SHORTSET{0..7}, NIL));
      ELSE
        INC(y,fontHeight);
        i := fontHeight;
        REPEAT
          DEC(ri.ryOffset);
          MakeThink(Sync,f,f);
          DEC(y);
          SYS.SETREG(0,g.BltBitMap(BM, 0,y, BM, 0,y+PageHeight, NuScreen.width,1, 0C0X, SHORTSET{0..7}, NIL));
          DEC(i);
        UNTIL i=0;
      END;
    END;
  END;   (* IF TopLine#NIL ... *)
END ScrollUp;


(*------  Undo last AddBottomLine:  ------*)


PROCEDURE DelLine();

VAR
  i,j: INTEGER;
  y: INTEGER;
  text: TextLine;

BEGIN
  IF ~Scrollable THEN
    g.SetAPen(rp,0); g.SetBPen(rp,0);
    IF Fast THEN
      g.ScrollRaster(rp,0,-fontHeight,0,0,NuScreen.width-1,NuScreen.height-1);
      IF Sync THEN g.WaitTOF END;
    ELSE
      i := fontHeight;
      REPEAT
        g.ScrollRaster(rp,0,-1,0,0,NuScreen.width-1,NuScreen.height-1);
        IF Sync THEN g.WaitTOF END;
        DEC(i)
      UNTIL i=0;
    END;
    IF TopLine.prev#NIL THEN Type(0,TopLine.prev) ELSE Type(0,FirstLine) END;
  ELSE
    IF ScreenPos=0 THEN
      ri.ryOffset  := NumLines*fontHeight;
      ScreenPos := NumLines;
    END;
    DEC(ScreenPos);
    IF TopLine.prev#NIL THEN Type(ScreenPos,TopLine.prev) ELSE Type(ScreenPos,FirstLine) END;
    y := (ScreenPos+1)*fontHeight;
    IF Fast THEN
      DEC(ri.ryOffset,fontHeight);
      MakeThink(Sync,w,f);
      DEC(y,fontHeight);
      SYS.SETREG(0,g.BltBitMap(BM, 0,y, BM, 0,y+PageHeight, NuScreen.width,fontHeight , 0C0X, SHORTSET{0..7}, NIL));
    ELSE
      i := fontHeight;
      REPEAT
        DEC(ri.ryOffset);
        MakeThink(Sync,f,f);
        DEC(y);
        SYS.SETREG(0,g.BltBitMap(BM, 0,y, BM, 0,y+PageHeight, NuScreen.width,1, 0C0X, SHORTSET{0..7}, NIL));
        DEC(i);
      UNTIL i=0;
    END;
  END;
END DelLine;


(*------  Convert Integer to String:  ------*)


PROCEDURE IntToStr(VAR String: String;
                       At,Chars: INTEGER;
                       int: LONGINT);

VAR
  Cnt: INTEGER;
  Ziff: LONGINT;

BEGIN
  INC(Chars,At);
  IF (str.Length(String)<Chars) & (Chars < SIZE(String)) THEN
    String[Chars] := 0X
  END;
  REPEAT
    DEC(Chars);
    String[Chars] := CHR(int MOD 10 + ORD(30X)); int := int DIV 10;
  UNTIL (Chars=At) OR (int=0);
  WHILE Chars>At DO DEC(Chars); String[Chars] := 20X END;
END IntToStr;


(*------  Convert String to Integer:  ------*)


PROCEDURE StrToInt(str: StringPtr; base: INTEGER): LONGINT;

VAR
  i,j: INTEGER;
  num: LONGINT;
  ch : CHAR;
  neg: BOOLEAN;

BEGIN
  num := 0; i := 0; neg := f;
  IF  str^[0] = '-'                   THEN str:=SYS.ADR(str[1]); neg := w END;
  IF  str^[0] = '$'                   THEN str:=SYS.ADR(str[1]); base:=16 END;
  IF (str^[0]='0')&(CAP(str^[1])='X') THEN str:=SYS.ADR(str[2]); base:=16 END;
  LOOP
    IF i=LEN(str^) THEN EXIT END;
    ch := CAP(str^[i]);
    IF ch=0X THEN EXIT END;
    j := ORD(ch);
    CASE ch OF
      "0".."9": DEC(j,ORD('0'))   |
      "A".."F": DEC(j,ORD('A')-10);
                IF base=10 THEN base:=16; i:=-1; j:=0; num:=0 END;
      ELSE EXIT
    END;
    num := num * base + j;
    INC(i);
  END;
  IF neg THEN num := -num END;
  RETURN num
END StrToInt;


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


PROCEDURE GetStr(name: StringPtr; VAR str: String);
VAR i: INTEGER;
BEGIN
  i := -1;
  REPEAT
    INC(i); str[i] := name^[i]
  UNTIL (name^[i]=0X) OR (i=LEN(str)-1);
  str[i] := 0X
END GetStr;


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


PROCEDURE GetLength(t: TextLinePtr);

BEGIN
  TextLength := 0; AnzLines := 0;
  WHILE t#NIL DO INC(AnzLines); INC(TextLength,t.len); t := t.prev END;
END GetLength;


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


PROCEDURE NewDisplay();
(* Zeichnet ab BottomLine neu *)

VAR
  i: INTEGER;
  l: TextLinePtr;

BEGIN
  ClearBitMaps;
  l := BottomLine.prev;
  IF l#NIL THEN BottomLine := l END;
  l := BottomLine;
  i := NumLines-1;
  WHILE (i>0) & TryBottomnext() DO
    BottomLine := BottomLine.next;
    DEC(i);
  END;
  WHILE (i>0) & (l.prev#NIL) DO
    l := l.prev;
    DEC(i);
  END;
  TopLine := l.next;
  WHILE i<NumLines DO
    BottomLine := l;
    TypeTo(BottomLine^,i);
    INC(i);
    l := l.next;
  END;
  GetLength(BottomLine);
  Scroll := f;
END NewDisplay;


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


PROCEDURE * ShowProc;

VAR
  l: TextLinePtr;
  Down: BOOLEAN;               (* Scroll-Direction                  *)
  End: BOOLEAN;                (* Quit next time Space is pressed ? *)
  i,j,k,m: INTEGER;
  MyMsg: I.IntuiMessage;       (* contains Message                  *)
  Shift: BOOLEAN;              (* Shifted Keystroke ?               *)
  Alt: BOOLEAN;                (* Altered Keystroke ?               *)
  Find,FindStr: ARRAY 80 OF CHAR; (* findstring / capitalized findstring *)
  Goto: ARRAY 10 OF CHAR;      (* string containing goto line #  *)
  li: LONGINT;                 (* longint value of line to go to *)
  flen: INTEGER;               (* length of findstring *)
  HiText: TextLine;            (* Highlited textline *)
  OldHiText: TextLinePtr;      (* original, un-hilited text *)
  found: BOOLEAN;              (* TRUE, if find was successful *)
  chr: CHAR;                   (* converted keycode *)


  PROCEDURE WaitAllRead();

  BEGIN
    IF MyFile#NIL THEN
      Busy;
      SignalAllRead := w;
      SYS.SETREG(0,e.Wait(mySig));
      SignalAllRead := f;
      UnBusy;
    END;
  END WaitAllRead;


  PROCEDURE HiLite(at,len: INTEGER);
  (* Hilites len chars of BottomLine.text starting at position at *)

  VAR
    c: INTEGER;
    col: CHAR;

  BEGIN
    OldHiText := BottomLine; HiText := OldHiText^; BottomLine := SYS.ADR(HiText);
    IF at+len+2<MaxLen THEN
      c := 0; col := 5X;
      WHILE c<at DO
        IF HiText.text[c]<CHR(17) THEN col := HiText.text[c] END;
        INC(c);
      END;
      HiText.text[at] := CHR(17-ORD(col));
      c := at; INC(len,at);
      WHILE c<len DO
        HiText.text[c+1] := OldHiText.text[c];
        INC(c);
      END;
      HiText.text[c+1] := col;
      REPEAT
        HiText.text[c+2] := OldHiText.text[c];
        INC(c);
      UNTIL HiText.text[c-1]=0X;
    END;
    IF HiText.next#NIL THEN HiText.next.prev := SYS.ADR(HiText) END;
    IF HiText.prev#NIL THEN HiText.prev.next := SYS.ADR(HiText) END;
  END HiLite;


  PROCEDURE UnHiLite();

  BEGIN
    IF HiText.next#NIL THEN HiText.next.prev := OldHiText END;
    IF HiText.prev#NIL THEN HiText.prev.next := OldHiText END;
  END UnHiLite;


  PROCEDURE ChkBotNewDisp;

  VAR
    c: INTEGER;
    t: LONGINT;

  BEGIN
    IF ~ found THEN
      I.DisplayBeep(NIL);
      IF TopLine.prev=NIL THEN BottomLine := TopLine
                          ELSE BottomLine := TopLine.prev END;
    END;
    NewDisplay;
    IF found THEN UnHiLite END;
  END ChkBotNewDisp;


  PROCEDURE Search(): BOOLEAN;
  (* searches string and hilites it if found. result is TRUE if string found *)

  BEGIN
    i := 0;
    IF BottomLine.len<NumColumns THEN m := BottomLine.len ELSE m := NumColumns END;
    WHILE i<BottomLine.size DO
      j := 0; k := i;
      WHILE CAP(BottomLine.text[k])=FindStr[j] DO
        INC(j); INC(k);
        IF FindStr[j]=0X THEN
          SYS.SETREG(0,TryBottomnext());
          FindLine := BottomLine;
          HiLite(k-flen,flen);
          found := w; RETURN w;
        END;
      END;
      INC(i);
    END;
    RETURN f;
  END Search;


  PROCEDURE DisplayInfo(Fast: BOOLEAN);
  VAR IStr: String;

  BEGIN
(* File: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xx % (xxxxxx of xxxxxx Bytes) xxxxxx Lines *)
    IStr := "XFile: "; IStr[0] := 7X;
    IF onePlane THEN IStr[0] := 2X END;
    Append(IStr,SYS.ADR(OldName));
    Append(IStr,SYS.ADR("                              "));
    IStr[36] := 0X;
    Append(IStr,SYS.ADR("xxx % (xxxxxx of xxxxxx Bytes) xxxxxx Lines"));
    IntToStr(IStr,36,3,TextLength * 100 DIV FileLength);
    IntToStr(IStr,43,6,TextLength);
    IntToStr(IStr,53,6,FileLength);
    IntToStr(IStr,67,6,AnzLines-1);
    i := 79;
    REPEAT IStr[i] := 20X; INC(i) UNTIL (i>=255) OR (i>=NumColumns+2);
    IStr[i] := 0X;
    CopyToWriteText(SYS.ADR(IStr));
    AddBottomLine(SYS.ADR(writeText),Fast);
    Info := w;
  END DisplayInfo;

  PROCEDURE GetString(VAR str: ARRAY OF CHAR; int: BOOLEAN);
  VAR
    msg: I.IntuiMessagePtr;
    class: LONGSET;
    oldheight: INTEGER;

  BEGIN
    MakeThink(f,f,w);
    Busy;
    NuWindow.leftEdge    := 100;
    NuWindow.topEdge     := NuScreen.height DIV 2 - 6;
    IF Scrollable THEN INC(NuWindow.topEdge,ri.ryOffset) END;
    NuWindow.width       := NuScreen.width-200;
    NuWindow.height      := Screen.font.ySize+4;
    NuWindow.blockPen    := 1;
    NuWindow.idcmpFlags  := LONGSET{I.gadgetUp,I.activeWindow,I.inactiveWindow};
    NuWindow.flags       := LONGSET{I.rmbTrap,I.activate};
    NuWindow.firstGadget := SYS.ADR(StrGadget);
    NuWindow.screen      := Screen;
    NuWindow.type        := I.customScreen;
    StrGadget.leftEdge   := 4;
    StrGadget.topEdge    := 2;
    StrGadget.width      := NuWindow.width-8;
    StrGadget.height     := NuWindow.height-4;
    StrGadget.activation := {I.stringCenter,I.relVerify};
    IF int THEN INCL(StrGadget.activation,I.longint) END;
    StrGadget.gadgetType := I.strGadget;
    StrGadget.specialInfo:= SYS.ADR(StrInfo);
    StrInfo.buffer       := SYS.ADR(str);
    StrInfo.maxChars     := SHORT(LEN(str))-1;
    oldheight := Screen.height;
    IF Scrollable THEN INC(Screen.height,Screen.height) END;
    win := I.OpenWindow(NuWindow);
    Screen.height := oldheight;
    IF win#NIL THEN
      e.WaitPort(win.userPort);
      SYS.SETREG(0,I.ActivateGadget(StrGadget,win,NIL));
      LOOP
        e.WaitPort(win.userPort);
        msg := e.GetMsg(win.userPort);
        IF msg#NIL THEN
          class := msg.class;
          e.ReplyMsg(msg);
          IF I.gadgetUp IN class THEN
            EXIT
          ELSIF I.inactiveWindow IN class THEN
            I.OldActivateWindow(win);
            g.WaitTOF; g.WaitTOF; g.WaitTOF;
            SYS.SETREG(0,I.ActivateGadget(StrGadget,win,NIL));
          END;
        END;
      END;
      I.CloseWindow(win); win := NIL;
    END;
    UnBusy;
  END GetString;


  PROCEDURE Help; (* executed when HELP or H is pressed *)
  TYPE
    UsageType = ARRAY 26 OF StringPtr;
  CONST
    Usage = UsageType(SYS.ADR(""),
                      SYS.ADR("\x13                \x15  MuchMore V3.2.7 Commands:  "),
                      SYS.ADR(""),
                      SYS.ADR(" \x0dSpace\x05,\x0d LMB\x05:         Start / Stop scrolling. Quit at end of file."),
                      SYS.ADR(" \x0dBackSpace\x05,\x0d RMB\x05:     Start / Stop scrolling backwards."),
                      SYS.ADR(" \x0dUp\x05/\x0dDown\x05:            Move one line \x0dup\x05 or \x0ddown\x05."),
                      SYS.ADR(" \x0dShift \x05+\x0d Up\x05/\x0dDn\x05:      Start / Stop quick scrolling \x0dup\x05 or \x0ddown\x05."),
                      SYS.ADR(" \x0dControl\x05:            Increase scroll speed"),
                      SYS.ADR(" \x0dAlt\x05+\x0dUp\x05/\x0dDn\x05,\x0d PgUp\x05/\x0dDn\x05: Move one page \x0dup\x05 or \x0ddown\x05."),
                      SYS.ADR(" \x0dT\x05,\x0d Home \x05/\x0d B\x05,\x0d End\x05:   Goto \x0dt\x05op / \x0db\x05ottom of text."),
                      SYS.ADR(" \x0dF\x05,\x0dN\x05,\x0dP\x05:              \x0dF\x05ind string, \x0dN\x05ext, \x0dP\x05revious occurance"),
                      SYS.ADR(" \x0dShift \x05+\x0d Fn\x05:         Set textmarker #n to current position"),
                      SYS.ADR(" \x0dFn\x05:                 Goto marker #n or set marker #n if it wasn't set yet"),
                      SYS.ADR(" \x0dG\x05:                  \x0dG\x05oto line #n"),
                      SYS.ADR(" \x0dNK 0\x05:               Display Filelength etc."),
                      SYS.ADR(" \x0dShift \x05+\x0d Alt \x05+\x0d O\x05:    Create print\x0do\x05ut of the text"),
                      SYS.ADR(" \x0dW\x05:                  \x0dW\x05rite block between Marker #1 and #2 to file or prt"),
                      SYS.ADR(" \x0dL\x05:                  \x0dL\x05oad new text"),
                      SYS.ADR(" \x0dHELP\x05,\x0d H\x05:            Show Commands."),
                      SYS.ADR(" \x0dESC\x05,\x0d Q\x05,\x0d X\x05,\x0d NK 5\x05:\x0d    Q\x05uit."),
                      SYS.ADR(""),
                      SYS.ADR("© \x131992 Fridtjof Siebert, Nobileweg 67, D-7000 Stuttgart 40"),
                      SYS.ADR("  \x131993 Christian Stiens, Heustiege 2, D-4710 Lüdinghausen"),
                      SYS.ADR("  \x13Please refer to MuchMore.ReadMe for a detailed copyright notice"),
                      SYS.ADR(""),
                      SYS.ADR("  This is another product of the Amiga MODULA & OBERON Klub Stuttgart - \x0d\x13AMOK"));
  VAR
    i,j: INTEGER;

  BEGIN
    ClearBitMaps();
    j := (NumLines - 26) DIV 2; IF j<0 THEN j:=0 END;
    i := 0;
    WHILE (i<26) & (i+j<NumLines) DO
      WriteTo(Usage[i],j+i);
      INC(i);
    END;
    LOOP
      e.WaitPort(Window.userPort);
      MyMsgPtr := e.GetMsg(Window.userPort);
      IF (LONGSET{I.rawKey,I.mouseButtons}*MyMsgPtr.class#LONGSET{}) & (MyMsgPtr.code<128) THEN EXIT END;
      e.ReplyMsg(MyMsgPtr);
    END;
    e.ReplyMsg(MyMsgPtr);
    BottomLine := TopLine;
    NewDisplay
  END Help;


  PROCEDURE Bottom;  (* executed when END or B is pressed *)

  BEGIN
    WaitAllRead;
    BottomLine := LastLine;
    NewDisplay
  END Bottom;


  PROCEDURE Space(): BOOLEAN;  (* executed if space or LMB is pressed *)
  (* IF result=w THEN EXIT END *)
  VAR oldFast: BOOLEAN;

  BEGIN
    oldFast := Fast;
    Fast := Shift;
    IF (MyFile=NIL) & (BottomLine.next=NIL) THEN
      IF End THEN RETURN w ELSE End:=w END;
    ELSE
      End := f;
    END;
    IF Down THEN
      IF Scroll OR End THEN DisplayInfo(oldFast) END;
      Scroll := ~Scroll;
    ELSE
      Down := w;
      Scroll := w;
    END;
    RETURN f;
  END Space;


  PROCEDURE BackSpace;  (* executed if backspace or RMB is pressed *)

  BEGIN
    Fast := Shift;
    Scroll := Down OR ~ Scroll;
    Down := f
  END BackSpace;


BEGIN
  (* $IF SmallData *)
  SYS.SETREG(13,e.exec.thisTask.userData);
  (* $END *)
  SYS.SETREG(0,e.Wait(mySig));

  Down := w; End := f; Find[0] := 0X; Goto[0] := 0X;

  LOOP

    IF NewDisp THEN NewDisp := f; NewDisplay END;

(*------  Type Text:  ------*)

    LOOP
      IF Scroll THEN
        IF Down THEN
          ScrollDown(Fast);
          Scroll := (MyFile#NIL) OR (BottomLine.next#NIL);
        ELSE
          ScrollUp(Fast);
          Scroll := (TopLine.prev#NIL) & (TopLine.prev.prev#NIL);
        END;
      ELSE
        MakeThink(f,f,w);
        e.WaitPort(Window.userPort);
      END;

      MyMsgPtr := e.GetMsg(Window.userPort);

      IF (MyMsgPtr#NIL) THEN
        IF ~(I.inactiveWindow IN MyMsgPtr.class) THEN EXIT END;
        e.ReplyMsg(MyMsgPtr);
        I.OldModifyIDCMP(Window,MyIdcmp-LONGSET{I.mouseButtons});
        e.WaitPort(Window.userPort);
        I.OldModifyIDCMP(Window,MyIdcmp);
      END;
    END;

    MyMsg := MyMsgPtr^;
    e.ReplyMsg(MyMsgPtr);

    IF (I.rawKey       IN MyMsg.class) & (MyMsg.code<80H) OR
       (I.mouseButtons IN MyMsg.class) & ({ie.leftButton,ie.rightButton}*MyMsg.qualifier#{})
    THEN
      IF ~Info THEN
        IF MyMsg.code=0FH THEN DisplayInfo(Fast); Scroll := f END;
      ELSE
        DelLine;
        Info := f
      END;
    END;

    Shift := {} # {ie.lShift,ie.rShift,ie.capsLock} * MyMsg.qualifier;
    Alt   := {} # {ie.lAlt  ,ie.rAlt}               * MyMsg.qualifier;
    Sync  := ~  (                     ie.control IN MyMsg.qualifier);
    IF ~(Sync OR Alt) THEN Shift := w END;

    IF I.mouseButtons IN MyMsg.class THEN

      IF    (ie.leftButton IN MyMsg.qualifier) & Space() THEN EXIT
      ELSIF ie.rightButton IN MyMsg.qualifier            THEN BackSpace END;

    ELSIF (I.rawKey IN MyMsg.class) & (MyMsg.code<80H) THEN

      CASE MyMsg.code OF

      | 40H: IF Space() THEN EXIT END                 (* Space *)

      | 41H: BackSpace                                (* BackSpace *)

      | 4DH,1EH,1FH:                                  (* Down *)
            IF Shift THEN
              Scroll := ~(Down & Scroll) OR ~ Fast;
              Fast := w; Down := w;
            ELSE
              IF Alt OR (MyMsg.code=1FH) THEN
                IF BottomLine.next#NIL THEN BottomLine := BottomLine.next END;
                NewDisplay;
              ELSE
                ScrollDown(~ Shift);
              END;
              Scroll := f;
            END

      | 4CH,3EH,3FH:                                      (* Up *)
            IF Shift THEN
              Scroll := Down OR ~ Scroll OR ~ Fast;
              Fast := w; Down := f;
            ELSE
              IF Alt OR (MyMsg.code=3FH) THEN
                IF TopLine.prev#NIL THEN
                  i := NumLines-1;
                  BottomLine := TopLine;
                  WHILE (i>0) & (BottomLine.prev#NIL) DO
                    BottomLine := BottomLine.prev;
                    DEC(i);
                  END;
                  NewDisplay;
                END;
              ELSE
                ScrollUp(~ Shift);
              END;
              Scroll := f;
            END;

      | 44H,43H:                                          (* CR *)
            ScrollDown(f);
            Scroll := f;

      | 3DH: BottomLine := FirstLine; NewDisplay          (* Home *)

      | 1DH: Bottom                                       (* End *)

      | 50H..59H:                                         (* F1..F10 *)
            i := MyMsg.code-50H;
            IF ~ Shift & (TextMarkers[i]#NIL) THEN
              BottomLine := TextMarkers[i];
              IF BottomLine.prev#NIL THEN BottomLine := BottomLine.prev END;
              NewDisplay;
            ELSE
              TextMarkers[i] := TopLine;
            END

      | 5FH:  Help

      | 45H,2EH: IF ~ Alt THEN EXIT END                   (* Quit *)

      ELSE

        IF MyMsg.code<40H THEN (* examine vanilla keycode: *)

          chr := KeyMap[MyMsg.code];

          CASE chr OF

          | "t": BottomLine := FirstLine; NewDisplay      (* Home *)

          | "b": Bottom;                                  (* End *)

          | "f","n","p":                                  (* Find, Next, Previous *)

            IF chr="f" THEN
              GetString(Find,f); FindLine := NIL; flen := 0;
              LOOP
                FindStr[flen] := CAP(Find[flen]);
                IF    FindStr[flen]>80X THEN DEC(FindStr[flen],32)
                ELSIF FindStr[flen]=0X  THEN EXIT END;
                INC(flen);
              END;
              ClearBitMaps();
            END;
            found := f;
            IF FindStr[0]#0X THEN
              Busy;
              i := NumLines;
              IF FindLine#NIL THEN FindLine := FindLine.next END;
              WHILE (i>0) & (BottomLine#NIL) & (BottomLine#FindLine) DO
                BottomLine := BottomLine^.prev; DEC(i);
              END;
              IF (BottomLine#FindLine) OR (BottomLine=NIL) THEN BottomLine := TopLine END;
              IF chr#"p" THEN (* next *)
                WHILE (BottomLine#NIL) & ~ Search() DO
                  SYS.SETREG(0,TryBottomnext());
                  BottomLine := BottomLine.next;
                END;
              ELSE                   (* previous *)
                IF BottomLine.prev#NIL THEN BottomLine:=BottomLine.prev END;
                REPEAT
                  BottomLine := BottomLine.prev
                UNTIL (BottomLine=NIL) OR Search();
              END;
              IF BottomLine#NIL THEN
                li := NumLines DIV 2;
                WHILE (li>0) & (BottomLine.prev#NIL) DO BottomLine := BottomLine.prev; DEC(li) END;
              END;
              UnBusy;
            ELSE
              BottomLine := NIL
            END;
            ChkBotNewDisp;

          | "w":                                                (* write block *)

            IF (TextMarkers[0]#NIL) & (TextMarkers[1]#NIL) & ~ print & ~ save THEN
              savefrom := 0; savesize := 0;
              l := TextMarkers[0].prev; WHILE l.prev#NIL DO l := l.prev; INC(savefrom,l.len) END;
              l := TextMarkers[1].prev; WHILE l#NIL DO INC(savesize,l.len); l := l.prev END;
              l := TextMarkers[1]; i := NumLines; WHILE (i>1) & (l#NIL) DO DEC(i); INC(savesize,LONG(l.len)); l := l.next END;
              DEC(savesize,savefrom);
              IF savesize>0 THEN
                GetString(WriteName,f);
                WaitAllRead; save := w; e.Signal(SYS.ADR(Me.task),mySig); NewDisplay;
              END
            END

          | "o": IF Shift & Alt & ~print & ~save THEN           (* Printout *)
                 PStr := 'TYPE "'; Append(PStr,SYS.ADR(Name)); Append(PStr,SYS.ADR('" TO PRT:'));
                 WaitAllRead; print := w; e.Signal(SYS.ADR(Me.task),mySig);
               END

          | "l": ClearBitMaps;                                  (* Load Text *)
               NewText := w; e.Signal(SYS.ADR(Me.task),mySig);
               REPEAT UNTIL (mySigBit IN e.Wait(mySig)) & ~ NewText |

          | "g":                                                (* goto *)
            GetString(Goto,w);
            li := SHORT(StrInfo.longInt);
            Busy;
            BottomLine := FirstLine;
            WHILE (li>0) & TryBottomnext() DO
              BottomLine := BottomLine.next;
              DEC(li)
            END;
            UnBusy;
            NewDisplay

          | "h": Help                                           (* Help *)

          | "q","x": EXIT                                       (* Quit *)

          ELSE END;

        END;   (* IF MyMsg.code<40H THEN *)

      END;   (* CASE MyMsg.code OF *)

    END;   (* IF I.rawKey IN MyMsg.class THEN *)

  END;   (* LOOP *)

  Done := w;
  e.Signal(SYS.ADR(Me.task),mySig);
  LOOP SYS.SETREG(0,e.Wait(LONGSET{})) END;

END ShowProc;

(* $Debug= *)

(*--------------------------  File Requester:  ----------------------------*)


PROCEDURE FileReq(VAR Name: String);

CONST
  dummy     = u.user + 80000H;
  taghail   = dummy + 1;
  window    = dummy + 2;
  leftEdge  = dummy + 3;         (* Initialize LeftEdge                  *)
  topEdge   = dummy + 4;         (* Initialize TopEdge                   *)
  width     = dummy + 5;
  height    = dummy + 6;
  hookFunc  = dummy + 7;         (* Hook function pointer                *)
  file      = dummy + 8;         (* Initial name of file follows         *)
  dir       = dummy + 9;         (* Initial string for filerequest dir   *)
  pattern   = dummy + 10;
  funcFlags = dummy + 20;
  fiDir     = u.user + 50;
  longPath  = 0;
  patGad    = 0;

VAR
  fr: FileRequesterPtr;
  pscr: I.ScreenPtr;
  res: BOOLEAN;

  PROCEDURE AllocAslRequest  {asl,-48} (type{0}: LONGINT;
                                        tag{8}..: e.APTR): FileRequesterPtr;
  PROCEDURE FreeAslRequest   {asl,-54} (fr{8}: FileRequesterPtr);
  PROCEDURE RequestFile      {asl,-42} (fr{8}: FileRequesterPtr): e.APTR;

  PROCEDURE FileRequest{arp,-294}(VAR filereq{8}: FileRequester): BOOLEAN;

BEGIN
  LOOP
    j := SHORT(str.Length(Name));
    WHILE (j>0) & (Name[j]#":") & (Name[j]#"/") DO DEC(j) END;
    IF j=0 THEN j := -1 END;
    i := 0;
    WHILE i<=j DO Dirname[i] := Name[i]; INC(i) END; Dirname[i] := 0X;
    j := 0;
    REPEAT Filename[j] := Name[i]; INC(j); INC(i) UNTIL Name[i-1]=0X;

    LOOP
      IF I.int.libNode.version >= 37 THEN
        pscr := I.LockPubScreen(NIL);
        IF pscr#NIL THEN
          I.ScreenToFront(pscr);
          I.UnlockPubScreen(NIL,pscr);
          EXIT
        END;
      END;
      SYS.SETREG(0,I.WBenchToFront());
      EXIT
    END;

    IF asl=NIL THEN asl := e.OpenLibrary("asl.library",36) END;
    IF asl#NIL THEN
      fr := AllocAslRequest(0,taghail, SYS.ADR(MuchText),
                              leftEdge,25,
                              topEdge, 15,
                              width,   300,
                              height,  184,
                              file,    SYS.ADR(Filename),
                              dir,     SYS.ADR(Dirname),
                              pattern, SYS.ADR(Pattern),
                              funcFlags,ASH(1,patGad),
                              u.done);
      IF fr=NIL THEN Request(oom) END;
      res := RequestFile(fr)#NIL;
      IF res THEN
        Filename := fr.ddef^;
        Dirname  := fr.ddir^;
      END;
      FreeAslRequest(fr);
      IF ~res THEN EXIT END;
    ELSE
      IF arp=NIL THEN arp := e.OpenLibrary("arp.library",39) END;
      IF arp#NIL THEN
        FR.hail := SYS.ADR(MuchText);
        FR.ddef := SYS.ADR(Filename);
        FR.ddir := SYS.ADR(Dirname);
        FR.flags2 := SHORTSET{longPath};
        FR.wind := NIL;
        IF ~FileRequest(FR) THEN EXIT END;
      ELSE
        Request(noarp)
      END;
    END;
    Name := Dirname;
    i := SHORT(str.Length(Name));
    IF (i>0) THEN
      CASE Name[i-1] OF "/",":": ELSE
        Name[i] := "/"; INC(i);
      END;
    END;
    j := 0;
    LOOP
      Name[i] := Filename[j];
      IF (Name[i]=0X) OR (i=255) THEN EXIT END;
      INC(i);
      INC(j);
    END;
    Name[i] := 0X;
    IF Screen#NIL THEN I.ScreenToFront(Screen) END;
    IF Window#NIL THEN IF I.ActivateWindow(Window) THEN END END;
    RETURN
  END;
  HALT(0);
END FileReq;


(*--------------------------  Decrunch:  ----------------------------*)


PROCEDURE DirExists(name: ARRAY OF CHAR): BOOLEAN; (* $CopyArrays- *)
VAR lock: d.FileLockPtr;
    result: BOOLEAN;
    oldwp: e.APTR;
BEGIN
  result := f;
  oldwp := Me.windowPtr;
  Me.windowPtr := -1;
  lock := d.Lock(name,d.sharedLock);
  IF lock # NIL THEN
    result := w;
    d.UnLock(lock)
  END;
  Me.windowPtr := oldwp;
  RETURN result
END DirExists;


PROCEDURE RFProc; (* $EntryExitCode- *)
BEGIN
  SYS.INLINE(016C0U, 04E75U)
END RFProc;


PROCEDURE Decrunch;

CONST
  tagBase    = u.user + ORD("X")*256 + ORD("P");
  inName     = tagBase+01H;
  inFH       = tagBase+02H;
  outName    = tagBase+10H;
  password   = tagBase+24H;
  getError   = tagBase+25H;
  shortError = tagBase+31H;
  typePacked = 1;

TYPE
  XpkFib = STRUCT
    type       : LONGINT;    (* Unpacked, packed, archive? *)
    uLen       : LONGINT;
    cLen       : LONGINT;
    nLen       : LONGINT;
    uCur       : LONGINT;
    cCur       : LONGINT;
    id         : LONGINT;
    packer     : ARRAY 6 OF CHAR;
    subVersion : INTEGER;
    masVersion : INTEGER;
    flags      : LONGSET;
    head       : ARRAY 16 OF CHAR;
    ratio      : LONGINT;
    reserved   : ARRAY 8 OF LONGINT;
  END;

VAR
  file: d.FileHandlePtr;
  err: LONGINT;
  xpkFib: XpkFib;
  errBuf: ARRAY 81 OF CHAR;

  PROCEDURE ExamineTags {xpk,-36}(VAR fib{8}  : XpkFib;
                                  tag1{9}..   : e.APTR): LONGINT;
  PROCEDURE UnpackTags  {xpk,-48}(tag1{8}..   : e.APTR): LONGINT;

BEGIN
  Decrunched := f;
  OldName := Name;

  IF DirExists("T:") THEN Name := "T:" ELSE Name := "RAM:" END;

  e.RawDoFmt("MM_decr.%lx",SYS.ADR(Me),RFProc,SYS.ADR(Name[str.Length(Name)]));

  IF xpk=NIL THEN
    xpk := e.OpenLibrary("xpkmaster.library",1);
  END;
  IF xpk#NIL THEN
    err := ExamineTags(xpkFib,inFH,SYS.VAL(LONGINT,MyFile),u.done);
    IF (err#0) & (err#-20) THEN
      Request(oom)
    END;
    IF (err=-20) OR (xpkFib.type#typePacked) THEN
      Name := OldName;
      RETURN
    END;
    Busy;
    err := UnpackTags(inFH,   SYS.VAL(LONGINT,MyFile),
                  outName,    SYS.ADR(Name),
                  password,   SYS.ADR(Password),
                  getError,   SYS.ADR(errBuf),
                  shortError, e.true,
                  u.done);
    UnBusy;
    IF err#0 THEN Request(errBuf) END;
    file := d.Open(Name,d.oldFile);
    IF file#NIL THEN
      Decrunched := w;
      d.OldClose(MyFile);
      MyFile := file;
      RETURN
    ELSE
      Request(cof);
    END;
  END;
  Name := OldName;
END Decrunch;


(*-----------------  Screen Mode Requester:  ------------------------*)


PROCEDURE GetNode (VAR list: e.List; index: LONGINT): e.NodePtr;
  VAR n: e.NodePtr;
BEGIN
  n := list.head;
  WHILE index > 0 DO
    IF n.succ=NIL THEN RETURN NIL END;
    n := n.succ;
    DEC(index);
  END;
  RETURN n;
END GetNode;


PROCEDURE ScreenModeReq (VAR displayID : LONGINT): INTEGER;
CONST
  gadLISTVIEW    = 1;
  gadBUTTONSave  = 2;
  gadBUTTONUse   = 3;
  gadBUTTONCancel= 4;

  Topaz80 = g.TextAttr(SYS.ADR("topaz.font"), 8, SHORTSET{}, SHORTSET{});

TYPE
  MyNodePtr = UNTRACED POINTER TO MyNode;
  MyNode = STRUCT (node: e.Node)
             displayID: LONGINT;
           END;

VAR
  ng          : gt.NewGadget;
  gad,glist   : I.GadgetPtr;
  win         : I.WindowPtr;
  pub         : I.ScreenPtr;
  result,index: INTEGER;
  firstindex  : INTEGER;
  topborder   : INTEGER;
  terminated  : BOOLEAN;
  lvlist      : e.List;
  lvSelectedTag:LONGINT;
  vi          : e.APTR;
  imsg        : I.IntuiMessagePtr;
  node        : MyNodePtr;
  dispID      : LONGINT;
  displayInfo : g.DisplayInfo;
  nameInfo    : g.NameInfo;
BEGIN
  (* $IFNOT ClearVars *)
  win := NIL; vi := NIL; glist := NIL; pub := NIL; result := 0;
  (* $END *)

  LOOP
    pub := I.LockPubScreen(NIL);         IF pub=NIL THEN EXIT END;
    vi  := gt.GetVisualInfo(pub,u.done); IF vi =NIL THEN EXIT END;
  
    topborder := pub.wBorTop + pub.font.ySize + 1;

    gad := gt.CreateContext(glist);

    ng.textAttr := SYS.ADR(Topaz80);
    ng.visualInfo := vi;
    ng.userData := NIL;

    ng.leftEdge := 16+100*0;
    ng.topEdge := 90+topborder;
    ng.width := 64;
    ng.height := 12;
    ng.gadgetText := SYS.ADR("Save");
    ng.gadgetID := gadBUTTONSave;
    ng.flags := LONGSET{};

    gad := gt.CreateGadget(gt.buttonKind, gad, ng, u.done);

    ng.leftEdge := 16+100*1;
    ng.gadgetText := SYS.ADR("Use");
    ng.gadgetID := gadBUTTONUse;

    gad := gt.CreateGadget(gt.buttonKind, gad, ng, u.done);

    ng.leftEdge := 16+100*2;
    ng.gadgetText := SYS.ADR("Cancel");
    ng.gadgetID := gadBUTTONCancel;

    gad := gt.CreateGadget(gt.buttonKind, gad, ng, u.done);

    lvlist.head := SYS.ADR(lvlist.tail);
    lvlist.tail := NIL;
    lvlist.tailPred := SYS.ADR(lvlist.head);

    index := 0;
    firstindex := -1;
    dispID := g.NextDisplayInfo(g.invalidID);

    WHILE dispID # g.invalidID DO
      IF (g.GetDisplayInfoData(NIL,displayInfo,SIZE(displayInfo),g.dtagDisp,dispID) > 0) &
         (displayInfo.notAvailable = 0) &
         (LONGSET{g.isHAM,g.isExtraHalfBrite,g.isDualPF} * displayInfo.propertyFlags = LONGSET{}) &
         (g.GetDisplayInfoData(NIL,nameInfo,SIZE(nameInfo),g.dtagName,dispID) > 0)
      THEN
        SYS.ALLOCATE(node);
        IF node # NIL THEN
          SYS.ALLOCATE(node.node.name);
          IF node.node.name # NIL THEN
            COPY(nameInfo.name,node.node.name^);
            node.displayID := dispID;
            e.AddTail(lvlist, node);
            IF dispID=displayID THEN firstindex := index END;
            INC(index);
          ELSE
            EXIT
          END;
        ELSE
          EXIT
        END;
      END;
      dispID := g.NextDisplayInfo(dispID);
    END;

    ng.leftEdge := 16;
    ng.topEdge := 16+topborder;
    ng.width := 265;
    ng.height := 66;
    ng.gadgetText := SYS.ADR("Choose Display Mode:");
    ng.gadgetID := gadLISTVIEW;
    ng.flags := LONGSET{gt.highLabel,gt.placeTextAbove};

    IF firstindex >= 0 THEN lvSelectedTag := gt.lvSelected
                       ELSE lvSelectedTag := u.ignore END;

    gad := gt.CreateGadget(gt.listViewKind, gad, ng,
                    gt.lvLabels,       SYS.ADR(lvlist),
                    gt.lvShowSelected, NIL,
                    I.layoutaSpacing,  2,
                    lvSelectedTag,     firstindex,
                    u.done);
    IF gad = NIL THEN EXIT END;

    win := I.OpenWindowTagsA(NIL,
                              I.waLeft,        0,
                              I.waTop,         8,
                              I.waInnerWidth,  290,
                              I.waInnerHeight, 106,
                              I.waTitle,       SYS.ADR(MuchText),
                              I.waFlags,       LONGSET{I.activate,I.windowDrag,I.windowDepth,I.simpleRefresh,I.rmbTrap},
                              I.waIDCMP,       gt.listViewIDCMP+gt.buttonIDCMP+LONGSET{I.gadgetUp,I.refreshWindow},
                              I.waCustomScreen,pub,
                              I.waGadgets,     glist,
                              u.done);
    IF win = NIL THEN EXIT END;
    gt.RefreshWindow(win, NIL);

    dispID := displayID;
    terminated := f;

    WHILE ~terminated DO
      e.WaitPort(win.userPort);
      LOOP
        IF terminated THEN EXIT END;
        imsg := gt.GetIMsg(win.userPort);
        IF imsg=NIL THEN EXIT END;
        gad := imsg.iAddress;
  
        IF I.gadgetUp IN imsg.class THEN
          CASE gad.gadgetID OF
          | gadBUTTONSave:   result := 1; displayID := dispID; terminated := w;
          | gadBUTTONUse:    result := 2; displayID := dispID; terminated := w;
          | gadBUTTONCancel: terminated := w;
          | gadLISTVIEW:     node := GetNode(lvlist,imsg.code);
                             IF node # NIL THEN dispID := node.displayID END;
          ELSE
          END;
        END;
  
        IF I.refreshWindow IN imsg.class THEN
          gt.BeginRefresh(win);
          gt.EndRefresh(win, I.LTRUE);
        END;

        gt.ReplyIMsg(imsg);
  
      END;
    END;

    EXIT
  END;

  IF win   # NIL THEN I.CloseWindow(win) END;
  IF glist # NIL THEN gt.FreeGadgets(glist) END;
  IF vi    # NIL THEN gt.FreeVisualInfo(vi) END;
  IF pub   # NIL THEN I.UnlockPubScreen(NIL,pub) END;

  RETURN result;
END ScreenModeReq;


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


PROCEDURE SavePrefs;

VAR
  lock: d.FileLockPtr;

  PROCEDURE SavePrefsName(name: StringPtr);
  VAR file: d.FileHandlePtr;
      l: LONGINT;
  BEGIN
    file := d.Open(name^,d.newFile);
    IF file # NIL THEN
      SYS.SETREG(0,d.Write(file,"FORM",4));
      l := 16;
      SYS.SETREG(0,d.Write(file,l,4));
      SYS.SETREG(0,d.Write(file,"MUMO",4));
      SYS.SETREG(0,d.Write(file,"DPID",4));
      l := 4;
      SYS.SETREG(0,d.Write(file,l,4));
      SYS.SETREG(0,d.Write(file,id,4));
      d.OldClose(file);
    END;
  END SavePrefsName;

BEGIN
  IF DirExists("ENV:") THEN
    IF ~DirExists("ENV:MuchMore") THEN
      lock := d.CreateDir("ENV:MuchMore");
      IF lock#NIL THEN d.UnLock(lock) END;
    END;
    SavePrefsName(SYS.ADR("ENV:MuchMore/MuchMore.prefs"))
  END;
  IF DirExists("ENVARC:") THEN
    IF ~DirExists("ENVARC:MuchMore") THEN
      lock := d.CreateDir("ENVARC:MuchMore");
      IF lock#NIL THEN d.UnLock(lock) END;
    END;
    SavePrefsName(SYS.ADR("ENVARC:MuchMore/MuchMore.prefs"))
  END;
END SavePrefs;


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


PROCEDURE LoadPrefs;

VAR file: d.FileHandlePtr;
    l,i,s: LONGINT;
    ok: BOOLEAN;

BEGIN
  file := NIL;
  IF DirExists("PROGDIR:") THEN
    file := d.Open("PROGDIR:MuchMore.prefs",d.oldFile);
  END;
  IF (file=NIL) & DirExists("ENV:") THEN
    file := d.Open("ENV:MuchMore/MuchMore.prefs",d.oldFile);
  END;
  IF file # NIL THEN
    LOOP
      IF (d.Read(file,i,4) <= 0)        OR
         (i # SYS.VAL(LONGINT,"FORM"))  OR
         (d.Read(file,s,4) <= 0)        OR
         (d.Read(file,i,4) <= 0)        OR
         (i # SYS.VAL(LONGINT,"MUMO"))    THEN EXIT END;
      WHILE w DO
        IF (d.Read(file,i,4) <= 0)      OR
           (d.Read(file,s,4) <= 0)        THEN EXIT END;
        IF ODD(s) THEN INC(s) END;
        IF (s=4) & (i=SYS.VAL(LONGINT,"DPID")) THEN
          SYS.SETREG(0,d.Read(file,id,4));
        ELSE
          IF s < 0 THEN EXIT END;
          SYS.SETREG(0,d.Seek(file,s,d.current));
        END;
      END;

      EXIT;
    END;
    d.OldClose(file);
  END;
END LoadPrefs;


(*------------------------  Get Tooltypes:  --------------------------*)


PROCEDURE EvalIcon(icon: DiskObjectPtr);
VAR tt: StringPtr;

BEGIN
  IF icon # NIL THEN
    tt := FindToolType(icon.toolTypes,"COLORS");     IF (tt # NIL) THEN COPY(tt^,Pens) END;
    tt := FindToolType(icon.toolTypes,"EXTRASPACE"); IF (tt # NIL) THEN spacing := SHORT(StrToInt(tt,10)) END;
    tt := FindToolType(icon.toolTypes,"FONT");       IF (tt # NIL) THEN COPY(tt^,FontName) END;
    tt := FindToolType(icon.toolTypes,"NOOSCAN");    IF (tt # NIL) & (MatchToolValue(tt^,"TRUE")) THEN noOscan := w END;
    tt := FindToolType(icon.toolTypes,"ONEPLANE");   IF (tt # NIL) & (MatchToolValue(tt^,"TRUE")) THEN onePlane := w END;
    tt := FindToolType(icon.toolTypes,"PASSWORD");   IF (tt # NIL) THEN COPY(tt^,Password) END;
    tt := FindToolType(icon.toolTypes,"SCROLLMODE"); IF (tt # NIL) THEN scrollmode := SHORT(StrToInt(tt,10)) END;
    tt := FindToolType(icon.toolTypes,"TABWIDTH");   IF (tt # NIL) THEN tabw := SHORT(StrToInt(tt,10)) END;
    tt := FindToolType(icon.toolTypes,"TOGGLELACE"); IF (tt # NIL) & (MatchToolValue(tt^,"TRUE")) THEN lace := ~lace END;
    tt := FindToolType(icon.toolTypes,"TOOLPRI");    IF (tt # NIL) THEN taskpri := SHORT(SHORT(StrToInt(tt,10))) END;
    FreeDiskObject(icon);
  END;
END EvalIcon;


(*------------------------------  MAIN:  ----------------------------------*)

BEGIN

(*------  Init:  ------*)

  ol.OutOfMemHandler := OutOfMemHandler;

  mySigBit := -1; Me := SYS.VAL(d.ProcessPtr,ol.Me);
  WriteName := "PRT:"; MemIndex := ChunkSize; OldDir := Me.currentDir;
  oldpri := Me.task.node.pri;
  taskpri := oldpri;
  Sync := w;
  FontSize := 8;
  Pattern := "~(#?.info)";
  id := g.invalidID;
  tabw := 8;
  IF g.gfx.libNode.version >= 37 THEN scrollmode := 1 END;


  I.GetPrefs(Prefs,SIZE(Prefs));
  lace := Prefs.laceWB # SHORTSET{};

  mySigBit := e.AllocSignal(-1);
  IF mySigBit<0 THEN HALT(0) END;
  mySig := LONGSET{mySigBit};

  iconBase := e.OpenLibrary("icon.library",33);

(*------  Setup:  ------*)

  NEW(FirstLine);
(*FirstLine.size := 0;
  FirstLine.text[0] := 0X; *)

(*------  Start:  ------*)

  LoadPrefs;

  IF ol.wbStarted THEN

    wbm := ol.wbenchMsg;
    IF iconBase # NIL THEN
      oldLace:=lace;
      j := SHORT(wbm.numArgs); IF j>2 THEN j := 2 END;
      FOR i := 0 TO j-1 DO
        lace := oldLace;
        SYS.SETREG(0,d.CurrentDir(wbm.argList[i].lock));
        nameptr := wbm.argList[i].name;
        icon := GetDiskObject(nameptr^);
        IF (icon=NIL) & (d.base.lib.version >= 37) THEN
          SYS.SETREG(0,d.CurrentDir(d.GetProgramDir()));
          icon := GetDiskObject(nameptr^);
        END;
        EvalIcon(icon);
      END;
    END; (* IF iconBase#NIL *)

    IF wbm.numArgs >= 2 THEN
      ArgPtr := wbm.argList^[1].name; Name := ArgPtr^;
      SYS.SETREG(0,d.CurrentDir(wbm.argList^[1].lock));
    ELSE
      SYS.SETREG(0,d.CurrentDir(wbm.argList^[0].lock));
      FileReq(Name)
    END;

  ELSE

    IF d.dos.lib.version < 37 THEN
      IF ol.dosCmdLen > 1 THEN
        CommLine := ol.dosCmdBuf;
        j := 0;
        LOOP
          i := 0;
          WHILE CommLine[j]=20X DO INC(j) END;
          IF CommLine[j]=0AX THEN EXIT END;
          IF CommLine[j]='"' THEN
            INC(j);
            LOOP
              CASE CommLine[j] OF
              | '"': INC(j); EXIT
              | 0AX:                     EXIT
              ELSE
                option[i] := CommLine[j]; INC(i); INC(j);
              END;
            END;
          ELSE
            WHILE (CommLine[j]#0AX) & (CommLine[j]#20X) DO
              option[i] := CommLine[j]; INC(i); INC(j);
            END;
          END;
          option[i]:= 0X;
          IF option="?"  THEN Request(usage) END;
          IF option[0]="-" THEN
            CASE CAP(option[1]) OF
              "A": tabw := SHORT(StrToInt(SYS.ADR(option[2]),10)) |
              "C": GetStr(SYS.ADR(option[2]),Pens) |
              "E": spacing := SHORT(StrToInt(SYS.ADR(option[2]),10)) |
              "F": GetStr(SYS.ADR(option[2]),FontName) |
              "L": lace := ~lace |
              "O": onePlane := w |
              "P": GetStr(SYS.ADR(option[2]),Password) |
              "S": scrollmode := SHORT(StrToInt(SYS.ADR(option[2]),10)) |
              "T": taskpri := SHORT(SHORT(StrToInt(SYS.ADR(option[2]),10))) |
            ELSE
              Request(usage);
            END;
          ELSE
            COPY(option,Name);
          END;
        END;
      END;

    ELSE  (* d.dos.lib.version >= 37 *)

      IF iconBase#NIL THEN
        oldcd := d.CurrentDir(d.GetProgramDir());
        IF d.GetProgramName(Name,LEN(Name)) THEN
          icon := GetDiskObject(Name);
          EvalIcon(icon);
          Name[0] := 0X;
        END;
        oldcd := d.CurrentDir(oldcd);
      END;

      rd := d.ReadArgs("A=TABWIDTH/N/K,C=COLORS/K,D=DISPMODEREQ/S,E=EXTRASPACE/N/K,F=FONT/K,N=NOOSCAN/S,O=ONEPLANE/S,P=PASSWORD/K,S=SCROLLMODE/N/K,T=TOOLPRI/N/K,FILE",args,NIL);
      IF rd=NIL THEN
        SYS.SETREG(0,d.PrintFault(d.IoErr(),NIL));
        HALT(d.warn)
      END;
      IF args.a # NIL THEN tabw := SHORT(args.a^) END;
      IF args.c # NIL THEN COPY(args.c^,Pens) END;
      IF args.d # NIL THEN modeReq := w END;
      IF args.e # NIL THEN spacing := SHORT(args.e^) END;
      IF args.f # NIL THEN COPY(args.f^,FontName) END;
      IF args.n # NIL THEN noOscan := w END;
      IF args.o # NIL THEN onePlane := w END;
      IF args.p # NIL THEN COPY(args.p^,Password) END;
      IF args.s # NIL THEN scrollmode := SHORT(args.s^) END;
      IF args.t # NIL THEN taskpri := SHORT(SHORT(args.t^)) END;
      IF args.file # NIL THEN COPY(args.file^,Name) END;
      d.FreeArgs(rd); rd := NIL;
    END;
  END;

  IF tabw <  1 THEN tabw :=  1 END;

  IF taskpri#oldpri THEN SYS.SETREG(0,e.SetTaskPri(Me,taskpri)) END;

  IF FontName[0]#0X THEN
    i := 0;
    j := SHORT(str.Length(FontName));
    LOOP
      IF i >= j THEN EXIT END;
      IF FontName[i]='/' THEN
        FontName[i] := 0X;
        FontSize := SHORT(StrToInt(SYS.ADR(FontName[i+1]),10));
        j := i;
        EXIT
      END;
      INC(i);
    END;
    IF j<LEN(FontName)-6 THEN
      e.CopyMem(".font",FontName[j],6);
    END;
  END;

  IF FontSize>50 THEN FontSize := 50 END;
  IF FontSize< 5 THEN FontSize :=  5 END;

  IF spacing< 0 THEN spacing :=  0 END;
  IF spacing>20 THEN spacing := 20 END;

  IF modeReq & (gt.base#NIL) THEN
    IF ScreenModeReq(id) = 1 THEN SavePrefs END;
  END;

(*------  Open File:  ------*)

  LOOP
    MyFile := d.Open(Name,d.oldFile);
    IF MyFile#NIL THEN EXIT END;
    FileReq(Name)
  END;

(*------------------------  Open Display:  --------------------------------*)

(*------  Open Screen:  ------*)

  IF lace THEN
    NuScreen.viewModes := {g.hires,g.lace};
    NuScreen.height := g.gfx.normalDisplayRows*2
  ELSE
    NuScreen.viewModes := {g.hires};
    NuScreen.height := g.gfx.normalDisplayRows
  END;
  NuScreen.width  := g.gfx.normalDisplayColumns;
  NuScreen.depth  := 2;
  NuScreen.type   := I.customScreen+{I.screenQuiet};

  IF onePlane THEN NuScreen.depth := 1; END;

  LOOP

    IF I.int.libNode.version >= 37 THEN

      IF id=g.invalidID THEN
        id := g.defaultMonitorID;
        pub := I.LockPubScreen("Workbench");
        IF pub # NIL THEN
          id := g.GetVPModeID(SYS.ADR(pub.viewPort));
          I.UnlockPubScreen(NIL,pub);
        END;
      END;

      IF g.GetDisplayInfoData(NIL,dims,SIZE(dims),g.dtagDims,id) > 0 THEN
        IF noOscan THEN
          NuScreen.width     := dims.nominal.maxX - dims.nominal.minX + 1;
          NuScreen.height    := dims.nominal.maxY - dims.nominal.minY + 1;
          overscanTag := u.ignore;
        ELSE
          NuScreen.width     := dims.txtOScan.maxX - dims.txtOScan.minX + 1;
          NuScreen.height    := dims.txtOScan.maxY - dims.txtOScan.minY + 1;
          overscanTag := I.saOverscan;
        END;
        IF g.GetDisplayInfoData(NIL,disp,SIZE(disp),g.dtagDisp,id) > 0 THEN
          lace := g.isLace IN disp.propertyFlags;
          Scrollable := (g.isDraggable IN disp.propertyFlags) (* & ((g.gfx.libNode.version<39) OR ~(20 IN disp.propertyFlags)) *);
          IF scrollmode=3 THEN Scrollable := f END;
          IF Scrollable THEN INC(NuScreen.height,NuScreen.height) END;

          Screen := I.OpenScreenTags(NuScreen,
                                     I.saDisplayID,id,
                                     overscanTag,I.oScanText,
                                     I.saPens,SYS.ADR("\xFF\xFF"),
                                  (* saInterleaved,I.LFALSE, *)
                                     u.end);
        END;
      END;

    ELSE

      Scrollable := w;
      IF scrollmode=3 THEN Scrollable := f END;
      IF Scrollable THEN INC(NuScreen.height,NuScreen.height) END;
      Screen := I.OpenScreen(NuScreen);

    END;

    IF Screen#NIL THEN EXIT END;

    DEC(NuScreen.depth);
    IF NuScreen.depth=0 THEN Request(cos) END;
  END;

  IF NuScreen.depth=1 THEN onePlane := w END;

  rp := SYS.ADR(Screen.rastPort);
  BM := rp.bitMap;


  IF Pens[0]#0X THEN
    i := 0;
    chptr := SYS.ADR(Pens);
    Pens[LEN(Pens)-1] := 0X;
    LOOP
      Cols[i] := SHORT(StrToInt(SYS.VAL(StringPtr,chptr),16));
      INC(i); IF i=4 THEN EXIT END;
      WHILE (chptr^#0X) & (chptr^#",") DO
        chptr:=SYS.VAL(e.APTR,SYS.VAL(LONGINT,chptr)+1)
      END;
      IF chptr^="," THEN
        chptr:=SYS.VAL(e.APTR,SYS.VAL(LONGINT,chptr)+1)
      ELSE
        EXIT
      END;
    END;
    g.LoadRGB4(SYS.ADR(Screen.viewPort),Cols,i);
  END;

  IF FontName[0]#0X THEN
    MyAttr.name := SYS.ADR(FontName);
    MyAttr.ySize := FontSize;
    IF diskFontBase=NIL THEN diskFontBase := e.OpenLibrary("diskfont.library",33) END;
    IF diskFontBase#NIL THEN MyFont := OpenDiskFont(MyAttr) END;
    IF (MyFont#NIL) & ~(g.proportional IN MyFont.flags) THEN g.SetFont(rp,MyFont) END;
  END;

  LOOP
    fontWidth  := rp.font.xSize;
    fontHeight := rp.font.ySize;
    IF (fontWidth<=50) OR (fontHeight<=50) & (fontWidth>2) & (fontHeight>2) THEN EXIT END;
    MyAttr.name := SYS.ADR("topaz.font");
    MyAttr.ySize := 8;
    MyFont := g.OpenFont(MyAttr);
    IF MyFont=NIL THEN HALT(0) END;
    g.SetFont(rp,MyFont);
  END;
  INC(fontHeight,spacing);               (* extra spacing *)
  fontBaseLine := rp.font.baseline;
  NumColumns := Screen.width DIV fontWidth;
  IF Scrollable THEN NumLines   := Screen.height DIV 2 DIV fontHeight;
                ELSE NumLines   := Screen.height DIV fontHeight; END;
  LineSize   := LONG(fontHeight)*BM.bytesPerRow;
  PageSize   := LineSize*NumLines;
  PageHeight := fontHeight*NumLines;

  NuScreen.height := fontHeight*NumLines;
  NuScreen.width  := Screen.width;

  IF Scrollable THEN
    (*
    IF NuScreen.depth=1 THEN BM.planes[1] := BM.planes[0] END;
    *)
    ri := Screen.viewPort.rasInfo;
    ClearBitMaps;
    ri.ryOffset := 32;
    Screen.height := NuScreen.height;
    I.MakeScreen(Screen);
    I.RethinkDisplay;
  END;

(*------  Open Window:  ------*)

  NuWindow.flags  := LONGSET{I.rmbTrap,I.activate,I.borderless};
  NuWindow.screen := Screen;
  NuWindow.type   := I.customScreen;
  NuWindow.topEdge:= 10;
  NuWindow.width  := Screen.width;
  NuWindow.height := Screen.height-10;
  NuWindow.idcmpFlags := MyIdcmp;
  Window := I.OpenWindow(NuWindow);
  IF Window=NIL THEN Request(cow) END;
  ClearBitMaps;

(*------  Get KeyMap:  ------*)

  IF e.OpenDevice("console.device",-1,SYS.ADR(wreq),LONGSET{})#0 THEN Request(conerr) END;
  console := wreq.device;
(*ievent.nextEvent := NIL;
  ievent.qualifier := {};
  ievent.eventAddress := NIL; *)
  ievent.class := ie.rawkey;

  FOR i := 0 TO 3FH DO
    ievent.code := i;
    RawKeyConvert(SYS.ADR(ievent),SYS.ADR(KeyMap[i]),10H,NIL);
  END;

(*------  Decrunch:  ------*)

  Decrunch;

(*------  Init 2nd Task:  ------*)

  ShowTask.spLower := SYS.ADR(ShowStack);
  ShowTask.spUpper := SYS.ADR(ShowStack[1000]);
  ShowTask.spReg   := ShowTask.spUpper;
  ShowTask.node.type := e.task;
  ShowTask.node.name := SYS.ADR("Show.MM");
  ShowTask.node.pri  := Me.task.node.pri+1;
  (* $IF SmallData *)
  ShowTask.userData  := SYS.REG(13);           (* VarBase *)
  (* $END *)

  e.Forbid;
    e.AddTask(SYS.ADR(ShowTask),ShowProc,NIL);
    ShowTaskRunning := w;
    Window.userPort.sigTask := SYS.ADR(ShowTask);
  e.Permit;

(*------  Main Load / Display Loop:  ------*)

  LOOP

    fg := 1; bg := 0; style := SHORTSET{};
    RQLen := -1; RQPos := -1;
    AnzLines := 1;
    LastLine := FirstLine;
    BottomLine := FirstLine;
    TopLine    := FirstLine;
    TextLength := 0;
    FindLine   := NIL;
    FOR i := 0 TO 9 DO TextMarkers[i] := NIL END;

    MyLock := d.Lock(Name,d.sharedLock);
    IF MyLock=NIL THEN Request(cof) END;
    IF ~ d.Examine(MyLock,FileInfo) THEN Request(cof) END;
    FileLength := FileInfo.size;

    d.UnLock(MyLock); MyLock := NIL;
    IF FileLength=0 THEN Request("File empty") END;

    (*------  Start displaying & Loading:  ------*)

    NewDisp := w;

    e.Signal(SYS.ADR(ShowTask),mySig);

    REPEAT
      LoadLine := GetTextLine();
      IF LoadLine=NIL THEN
        d.OldClose(MyFile);
        MyFile := NIL;
      ELSE
        LoadLine.prev := LastLine;
        LastLine.next := LoadLine;
        LastLine := LoadLine;
      END;
      IF SignalNewData THEN e.Signal(SYS.ADR(ShowTask),mySig) END;
    UNTIL (MyFile=NIL) OR Done OR NewText;
    IF SignalAllRead THEN e.Signal(SYS.ADR(ShowTask),mySig) END;
    REPEAT
      SYS.SETREG(0,e.Wait(mySig));
      IF print THEN
        in := d.Open("NIL:",d.oldFile); out := d.Open("NIL:",d.newFile);
        SYS.SETREG(0,d.Execute(PStr,in,out));
        d.OldClose(in); in := NIL; d.OldClose(out); out := NIL; print := f;
      END;
      IF save THEN
        in := d.Open(Name,d.oldFile);
        IF in=NIL THEN I.DisplayBeep(NIL) ELSE
          ol.New(buffer,savesize);
          SYS.SETREG(0,d.Seek(in,savefrom,0));
          IF d.Read(in,buffer^,savesize)#savesize THEN
            I.DisplayBeep(NIL); d.OldClose(in); in := NIL;
          ELSE
            d.OldClose(in); in := NIL;
            out := d.Open(WriteName,d.newFile);
            IF out=NIL THEN I.DisplayBeep(NIL) ELSE
              IF d.Write(out,buffer^,savesize)#savesize THEN I.DisplayBeep(NIL) END;
              d.OldClose(out); out := NIL;
            END;
          END;
          DISPOSE(buffer);
        END;
        save := f;
      END;
      IF Done THEN EXIT END;
    UNTIL NewText;
    IF MyFile#NIL THEN d.OldClose(MyFile); MyFile := NIL END;
    IF Decrunched & d.DeleteFile(Name) THEN END;
    Decrunched := f;
    DisposeLines();
    FirstLine^.next := NIL; NewText := f;
    Name := OldName;
    REPEAT
      FileReq(Name);
      MyFile := d.Open(Name,d.oldFile);
    UNTIL MyFile#NIL;
    Decrunch;
  END;   (* LOOP *)

CLOSE

  IF win         #NIL THEN I.CloseWindow(win)                   END;
  IF ShowTaskRunning  THEN e.RemTask(SYS.ADR(ShowTask))         END;
  IF console     #NIL THEN e.CloseDevice(SYS.ADR(wreq))         END;
  IF Window      #NIL THEN I.CloseWindow(Window)                END;
  IF Screen      #NIL THEN d.Delay(2); I.OldCloseScreen(Screen) END;
  IF MyFont      #NIL THEN g.CloseFont(MyFont)                  END;
  IF MyFile      #NIL THEN d.OldClose(MyFile)                   END;
  IF in          #NIL THEN d.OldClose(in)                       END;
  IF out         #NIL THEN d.OldClose(out)                      END;
  IF Decrunched       THEN SYS.SETREG(0,d.DeleteFile(Name))     END;
  IF MyLock      #NIL THEN d.UnLock(MyLock)                     END;
  IF xpk         #NIL THEN e.CloseLibrary(xpk)                  END;
  IF arp         #NIL THEN e.CloseLibrary(arp)                  END;
  IF asl         #NIL THEN e.CloseLibrary(asl)                  END;
  IF diskFontBase#NIL THEN e.CloseLibrary(diskFontBase)         END;
  IF iconBase    #NIL THEN e.CloseLibrary(iconBase)             END;
  IF rd          #NIL THEN d.FreeArgs(rd)                       END;
  IF mySigBit>=0      THEN e.FreeSignal(mySigBit)               END;
  IF taskpri  #oldpri THEN oldpri := e.SetTaskPri(Me,oldpri)    END;
  OldDir := d.CurrentDir(OldDir);

END MuchMore.

