home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / PASCAL.ZIP / BROWSE.PAS next >
Encoding:
Pascal/Delphi Source File  |  1985-09-20  |  39.4 KB  |  1,244 lines

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