home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / BROWSE2.ZIP / BROWSE2.PAS
Encoding:
Pascal/Delphi Source File  |  1986-03-28  |  43.1 KB  |  1,335 lines

  1.   {$C-}
  2.  
  3. PROGRAM browse;
  4.  
  5. {bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}
  6.  
  7.     {*************************************************************************}
  8.     {*         Copyright (c) Kim Kokkonen, TurboPower Software, 1985         *}
  9.     {*  Released to the public domain for personal, non-commercial use only  *}
  10.     {*************************************************************************}
  11.  
  12.     {Source: Kim Kokkonen's disk 1/86
  13.      revisions by [SD]
  14.      * 1986 *
  15.      - 02/27 : read cmd line param for file name; use cursor pad keys; F1, F6
  16.      - 03/01 : force user enter filename if file not found
  17.      }
  18.  
  19.  
  20.     {.F-}
  21.     {
  22.     --- IBM PC SPECIFIC ---
  23.     BROWSE through two files simultaneously in two independent windows.
  24.  
  25.     writes directly to video memory, but with timing interlocks for c/g card.
  26.     keys are set up to resemble WordStar cursor movements.
  27.     see GETKEY to do keyboard customization.
  28.     requires Turbo version 3.0 to compile as it stands.
  29.     set min/max heap=$200/$200 paragraphs.
  30.     written 8/2/85, Kim Kokkonen, 408-378-3672, CompuServe [72457,2131]
  31.     updated 10/3/85 for faster video writes.
  32.  
  33.     FEATURES:
  34.         filenames may include drive and path
  35.         unlimited size files (or 32767 lines of 199 chars each, anyway)
  36.         two circular RAM buffers each of 127 text lines paged from disk
  37.         full forward and reverse scrolling at high speed
  38.         horizontal scrolling
  39.         synchronized windows mode
  40.         pop-up help (type ^H for help)
  41.         seek to line number
  42.         search for strings (forward, uppercase modes only)
  43.         invoke DOS process and return to place in BROWSE
  44.           (requires ~96K free RAM at entry to BROWSE to invoke DOS)
  45.     }
  46.     {.F+}
  47.  
  48.   CONST
  49. {* NOTE: 127 lines * 199 char/line = 25273 chars }
  50. {*} Version : string[30] = '0.0a-PC Oct. 3, 1985 [SD 3/86]';
  51.     LineSize = 199;           {max line length stored, longer lines truncated}
  52.     BufSize = 140;            {lines of text (-1) in each textbuffer; orig=127}
  53.     LinAvail = 32;            {lines to always keep available at top of buffer}
  54.     slines = 11;              {number of lines (-1) in each window}
  55.     WindColor = 15;           {video attribute to use in text windows}
  56.     GoodColorCard = True;     {set false for IBM CGA, which requires
  57.                               timing interlocks to avoid snow on screen.
  58.                               Compaq graphics card among others allows
  59.                               this constant to be TRUE, and faster as a
  60.                               result. GoodColorCard is ignored if mono
  61.                               adapter is in use.}
  62.   TYPE
  63.     Window = 0..1;
  64.     PathString = STRING[64];
  65.     TextFile = Text[1024];
  66.     LineBuffer = STRING[LineSize];
  67.     ByteBuffer = ARRAY[0..LineSize] OF Byte;
  68.     TextBuffer = ARRAY[0..BufSize] OF LineBuffer;
  69.     TwoText = ARRAY[Window] OF TextFile;
  70.     TwoBuffer = ARRAY[Window] OF TextBuffer;
  71.     TwoPath = ARRAY[Window] OF PathString;
  72.     TwoState = ARRAY[Window] OF Boolean;
  73.     TwoInt = ARRAY[Window] OF Integer;
  74.     FullScreen = ARRAY[1..4000] OF Byte;
  75.     regpack = RECORD
  76.                 CASE Integer OF
  77.                   1 : (ax, bx, cx, dx, bp, si, di, ds, es, flags : Integer);
  78.                   2 : (al, ah, bl, bh, cl, ch, dl, dh : Byte);
  79.               END;
  80.  
  81.   VAR
  82.     f : TwoText;
  83.     b : TwoBuffer;
  84.     p : TwoPath;
  85.     NotPadded, s : TwoState;
  86.     LinNum, OldPtr, BotPtr, TopPtr, BufPtr, OldEdge, LeftEdge : TwoInt;
  87.     SearchString : LineBuffer;
  88.     SeekNumber : Integer;
  89.  
  90.     {watch out -- the following l, bl, len, reg are used at all scope levels}
  91.     l : LineBuffer;
  92.     bl : ByteBuffer ABSOLUTE l;
  93.     len : Byte ABSOLUTE l;
  94.     reg : regpack;
  95.  
  96.     TmpWind, N : Window;      {which window we are in}
  97.     Bufmod, CursorType, VidStatPort, VidModePort, ScreenSeg : Integer;
  98.     SavScr : FullScreen;
  99.     ch : Byte;
  100.     NoWait, Synced : Boolean;
  101.     {BIOS stores most recent video mode select port value here}
  102.     ModePortData : Byte ABSOLUTE $40 : $65;
  103.  
  104. {*} Use_Params : BOOLEAN;
  105.  
  106.   PROCEDURE CursorOn(CursorType : Integer);
  107.       {restore the stored cursor}
  108.     BEGIN
  109.       reg.cx := CursorType;
  110.       reg.ah := 1;
  111.       Intr($10, reg);
  112.     END;                      {cursoron}
  113.  
  114.   PROCEDURE CursorOff(VAR CursorType : Integer);
  115.       {save the current cursor and turn off the cursor}
  116.     VAR
  117.       DOScursor : Integer ABSOLUTE $40 : $60;
  118.     BEGIN
  119.       CursorType := DOScursor;
  120.       reg.ch := $20;
  121.       reg.ah := 1;
  122.       Intr($10, reg);
  123.     END;                      {cursoroff}
  124.  
  125.   FUNCTION which_screen(VAR CursorType, VidStatPort, VidModePort : Integer)
  126.       : Integer;
  127.       {-determines which screen TURBO is writing to}
  128.       {-and gets a default cursor type and video port}
  129.     VAR
  130.       {holds video controller port base address}
  131.       vid : Integer ABSOLUTE $40 : $63;
  132.     BEGIN
  133.       GoToXY(1, 1);
  134.       Mem[$B000 : 0] := 0;
  135.       Mem[$B800 : 0] := 0;
  136.       Write(' ');
  137.       IF Mem[$B000 : 0] = $20 THEN BEGIN
  138.         {monochrome adapter}
  139.         which_screen := $B000;
  140.         CursorType := $B0C;
  141.         NoWait := True;
  142.       END ELSE BEGIN
  143.         {color/graphics adapter}
  144.         which_screen := $B800;
  145.         CursorType := $607;
  146.         NoWait := False;
  147.       END;
  148.       VidStatPort := vid+6;   {video status port for either card}
  149.       VidModePort := vid+4;   {video mode port for either card}
  150.       NoWait := NoWait OR GoodColorCard;
  151.     END;                      {which_screen}
  152.  
  153.   PROCEDURE DrawCenterBar(i : Byte);
  154.       {-center bar holds context sensitive "prompts"}
  155.     BEGIN
  156.       GoToXY(1, 13);
  157.       IF i = 1 THEN
  158.         Write('═══════════════════════════════════════════════════════════════════════════════')
  159.       ELSE IF i = 2 THEN
  160. {        Write('═════════════════════════════ Press ^H for Help ═══════════════════════════════')}
  161. {*}      Write('══════════════════════════ Press F1 or ^H for Help ════════════════════════════')
  162.       ELSE IF i = 3 THEN
  163.         Write('══════════════════ Searches are "Forward, Uppercase" only ═════════════════════');
  164.     END;                      {drawcenterbar}
  165.  
  166.   PROCEDURE DrawBorder;
  167.       {-draw a border for each window}
  168.     BEGIN
  169.       TextColor(WindColor);
  170.       {note following clrscr sets the attribute bytes for the text areas we later write to}
  171.       ClrScr;
  172.       TextColor(0);
  173.       TextBackground(7);
  174.       GoToXY(1, 12);
  175.       Write('' : 79);
  176.       GoToXY(1, 14);
  177.       Write('' : 79);
  178.     END;                      {drawborder}
  179.  
  180.   PROCEDURE ClearStat(N : Window; l, r : Byte);
  181.       {clear status line from col l to col r inclusive}
  182.     BEGIN
  183.       GoToXY(l, 12+2*N);
  184.       Write(' ' : (r-l+1));
  185.     END;
  186.  
  187.   PROCEDURE MoveToStat(N : Window; l : Byte);
  188.       {-move to position on status line}
  189.     BEGIN
  190.       GoToXY(l, 12+2*N);
  191.     END;                      {movetostat}
  192.  
  193.   PROCEDURE DrawTitle(N : Window);
  194.       {-show the fixed title info}
  195.     BEGIN
  196.       MoveToStat(N, 49); Write('Line ');
  197.       MoveToStat(N, 67); Write('Col ');
  198.     END;                      {drawtitle}
  199.  
  200.   PROCEDURE ShowWindow(N : Window);
  201.       {-make obvious which window is active}
  202.     BEGIN
  203.       CursorOn(CursorType);
  204.       IF Synced THEN BEGIN
  205.         {show char in opposite window also}
  206.         MoveToStat(1-N, 45);
  207.         Write(Chr(25-N), Chr(25-N));
  208.       END ELSE
  209.         {erase char from previous window}
  210.         ClearStat(1-N, 45, 46);
  211.  
  212.       {put appropriate char in this window}
  213.       MoveToStat(N, 45); Write(Chr(24+N), Chr(24+N));
  214.       CursorOff(CursorType);
  215.     END;                      {showwindow}
  216.  
  217.   PROCEDURE WaitOn(N : Window);
  218.       {-show a wait symbol}
  219.     BEGIN
  220.       CursorOn(CursorType);
  221.       ClearStat(N, 3, 43);
  222.       MoveToStat(N, 16);
  223.       TextColor(16);          {blinking reverse video}
  224.       Write('--- W A I T ---');
  225.       TextColor(0);
  226.       CursorOff(CursorType);
  227.     END;                      {waiton}
  228.  
  229.   PROCEDURE WaitOff(N : Window);
  230.       {-turn off wait symbol and restore what it overwrote}
  231.     BEGIN
  232.       {put up file name again}
  233.       CursorOn(CursorType);
  234.       ClearStat(N, 3, 43);
  235.       MoveToStat(N, 3);
  236.       Write('File: ', p[N]);
  237.       CursorOff(CursorType);
  238.     END;                      {waitoff}
  239.  
  240.   FUNCTION BuffPrior(N : Window;
  241.                      VAR BotPtr, BufPtr : TwoInt) : Integer;
  242.       {-return the number of lines to bottom of buffer}
  243.     BEGIN
  244.       IF BotPtr[N] > BufPtr[N] THEN
  245.         BuffPrior := BufPtr[N]-BotPtr[N]+Bufmod
  246.       ELSE
  247.         BuffPrior := BufPtr[N]-BotPtr[N];
  248.     END;                      {buffprior}
  249.  
  250.   FUNCTION BuffAfter(N : Window;
  251.                      VAR TopPtr, BufPtr : TwoInt) : Integer;
  252.       {-return the number of lines to top of buffer}
  253.     BEGIN
  254.       IF TopPtr[N] < BufPtr[N] THEN
  255.         BuffAfter := TopPtr[N]+Bufmod-BufPtr[N]
  256.       ELSE
  257.         BuffAfter := TopPtr[N]-BufPtr[N];
  258.     END;                      {buffafter}
  259.  
  260.   FUNCTION CurrLine(N : Window) : Integer;
  261.       {-return the current line number of the file in the window}
  262.     BEGIN
  263.       {lines from bottom of buffer to current plus lines to bottom of file}
  264.       CurrLine := BuffPrior(N, BotPtr, BufPtr)+LinNum[N];
  265.     END;                      {currline}
  266.  
  267.   PROCEDURE ShowLin(N : Window;
  268.                     VAR LinNum, BotPtr, BufPtr : TwoInt);
  269.       {-display the current range of lines}
  270.     VAR
  271.       lins : Integer;
  272.     BEGIN
  273.       {erase previous range}
  274.       CursorOn(CursorType);
  275.       ClearStat(N, 54, 64);
  276.       lins := CurrLine(N);
  277.       {write new range}
  278.       MoveToStat(N, 54); Write(lins, '-', lins+slines-1);
  279.       {turn off cursor again}
  280.       CursorOff(CursorType);
  281.     END;                      {showlin}
  282.  
  283.   PROCEDURE ShowCol(N : Window; LeftEdge : TwoInt);
  284.       {-show the current column range}
  285.     VAR
  286.       t : Integer;
  287.     BEGIN
  288.       {erase previous range}
  289.       CursorOn(CursorType);
  290.       ClearStat(N, 71, 77);
  291.       t := LeftEdge[N];
  292.       {write new range}
  293.       MoveToStat(N, 71); Write(t, '-', t+79);
  294.       CursorOff(CursorType);
  295.     END;                      {showcol}
  296.  
  297.   FUNCTION ModSucc(N : Integer) : Integer;
  298.       {-increment the argument mod bufmod}
  299.     BEGIN
  300.       ModSucc := (N+1) MOD Bufmod;
  301.     END;                      {modsucc}
  302.  
  303.   FUNCTION ModPred(N : Integer) : Integer;
  304.       {-decrement the argument mod bufmod}
  305.     BEGIN
  306.       IF N <= 0 THEN ModPred := BufSize ELSE ModPred := N-1;
  307.     END;                      {modpred}
  308.  
  309.   PROCEDURE BrkOff;
  310.       {-shut off ctrl-break check to assure WordStar ^C gets through}
  311.     BEGIN
  312.       reg.ax := $3301;
  313.       reg.dx := 0;
  314.       MsDos(reg);
  315.     END;                      {brkoff}
  316.  
  317.   PROCEDURE GetFile(N : Window;
  318.                     VAR f : TwoText;
  319.                     VAR p : TwoPath;
  320.                     VAR s : TwoState);
  321.       {-open either of the files for read}
  322.     VAR
  323.       good : Boolean;
  324.       ch : Char;
  325.     BEGIN
  326.       DrawCenterBar(1);
  327.       CursorOn(CursorType);
  328.       REPEAT
  329.         ClearStat(N, 3, 77);
  330.  
  331. {*} {only use cmd line params the 1st time program is invoked}
  332.         if (Use_Params) AND ( ParamCount > N ) then   {eg 0 for 1st file; 1 for 2nd}
  333.         begin
  334.            MoveToStat(N,3);
  335.            p[N] := ParamStr(N+1);
  336.            s[N] := TRUE;
  337.         end
  338.         else
  339.         begin
  340.           MoveToStat(N, 3);
  341.           Write('Enter file for this window (<ret> for none): ');
  342.           Read(p[N]);
  343.           s[N] := (p[N] <> ''); {false means no file in window}
  344.         end; {if,ParamCount}
  345.  
  346.         ClearStat(N, 3, 77);
  347.         MoveToStat(N, 3);
  348.  
  349.         IF s[N] THEN BEGIN
  350.           {see if file exists}
  351.           Assign(f[N], p[N]);
  352.           {$I-} Reset(f[N]);  {$I+}
  353.           good := (IOResult = 0);
  354.           IF good THEN
  355.             Write('File: ', p[N])
  356.           ELSE BEGIN
  357.             Write(p[N], ' not found... press any key to try again');
  358.             Use_Params := FALSE;
  359.             Read(Kbd, ch);
  360.           END;
  361.         END ELSE BEGIN
  362.           good := True;
  363.           Write('File: none');
  364.         END;
  365.       UNTIL good;
  366.       DrawCenterBar(2);
  367.       CursorOff(CursorType);
  368.     END;                      {getfile}
  369.  
  370.   PROCEDURE ReadAndExpandLine(VAR f : TextFile; VAR nl : LineBuffer);
  371.       {-read a line from the file and expand tabs, returning a line}
  372.     VAR
  373.       i, o : Byte;
  374.     BEGIN
  375.       ReadLn(f, l);
  376.       i := 1;
  377.       o := 0;
  378.       WHILE i <= len DO BEGIN
  379.         IF l[i] = #9 THEN BEGIN
  380.           {expand tabs}
  381.           o := o+1;
  382.           nl[o] := #32;
  383.           WHILE (o MOD 8) <> 0 DO BEGIN
  384.             o := o+1;
  385.             nl[o] := #32;
  386.           END;
  387.         END ELSE BEGIN
  388.           {insert regular character}
  389.           {could insert a high bit or other filter here}
  390.           o := o+1;
  391.           nl[o] := l[i];
  392.         END;
  393.         i := i+1;
  394.       END;
  395.       {set length of nl}
  396.       nl[0] := Chr(o);
  397.     END;                      {readandexpandline}
  398.  
  399.   PROCEDURE PadBuffer(N : Window;
  400.                       VAR BotPtr, TopPtr, BufPtr, LinNum : TwoInt;
  401.                       VAR NotPadded : TwoState);
  402.       {-assure end of buffer will fill screen with blanks}
  403.     VAR
  404.       cnt : Integer;
  405.     BEGIN
  406.       cnt := 1;
  407.       WHILE cnt < slines DO BEGIN
  408.         {fill with empty lines}
  409.         b[N][TopPtr[N]] := '';
  410.         TopPtr[N] := ModSucc(TopPtr[N]);
  411.         IF TopPtr[N] = BotPtr[N] THEN BEGIN
  412.           {buffer full, compensate}
  413.           BotPtr[N] := ModSucc(BotPtr[N]);
  414.           LinNum[N] := LinNum[N]+1;
  415.         END;
  416.         cnt := cnt+1;
  417.       END;
  418.       NotPadded[N] := False;
  419.     END;                      {padbuffer}
  420.  
  421.   PROCEDURE FillBuff(N : Window;
  422.                      linstart : Integer;
  423.                      VAR BotPtr, TopPtr, BufPtr, LinNum : TwoInt;
  424.                      VAR NotPadded : TwoState);
  425.       {-fill the buffer from the beginning, referenced from linstart}
  426.     VAR
  427.       ch : Char;
  428.     BEGIN
  429.       NotPadded[N] := True;
  430.       BotPtr[N] := 0;
  431.       BufPtr[N] := 0;
  432.       TopPtr[N] := 0;
  433.       LinNum[N] := linstart;  {always holds line num at bottom of buffer}
  434.       IF s[N] THEN BEGIN
  435.         WHILE NOT(EoF(f[N])) AND (TopPtr[N] < BufSize) DO BEGIN
  436.           ReadAndExpandLine(f[N], b[N][TopPtr[N]]);
  437.           TopPtr[N] := TopPtr[N]+1;
  438.           IF KeyPressed THEN Read(Kbd, ch);
  439.         END;
  440.         {pad buffer with blanks for short files}
  441.         IF EoF(f[N]) THEN
  442.           PadBuffer(N, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  443.       END;
  444.     END;                      {fillbuff}
  445.  
  446.   FUNCTION CalcScrAdd(r : Byte) : Integer;
  447.       {-return the offset into the screen segment of a given row at column 1}
  448.     VAR
  449.       t : Integer;
  450.     BEGIN
  451.       {fast way of saying 160*(r-1)}
  452.       t := (r-1) SHL 4;
  453.       CalcScrAdd := ((t SHL 2)+t) SHL 1;
  454.     END;                      {calcscradd}
  455.  
  456.   PROCEDURE ScrollWind(r1, r2, lines : Byte);
  457.       {-scroll the region between rows r1 and r2 by lines lines}
  458.       {lines>0 scrolls up, <0 scrolls down, =0 erases window}
  459.     BEGIN
  460.       reg.al := Abs(lines);
  461.       IF lines > 0 THEN reg.ah := 6 ELSE reg.ah := 7;
  462.       reg.cx := (r1-1) SHL 8;
  463.       reg.dx := ((r2-1) SHL 8) OR 79;
  464.       reg.bh := WindColor;
  465.       Intr($10, reg);
  466.     END;                      {scollwind}
  467.  
  468.   PROCEDURE MoveToScreen(N : Window; VAR LeftEdge, BufPtr, OldPtr : TwoInt);
  469.       {-move buffer info to screen}
  470.     VAR
  471.       cnt, r, sadd, left : Integer;
  472.       rt, rb, lines : Byte;
  473.  
  474.     PROCEDURE WriteLine(r, sadd, left : Integer);
  475.         {-write the line of text ref'ed by r to video memory starting at sadd}
  476.       BEGIN
  477.         {assure l right padded with blanks}
  478.         FillChar(l[1], LineSize, #32);
  479.         {put real data at beginning of l}
  480.         l := Copy(b[N][r], left, 80);
  481.  
  482.         IF NoWait THEN BEGIN
  483.           {write to video, no timing interlocks}
  484.           INLINE(
  485.             $A1/ScreenSeg/    {MOV    AX,screenseg}
  486.             $8E/$C0/          {MOV    ES,AX}
  487.             $8B/$7E/$06/      {MOV    DI,[BP+06]}
  488.             $BE/l/            {MOV    SI,offset(l)}
  489.             $46/              {INC    SI, skip length byte}
  490.             $B9/$50/$00/      {MOV    CX,0050}
  491.             $FC/              {CLD    }
  492.             $A4/              {MOVSB    }
  493.             $47/              {INC    DI, skip attribute}
  494.             $E2/$FC           {LOOP    0116}
  495.             );
  496.         END ELSE BEGIN
  497.           {write to video memory with timing interlocks}
  498.           {.F+}
  499.           INLINE(
  500.             {get screenseg into es}
  501.             $A1/ScreenSeg/    {MOV    AX,screenseg}
  502.             $8E/$C0/          {MOV    ES,AX}
  503.  
  504.             {get screen offset sadd into di}
  505.             $8B/$7E/$06/      {MOV    DI,[BP+06]}
  506.  
  507.             {get video status port into dx}
  508.             $8B/$16/VidStatPort/ {MOV    DX,vidstatport}
  509.  
  510.             {point to string data with si, string is in dataseg}
  511.             $BE/l/            {MOV    SI,offset(l)}
  512.             $46/              {INC    SI}
  513.  
  514.             {move 80 bytes to fill one row of screen}
  515.             $B9/$50/$00/      {MOV    CX,0050}
  516.             $FC/              {CLD    }
  517.  
  518.             {loop to write to screen}
  519.             $AC/              {LODSB    }
  520.             $8A/$D8/          {MOV    BL,AL}
  521.             $B4/$09/          {MOV    AH,09}
  522.             $FA/              {CLI    }
  523.             $EC/              {IN    AL,DX}
  524.             $D0/$D8/          {RCR    AL,1}
  525.             $72/$FB/          {JB    0106}
  526.             $EC/              {IN    AL,DX}
  527.             $20/$E0/          {AND    AL,AH}
  528.             $75/$FB/          {JNZ    010B}
  529.             $8A/$C3/          {MOV    AL,BL}
  530.             $AA/              {STOSB    }
  531.             $47/              {INC    DI}
  532.             $FB/              {STI    }
  533.             $E2/$E9           {LOOP    0100}
  534.             {.F+}
  535.             );                {end of inline}
  536.         END;
  537.       END;                    {writeline}
  538.  
  539.     BEGIN
  540.       r := BufPtr[N];         {starting row in buffer}
  541.       rt := 14*N+1;           {starting row on screen}
  542.       left := LeftEdge[N];    {first char of string that we display}
  543.  
  544.       {redraw entire window}
  545.       sadd := CalcScrAdd(rt); {starting address on screen for this window}
  546.       FOR cnt := 1 TO slines DO BEGIN
  547.         {loop through lines, all guaranteed to exist in buffer}
  548.         WriteLine(r, sadd, left);
  549.         {address of next line of screen}
  550.         sadd := sadd+160;
  551.         {next row of buffer}
  552.         r := ModSucc(r);
  553.       END;
  554.  
  555.     END;                      {movetoscreen}
  556.  
  557.   PROCEDURE InitWindow(N : Window;
  558.                        VAR BotPtr, TopPtr, BufPtr, OldPtr,
  559.                        LeftEdge, OldEdge, LinNum : TwoInt;
  560.                        VAR NotPadded : TwoState);
  561.       {-initialize everything for a single window}
  562.     BEGIN
  563.       NotPadded[N] := True;
  564.       FillBuff(N, 1, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  565.       LeftEdge[N] := 1;
  566.       OldEdge[N] := 1;
  567.       OldPtr[N] := BufPtr[N];
  568.       MoveToScreen(N, LeftEdge, BufPtr, OldPtr);
  569.       DrawTitle(N);
  570.       ShowLin(N, LinNum, BotPtr, BufPtr);
  571.       ShowCol(N, LeftEdge);
  572.     END;                      {initwindow}
  573.  
  574.   PROCEDURE SwitchN(VAR N : Window);
  575.       {-switch to opposite window}
  576.     BEGIN
  577.       IF s[1-N] THEN
  578.         N := 1-N
  579.       ELSE BEGIN
  580.         {get another file for the unused window}
  581.         GetFile(1-N, f, p, s);
  582.         IF s[1-N] THEN BEGIN
  583.           {got a file, initialize it and window}
  584.           N := 1-N;
  585.           DrawTitle(N);
  586.           InitWindow(N, BotPtr, TopPtr, BufPtr, OldPtr,
  587.           LeftEdge, OldEdge, LinNum, NotPadded);
  588.         END;
  589.       END;
  590.       ShowWindow(N);
  591.     END;                      {switchn}
  592.  
  593.   PROCEDURE UpdateBuff(N : Window;
  594.                        VAR BotPtr, TopPtr, BufPtr, LinNum : TwoInt;
  595.                        VAR NotPadded : TwoState);
  596.       {-determine whether to add to buffer and do so}
  597.     VAR
  598.       buffleft : Integer;
  599.     BEGIN
  600.       {see if we are within linavail of top of buffer}
  601.       IF BuffAfter(N, TopPtr, BufPtr) < LinAvail THEN BEGIN
  602.         {add linavail's worth of buffer}
  603.         buffleft := LinAvail;
  604.         WHILE NOT(EoF(f[N])) AND (buffleft > 0) DO BEGIN
  605.           ReadAndExpandLine(f[N], b[N][TopPtr[N]]);
  606.           buffleft := buffleft-1;
  607.           TopPtr[N] := ModSucc(TopPtr[N]);
  608.           BotPtr[N] := ModSucc(BotPtr[N]);
  609.           LinNum[N] := LinNum[N]+1;
  610.         END;
  611.         IF EoF(f[N]) AND NotPadded[N] THEN
  612.           PadBuffer(N, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  613.       END;
  614.     END;                      {updatebuff}
  615.  
  616.   PROCEDURE ShowRight(N : Byte; VAR BufPtr, LeftEdge : TwoInt);
  617.       {-set leftedge so end of longest line shows}
  618.     VAR
  619.       maxlen, len, lin, cnt : Integer;
  620.     BEGIN
  621.       lin := BufPtr[N];
  622.       maxlen := 0;
  623.       {get maximum line length in window}
  624.       FOR cnt := 1 TO slines DO BEGIN
  625.         len := Length(b[N][lin]);
  626.         IF len > maxlen THEN maxlen := len;
  627.         lin := ModSucc(lin);
  628.       END;
  629.       {convert to a leftedge value}
  630.       len := maxlen-79;
  631.       IF len < 1 THEN len := 1;
  632.       {reset leftedge if appropriate}
  633.       IF LeftEdge[N] < len THEN LeftEdge[N] := len;
  634.     END;                      {showright}
  635.  
  636.   PROCEDURE SeekBack(N : Window; backn : Integer;
  637.                      VAR BotPtr, TopPtr, BufPtr, OldPtr, LinNum : TwoInt);
  638.       {-resynchronize buffer from beginning}
  639.       {-backn is the number of lines to move final bufptr back}
  640.     VAR
  641.       linsave, linbegin, r : Integer;
  642.       ch : Char;
  643.     BEGIN
  644.  
  645.       {may take a while, put up a wait sign}
  646.       WaitOn(N);
  647.  
  648.       {get and save current line number - note that bufprior=0 to arrive here}
  649.       linsave := LinNum[N];
  650.  
  651.       {new line to resynchronize beginning of buffer}
  652.       linbegin := linsave-(BufSize SHR 1);
  653.       IF linbegin < 1 THEN linbegin := 1;
  654.  
  655.       {reset file and leave file pointer ready to read linbegin}
  656.       Reset(f[N]);
  657.       Flush(f[N]);
  658.       FOR r := 1 TO linbegin DO BEGIN
  659.         ReadLn(f[N], l);
  660.         {clear keyboard buffer of any strokes built up during delay}
  661.         IF KeyPressed THEN Read(Kbd, ch);
  662.       END;
  663.  
  664.       {fill buffer starting at linbegin}
  665.       FillBuff(N, linbegin, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  666.  
  667.       {set bufptr to be at correct line}
  668.       BufPtr[N] := linsave-linbegin-backn;
  669.       {guarantee we redisplay}
  670.       OldPtr[N] := -2;
  671.       WaitOff(N);
  672.     END;                      {seekback}
  673.  
  674.   FUNCTION GetLineNumber(N : Window) : Integer;
  675.       {-return a legal line number to seek}
  676.     VAR
  677.       seeknum : Integer;
  678.       good : Boolean;
  679.       ch : Char;
  680.     BEGIN
  681.       {prompt for the number}
  682.       DrawCenterBar(1);
  683.       CursorOn(CursorType);
  684.       REPEAT
  685.         ClearStat(N, 3, 43);
  686.         MoveToStat(N, 3);
  687.         Write('Enter line number: ');
  688.         {$I-} Read(seeknum);  {$I+}
  689.         good := (IOResult = 0);
  690.         IF NOT(good) THEN BEGIN
  691.           ClearStat(N, 3, 43);
  692.           MoveToStat(N, 3);
  693.           Write('Illegal number... Press key to try again');
  694.           Read(Kbd, ch);
  695.         END ELSE BEGIN
  696.           good := (seeknum >= 1);
  697.           IF NOT(good) THEN BEGIN
  698.             ClearStat(N, 3, 43);
  699.             MoveToStat(N, 3);
  700.             Write('Illegal number... Press key to try again');
  701.             Read(Kbd, ch);
  702.           END;
  703.         END;
  704.       UNTIL good;
  705.       DrawCenterBar(2);
  706.       CursorOff(CursorType);
  707.       GetLineNumber := seeknum;
  708.     END;                      {getlinenumber}
  709.  
  710.   PROCEDURE SeekLine(N : Window; seeknum : Integer;
  711.                      VAR BotPtr, TopPtr, BufPtr, OldPtr, LinNum : TwoInt;
  712.                      VAR NotPadded : TwoState);
  713.       {-seek to a desired line number}
  714.     VAR
  715.       lin : Integer;
  716.  
  717.     PROCEDURE SeekAhead(N : Window; seeknum, lin : Integer;
  718.                         VAR BotPtr, TopPtr, BufPtr, LinNum : TwoInt;
  719.                         VAR NotPadded : TwoState);
  720.         {-seek forward to find line seeknum, starting at line lin}
  721.         {return buffers set up at line found, or at end of file}
  722.       BEGIN
  723.         WHILE (BuffAfter(N, TopPtr, BufPtr) > slines) AND (lin < seeknum) DO BEGIN
  724.           {update file buffer as needed}
  725.           UpdateBuff(N, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  726.           lin := lin+1;
  727.           BufPtr[N] := ModSucc(BufPtr[N]);
  728.         END;
  729.       END;                    {seekahead}
  730.  
  731.     BEGIN
  732.  
  733.       WaitOn(N);
  734.  
  735.       lin := CurrLine(N);
  736.       IF seeknum > lin THEN BEGIN
  737.         {seeknum is ahead of us}
  738.         SeekAhead(N, seeknum, lin, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  739.       END ELSE IF seeknum >= LinNum[N] THEN BEGIN
  740.         {seeknum is behind us, but in current buffer}
  741.         BufPtr[N] := BotPtr[N];
  742.         lin := LinNum[N];
  743.         SeekAhead(N, seeknum, lin, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  744.       END ELSE BEGIN
  745.         {seeknum is behind current buffer}
  746.         {reset}
  747.         Reset(f[N]);
  748.         Flush(f[N]);
  749.         NotPadded[N] := True;
  750.         {refill the buffer}
  751.         FillBuff(N, 1, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  752.         {now seeknum is ahead of us}
  753.         SeekAhead(N, seeknum, 1, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  754.       END;
  755.  
  756.       WaitOff(N);
  757.     END;                      {seekline}
  758.  
  759.   PROCEDURE SeekString(N : Window;
  760.                        VAR BotPtr, TopPtr, BufPtr, OldPtr, LinNum : TwoInt;
  761.                        VAR NotPadded : TwoState;
  762.                        VAR SearchString : LineBuffer);
  763.       {-search for a string}
  764.     VAR
  765.       notfound : Boolean;
  766.       ch : Char;
  767.       spos : Integer;
  768.  
  769.     FUNCTION StUpcase(s : LineBuffer) : LineBuffer;
  770.         {-return the uppercase of a string}
  771.       VAR
  772.         i : Byte;
  773.       BEGIN
  774.         FOR i := 1 TO Length(s) DO s[i] := UpCase(s[i]);
  775.         StUpcase := s;
  776.       END;                    {stupcase}
  777.  
  778.     BEGIN
  779.       IF SearchString = '' THEN BEGIN
  780.         {get the search string}
  781.         DrawCenterBar(3);
  782.         CursorOn(CursorType);
  783.         ClearStat(N, 3, 43);
  784.         MoveToStat(N, 3);
  785.         Write('Search String: ');
  786.         Read(SearchString);
  787.         SearchString := StUpcase(SearchString);
  788.         DrawCenterBar(2);
  789.         WaitOff(N);
  790.       END;
  791.  
  792.       IF SearchString <> '' THEN BEGIN
  793.         {do the search}
  794.         WaitOn(N);
  795.         notfound := True;
  796.         WHILE notfound AND (BuffAfter(N, TopPtr, BufPtr) > slines) DO BEGIN
  797.           {scan forward through buffer and update buffer as necessary}
  798.           BufPtr[N] := ModSucc(BufPtr[N]);
  799.           notfound := (Pos(SearchString, StUpcase(b[N][BufPtr[N]])) = 0);
  800.           UpdateBuff(N, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  801.         END;
  802.         IF notfound THEN BEGIN
  803.           {complain}
  804.           ClearStat(N, 3, 43);
  805.           MoveToStat(N, 3);
  806.           Write('String not found. Press any key...');
  807.           Read(Kbd, ch);
  808.         END ELSE BEGIN
  809.           {make sure string found shows on screen}
  810.           spos := Pos(SearchString, StUpcase(b[N][BufPtr[N]]));
  811.           IF spos < LeftEdge[N] THEN
  812.             {string is off left edge of screen}
  813.             LeftEdge[N] := spos
  814.           ELSE IF (spos+Length(SearchString)) > (LeftEdge[N]+79) THEN
  815.             {string is off right edge of screen}
  816.             LeftEdge[N] := spos+Length(SearchString)-80;
  817.         END;
  818.         WaitOff(N);
  819.       END;
  820.  
  821.     END;                      {seekstring}
  822.  
  823.   PROCEDURE initialize(VAR N : Window);
  824.       {-set up everything}
  825.     VAR
  826.       w : Window;
  827.     BEGIN
  828.       {avoid problems with ^C char used for WordStar scrolling}
  829.       BrkOff;
  830.       {some constants}
  831.       Bufmod := BufSize+1;
  832.       SearchString := '';
  833.       Synced := False;
  834.  
  835.       {initialize selected windows}
  836.       FOR w := 0 TO 1 DO IF s[w] THEN
  837.         InitWindow(w, BotPtr, TopPtr, BufPtr, OldPtr,
  838.         LeftEdge, OldEdge, LinNum, NotPadded);
  839.  
  840.       {pick initial window}
  841.       IF s[0] THEN N := 0 ELSE N := 1;
  842.  
  843.       {show which window is active}
  844.       ShowWindow(N);
  845.     END;                      {initialize}
  846.  
  847.   PROCEDURE CloseUp;
  848.       {-close up to quit}
  849.     BEGIN
  850.       IF s[0] THEN Close(f[0]);
  851.       IF s[1] THEN Close(f[1]);
  852.       GoToXY(1, 25);
  853.       CursorOn(CursorType);
  854.       TextColor(7);
  855.       TextBackground(0);
  856.       Halt;
  857.     END;                      {closeup}
  858.  
  859.   PROCEDURE VideoOff;
  860.       {-avoid snow writing full screen to c/g card}
  861.     BEGIN
  862.       {clear video enable bit}
  863.       Port[VidModePort] := ModePortData AND 247;
  864.     END;
  865.  
  866.   PROCEDURE VideoOn;
  867.       {-reenable video}
  868.     BEGIN
  869.       {set video enable bit}
  870.       Port[VidModePort] := ModePortData OR 8;
  871.     END;
  872.  
  873.   PROCEDURE SaveScreen;
  874.     BEGIN
  875.       Move(Mem[ScreenSeg : 0], SavScr, 4000);
  876.     END;                      {savescreen}
  877.  
  878.   PROCEDURE RestoreScreen;
  879.     BEGIN
  880.       Move(SavScr, Mem[ScreenSeg : 0], 4000);
  881.     END;                      {restorescreen}
  882.  
  883.   PROCEDURE invokeDOS;
  884.       {-start a new DOS shell, then return to browser}
  885.     VAR
  886.       save_ax : Integer;
  887.       ch : Char;
  888.  
  889.     PROCEDURE execute_string(s : PathString; VAR sav_ax : Integer);
  890.         {-execute a command line}
  891.         {-provided by russ nelson, potsdam, ny}
  892.       VAR
  893.         save_ax : Integer;
  894.       CONST
  895.         save_ss : Integer = 0;
  896.         save_sp : Integer = 0;
  897.       BEGIN
  898.         s[Length(s)+1] := ^M;
  899.         INLINE(
  900.           $1E/                {push    ds}
  901.           $55/                {push    bp}
  902.           $2E/$8C/$16/save_ss/ {mov     cs:[save_ss],ss}
  903.           $2E/$89/$26/save_sp/ {mov     cs:[save_sp],sp}
  904.           $8C/$D0/            {mov     ax,ss}
  905.           $8E/$D8/            {mov     ds,ax}
  906.           $8D/$76/< s/        {lea     si,s[bp]}
  907.           $CD/$2E/            {int     2Eh}
  908.           $2E/$8E/$16/save_ss/ {mov     ss,cs:[save_ss]}
  909.           $2E/$8B/$26/save_sp/ {mov     sp,cs:[save_sp]}
  910.           $5D/                {pop     bp}
  911.           $1F/                {pop     ds}
  912.           $89/$46/< save_ax   {mov     save_ax[bp],ax}
  913.           );
  914.         sav_ax := save_ax;
  915.       END;                    {execute_string}
  916.  
  917.     BEGIN
  918.       {store screen}
  919.       IF NOT(NoWait) THEN VideoOff;
  920.       SaveScreen;
  921.       IF NOT(NoWait) THEN VideoOn;
  922.       CursorOn(CursorType);
  923.       TextColor(7);
  924.       TextBackground(0);
  925.  
  926.       {clear screen and put up help}
  927.       ClrScr;
  928.       WriteLn('Type EXIT to return to BROWSE...');
  929.  
  930.       {start new DOS shell}
  931.       execute_string('command', save_ax);
  932.  
  933.       {restore screen}
  934.       TextColor(0);
  935.       TextBackground(7);
  936.       CursorOff(CursorType);
  937.       VideoOff;
  938.       RestoreScreen;
  939.       VideoOn;
  940.  
  941.       {check errors - this particular interrupt doesn't provide many}
  942.       IF save_ax <> 0 THEN BEGIN
  943.         DrawCenterBar(3);
  944.         CursorOn(CursorType);
  945.         ClearStat(N, 3, 43);
  946.         MoveToStat(N, 3);
  947.         Write('Can''t invoke DOS. Press any key...');
  948.         Read(Kbd, ch);
  949.         DrawCenterBar(2);
  950.         WaitOff(N);
  951.       END;
  952.  
  953.     END;                      {invokeDOS}
  954.  
  955.   PROCEDURE ShowHelp;
  956.       {-provide a pop-up help screen}
  957.     CONST
  958.       helpLines = 16;   {was 15 in orig. version}
  959.       helpX = 11;
  960.       helpY = 6;
  961.     TYPE
  962.       HelpArray = ARRAY[1..helpLines] OF STRING[60];
  963.     VAR
  964.       ch : Char;
  965.       i, r : Byte;
  966.     CONST
  967.       ha : HelpArray =
  968.       (
  969.       '╔════════════════════════════════════════════════════════╗',
  970. {*}   '║          Version                                       ║',
  971.       '║         Window Browser - by TurboPower Software        ║',
  972.       '║ ┌────────────────────────┐ ┌─────────────────────────┐ ║',
  973.       '║ │   ^Z,^X   line up      │ │  ^S     column left     │ ║',
  974.       '║ │   ^W,^E   line down    │ │  ^D     column right    │ ║',
  975.       '║ │   ^C      page up      │ │  ^Q^S   left of line    │ ║',
  976.       '║ │   ^R      page down    │ │  ^Q^D   right of line   │ ║',
  977.       '║ │   ^Q^R    file home    │ │  ^Q^N   seek line number│ ║',
  978.       '║ │   ^Q^C    file end     │ │  ^Q^F   string search   │ ║',
  979.       '║ │   ^K^R    new file     │ │  ^L     search again    │ ║',
  980. {*}   '║ │   ^K^D    quit         │ │  F6,^N  switch windows  │ ║',
  981.       '║ │   ^K^I    invoke DOS   │ │  ^B     (un)sync windows│ ║',
  982.       '║ └────────────────────────┘ └─────────────────────────┘ ║',
  983.       '║       Press any key to return to your browsing....     ║',
  984.       '╚════════════════════════════════════════════════════════╝'
  985.       );
  986.  
  987.     PROCEDURE SetScreenAttr(a : Byte);
  988.         {-set entire screen to a given attribute}
  989.       VAR
  990.         i : Integer;
  991.       BEGIN
  992.         i := 1;
  993.         WHILE i < 4000 DO BEGIN
  994.           Mem[ScreenSeg : i] := a;
  995.           i := i+2;
  996.         END;
  997.       END;                    {setscreenattr}
  998.  
  999.     BEGIN
  1000. {*insert version #}
  1001.   for i:=1 to Length(Version) do ha[2][i+1+Pos('n',ha[2])] := Version[i];
  1002.  
  1003.       IF NOT(NoWait) THEN VideoOff;
  1004.       SaveScreen;
  1005.       SetScreenAttr(7);       {put background in low video}
  1006.       IF NOT(NoWait) THEN VideoOn;
  1007.       {write help}
  1008.       CursorOn(CursorType);
  1009.       TextColor(15);
  1010.       TextBackground(0);
  1011.       r := helpY;
  1012.       FOR i := 1 TO helpLines DO BEGIN
  1013.         GoToXY(helpX, r);
  1014.         Write(ha[i]);
  1015.         r := r+1;
  1016.       END;
  1017.       TextColor(0);
  1018.       TextBackground(7);
  1019.       CursorOff(CursorType);
  1020.       Read(Kbd, ch);
  1021.       IF NOT(NoWait) THEN VideoOff;
  1022.       RestoreScreen;
  1023.       IF NOT(NoWait) THEN VideoOn;
  1024.     END;                      {showhelp}
  1025.  
  1026. {-----------------------------------------------------------
  1027.  key action codes used here:
  1028.    15:  ^H      help
  1029.     2:  ^Z,^X   line up        9: ^S     column left
  1030.     1:  ^W,^E   line down     10: ^D     column right
  1031.     4:  ^C      page up       11: ^Q^S   left of line
  1032.     3:  ^R      page down     12: ^Q^D   right of line
  1033.     5:  ^Q^R    file home     14: ^Q^N   seek line number
  1034.     6:  ^Q^C    file end      16: ^Q^F   string search
  1035.    13:  ^K^R    new file      17: ^L     search again
  1036.     7:  ^K^D    quit           8: ^N     switch windows
  1037.    19:  ^K^I    invoke DOS    18: ^B     (un)sync windows
  1038.  
  1039.   cursor pad keys produce <Esc> then the byte:
  1040.    UP = ESC-72 = 'H'= code 2 above    HOME = 71 = 'G'= code 11
  1041.    DN =    -80 = 'P'=  1              PGUP = 73 = 'I'=   4
  1042.    LT =    -75 = 'K'=  9              END  = 79 = 'O'=   12
  1043.    RT =    -77 = 'M'= 10              PGDN = 81 = 'Q'=   3
  1044.  
  1045. CTL-HOME = 119 = 'w' = 5
  1046.     END  = 117 = 'u' = 6
  1047.  
  1048.     F1 = HELP = 59 = ';' = code 15
  1049.     F6 = switch windows = 64 = '@' = 8
  1050.  -----------------------------------------------------------}
  1051.  
  1052.   FUNCTION GetKey : Byte;
  1053.       {-return a keycode for a legal keystroke}
  1054.       {customize here for favorite cursor keys}
  1055.       {this is currently set up for WordStar-like keys only}
  1056. {*note 2/86: i've added the pc's cursor pad keys: SD}
  1057.  
  1058.     VAR
  1059.       good : Boolean;
  1060.       ch : Char;
  1061. {*}   ESC : BOOLEAN;
  1062.     BEGIN
  1063.       {don't let keystrokes get ahead of the action}
  1064.       WHILE KeyPressed DO Read(Kbd, ch);
  1065.       REPEAT
  1066.         Read(Kbd, ch);
  1067.  
  1068. {*}     ESC := (ch=^[);
  1069.         if ESC then read(kbd,ch);
  1070.  
  1071.         good := True;
  1072.         CASE ch OF
  1073.  
  1074. {[SD] *** IBM-PC cursor key-pad ***}
  1075. {LEFT}    'K' : GetKey := 9;
  1076. {RIGHT}   'M' : GetKey := 10;
  1077. {DOWN}    'H' : GetKey := 1;
  1078. {UP}      'P' : GetKey := 2;
  1079. {HOME}    'G' : GetKey := 11;
  1080. {END}     'O' : GetKey := 12;
  1081. {PGDN}    'Q' : GetKey := 4;
  1082. {PGUP}    'I' : GetKey := 3;
  1083. {CTL-HOME} 'w': GetKey := 5;
  1084. {CTL-END}  'u': GetKey := 6;
  1085. {F1:HELP}  ';': GetKey := 15;
  1086. {F6:sw win} '@': GetKey := 8;
  1087.  
  1088.  
  1089. { ORIGINAL keyboard set up (still in effect) }
  1090.           ^S     : GetKey := 9;
  1091.           ^D     : GetKey := 10;
  1092.           ^W, ^E : GetKey := 1;
  1093.           ^Z, ^X : GetKey := 2;
  1094.           ^Q : BEGIN
  1095.                  Read(Kbd, ch);
  1096.                  CASE ch OF
  1097.                    ^R, 'r', 'R' : GetKey := 5;
  1098.                    ^C, 'c', 'C' : GetKey := 6;
  1099.                    ^S, 's', 'S' : GetKey := 11;
  1100.                    ^D, 'd', 'D' : GetKey := 12;
  1101.                    ^N, 'n', 'N' : GetKey := 14;
  1102.                    ^F, 'f', 'F' : GetKey := 16;
  1103.                  ELSE
  1104.                    good := False;
  1105.                  END;
  1106.                END;
  1107.           ^K : BEGIN
  1108.                  Read(Kbd, ch);
  1109.                  CASE ch OF
  1110.                    ^X, ^D, 'x', 'X', 'd', 'D' : GetKey := 7;
  1111.                    ^R, 'r', 'R' : GetKey := 13;
  1112.                    ^I, 'i', 'I' : GetKey := 19;
  1113.                  ELSE
  1114.                    good := False;
  1115.                  END;
  1116.                END;
  1117.           ^N : GetKey := 8;
  1118.           ^B : GetKey := 18;
  1119.           ^R : GetKey := 3;
  1120.           ^C : GetKey := 4;
  1121.           ^H : GetKey := 15;
  1122.           ^L : GetKey := 17;
  1123.         ELSE
  1124.           good := False;
  1125.         END;
  1126.       UNTIL good;
  1127.     END;                      {getkey}
  1128.  
  1129.   PROCEDURE update(N : Window);
  1130.       {-update the screen and buffers for window n}
  1131.     BEGIN
  1132.       {update file buffer as needed}
  1133.       UpdateBuff(N, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  1134.  
  1135.       IF (OldPtr[N] <> BufPtr[N]) OR (OldEdge[N] <> LeftEdge[N]) THEN BEGIN
  1136.         {update screen data}
  1137.         MoveToScreen(N, LeftEdge, BufPtr, OldPtr);
  1138.         IF OldPtr[N] <> BufPtr[N] THEN BEGIN
  1139.           OldPtr[N] := BufPtr[N];
  1140.           {update line status}
  1141.           ShowLin(N, LinNum, BotPtr, BufPtr);
  1142.         END;
  1143.         IF OldEdge[N] <> LeftEdge[N] THEN BEGIN
  1144.           OldEdge[N] := LeftEdge[N];
  1145.           {update column status}
  1146.           ShowCol(N, LeftEdge);
  1147.         END;
  1148.       END;
  1149.     END;                      {update}
  1150.  
  1151.   PROCEDURE doOp(VAR N : Window; ch : Byte);
  1152.     VAR
  1153.       temp : Integer;
  1154.     BEGIN
  1155.       {-operate based on key}
  1156.       CASE ch OF
  1157.  
  1158.         {scroll one line up}
  1159.         1 : IF BuffPrior(N, BotPtr, BufPtr) > 0 THEN
  1160.               BufPtr[N] := ModPred(BufPtr[N])
  1161.             ELSE IF LinNum[N] > 1 THEN
  1162.               {need to rebuild buffer from beginning}
  1163.               SeekBack(N, 1, BotPtr, TopPtr, BufPtr, OldPtr, LinNum);
  1164.  
  1165.         {scroll one line down}
  1166.         2 : IF BuffAfter(N, TopPtr, BufPtr) > slines THEN
  1167.               BufPtr[N] := ModSucc(BufPtr[N]);
  1168.  
  1169.         {scroll page up}
  1170.         3 : IF BuffPrior(N, BotPtr, BufPtr) > slines THEN
  1171.               FOR temp := 1 TO slines DO BufPtr[N] := ModPred(BufPtr[N])
  1172.             ELSE IF LinNum[N] > 1 THEN
  1173.               {need to rebuild buffer from beginning}
  1174.               SeekBack(N, slines, BotPtr, TopPtr, BufPtr, OldPtr, LinNum)
  1175.             ELSE
  1176.               {set to beginning of file}
  1177.               BufPtr[N] := BotPtr[N];
  1178.  
  1179.         {scroll page down}
  1180.         4 : IF BuffAfter(N, TopPtr, BufPtr) > slines THEN
  1181.               FOR temp := 1 TO slines DO
  1182.                 IF BuffAfter(N, TopPtr, BufPtr) > slines THEN
  1183.                   BufPtr[N] := ModSucc(BufPtr[N]);
  1184.  
  1185.         {home}
  1186.         5 : IF LinNum[N] > 1 THEN BEGIN
  1187.               {reset}
  1188.               WaitOn(N);
  1189.               Reset(f[N]);
  1190.               Flush(f[N]);
  1191.               LeftEdge[N] := 1;
  1192.               NotPadded[N] := True;
  1193.               {refill the buffer}
  1194.               FillBuff(N, 1, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  1195.               OldPtr[N] := -2; {guarantee redisplay}
  1196.               WaitOff(N);
  1197.             END ELSE
  1198.               BufPtr[N] := 0;
  1199.  
  1200.         {end}
  1201.         6 : BEGIN
  1202.               IF NOT(EoF(f[N])) THEN WaitOn(N);
  1203.               WHILE NOT(EoF(f[N])) DO BEGIN
  1204.                 BufPtr[N] := TopPtr[N];
  1205.                 UpdateBuff(N, BotPtr, TopPtr, BufPtr, LinNum, NotPadded);
  1206.               END;
  1207.               BufPtr[N] := TopPtr[N];
  1208.               FOR temp := 1 TO slines DO
  1209.                 BufPtr[N] := ModPred(BufPtr[N]);
  1210.               {guarantee redisplay}
  1211.               OldPtr[N] := -2;
  1212.               WaitOff(N);
  1213.             END;
  1214.  
  1215.         {quit}
  1216.         7 : CloseUp;
  1217.  
  1218.         {switch windows}
  1219.         8 : SwitchN(N);
  1220.  
  1221.         {scroll left}
  1222.         9 : IF LeftEdge[N] > 1 THEN
  1223.               LeftEdge[N] := LeftEdge[N]-1;
  1224.  
  1225.         {scroll right}
  1226.         10 : IF LeftEdge[N] < LineSize-79 THEN
  1227.                LeftEdge[N] := LeftEdge[N]+1;
  1228.  
  1229.         {set left edge to beginning of screen}
  1230.         11 : LeftEdge[N] := 1;
  1231.  
  1232.         {set right edge to show longest line}
  1233.         12 : ShowRight(N, BufPtr, LeftEdge);
  1234.  
  1235.         {open new file in current window}
  1236.         13 : BEGIN
  1237.                Close(f[N]);
  1238.                GetFile(N, f, p, s);
  1239.                IF s[N] THEN BEGIN
  1240.                  {open the new window}
  1241.                  InitWindow(N, BotPtr, TopPtr, BufPtr, OldPtr,
  1242.                  LeftEdge, OldEdge, LinNum, NotPadded);
  1243.                  ShowWindow(N);
  1244.                END ELSE BEGIN
  1245.                  {clear the now-empty window}
  1246.                  ScrollWind(1+14*N, 11+14*N, 0);
  1247.                  {make sure other window is open and switch to it}
  1248.                  IF s[1-N] THEN SwitchN(N) ELSE CloseUp;
  1249.                END;
  1250.              END;
  1251.  
  1252.         {seek to line number}
  1253.         14 : BEGIN
  1254.                SeekNumber := GetLineNumber(N);
  1255.                SeekLine(N, SeekNumber,
  1256.                BotPtr, TopPtr, BufPtr, OldPtr, LinNum, NotPadded);
  1257.              END;
  1258.  
  1259.         15 : ShowHelp;
  1260.  
  1261.         {search for a new string}
  1262.         16 : BEGIN
  1263.                SearchString := '';
  1264.                SeekString(N, BotPtr, TopPtr, BufPtr,
  1265.                OldPtr, LinNum, NotPadded, SearchString);
  1266.              END;
  1267.  
  1268.         {search for prior string}
  1269.         17 : SeekString(N, BotPtr, TopPtr, BufPtr,
  1270.           OldPtr, LinNum, NotPadded, SearchString);
  1271.  
  1272.         19 : invokeDOS;
  1273.  
  1274.       END;                    {case}
  1275.     END;                      {DoOp}
  1276.  
  1277.   BEGIN                       {main}
  1278.  
  1279.     {get screen segment, cursortype, and video port addresses}
  1280.     ScreenSeg := which_screen(CursorType, VidStatPort, VidModePort);
  1281.     {draw screen border and titles}
  1282.     DrawBorder;
  1283.  
  1284.     {get input files}
  1285.  
  1286. {*} Use_Params := TRUE;   {get filename from cmd line}
  1287.     GetFile(0, f, p, s);
  1288.     GetFile(1, f, p, s);
  1289.     Use_Params := FALSE;  {from now on, get file name from user}
  1290.  
  1291.     {close up if no files}
  1292.     IF NOT(s[0] OR s[1]) THEN BEGIN
  1293.       GoToXY(1, 25);
  1294.       Halt;
  1295.     END;
  1296.  
  1297.     {initialize globals and turn off cursor, return active window}
  1298.     initialize(N);
  1299.  
  1300.     {main loop}
  1301.     WHILE True DO BEGIN
  1302.  
  1303.       {update screen and buffers}
  1304.       update(N);
  1305.       IF Synced AND s[0] AND s[1] THEN update(1-N);
  1306.  
  1307.       {get new keystroke}
  1308.       ch := GetKey;
  1309.  
  1310.       {operate based on keystroke}
  1311.       IF ch = 18 THEN BEGIN
  1312.         {handle window synchronization}
  1313.         Synced := NOT(Synced);
  1314.         ShowWindow(N);
  1315.       END ELSE BEGIN
  1316.         {do any other operation}
  1317.         doOp(N, ch);
  1318.         IF Synced AND s[0] AND s[1] AND (ch <> 15) AND (ch <> 19) THEN BEGIN
  1319.           IF ch = 14 THEN
  1320.             {seek to same line number as other window}
  1321.             SeekLine(1-N, SeekNumber, BotPtr, TopPtr, BufPtr, OldPtr, LinNum, NotPadded)
  1322.           ELSE BEGIN
  1323.             {convert string search to one for same string in other window}
  1324.             IF ch = 16 THEN ch := 17;
  1325.             {do operation in other window}
  1326.             TmpWind := 1-N;
  1327.             doOp(TmpWind, ch);
  1328.           END;
  1329.         END;
  1330.       END;
  1331.  
  1332.     END;                      {while true}
  1333.  
  1334.   END.
  1335.