{$R-}    {Range checking off}                                          {.CP5}
{$B-}    {Boolean complete evaluation off}
{$S+}    {Stack checking on}
{$I+}    {I/O checking on}
{$N-}    {No numeric coprocessor}

Unit PXLINIT;

Interface

Uses
  Crt,
  Dos;

const                                                                  {.CP9}
   StdLineWidth   = 80;
   ScreenSize     = 2000;
   Triggers: set of char = [#27,#3];
   NoIncFiles = 8;
   BoxT =  5;
   BoxB = 21;
   BoxL = 10;
   BoxR = 70;

type                                                                  {.CP25}
   ColType      =  record                    {These 3 make a scrn size array}
                      case boolean of        {Addressed like BASIC'S screen }
                         True:  (C,A: byte); {[Row,Col].C = char            }
                         False: (I: word)    {[Row,Col].A = attribute       }
                   end;                      {[Row,Col].I = both, but with  }
   RowType      =  array[1..80] of ColType;  {   attribute in hi byte       }
   ScrType      =  array[1..25] of RowType;  {   character in lo byte       }
   ScrPtrType   =  ^ScrType;
   MonitorType  =  (MDA,CGA,EGA);
   LineType     =  string[StdLineWidth];
   CharSet      =  set of char;
   TpFace   = (MrkB,MrkE,EliteB,EliteE,CondB,CondE,FF);
   ByteLine = array[0..3] of byte;
   Bytes    = array[MrkB..FF] of Byteline;
   Fil      = file of ByteLine;
   Str255   = string[255];
   CMD      = string[128]; {For command line}
   Str20    = string[20];
   Str10    = string[10]; {Must be large enough for longest reserved word}
   Str9     = string[9];
   Str5     = string[5];
   Str4     = string[4];
   Str3     = string[3];
   str2     = string[2];
   ResArr   = array[1..100] of Str20;

var                                                                   {.CP20}
   CRTube:    ScrPtrType;  {Set to point at real screen buffer}
   CRTAddr:   array[1..2] of word absolute CRTube;
   Monitor:   MonitorType;
   OrigAtt:   byte;
   BlnkLn:    LineType;
   Scr:                ScrType;
   C:                  char;
   Inside,
   BottomMargin,
   MaxLin,
   NormalColor,
   FrameColor,
   Background,
   Bright,Dim:         byte;
   F,Lst:              text;
   IFil:               array[1..NoIncFiles] of text;
   IFileName:          array[1..NoIncFiles] of LineType;
   IFN:                1..NoIncFiles;
   PathSign,
   FileName:           LineType;
   Opening,Closing:    Str3;                                          {.CP22}
   PrintDate,
   FileDate:           Str20;
   PrintTime,
   FileTime:           Str10;
   UserID:             string[25];
   Number:             string[16];
   Line:               Str255;
   Day,I,LineNumber,
     PageLineNumber,
     Page,Year,NRes:  integer;
   ScrSeg:             word;
   GotPrnData,Plain,
     Mrk,XRef,Wide,
     XRefOnly,Enough,
     NumberLines,
     InABatch,FFeed,
     DataFiles:        boolean;
   Inst:               Bytes;
   T:                  TpFace;
   Istring:            array[MrkB..CondE] of Str3;
   Reserv:             ResArr;
   OutputDevice:       string[14];
   Command:            CMD;

procedure Bip;
procedure Beep;
procedure Bop;
procedure ToScrn(var S: ScrType);
procedure FromScrn(var S: ScrType);
procedure FillWd(Segm,Offst,Num,Wd: word);
procedure FillOdd(Segm,Offst,Num: integer; Bt: byte);
procedure SkipMove(var From,Target; Num: word);
procedure GetScreen;
procedure WipeSlate(var S: ScrType; Clr: byte);
procedure Rectangle(var S: ScrType; R1,C1,R2,C2,Att,Vert,Hor: byte);
procedure WriteIt(var Scr: ScrType; Str: LineType; R,C,Color: byte);
procedure WriteCRT(Str: LineType; Row,Col,Att: byte);
procedure CenterCRT(S: LineType; Row,Attrib,Width: byte);
procedure Center(var Scr: ScrType; Str: LineType; Line,Color,Width: byte);
function CurrentAttribute: byte;
procedure CursorOff; {invisible but present}
procedure CursorOn;
procedure RestoreScreen;
procedure SetErrorLevel(Level: byte);
procedure SetScrAtt(Att: byte);

function IsIntense(A: byte): boolean;
function Intensified(A: byte): byte;
function Dimmed(A: byte): byte;
function IsBlinking(A: byte): boolean;
function Blinking(A: byte): byte;
function UnBlinking(A: byte): byte;
function BlackBackground(A: byte): boolean;
function BlackForeground(A: byte): boolean;
function BackgroundOf(A: byte): byte;
function ForegroundOf(A: byte): byte;
function InverseOf(A: byte): byte;
function CombinedAttributeOf(F,B: byte): byte;

function PadOrChop(L: LineType; Len: byte):LineType;
procedure Replace(This,WithThat: LineType; var TheLine: LineType);

function StrgB(B,L: Byte): LineType;
function StrgR(R: real;L1,L2: Byte): LineType;
function StrgI(B,L: Integer): Str9;
function Strip(L: LineType; NoNo: CharSet): LineType;
function InCapitals(L: LineType): LineType;

function KbIn(var Extended: boolean): char;
function EditTrm(N: byte): LineType;

function CurrentDriveAndDirectory: LineType;
   {Returns full current drive:\directory}

function EnvironLine(LineStart: LineType): LineType;                   {.CP3}
   {Searches DOS environment for line beginning with LineStart}
   {If found, returned in EnvironLine.  If not returns "NONE"}
function FindFile(var FName: LineType): boolean;                       {.CP4}
   {Takes File name.  Searches for file on default drive & along DOS PATH. }
   {Reports success or failure in FindFile.                                }
   {If file is found, returns openable FName with successful path prefixed.}
procedure CloseCarefully(var F: text);

function Escape: boolean;                                              {.CP3}
{  Empties the keyboard buffer & returns False if no trigger}
{  Does not wait for a keypress}

procedure Blank(Top,Bot: integer);
procedure ByeBye;
procedure GetOutOfHere;
procedure PXLRectangle;
procedure CantCont(FilNam,Comment: LineType);
procedure GetPrinterData;                                              {.CP3}
{If constant DataFiles is True, this procedure loads printer control }
{symbols from PXL.PRN.  If it's false, they're set here.             }
function DefaultDrive: char; {Returns letter of Default Drive}
procedure FixUpFileName(Var FilNam: LineType);
function Shortened(FileName: LineType): Str20;

{===========================================================================}

Implementation

procedure Bip;                                                         {.CP5}
begin
   sound(1760); delay(10); sound(440); delay(30);
   sound(1760); delay(15); nosound
end;

procedure Beep;                                                        {.CP4}
begin
   sound(456);
end; {Beep}

procedure Bop;                                                         {.CP4}
begin
   delay(100); nosound; delay(150); sound(362); delay(400); nosound;
end; {Bop}

procedure ToScrn;                                                     {.CP14}
const
   ScrnPort        = $3D8;           {for CGA board}
   On              =  45;            {for ScrnPort}
   Off             =   5;
begin
   if Monitor=CGA then begin
      Port[ScrnPort] := Off;
      CRTube^ := S;
      Port[ScrnPort] := On
   end {if CGA}
   else
      CRTube^ := S
end; {ToScrn}

procedure FromScrn;                                                   {.CP14}
const
   ScrnPort        = $3D8;           {for CGA board}
   On              =  45;            {for ScrnPort}
   Off             =   5;
begin
   if Monitor=CGA then begin
      Port[ScrnPort] := Off;
      S := CRTube^;
      Port[ScrnPort] := On
   end {if CGA}
   else
      S := CRTube^
end; {FromScrn}

procedure FillWd;                                                     {.CP18}
{ Like FillChar but fills with 2-byte integers.  Here declared:      }
{      procedure FillWd(Segm,Offst,Num,Wd: integer)                  }
{ Can also be declared:                                              }
{      procedure FillWd(var S; Num,Wd: integer)                      }
begin
   inline
                  {FILLWD PROC NEAR                                  }
                  {       INLINE                                     }
   ($C4/$7E/$0A/  { <     les  di, 08H[bp] ; load di and ds at once  }
                  {                                                  }
   $8B/$4E/$08/   { <     MOV  CX,6[BP]    ; Num                     }
   $8B/$46/$06/   { <     MOV  AX,4[BP]    ; Wd                      }
                  {                                                  }
   $FC/           {       cld              ;8088 ==> autoincrement   }
   $F3/           {       rep              ;store CX copies of AX in }
   $AB)           {       stosw            ; ES:[DI] (not DS:[DI])   }
end; {FillWd}

procedure FillOdd;                                                    {.CP21}
   {Turbo Pascal INLINE procedure like FillChar but skips even bytes in }
   {target.  Use it To write without coloring or color without writing. }
   {Here declared:                                                      }
   {   procedure FillOdd(Segm,Offst,Num: integer; Bt: byte);            }
   {Can also be declared:                                               }
   {   procedure FillWd(var V; Num, integer; Bt: byte);                 }
begin                  {       INLINE                                   }
   inline(             {FILLWD PROC NEAR                                }
   $1E/                {       PUSH DS          ; Save DS               }
   $8E/$5E/$0C/        {       MOV  DS,0AH[BP]  ; Segm                  }
   $8B/$7E/$0A/        {       MOV  DI,8H[BP]   ; Offst                 }
   $8B/$4E/$08/        {       MOV  CX,6H[BP]   ; Num                   }
   $29/$C0/            {       SUB  AX,AX                               }
   $8A/$46/$06/        {       MOV  AL,4H[BP]   ; Bt                    }
   $88/$05/            {START  MOV  [DI],AL     ; Put Bt in target      }
   $47/                {       INC  DI          ; Shift target          }
   $47/                {       INC  DI          ;  twice                }
   $E2/$FA/            {       LOOP START       ; Loop CX (Num) times   }
   $1F)                {       POP  DS          ; Restore DS            }
end; {FillOdd}         {       ENDP                                     }

procedure SkipMove;                                                   {.CP27}
  {Moves Num bytes from source to Target, skipping bytes in Target.     }
  {Could write to screen w/o coloring. (Beware: can't handle overlap).  }
  {Here declared:                                                       }
  {   procedure SkipMove(var From,Target; Num: integer);                }
  {Can also be declared:                                                }
  {   procedure SkipMove(SegS,OffS,SegT,OffT,Num: integer);             }
begin                   {                                               }
   inline(              {       inline                                  }
                        {FILLWD PROC NEAR                               }
   $1E/                 {       push ds                                 }
   $8E/$46/$0E/         {       mov  es,0ch[bp]  ;SegS                  }
   $8B/$76/$0C/         {       mov  si,0ah[bp]  ;OffS                  }
                        {                                               }
   $8E/$5E/$0A/         {       MOV  DS,08H[BP]  ; SegT                 }
   $8B/$7E/$08/         {       MOV  DI,06H[BP]  ; OffT                 }
   $28/$ED/             {       sub  ch,ch                              }
   $8A/$4E/$06/         {       MOV  CL,04h[BP]    ; Num in CX          }
                        {                                               }
   $26/$8A/$04/         {START  mov  al,es:[si]  ; Get byte from source }
   $88/$05/             {       MOV  [DI],al     ; Put byte in target   }
   $47/                 {       INC  DI          ; Shift target         }
   $47/                 {       INC  DI          ;  twice               }
   $46/                 {       inc  si          ; Shift source once    }
   $E2/$F6/             {       LOOP START       ; Loop CX (Num) times  }
   $1F)                 {       POP  DS          ; Restore DS           }
end; {SkipMove}         {       ENDP                                    }

procedure GetScreen;                                                  {.CP13}

   function MonitorIsEGA: boolean;
   var
      R: Registers;
   begin
     with R do begin
        AH := $12;
        BX := $FF10;
        intr($10,R);
        MonitorIsEga := BH<>$FF
     end {with}
  end; {MonitorIsEGA}

begin                                                                 {.CP12}
   if (Lo(LastMode)=7) then begin
      CRTube := Ptr($B000,0000);
      Monitor := MDA;
   end {if mode 7}
   else begin
      CRTube := Ptr($B800,0000);
      if MonitorIsEGA
         then Monitor := EGA
         else Monitor := CGA
   end {else not 7}
end; {GetScrn}

procedure WipeSlate;                                                   {.CP8}
{Set attributes all to same color)}
var
   Filler:    integer;
begin
   Filler := (Clr shl 8) + $20;
   FillWd(seg(S),ofs(S),2000,Filler);
end; {WipeSlate}

procedure Rectangle;                                                  {.CP11}
{R1,C1 is row & col of upper left corner, R2,C2 is lower right      }
{Vert is single or double vert char, Hor is single or 2ble horizontal}
const
   OK: set of byte = [1..2];
type
   Rchars = (Hr,Vr,UL,UR,LL,LR);
var
   Element: array[Hr..LR] of byte;
   Row:     byte;
   Filler:  integer;
begin                                                                 {.CP20}
   if not (Hor in OK) then Hor := 1;
   if not (Vert in OK) then Vert := 1;
   if Vert=1 then begin
      Element[Vr] := 179;
      if Hor=1 then begin
         Element[Hr] := 196;       {V1 H1}
         Element[UL] := 218;
         Element[UR] := 191;
         Element[LL] := 192;
         Element[LR] := 217;
      end {if Hor=1}
      else if Hor=2 then begin    {V1 H2}
         Element[Hr] := 205;
         Element[UL] := 213;
         Element[UR] := 184;
         Element[LL] := 212;
         Element[LR] := 190;
       end {if Hor=2}
   end {if V1}
   else begin                                                         {.CP17}
      Element[Vr] := 186;
      if Hor=1 then begin
         Element[Hr] := 196;       {V2 H1}
         Element[UL] := 214;
         Element[UR] := 183;
         Element[LL] := 211;
         Element[LR] := 189;
      end {if Hor=1}
      else if Hor=2 then begin    {V2 H2}
         Element[Hr] := 205;
         Element[UL] := 201;
         Element[UR] := 187;
         Element[LL] := 200;
         Element[LR] := 188;
       end {if Hor=2}
   end; {else Ver=2}

   Filler := Att shl 8 + Element[Hr];                                 {.CP4}
   FillWd(seg(S[R1,C1].I),ofs(S[R1,C1].I),succ(C2-C1),Filler);
   FillWd(seg(S[R2,C1].I),ofs(S[R2,C1].I),succ(C2-C1),Filler);
   Filler := Att shl 8 + Element[Vr];

   for Row := succ(R1) to pred(R2) do begin                           {.CP4}
      S[Row,C1].I  := Filler;
      S[Row,C2].I  := Filler
   end; {for Row}

   S[R1,C1].I  := Att shl 8 + Element[UL];                            {.CP5}
   S[R2,C1].I  := Att shl 8 + Element[LL];
   S[R1,C2].I  := Att shl 8 + Element[UR];
   S[R2,C2].I  := Att shl 8 + Element[LR];
end; {Rectangle}

procedure WriteIt;                                                    {.CP6}
{R is row; C is column in which to start.}
begin
   FillWd(Seg(Scr[R,C]),Ofs(Scr[R,C]),ord(Str[0]),succ(Color shl 8));
   SkipMove(Str[1],Scr[R,C],ord(Str[0]));
end; {WriteIt}

procedure WriteCRT;                                                   {.CP4}
 { Writes characters quickly to the screen starting at Row, Col, }
 { using attribute Att.  Detects presence of Text or CGA board.  }
 { If it finds a CGA, chars are snuck in during vertical retrace }
 { to avoid snow. }
begin
   inline(      {         INLINE ; CHASM's famous Turbo feature  }    {.CP29}
                {WRITECRT PROC FAR                               }
   $1E/         {         PUSH DS                                }
   $1E/         {         PUSH DS                                }
   $8A/$46/$0A/ {         MOV  AL,08H[BP]     ; Row.             }
   $FE/$C8/     {         DEC  AL             ; Make top Row 1   }
   $B3/$50/     {         MOV  BL,80                             }
   $F6/$E3/     {         MUL  BL                                }
   $29/$DB/     {         SUB  BX,BX                             }
   $8A/$5E/$08/ {         MOV  BL,06H[BP]     ; Col              }
   $FE/$CB/     {         DEC  BL             ; Make 1st Col 1   }
   $01/$D8/     {         ADD  AX,BX                             }
   $01/$C0/     {         ADD  AX,AX                             }
   $8B/$F8/     {         MOV  DI,AX                             }
   $8A/$7E/$06/ {         MOV  BH,04H[BP] ; Attrib into BH       }
   $8E/$46/$0E/ {         MOV  ES,0CH[BP] ; Str SEG -> ES        }
   $8B/$76/$0C/ {         MOV  SI,0AH[BP] ; Str OFS -> SI        }
   $29/$C9/     {         SUB  CX,CX ; Addr of Str now in ES:SI  }
   $26/$8A/$0C/ {         MOV  CL,ES:[SI] ; Length of Str in CX  }
   $29/$C0/     {         SUB  AX,AX   ; See if graphix or mono  }
   $8E/$D8/     {         MOV  DS,AX                             }
   $3E/$A0/$49/$04/ {     MOV  AL,DS:[449H]                      }
   $1F/         {         POP  DS                                }
   $20/$C9/     {         AND  CL,CL ; If length(Str)=0 then done}
   $74/$26/     {         JZ   DONE                              }
   $BA/$00/$B0/ {         MOV  DX,0B000H      ; For MONO         }
   $8E/$DA/     {         MOV  DS,DX                             }
   $2C/$07/     {         SUB  AL,7                              }
   $74/$12/     {         JZ   GETCHAR                           }

   $BA/$00/$B8/ {GRAPHICS MOV  DX,0B800H ; Load display mem      }     {.CP9}
   $8E/$DA/     {         MOV  DS,DX   ;    into DS              }
   $BA/$DA/$03/ {         MOV  DX,3DAH ; Status port CGA board   }
   $EC/         {TESTLOW  IN   AL,DX   ; Await vert retr (Test 8)}
   $A8/$08/     {         TEST AL,8    ; (This code found in     }
   $75/$FB/     {         JNZ  TESTLOW ; Tech Ref Man BIOS listg)}
   $EC/         {TESTHI   IN   AL,DX                             }
   $A8/$08/     {         TEST AL,8                              }
   $74/$FB/     {         JZ   TESTHI                            }

   $46/         {GETCHAR  INC  SI     ; Point at next char in Str}     {.CP8}
   $26/$8A/$1C/ {         MOV  BL,ES:[SI] ; Get char into BL     }
   $3E/$89/$1D/ {         MOV  DS:[DI],BX ; Write wd into target }
   $47/         {         INC  DI         ; Shift aim            }
   $47/         {         INC  DI         ;   by 2 bytes         }
   $E2/$F5/     {         LOOP GETCHAR ; CX times (len of string)}

   $1F)         {DONE     POP  DS                                }
end; {WriteCRT} {         ENDP                                   }

procedure CenterCRT;                                                   {.CP8}
begin
   if Width >0 then begin
      BlnkLn[0] := char(Width);
      WriteCRT(BlnkLn,Row,41-(ord(BlnkLn[0]) div 2),Attrib);
   end; {if Width}
   WriteCRT(S,Row,41-(ord(S[0]) div 2),Attrib)
end;

procedure Center;                                                     {.CP13}
var
   StartCol:   byte;
   Filler:     integer;
begin
   if Width>0 then begin
      Filler := (Color shl 8) + $20;
      StartCol := 41 - (Width div 2);
      FillWd(seg(Scr),ofs(Scr[Line,StartCol]),Width,Filler);
   end; {if Width}
   StartCol := 41 - (ord(Str[0]) div 2);
   WriteIt(Scr,Str,Line,StartCol,Color)
end; {Center}

function CurrentAttribute;                                            {.CP12}
var
   R:    DOS.Registers;
begin
   GotoXY(1,pred(WhereY));
   with R do begin
      AH := $08;
      BH := 0;
      Intr($10,R);
      CurrentAttribute := AH
   end {with R}
end; {CurrentAttribute}

procedure CursorOff;                                                   {.CP9}
var
   R:    Registers;
begin
   R.AH := 1;
   R.CH := $20;
   R.CL := 0;
   intr($10,R)
end; {CursorOff}

procedure CursorOn;                                                   {.CP21}
var
   R:    Registers;
begin
   with R do begin {Make standard 2-line cursor}
      AH := 1;      {Make Cursor }
      if Monitor=CGA then begin
         CH := 6;   {top line 6}
         CL := 7;   {bot line 7}
      end {if CGA}
      else if Monitor=EGA then begin
         CH := 7;   {top line 7}
         CL := 10;  {bot line 10}
      end {else if EGA}
      else begin
         CH := 12;  {top 12}
         CL := 13;  {bot 13}
      end; {else MDA}
   end; {with R}
   Intr($10,R);    {BIOS Video service}
end; {CursorOn}

procedure RestoreScreen;                                              {.CP19}
{ Put screen back politely (if A is the atribute found by CurrentAttribute  }
{ on entry).  Scrolls up one line to set color, but does not overwrite any- }
{ other part of the screen.  Makes standard 2-line DOS cursor, placed at    }
{ bottom of the screen.                                                     }
var
   Filler:    integer;
   R:         Registers;
begin
   CursorOn;
   GotoXY(1,24);
   with R do begin {Scroll up one line at bottom of screen coloring   }
      AX := $0601;    {BIOS Video Svc 6 in AH, 1 line to scroll in AL }
      CX := $1700;    {Top row 23 in CH, Lft col 0 in CL }
      DX := $184F;    {Bot row 24 in CH, Rt col 79 in CL }
      BH := OrigAtt;        {Attribute in BH }
   end; {with R}
   Intr($10,R);    {BIOS Video service}
end; {RestoreScreen}

procedure SetErrorLevel(Level: byte);                                 {.CP21}
{Uses DOS function $4C to terminate, setting error level for DOS batch   }
{file to read.  Checks for DOS 2 or higher --$4C would crash DOS 1.10-.  }
{Since $4C also terminates program, handle like halt statement.  Be care-}
{ful.  If run from Turbo, it will terminate Turbo.                       }
var
   Regs:      Registers;
begin
   RestoreScreen;
   with Regs do begin
      AH := $30;                                            {Get DOS version}
      MsDos(Regs);                              {0 in AL if DOS 1.00 or 1.10}
      if AL>0 then begin                {if DOS 2 or higher, set error level}
         AL := Level;                                {--DOS 1 crashes on $4C}
         AH := $4C;                           {Terminate setting error level}
         MsDos(Regs)
      end {if AL>0}
      else
         halt
   end; {with Regs}
end; {SetErrorLevel}

procedure SetScrAtt;                                                   {.CP5}
{Set Turbo's internal variable}
begin
   TextAttr := Att;
end; {SetScrAtt}

function IsIntense;                                                    {.CP4}
begin
   IsIntense := (A and 8)=8
end; {IsDim}

function Intensified;                                                  {.CP4}
begin
   Intensified := A or 8;
end; {Intensified}

function Dimmed;                                                       {.CP4}
begin
   Dimmed := A and 247
end; {Dimmed}

function IsBlinking;                                                   {.CP4}
begin
   IsBlinking := (A and 128)=128
end; {IsBlinking}

function Blinking;                                                     {.CP4}
begin
   Blinking := A or 128
end; {Blinking}

function UnBlinking;                                                   {.CP4}
begin
   UnBlinking := A and 127;
end; {UnBlinking}

function BlackBackground;                                              {.CP4}
begin
   BlackBackground := (A and 112)=0
end; {BlackBackground}

function BlackForeground;                                              {.CP4}
begin
   BlackForeground := (A and 7)=0
end; {BlackForeground}

function BackgroundOf;                                                 {.CP4}
begin
   BackgroundOf := (A and 112) shr 4
end; {BackgroundOf}

function ForegroundOf;                                                 {.CP5}
{including intensity}
begin
   ForegroundOf := A and 15
end; {ForegroundOf}

function InverseOf;                                                    {.CP6}
{Switch background & foreground, preserving intensity}
begin
   InverseOf := (A and 128) + ((A and 112) shr 4)
                + (A and 8) + ((A and 7) shl 4)
end; {InverseOf}

function CombinedAttributeOf;                                          {.CP5}
{Intensity follows F(oreground); ignores blinking.}
begin
   CombinedAttributeOf := ((B and 7) shl 4) or (F and 15)
end; {CombinedAttributeOf}

function PadOrChop(L: LineType; Len: byte):LineType;                   {.CP6}
begin
   while L[0]<char(Len) do L := L + #32;
   if L[0]>char(Len) then L[0] := char(Len);
   PadOrChop := L;
end; {PadOrChop}

procedure Replace; {(This,WithThat: LineType; var TheLine: LineType); {.CP11}
var
   P,K: integer;
begin
   P := pos(This,TheLine);
   while P>0 do begin
      for K := 1 to ord(This[0]) do delete(TheLine,P,1);
      insert(WithThat,TheLine,P);
      P := pos(This,TheLine);
   end; {while P>0}
end; {Replace}

function StrgB;                                                        {.CP7}
var
   S: LineType;
begin
   str(B:L,S);
   StrgB := S
end; {StrgB}

function StrgR;                                                        {.CP7}
var
   S: LineType;
begin
   str(R:L1:L2,S);
   StrgR := S
end; {StrgR}

function StrgI;                                                        {.CP7}
   var
   S: LineType;
begin
   str(B:L,S);
   StrgI := S
end; {StrgB}

function Strip;                                                        {.CP7}
{remove leading & trailing junk (list comes in  NoNo)}
begin {Strip}
   while (L[0]>#0) and (L[1] in NoNo) do delete(L,1,1);
   while L[ord(L[0])] in NoNo do L[0] := pred(L[0]);
   Strip := L
end; {Strip}

function InCapitals;                                                   {.CP7}
var
   K:              byte;
begin {InCapitals}
   for K := 1 to ord(L[0]) do L[K] := UpCase(L[K]);
   InCapitals := L
end; {InCapitals}

function KbIn;                                                        {.CP15}
var
   C:              char;
   N:              integer;
   R:              DOS.Registers;
begin
   C := ReadKey;
   if C<>#0 then
      Extended := False
   else begin
      Extended := True;
      C := ReadKey
   end; {else}
   KbIn := C;
end; {KbIn}

function EditTrm;                                                      {.CP8}
const
   Outs: set of char = [#3,#13,#27];
var
   C:         char;
   S:         LineType;
   Ext:       boolean;
   X,Y:       byte;

   procedure DeleteOne;                                                {.CP9}
   begin
      if length(S)>0 then begin
         delete(S,length(S),1);
         write(#8,#32,#8)
      end {if length>0}
      else
         Bip
   end; {DeleteOne}

begin {EditTrm}                                                       {.CP21}
   S := '';
   CursorOn;
   repeat
      X := WhereX; Y := WhereY;
      C := Kbin(Ext);
      GotoXY(X,Y);
      if Ext then
         if C='K'                                                {back-arrow}
            then DeleteOne
            else bip                            {beep for improper keystroke}
      else if C=#8 then
         DeleteOne
      else if (C=#27) or (C=#3) then
         S := #27
      else if C<>#13 then begin
         S := S + C;
         write(C)
      end; {if}
   until (length(S)>=N) or (C in Outs);
   EditTrm := S;
   CursorOff
end; {EditTrm}

function CurrentDriveAndDirectory;                                      {.CP8}
{Returns full current drive:\directory}
{Needs types: LineType, DOS.Registers}
var
   Data: array[1..64] of char;
   Regs: DOS.Registers;
   Bt:  byte;
   S:    LineType;

   function CurrentDrive: byte;                                         {.CP9}
   {Returns 0 for A:, 1 for B:, etc.}
   var
      Regs: DOS.Registers;
   begin
      Regs.AH := $19;
      MsDos(Regs);
      CurrentDrive := Regs.AL
   end; {CurrentDrive}

begin                                                                  {.CP17}
   Bt := CurrentDrive;
   with Regs do begin
      AH := $47;
      DL := succ(Bt);
      DS := Seg(Data);
      SI := Ofs(Data);
      MsDos(Regs);
   end; {with Regs}
   S := char(Bt+65) + ':\';
   Bt := 1;
   while Data[Bt]<>#0 do begin
      S := S + UpCase(Data[Bt]);
      Bt := succ(Bt)
   end; {while}
   CurrentDriveAndDirectory := S
end; {CurrentDriveAndDirectory}

function EnvironLine;                                                  {.CP30}
{ Searches DOS Environment for line beginning with LineStart        }
{ Returns line with LineStart removed it in EnvironLine if found.   }
{ Returns "NONE" if not found. }
var
   S:               LineType;
   EnvAdd:          word;
   B:               byte;
   LineFound:       boolean;
begin
   EnvAdd := MemW[PrefixSeg:$2C];
   B := 0;
   LineFound := False;
   LineStart := InCapitals(LineStart);
   repeat
      S := '';
      while Mem[EnvAdd:B]<>0 do begin
         S := S + UpCase(char(Mem[EnvAdd:B]));
         B := succ(B)
      end; {while}
      if pos(LineStart,S)=1 then begin
         delete(S,1,ord(LineStart[0]));
         while S[1] in [' ','='] do delete(S,1,1);
         EnvironLine := S;
         LineFound := True
      end; {if PATH}
      B := succ(B)
   until (S[0]=#0) or LineFound;
   if not LineFound then EnvironLine := 'NONE'
end; {EnvironLine}

function FindFile;                                                      {.CP9}
{Takes File name.  Searches for file on default drive & along DOS PATH.  }
{Reports success or failure in FindFile.                                 }
{If file is found, returns openable FName with successful path prefixed. }
var
   Paths,
   Try:       LineType;
   F:         text;   {File type doesn't matter.  File only reset, not read.}
   GotIt:     boolean;

   function Path(var P: LineType): LineType;                          {.CP15}
   {Takes DOS PATH line and peels one path specifier from it.  }
   {Returns specifier in Path, bobtailed DOS PATH line in P.   }
   var
      Chunk:     LineType;
   begin
      Chunk := '';
      while (P[1]<>';') and (P[0]<>#0) do begin
         Chunk := Chunk + P[1];
         delete(P,1,1)
      end; {while not ";"}
      while (P[1]=';') and (P[0]<>#0) do delete(P,1,1);
      if Chunk[ord(Chunk[0])]<>'\' then Chunk := Chunk + '\';
      Path := Chunk
   end; {Path}

   function Found(var F: text): boolean;                              {.CP14}
   {Takes file variable, tries to open it.  Closes file if opened. }
   {Reports success or failure in Found.                           }
   begin
      {$I-}
      reset(F);
      {$I+}
      if IOresult=0 then begin
         Found := True;
         close(F);
      end {if 0}
      else
         Found := False;
   end; {Found}

begin {FindFile}                                                      {.CP23}
   assign(F,FName);
   if Found(F) then
      GotIt := True
   else begin                                          {Strip all path specs}
      while (pos(':',FName)<>0) or (pos('\',FName)<>0) do
         delete(FName,1,1);
      Paths := EnvironLine('PATH');               {Get PATH from Environment}
      if Paths='NONE' then begin
         assign(F,FName);                     {if no PATH, try default drive}
         GotIt := Found(F)
      end {if NONE}
      else begin                                     {else search along PATH}
         repeat
            Try :=  Path(Paths);
            assign(F,Try + FName);
            GotIt := Found(F)
         until (Try='\') or GotIt;
         if GotIt then FName := Try + FName
      end {else found a PATH}
   end; {else not on default drive}
   FindFile := GotIt;
end; {FindFile}

function Escape: boolean;                                             {.CP15}
{  Empties the keyboard buffer & returns False if no trigger}
{  Does not wait for a keypress}

var
   C:              char;
   Temp:           boolean;
begin {Escape}
   Temp := False;
   while KeyPressed and not Temp do begin
      C := ReadKey;
      if C in Triggers then Temp := True
   end; {while}
   Escape := Temp;
end; {Escape}

procedure CloseCarefully;                                               {.CP9}
var
   Err: word;
begin
   {$I-}
   close(F);
   {$I+}
   Err := IOresult;
end; {CloseCarefully}

procedure Blank(Top,Bot: integer);                                     {.CP6}
var
   Row:              integer;
begin
   for Row := Top to Bot do CenterCRT('',Row,Bright,Inside)
end; {Blank}

procedure ByeBye;                                                     {.CP19}
begin
   CloseCarefully(Lst);
   Blank(8,9);
   Blank(18,19);
   if Enough
      then CenterCRT('That''s it, then.',18,Bright,0)
      else CenterCRT('Done.  ' + FileName + ' sent to ' + OutputDevice
                     + '.',10,Bright,Inside);
   CenterCRT('Signing Off.',19,Bright,0);
   if InABatch and Enough then begin
      CenterCRT('Can''t find ' + FileName,11,Bright,0);
      SetErrorLevel(1)      {BEWARE: RUN FROM TURBO, THIS QUITS TO DOS}
   end {if InABatch}
   else begin
      RestoreScreen;
      halt
   end
end; {ByeBye}

procedure GetOutOfHere;                                                {.CP5}
begin
   Enough := True;
   ByeBye
end; {GetOutOfHere}

procedure PXLRectangle;                                               {.CP11}
var
   I: integer;
begin
   WipeSlate(Scr,Bright);
   Rectangle(Scr,BoxT,BoxL,BoxB,BoxR,Dim,2,2);
   Center(Scr,' Pascal X-ref Lister (v. 1.42)',pred(BoxT),Dim,Inside);
   WriteIt(Scr,'R. N. Wisan  fecit  7/85-4/88',succ(BoxB),41,Dim);
   Center(Scr,'To stop, press <Esc>',BoxB -2,Bright,Inside);
   ToScrn(Scr);
end; {Rectangle}

procedure CantCont(FilNam,Comment: LineType);                         {.CP18}
var
   B:              byte;
begin
   Beep;
   CloseCarefully(Lst);
   Blank(10,18);
   CenterCRT('Can''t continue',10,Bright,0);
   if FilNam<>'' then CenterCRT('Error reading ' + FilNam,12,Bright,0);
   CenterCRT(Comment,13,Bright,0);
   Bop;
   if InABatch then
      SetErrorLevel(1)
   else begin
      RestoreScreen;
      Halt
   end
end; {CantCont}

procedure GetPrinterData;                                           {.CP18}
{If constant DataFiles is True, this procedure loads printer control }
{symbols from PXL.PRN.  If it's false, they're set here.             }

   procedure ReadPrn;  {from PXL.PRN}
   var
      F:              Fil;
      FilNam:         LineType;
   begin
      FilNam := 'PXL.PRN';
      GotPrnData := FindFile(FilNam);
      if GotPrnData then begin
         assign(F,FilNam);
         reset(F);
         for T := MrkB to FF do if not Eof(F) then read(F,Inst[T]);
         close(F)
      end {if no error}
   end; {ReadPrn}

   procedure IntPrn; {Set here for Epson FX-80}                       {.CP20}
   begin
      {Note: MrkB & MrkE are set for underline.  If you prefer some other}
      {way of marking the key words, change them here.                   }
      Inst[MrkB,0] := 3;
         Inst[MrkB,1] := 27;   Inst[MrkB,2] := 45;   Inst[MrkB,3] := 1;
      Inst[MrkE,0] := 3;
         Inst[MrkE,1] := 27;   Inst[MrkE,2] := 45;   Inst[MrkE,3] := 0;
      Inst[EliteB,0] := 2;
         Inst[EliteB,1] := 27; Inst[EliteB,2] := 77; Inst[EliteB,3] := $FF;
      Inst[EliteE,0] := 2;
         Inst[EliteE,1] := 27; Inst[EliteE,2] := 80; Inst[EliteE,3] := $FF;
      Inst[CondB,0] := 1;
         Inst[CondB,1] := 15;  Inst[CondB,2] := $FF; Inst[CondB,3] := $FF;
      Inst[CondE,0] := 1;
         Inst[CondE,1] := 18;  Inst[CondE,2] := $FF; Inst[CondE,3] := $FF;
      Inst[FF,0] := 1;
         Inst[FF,1] := 12;     Inst[FF,2] := $FF;    Inst[FF,3] := $FF;
      GotPrnData := True;
   end; {IntPrn}

begin {GetPrinterData}                                                {.CP10}
   if DataFiles then
      ReadPrn
   else
      IntPrn;
   if not GotPrnData then
      Inst[FF,1] := 66;                          {Default to Vanilla printer}
   if (Inst[FF,1] in [12,255])                               {Set Lines/Page}
      then MaxLin := 66 - BottomMargin
      else MaxLin := Inst[FF,1] - (BottomMargin)
end; {GetPrinterData}

function DefaultDrive: char; {Returns letter of Default Drive}        {.CP10}
var
   Regs:      Registers;
begin
   with Regs do begin
      AH := $19;
      MsDos(Regs);
      DefaultDrive := char(65 + AL)
   end {with Regs}
end; {DefaultDrive}

procedure FixUpFileName(Var FilNam: LineType);                        {.CP31}
const
   PathSigns: set of char = [':','\'];
var
   B,Len:       byte;
begin
   while (FilNam[1]=#32) and (FilNam[0]>#0) do         {Strip leading blanks}
      delete(FilNam,1,1);
   while FilNam[ord(FilNam[0])]=#32 do                {Strip trailing blanks}
      FilNam[0] := pred(FilNam[0]);
   for B := 1 to Length(FilNam) do                               {Capitalize}
      FilNam[B] := UpCase(FilNam[B]);
   B := ord(FilNam[0]);                           {count length of bare name}
   while (B>0) and not (FilNam[B] in PathSigns) do
      B := pred(B);
   Len := ord(FilNam[0]) - B;
   if pos(':',FilNam)=0 then          {if no drive letter, add Default Drive}
      FilNam := DefaultDrive + ':' + FilNam;
   if pos('.',FilNam)<>0 then begin                  {if has a period       }
      while (FilNam[0]>#0) and (FilNam[ord(FilNam[0])]='.') do begin
         FilNam[0] := pred(FilNam[0]);                 {delete terminal dots}
         Len := pred(Len)                              {adjust length count }
      end {while terminal dot}
   end {if has "."}
   else if Len>10 then begin                    {else if long, insert period}
      B := ord(FilNam[0]) - Len + 8;
      FilNam := concat(copy(FilNam,1,B),'.',copy(FilNam,succ(B),3))
   end {else no "." & over long}
   else
      FilNam := concat(FilNam,'.PAS')            {otherwise, default to .PAS}
end; {FixUpFileName}

function Shortened(FileName: LineType): Str20;
begin
   while (pos(':',FileName)<>0) or (pos('\',FileName)<>0) do
      delete(FileName,1,1);
   Shortened := FileName;
end; {Shortened}

procedure MakeBlnkLn; {private to PXLINIT}                             {.CP9}
var
   K: integer;
begin
   BlnkLn := '';
   for K := 1 to StdLineWidth do
      BlnkLn := BlnkLn + #32;
   Inside := pred(BoxR) - succ(BoxL);
end; {MakeBlnkLn}

begin {initialize PXLINIT}                                             {.CP5}
   OrigAtt := CurrentAttribute;
   GetScreen;
   MakeBlnkLn;
end.
