home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload / ShartewareOverload.cdr / progm / tptools.zip / FIRSTED.ZIP / EDPTROP.PAS < prev    next >
Pascal/Delphi Source File  |  1987-12-21  |  34KB  |  1,183 lines

  1. {                          EDPTROP.PAS
  2.                              ED 4.0
  3.              Copyright (c) 1985, 87 by Borland International, Inc.            }
  4.  
  5. {$I eddirect.inc}
  6.  
  7. unit EdPtrOp;
  8.   {-Low level pointer operations for the Toolbox architecture}
  9.  
  10. interface
  11.  
  12. uses
  13.   Dos,                       {DOS calls - standard unit}
  14.   Errors,                    {Runtime error handler}
  15.   EdVars;                    {Global types and declarations}
  16.  
  17.   {----------- Primitive operations}
  18.  
  19. function EdMemAvail(Size, Margin : Word) : Boolean;
  20.   {-Return true if enough contiguous memory exists on heap}
  21.  
  22. procedure EdFwdPtr(var P);
  23.   {-Change p (Plinedesc or Pwindesc) to its forward link}
  24.  
  25. procedure EdBackPtr(var P);
  26.   {-Change p (Plinedesc or Pwindesc) to its backward link}
  27.  
  28. function EdPtrIsNil(var P) : Boolean;
  29.   {-Return true if pointer p is nil}
  30.  
  31. function EdPtrNotNil(var P) : Boolean;
  32.   {-Return true if pointer p is not nil}
  33.  
  34. procedure EdSetPtrNil(var P);
  35.   {-Initialize pointer p to nil}
  36.  
  37. procedure EdToggleBoolean(var B : Boolean);
  38.   {-Toggle a boolean}
  39.  
  40.   {----------- Line oriented operations}
  41.  
  42. function EdTopofStream(W : PwinDesc) : PlineDesc;
  43.   {-Return pointer to first line in stream}
  44.  
  45. function EdTextLength(P : PlineDesc) : Integer;
  46.   {-Return the length of line text, 0 if all blank}
  47.  
  48. function EdLineIndent(P : PlineDesc) : Integer;
  49.   {-Return the indent of the specified line, 0 if line is empty}
  50.  
  51. procedure EdClrFlag(P : PlineDesc; Mask : Word);
  52.   {-Clear the line flag}
  53.  
  54. procedure EdSetFlag(P : PlineDesc; Mask : Word);
  55.   {-Set the line flag}
  56.  
  57. function EdFlagSet(P : PlineDesc; Mask : Word) : Boolean;
  58.   {-Return True if the mask flag is set for line p}
  59.  
  60. procedure EdChangeFlag(P : PlineDesc; FlagVal : Boolean; FlagPos : Word);
  61.   {-Change a line flag, setting UpdateScreen if change encountered}
  62.  
  63. procedure EdToggleTextMarker;
  64.   {-Toggle visibility of text markers}
  65.  
  66. procedure EdFixBlockInsertedSpace(P : PlineDesc; Start : Integer; Num : Integer);
  67.   {-Fix up block markers after blank space is inserted}
  68.  
  69. procedure EdFixMarkInsertedSpace(P : PlineDesc; Start : Integer; Num : Integer);
  70.   {-Fix up text markers after space is inserted in a line}
  71.  
  72. procedure EdFixBlockInsertedLine(ThisL, NextL : PlineDesc;
  73.                                  BreakCol : Integer;
  74.                                  Delta : Integer);
  75.   {-Fix up block markers after a line is inserted}
  76.  
  77. procedure EdFixMarkInsertedLine(ThisL, NextL : PlineDesc;
  78.                                 BreakCol : Integer;
  79.                                 Delta : Integer);
  80.   {-Fix up text markers after a new line is inserted}
  81.  
  82. procedure EdLinkbuffer(P, Q : PlineDesc);
  83.   {-Link line q after line p}
  84.  
  85. procedure EdBufferCurrentLine;
  86.   {-Keep an extra image of current line to allow restore via ^QL}
  87.  
  88. procedure EdBackupCurline(W : PwinDesc);
  89.   {-Move up curline as needed to fit into smaller window}
  90.  
  91. procedure EdMoveCursorIntoLine;
  92.   {-Set current column within line buffer}
  93.  
  94. procedure EdGotoColumn(Cno : Integer);
  95.   {-Go to column cno on current line}
  96.  
  97. procedure EdGotoLine(Lno : Integer);
  98.   {-Go to line lno of current window}
  99.  
  100. procedure EdWindowTopFile;
  101.   {-Go to top of window}
  102.  
  103. procedure EdTopScreen;
  104.   {-Move cursor to top of screen}
  105.  
  106. procedure EdBottomScreen;
  107.   {-Move cursor to bottom of screen}
  108.  
  109. procedure EdWindowGoto(Wno : Byte);
  110.   {-Move cursor into window Wno, counted from top of screen}
  111.  
  112.   {----------- Window oriented operations}
  113.  
  114. function EdFindWindow(T : PlineDesc) : PwinDesc;
  115.   {-Return the window containing the designated line}
  116.  
  117. function EdFindWindesc(Wno : Byte) : PwinDesc;
  118.   {-return a window descriptor for a window number}
  119.  
  120. function EdWindowNumber : Byte;
  121.   {-Return the window number of the current window}
  122.  
  123. function EdGetWindowToDivide : Byte;
  124.   {-Return best window number to split in order to create a new window}
  125.  
  126. procedure EdWindowUp;
  127.   {-Process up window command}
  128.  
  129. procedure EdWindowDown;
  130.   {-Process down window command}
  131.  
  132. procedure EdRealign;
  133.   {-Realign windows over text streams}
  134.  
  135. procedure EdFixUpWindowSpan(P : PlineDesc);
  136.   {-When p is to be deleted, first fix up any other structures pointing to p}
  137.  
  138. procedure EdSetTextNo(W : PwinDesc);
  139.   {-Set the FirstTextNo of a window}
  140.  
  141. function EdLinkedWindow(W : PwinDesc) : Boolean;
  142.   {-return true if a window is linked to any others}
  143.  
  144. function EdNewstream : Word;
  145.   {-Return unique text stream identifier}
  146.  
  147. procedure EdChangeStreamName(Fname : Filepath);
  148.   {-Change name of current stream to fname}
  149.  
  150. procedure EdInitWindowSettings(W : PwinDesc);
  151.   {-Set window parameters to defaults}
  152.  
  153.   {----------- Block oriented operations}
  154.  
  155. function EdCursorInBlock(Q : PlineDesc; C : Integer; EndMarkOk : Boolean) : Boolean;
  156.   {-Return true if position q:c is in block}
  157.  
  158. function EdNoBlock : Boolean;
  159.   {-Return True if no block is marked and visible}
  160.  
  161. procedure EdOffblock;
  162.   {-Turn off Inblock bits for every text line in the system}
  163.  
  164. procedure EdCheckNoMarker;
  165.   {-If deletion operation has deleted all marked text, remove block markers altogether}
  166.  
  167. procedure EdBlockHide;
  168.   {-Toggle block display}
  169.  
  170. procedure EdFixBaseLine(WindFrom : PwinDesc);
  171.   {-Redefine window Topline and Curline before a block operation deletes them}
  172.  
  173. procedure EdBlockBegin;
  174.   {-Set block begin marker}
  175.  
  176. procedure EdBlockEnd;
  177.   {-Set block end marker}
  178.  
  179. procedure EdBlockWord;
  180.   {-Mark the current word as a block}
  181.  
  182.   {==========================================================================}
  183.  
  184. implementation
  185.  
  186. var
  187.   NextStream : Word;         {Next stream ID to be assigned}
  188.  
  189.   {$L EDPTROP}
  190.  
  191.   {$F+}
  192.   function EdGetEol(var Buf; C : Integer) : Integer; external;
  193.   {-Find end of a text buffer. Local to the unit, but called FAR.}
  194.   {$F-}
  195.  
  196.   function EdPtrDiff(HighPt, LowPt : Pointer) : LongInt;
  197.     {-Return the number of bytes between point highpt^ and point lowpt^}
  198.   var
  199.     HighVal, LowVal : LongInt;
  200.  
  201.   begin                      {EdPtrDiff}
  202.     HighVal := LongInt(Seg(HighPt^)) shl 4+LongInt(Ofs(HighPt^));
  203.     LowVal := LongInt(Seg(LowPt^)) shl 4+LongInt(Ofs(LowPt^));
  204.     EdPtrDiff := HighVal-LowVal;
  205.   end;                       {EdPtrDiff}
  206.  
  207.   function EdHeapEnd : Pointer;
  208.     {-Return the last available location for the heap in a normalized pointer}
  209.  
  210.   begin                      {EdHeapEnd}
  211.     if Ofs(FreePtr^) = 0 then
  212.       {Free list is empty}
  213.       EdHeapEnd := Ptr(Seg(FreePtr^)+$1000, 0)
  214.     else
  215.       EdHeapEnd := Ptr(Seg(FreePtr^)+Ofs(FreePtr^) shr 4, 0);
  216.   end;                       {EdHeapEnd}
  217.  
  218.   function EdMemAvail(Size, Margin : Word) : Boolean;
  219.     {-Return true if enough contiguous memory exists on heap}
  220.   var
  221.     HighestHeapEnd, SaveHeapPtr : Pointer;
  222.  
  223.   begin                      {EdMemAvail}
  224.     {Compute highest heap end based on current freelist buffer size}
  225.     HighestHeapEnd := Ptr(Seg(FreePtr^)+$1000-Succ(Margin shr 4), 0);
  226.  
  227.     {Compare to actual freelist size}
  228.     if EdPtrDiff(HighestHeapEnd, EdHeapEnd) > 0 then
  229.       {Actual free list is bigger than buffer we guarantee}
  230.       HighestHeapEnd := EdHeapEnd;
  231.  
  232.     if EdPtrDiff(HighestHeapEnd, HeapPtr) > Size then
  233.       {There is space at the top of the heap for the request}
  234.       EdMemAvail := True
  235.     else begin
  236.       {Temporarily ignore whatever space is at the top of heap}
  237.       SaveHeapPtr := HeapPtr;
  238.       HeapPtr := EdHeapEnd;
  239.       {Check the largest piece in the fragmented heap}
  240.       EdMemAvail := (Size < MaxAvail);
  241.       HeapPtr := SaveHeapPtr;
  242.     end;
  243.   end;                       {EdMemAvail}
  244.  
  245.   procedure EdFwdPtr(var P);
  246.     {-Change p (Plinedesc or Pwindesc) to its forward link}
  247.   var
  248.     Pl : PlineDesc absolute P;
  249.  
  250.   begin                      {EdFwdPtr}
  251.     Pl := Pl^.FwdLink;
  252.   end;                       {EdFwdPtr}
  253.  
  254.   procedure EdBackPtr(var P);
  255.     {-Change p (Plinedesc or Pwindesc) to its backward link}
  256.   var
  257.     Pl : PlineDesc absolute P;
  258.  
  259.   begin                      {EdBackPtr}
  260.     Pl := Pl^.Backlink;
  261.   end;                       {EdBackPtr}
  262.  
  263.   function EdPtrIsNil(var P) : Boolean;
  264.     {-Return true if pointer p is nil}
  265.   var
  266.     Pl : PlineDesc absolute P;
  267.  
  268.   begin                      {EdPtrIsNil}
  269.     EdPtrIsNil := (Pl = nil);
  270.   end;                       {EdPtrIsNil}
  271.  
  272.   function EdPtrNotNil(var P) : Boolean;
  273.     {-Return true if pointer p is not nil}
  274.   var
  275.     Pl : PlineDesc absolute P;
  276.  
  277.   begin                      {EdPtrNotNil}
  278.     EdPtrNotNil := (Pl <> nil);
  279.   end;                       {EdPtrNotNil}
  280.  
  281.   procedure EdSetPtrNil(var P);
  282.     {-Initialize pointer p to nil}
  283.   var
  284.     Pl : PlineDesc absolute P;
  285.  
  286.   begin                      {EdSetPtrNil}
  287.     Pl := nil;
  288.   end;                       {EdSetPtrNil}
  289.  
  290.   procedure EdToggleBoolean(var B : Boolean);
  291.     {-Toggle a boolean}
  292.  
  293.   begin                      {EdToggleBoolean}
  294.     B := not(B);
  295.   end;                       {EdToggleBoolean}
  296.  
  297.   function EdTopofStream(W : PwinDesc) : PlineDesc;
  298.     {-Return pointer to first line in stream}
  299.   var
  300.     P : PlineDesc;
  301.  
  302.   begin                      {EdTopofStream}
  303.     with W^ do
  304.       P := TopLine;
  305.     while EdPtrNotNil(P^.Backlink) do
  306.       EdBackPtr(P);
  307.     EdTopofStream := P;
  308.   end;                       {EdTopofStream}
  309.  
  310.   function EdFindWindow(T : PlineDesc) : PwinDesc;
  311.     {-Return the window containing the designated line}
  312.   var
  313.     P : PlineDesc;
  314.     W : PwinDesc;
  315.     Found : Boolean;
  316.  
  317.     function EdSearchBack(T, P : PlineDesc) : Boolean;
  318.       {-Starting at line p, search to top of stream for target line t}
  319.  
  320.     begin                    {EdSearchBack}
  321.       EdSearchBack := False;
  322.       while EdPtrNotNil(P) do
  323.         if P = T then begin
  324.           {Found target line}
  325.           EdSearchBack := True;
  326.           Exit;
  327.         end else
  328.           EdBackPtr(P);
  329.     end;                     {EdSearchBack}
  330.  
  331.     function EdSearchFwd(T, P : PlineDesc) : Boolean;
  332.       {-Starting at line p, search to bottom of stream for target line t}
  333.  
  334.     begin                    {EdSearchFwd}
  335.       EdSearchFwd := False;
  336.       while EdPtrNotNil(P) do
  337.         if P = T then begin
  338.           {Found target line}
  339.           EdSearchFwd := True;
  340.           Exit;
  341.         end else
  342.           EdFwdPtr(P);
  343.     end;                     {EdSearchFwd}
  344.  
  345.   begin                      {EdFindWindow}
  346.     W := CurWin;
  347.     repeat
  348.  
  349.       P := W^.TopLine;
  350.       Found := EdSearchFwd(T, P);
  351.       if not(Found) then
  352.         Found := EdSearchBack(T, P);
  353.       if Found then begin
  354.         EdFindWindow := W;
  355.         Exit;
  356.       end;
  357.  
  358.       EdFwdPtr(W);
  359.     until W = CurWin;
  360.     {Window not found}
  361.     EdFindWindow := nil;
  362.   end;                       {EdFindWindow}
  363.  
  364.   function EdFindWindesc(Wno : Byte) : PwinDesc;
  365.     {-return a window descriptor for a window number}
  366.   var
  367.     W : PwinDesc;
  368.     I : Integer;
  369.  
  370.   begin                      {EdFindWindesc}
  371.     W := Window1;
  372.     I := 1;
  373.     while I < Wno do begin
  374.       EdFwdPtr(W);
  375.       Inc(I);
  376.     end;
  377.     EdFindWindesc := W;
  378.   end;                       {EdFindWindesc}
  379.  
  380.   function EdWindowNumber : Byte;
  381.     {-Return the window number of the current window}
  382.   var
  383.     W : PwinDesc;
  384.     I : Integer;
  385.  
  386.   begin                      {EdWindowNumber}
  387.     W := Window1;
  388.     I := 1;
  389.     while W <> CurWin do begin
  390.       EdFwdPtr(W);
  391.       Inc(I);
  392.     end;
  393.     EdWindowNumber := I;
  394.   end;                       {EdWindowNumber}
  395.  
  396.   function EdGetWindowToDivide : Byte;
  397.     {-Return best window number to split in order to create a new window}
  398.   var
  399.     W : PwinDesc;
  400.     Wno : Integer;
  401.  
  402.   begin                      {EdGetWindowToDivide}
  403.  
  404.     {Assume current window by default}
  405.     EdGetWindowToDivide := EdWindowNumber;
  406.  
  407.     {See if current window is OK to divide}
  408.     with CurWin^ do
  409.       if (LastLineNo-FirstLineNo) > (MinWindowLines shl 1) then
  410.         Exit;
  411.  
  412.     {Start at top of screen and try the other windows}
  413.     W := Window1;
  414.     Wno := 1;
  415.     repeat
  416.       with W^ do
  417.         if (LastLineNo-FirstLineNo) > (MinWindowLines shl 1) then begin
  418.           EdGetWindowToDivide := Wno;
  419.           Exit;
  420.         end;
  421.       EdFwdPtr(W);
  422.       Inc(Wno);
  423.     until W = Window1;
  424.  
  425.   end;                       {EdGetWindowToDivide}
  426.  
  427.   {***}
  428.   procedure EdWindowUp;
  429.     {-Process up window command}
  430.  
  431.   begin                      {EdWindowUp}
  432.     EdBackPtr(CurWin);
  433.   end;                       {EdWindowUp}
  434.  
  435.   {***}
  436.   procedure EdWindowDown;
  437.     {-Process down window command}
  438.  
  439.   begin                      {EdWindowDown}
  440.     EdFwdPtr(CurWin);
  441.   end;                       {EdWindowDown}
  442.  
  443.   function EdCursorInBlock(Q : PlineDesc; C : Integer; EndMarkOk : Boolean) : Boolean;
  444.     {-Return true if position q:c is in block}
  445.   var
  446.     P : PlineDesc;
  447.     F, T : Integer;
  448.     MnOk : Boolean;
  449.  
  450.   begin                      {EdCursorInBlock}
  451.  
  452.     EdCursorInBlock := False;
  453.     P := Blockfrom.Line;
  454.     T := Blockto.Col;
  455.     F := Blockfrom.Col;
  456.     MnOk := not(EndMarkOk);
  457.  
  458.     while EdPtrNotNil(P) do begin
  459.  
  460.       if P = Q then begin
  461.         if P = Blockfrom.Line then
  462.           if P = Blockto.Line then
  463.             EdCursorInBlock := ((C > F) and (C < T)) or (MnOk and ((C = F) or (C = T)))
  464.           else
  465.             EdCursorInBlock := (C > F) or (MnOk and (C = F))
  466.         else if (P = Blockto.Line) then
  467.           EdCursorInBlock := (C < T) or (MnOk and (C = T))
  468.         else
  469.           EdCursorInBlock := True;
  470.         Exit;
  471.       end;
  472.  
  473.       if P = Blockto.Line then
  474.         {Exit loop}
  475.         EdSetPtrNil(P)
  476.       else
  477.         EdFwdPtr(P);
  478.  
  479.     end;
  480.   end;                       {EdCursorInBlock}
  481.  
  482.   function EdNoBlock : Boolean;
  483.     {-Return True if no block is marked and visible}
  484.  
  485.   begin                      {EdNoBlock}
  486.     EdNoBlock := Blockhide or
  487.     EdPtrIsNil(Blockfrom.Line) or EdPtrIsNil(Blockto.Line) or
  488.     ((Blockfrom.Line = Blockto.Line) and (Blockfrom.Col >= Blockto.Col));
  489.   end;                       {EdNoBlock}
  490.  
  491.   function EdTextLength(P : PlineDesc) : Integer;
  492.     {-Return the length of line text, 0 if all blank}
  493.   begin                      {EdTextLength}
  494.     with P^ do
  495.       EdTextLength := EdGetEol(Txt^, Bufflen);
  496.   end;                       {EdTextLength}
  497.  
  498.   {***}
  499.   procedure EdRealignOne(W : PwinDesc);
  500.     {-Realign one window}
  501.   var
  502.     P : PlineDesc;
  503.     Size : Integer;
  504.  
  505.   begin                      {EdRealignOne}
  506.     with W^ do begin
  507.       {Realign this window}
  508.       LineNo := 1;
  509.       {Topline should always be defined!}
  510.       P := TopLine;
  511.       Size := LastLineNo-FirstTextNo;
  512.       {Curline must always be equal to or forward from Topline!}
  513.       while (P <> CurLine) and EdPtrNotNil(P) do begin
  514.         {Scan until we find the current line}
  515.         EdFwdPtr(P);
  516.         if LineNo > Size then
  517.           EdFwdPtr(TopLine)
  518.         else
  519.           Inc(LineNo);
  520.       end;
  521.     end;
  522.   end;                       {EdRealignOne}
  523.  
  524.   procedure EdRealign;
  525.     {-Realign windows over text streams}
  526.   var
  527.     W : PwinDesc;
  528.  
  529.   begin                      {EdRealign}
  530.     W := Window1;
  531.     repeat
  532.       EdRealignOne(W);
  533.       EdFwdPtr(W);
  534.     until W = Window1;
  535.   end;                       {EdRealign}
  536.  
  537.   function EdLineIndent(P : PlineDesc) : Integer;
  538.     {-Return the indent of the specified line, 0 if line is empty}
  539.   var
  540.     I : Integer;
  541.  
  542.   begin                      {EdLineIndent}
  543.     with P^ do begin
  544.       I := 1;
  545.       while (I < Bufflen) and (Txt^[I] = Blank) do
  546.         Inc(I);
  547.       if I = Bufflen then
  548.         {Line is all blank}
  549.         I := 0;
  550.     end;
  551.     EdLineIndent := I;
  552.   end;                       {EdLineIndent}
  553.  
  554.   procedure EdClrFlag(P : PlineDesc; Mask : Word);
  555.     {-Clear the line flag}
  556.  
  557.   begin                      {EdClrFlag}
  558.     P^.Flags := P^.Flags and not(Mask);
  559.   end;                       {EdClrFlag}
  560.  
  561.   procedure EdSetFlag(P : PlineDesc; Mask : Word);
  562.     {-Set the line flag}
  563.  
  564.   begin                      {EdSetFlag}
  565.     P^.Flags := P^.Flags or Mask;
  566.   end;                       {EdSetFlag}
  567.  
  568.   function EdFlagSet(P : PlineDesc; Mask : Word) : Boolean;
  569.     {-Return True if the mask flag is set for line p}
  570.  
  571.   begin                      {EdFlagSet}
  572.     EdFlagSet := (P^.Flags and Mask) <> 0;
  573.   end;                       {EdFlagSet}
  574.  
  575.   procedure EdChangeFlag(P : PlineDesc; FlagVal : Boolean; FlagPos : Word);
  576.     {-Change a line flag, setting UpdateScreen if change encountered}
  577.  
  578.   begin                      {EdChangeFlag}
  579.     if FlagVal then begin
  580.       {Setting a flag}
  581.       if not(EdFlagSet(P, FlagPos)) then begin
  582.         UpdateScreen := True;
  583.         EdSetFlag(P, FlagPos);
  584.       end;
  585.     end else if EdFlagSet(P, FlagPos) then begin
  586.       {Clearing a flag}
  587.       UpdateScreen := True;
  588.       EdClrFlag(P, FlagPos);
  589.     end;
  590.   end;                       {EdChangeFlag}
  591.  
  592.   procedure EdToggleTextMarker;
  593.     {-Toggle visibility of text markers}
  594.   var
  595.     M : Integer;
  596.  
  597.   begin                      {EdToggleTextMarker}
  598.     MarkHide := not(MarkHide);
  599.     {Set line flags}
  600.     for M := 0 to MaxMarker do
  601.       with Marker[M] do
  602.         if EdPtrNotNil(Line) then
  603.           if MarkHide then
  604.             EdClrFlag(Line, InMark)
  605.           else
  606.             EdSetFlag(Line, InMark);
  607.   end;                       {EdToggleTextMarker}
  608.  
  609.   procedure EdFixUpWindowSpan(P : PlineDesc);
  610.     {-When p is to be deleted, first fix up any other structures pointing to p}
  611.   var
  612.     W : PwinDesc;
  613.     T : PlineDesc;
  614.  
  615.   begin                      {EdFixUpWindowSpan}
  616.  
  617.     {Check window Toplines, Curlines, and shortening window's span}
  618.     W := Window1;
  619.     repeat
  620.       with W^ do begin
  621.         if P = TopLine then begin
  622.           {Deleting the top line of a window}
  623.           if EdPtrIsNil(P^.Backlink) then begin
  624.             if EdPtrIsNil(P^.FwdLink) then begin
  625.               {Deleting only line in file, must be linked windows}
  626.               CurLine := CurWin^.CurLine;
  627.               TopLine := CurLine;
  628.             end else begin
  629.               {Deleting first line of file}
  630.               if P = CurLine then begin
  631.                 EdFwdPtr(CurLine);
  632.                 LineNo := 1;
  633.               end else
  634.                 Dec(LineNo);
  635.               EdFwdPtr(TopLine);
  636.             end;
  637.           end else begin
  638.             {Somewhere in middle or end of file}
  639.             if P = CurLine then begin
  640.               EdBackPtr(CurLine);
  641.               LineNo := 1;
  642.             end;
  643.             EdBackPtr(TopLine);
  644.           end;
  645.         end else if P = CurLine then begin
  646.           {Deleting the current line of a window}
  647.           if EdPtrIsNil(P^.FwdLink) then begin
  648.             {Last line in file}
  649.             EdBackPtr(CurLine);
  650.             Dec(LineNo);
  651.           end else
  652.             EdFwdPtr(CurLine);
  653.         end else begin
  654.           {Check for line in the middle of window}
  655.           T := TopLine;
  656.           while T <> CurLine do
  657.             if T = P then begin
  658.               {Try to advance curline}
  659.               if EdPtrIsNil(CurLine^.FwdLink) then
  660.                 Dec(LineNo)
  661.               else
  662.                 EdFwdPtr(CurLine);
  663.               {Force exit}
  664.               T := CurLine;
  665.             end else
  666.               EdFwdPtr(T);
  667.         end;
  668.       end;
  669.       EdFwdPtr(W);
  670.     until W = Window1;
  671.  
  672.   end;                       {EdFixUpWindowSpan}
  673.  
  674.   procedure EdFixBlockInsertedSpace(P : PlineDesc; Start : Integer; Num : Integer);
  675.     {-Fix up block markers after blank space is inserted}
  676.  
  677.   begin                      {EdFixBlockInsertedSpace}
  678.     if (P = Blockfrom.Line) then
  679.       if (Start < Blockfrom.Col) then
  680.         Blockfrom.Col := Blockfrom.Col+Num;
  681.     if (P = Blockto.Line) then
  682.       if (Start < Blockto.Col) then
  683.         Blockto.Col := Blockto.Col+Num;
  684.   end;                       {EdFixBlockInsertedSpace}
  685.  
  686.   procedure EdFixMarkInsertedSpace(P : PlineDesc; Start : Integer; Num : Integer);
  687.     {-Fix up text markers after space is inserted in a line}
  688.   var
  689.     M : Integer;
  690.  
  691.   begin                      {EdFixMarkInsertedSpace}
  692.     if EdFlagSet(P, InMark) then
  693.       for M := 0 to MaxMarker do
  694.         with Marker[M] do
  695.           if (Line = P) then
  696.             if (Col >= Start) then begin
  697.               Col := Col+Num;
  698.               if Col < 1 then
  699.                 Col := 1;
  700.             end;
  701.   end;                       {EdFixMarkInsertedSpace}
  702.  
  703.   procedure EdFixBlockInsertedLine(ThisL, NextL : PlineDesc;
  704.                                    BreakCol : Integer;
  705.                                    Delta : Integer);
  706.     {-Fix up block markers after a line is inserted}
  707.  
  708.   begin                      {EdFixBlockInsertedLine}
  709.     if not(Blockhide) and EdFlagSet(ThisL, InBlock) then
  710.       EdSetFlag(NextL, InBlock);
  711.     if (ThisL = Blockfrom.Line) then
  712.       if (BreakCol <= Blockfrom.Col) then begin
  713.         Blockfrom.Line := NextL;
  714.         Blockfrom.Col := Blockfrom.Col-Delta;
  715.         EdClrFlag(ThisL, InBlock);
  716.       end;
  717.     if (ThisL = Blockto.Line) then
  718.       if (BreakCol < Blockto.Col) then begin
  719.         Blockto.Line := NextL;
  720.         Blockto.Col := Blockto.Col-Delta;
  721.       end else
  722.         EdClrFlag(NextL, InBlock);
  723.   end;                       {EdFixBlockInsertedLine}
  724.  
  725.   procedure EdFixMarkInsertedLine(ThisL, NextL : PlineDesc;
  726.                                   BreakCol : Integer;
  727.                                   Delta : Integer);
  728.     {-Fix up text markers after a new line is inserted}
  729.   var
  730.     MarkGone : Boolean;
  731.     M : Integer;
  732.  
  733.   begin                      {EdFixMarkInsertedLine}
  734.     MarkGone := True;
  735.     for M := 0 to MaxMarker do
  736.       with Marker[M] do
  737.         if (Line = ThisL) then
  738.           if (Col >= BreakCol) then begin
  739.             Col := Succ(Col-Delta);
  740.             Line := NextL;
  741.             EdSetFlag(NextL, InMark);
  742.           end else
  743.             MarkGone := False;
  744.     if MarkGone then
  745.       EdClrFlag(ThisL, InMark);
  746.   end;                       {EdFixMarkInsertedLine}
  747.  
  748.   procedure EdOffblock;
  749.     {-Turn off Inblock bits for every text line in the system}
  750.   var
  751.     W : PwinDesc;
  752.     P, Q : PlineDesc;
  753.  
  754.   begin                      {EdOffblock}
  755.     {Start with current window}
  756.     W := CurWin;
  757.     repeat
  758.       {Do this to every window}
  759.       P := W^.TopLine;
  760.       Q := P;
  761.       while EdPtrNotNil(P) do begin
  762.         EdClrFlag(P, InBlock);
  763.         EdBackPtr(P);
  764.       end;
  765.       while EdPtrNotNil(Q) do begin
  766.         EdClrFlag(Q, InBlock);
  767.         EdFwdPtr(Q);
  768.       end;
  769.       EdFwdPtr(W);
  770.     until W = CurWin;
  771.   end;                       {EdOffblock}
  772.  
  773.   {***}
  774.   procedure EdSetTextNo(W : PwinDesc);
  775.     {-Set the FirstTextNo of a window}
  776.  
  777.   begin                      {EdSetTextNo}
  778.     with W^ do
  779.       FirstTextNo := Succ(FirstLineNo);
  780.   end;                       {EdSetTextNo}
  781.  
  782.   function EdLinkedWindow(W : PwinDesc) : Boolean;
  783.     {-Return true if a window is linked to any others}
  784.   var
  785.     V : PwinDesc;
  786.     Wstream : Word;
  787.  
  788.   begin                      {EdLinkedWindow}
  789.     EdLinkedWindow := False;
  790.     Wstream := W^.Stream;
  791.     V := W^.FwdLink;
  792.     while V <> W do
  793.       if V^.Stream = Wstream then begin
  794.         EdLinkedWindow := True;
  795.         Exit;
  796.       end else
  797.         EdFwdPtr(V);
  798.   end;                       {EdLinkedWindow}
  799.  
  800.   function EdBlockDiscontinuous : Boolean;
  801.     {-Return true if block is not within a continuous text stream}
  802.   var
  803.     P : PlineDesc;
  804.  
  805.   begin                      {EdBlockDiscontinuous}
  806.     EdBlockDiscontinuous := True;
  807.     P := Blockfrom.Line;
  808.     while EdPtrNotNil(P) do
  809.       if P = Blockto.Line then begin
  810.         {Block is continuous if columns are legal}
  811.         EdBlockDiscontinuous := (Blockfrom.Line = Blockto.Line) and (Blockfrom.Col >= Blockto.Col);
  812.         Exit;
  813.       end else
  814.         EdFwdPtr(P);
  815.   end;                       {EdBlockDiscontinuous}
  816.  
  817.   procedure EdResetWindowPrimitive(W : PwinDesc);
  818.     {-Low level routine for resetting window}
  819.  
  820.   begin                      {EdResetWindowPrimitive}
  821.     with W^ do begin
  822.       CurLine := TopLine;
  823.       Clineno := 1;
  824.       LineNo := 1;
  825.       ColNo := 1;
  826.       LeftEdge := 1;
  827.     end;
  828.   end;                       {EdResetWindowPrimitive}
  829.  
  830.   function EdNewstream : Word;
  831.     {-Return unique text stream identifier}
  832.  
  833.   begin                      {EdNewstream}
  834.     EdNewstream := NextStream;
  835.     NextStream := Succ(NextStream) mod MaxInt;
  836.   end;                       {EdNewstream}
  837.  
  838.   procedure EdChangeStreamName(Fname : Filepath);
  839.     {-Change name of current stream to fname}
  840.   var
  841.     W : PwinDesc;
  842.     S : Word;
  843.  
  844.   begin                      {EdChangeStreamName}
  845.     S := CurWin^.Stream;
  846.     W := CurWin;
  847.     repeat
  848.       if W^.Stream = S then
  849.         W^.Filename := Fname;
  850.       EdFwdPtr(W);
  851.     until W = CurWin;
  852.   end;                       {EdChangeStreamName}
  853.  
  854.   procedure EdCheckNoMarker;
  855.     {-If deletion operation has deleted all marked text, remove block markers altogether}
  856.  
  857.   begin                      {EdChecknomarker}
  858.     {Get out fast if no block now}
  859.     if EdPtrIsNil(Blockfrom.Line) or (Blockfrom.Line <> Blockto.Line) then
  860.       Exit;
  861.     if Blockfrom.Col >= Blockto.Col then begin
  862.       EdSetPtrNil(Blockfrom.Line);
  863.       EdSetPtrNil(Blockto.Line);
  864.       EdOffblock;
  865.     end;
  866.   end;                       {EdChecknomarker}
  867.  
  868.   {***}
  869.   procedure EdInitWindowSettings(W : PwinDesc);
  870.     {-Set window parameters to defaults}
  871.  
  872.   begin                      {EdInitWindowSettings}
  873.     with W^ do begin
  874.       InsertFlag := SaveInsertMode;
  875.       AI := SaveIndentMode;
  876.       Filename := NoFile;
  877.       Modified := False;
  878.       Clineno := 1;
  879.       LeftCol := 0;
  880.       LeftEdge := 1;
  881.       TcharNo := 0;
  882.  
  883.       {The rest of these aren't used in FirstEd}
  884.       {Initialize to harmless values}
  885.       WW := False;
  886.       TL := False;
  887.       JU := False;
  888.       PA := False;
  889.       AT := False;
  890.       FT := False;
  891.       Lmargin := 1;
  892.       Wmargin := 1;
  893.       Rmargin := MaxLineLength;
  894.       PageLen := MaxInt;
  895.       Tmargin := 0;
  896.       Bmargin := 0;
  897.       Visible := True;
  898.     end;
  899.   end;                       {EdInitWindowSettings}
  900.  
  901.   procedure EdLinkbuffer(P, Q : PlineDesc);
  902.     {-Link line q after line p}
  903.  
  904.   begin                      {EdLinkbuffer}
  905.     Q^.Backlink := P;
  906.     Q^.FwdLink := P^.FwdLink;
  907.     P^.FwdLink := Q;
  908.     if EdPtrNotNil(Q^.FwdLink) then
  909.       Q^.FwdLink^.Backlink := Q;
  910.   end;                       {EdLinkbuffer}
  911.  
  912.   procedure EdBufferCurrentLine;
  913.     {-Keep an extra image of current line to allow restore via ^QL}
  914.   var
  915.     Len : Integer;
  916.  
  917.   begin                      {EdBufferCurrentLine}
  918.     with CurWin^ do begin
  919.  
  920.       Len := CurLine^.Bufflen;
  921.  
  922.       {Store the text and flags of the current line}
  923.       with CurLineBuf^ do begin
  924.         Bufflen := Len;
  925.         Flags := CurLine^.Flags;
  926.         Move(CurLine^.Txt^[1], Txt^[1], Len);
  927.       end;
  928.  
  929.       {Store the current column number}
  930.       CurLineCol := ColNo;
  931.  
  932.       {Store block columns and lines}
  933.       if CurLine = Blockfrom.Line then
  934.         CurLineFrom.Col := Blockfrom.Col;
  935.       CurLineFrom.Line := Blockfrom.Line;
  936.  
  937.       if CurLine = Blockto.Line then
  938.         CurLineTo.Col := Blockto.Col;
  939.       CurLineTo.Line := Blockto.Line;
  940.  
  941.     end;
  942.   end;                       {EdBufferCurrentLine}
  943.  
  944.   procedure EdBackupCurline(W : PwinDesc);
  945.     {-Move curline up as needed to fit into smaller window}
  946.  
  947.   begin                      {EdBackupCurline}
  948.     with W^ do
  949.       while LineNo > Succ(LastLineNo-FirstTextNo) do begin
  950.         {Fix up the pointers}
  951.         EdFwdPtr(TopLine);
  952.         {Alternate behavior, replace previous line with --> EdBackPtr(Curline);}
  953.         Dec(LineNo);
  954.       end;
  955.   end;                       {EdBackupCurline}
  956.  
  957.   procedure EdMoveCursorIntoLine;
  958.     {-Set current column within line buffer}
  959.  
  960.   begin                      {EdMoveCursorIntoLine}
  961.     with CurWin^ do
  962.       if ColNo > CurLine^.Bufflen then
  963.         ColNo := CurLine^.Bufflen;
  964.   end;                       {EdMoveCursorIntoLine}
  965.  
  966.   procedure EdGotoColumn(Cno : Integer);
  967.     {-Go to column cno on current line}
  968.  
  969.   begin                      {EdGotoColumn}
  970.     CurWin^.ColNo := Cno;
  971.   end;                       {EdGotoColumn}
  972.  
  973.   procedure EdGotoLine(Lno : Integer);
  974.     {-Go to line Lno of current window}
  975.   var
  976.     P : PlineDesc;
  977.     I : Integer;
  978.  
  979.   begin                      {EdGotoLine}
  980.     with CurWin^ do begin
  981.       {Go to first line of file}
  982.       P := EdTopofStream(CurWin);
  983.       I := 1;
  984.       {Count lines to the right one}
  985.       while (I < Lno) and EdPtrNotNil(P^.FwdLink) do begin
  986.         Inc(I);
  987.         EdFwdPtr(P);
  988.       end;
  989.       CurLine := P;
  990.       TopLine := P;
  991.       LineNo := 1;
  992.       Clineno := I;
  993.     end;
  994.   end;                       {EdGotoLine}
  995.  
  996.   procedure EdWindowTopFile;
  997.     {-Go to top of file}
  998.   var
  999.     P : PlineDesc;
  1000.  
  1001.   begin                      {EdWindowTopFile}
  1002.     P := EdTopofStream(CurWin);
  1003.     CurWin^.TopLine := P;
  1004.     EdResetWindowPrimitive(CurWin);
  1005.   end;                       {EdWindowTopFile}
  1006.  
  1007.   {***}
  1008.   procedure EdTopScreen;
  1009.     {-Go to top of screen}
  1010.  
  1011.   begin                      {EdTopScreen}
  1012.     with CurWin^ do begin
  1013.       CurLine := TopLine;
  1014.       LineNo := 1;
  1015.     end;
  1016.   end;                       {EdTopScreen}
  1017.  
  1018.   {***}
  1019.   procedure EdBottomScreen;
  1020.     {-Move cursor to bottom of screen}
  1021.  
  1022.   begin                      {EdBottomScreen}
  1023.     with CurWin^ do begin
  1024.       CurLine := TopLine;
  1025.       LineNo := 1;
  1026.       while (LineNo <= LastLineNo-FirstTextNo) and EdPtrNotNil(CurLine^.FwdLink) do begin
  1027.         Inc(LineNo);
  1028.         EdFwdPtr(CurLine);
  1029.       end;
  1030.     end;
  1031.   end;                       {EdBottomScreen}
  1032.  
  1033.   procedure EdWindowGoto(Wno : Byte);
  1034.     {-Move cursor into window Wno, counted from top of screen}
  1035.  
  1036.   begin                      {EdWindowGoto}
  1037.     CurWin := EdFindWindesc(Wno);
  1038.   end;                       {EdWindowGoto}
  1039.  
  1040.   procedure EdBlockHide;
  1041.     {-Toggle block display}
  1042.  
  1043.   begin                      {EdBlockHide}
  1044.     if Blockhide then
  1045.       {Turn off blockhide only if From and To markers form a continuous stream}
  1046.       Blockhide := EdBlockDiscontinuous
  1047.     else begin
  1048.       {Turn off block display}
  1049.       Blockhide := True;
  1050.       {Reset block flags everywhere}
  1051.       EdOffblock;
  1052.     end;
  1053.   end;                       {EdBlockHide}
  1054.  
  1055.   procedure EdFixBaseLine(WindFrom : PwinDesc);
  1056.     {-Redefine window Topline and Curline before a block operation deletes them}
  1057.   var
  1058.     W : PwinDesc;
  1059.  
  1060.   begin                      {EdFixBaseLine}
  1061.     W := WindFrom;
  1062.     repeat
  1063.       {Check all windows sharing the text stream including windfrom itself}
  1064.       if W^.Stream = WindFrom^.Stream then
  1065.         with W^ do begin
  1066.           if EdCursorInBlock(TopLine, 1, False) or (TopLine = Blockto.Line) then
  1067.             TopLine := Blockfrom.Line;
  1068.           if EdCursorInBlock(CurLine, ColNo, False) or (CurLine = Blockto.Line) then begin
  1069.             CurLine := Blockfrom.Line;
  1070.             ColNo := Blockfrom.Col;
  1071.           end;
  1072.         end;
  1073.       EdFwdPtr(W);
  1074.     until (W = WindFrom);
  1075.   end;                       {EdFixBaseLine}
  1076.  
  1077.   procedure EdSetupBlock(var MarkA, MarkB : BlockMarker);
  1078.     {-Check for a contiguous block, and set marks and blockhide accordingly}
  1079.   var
  1080.     Len : Integer;
  1081.  
  1082.   begin                      {EdSetupBlock}
  1083.  
  1084.     {Turn off all lines in blocks}
  1085.     EdOffblock;
  1086.  
  1087.     {Repoint one end of the block}
  1088.     with CurWin^, MarkA do begin
  1089.       Line := CurLine;
  1090.       Col := ColNo;
  1091.       {Assure block stays within text buffer}
  1092.       Len := EdTextLength(CurLine);
  1093.       if Col > Len then
  1094.         Col := Succ(Len);
  1095.     end;
  1096.  
  1097.     {Assure complete block defined}
  1098.     if EdPtrIsNil(MarkB.Line) then begin
  1099.       {Only one end defined}
  1100.       Blockhide := True;
  1101.       Exit;
  1102.     end;
  1103.  
  1104.     Blockhide := EdBlockDiscontinuous;
  1105.     if Blockhide then begin
  1106.       EdSetPtrNil(MarkB.Line);
  1107.       MarkB.Col := 0;
  1108.     end;
  1109.  
  1110.   end;                       {EdSetupBlock}
  1111.  
  1112.   procedure EdBlockBegin;
  1113.     {-Set block begin marker}
  1114.  
  1115.   begin                      {EdBlockBegin}
  1116.     EdSetupBlock(Blockfrom, Blockto);
  1117.   end;                       {EdBlockBegin}
  1118.  
  1119.   procedure EdBlockEnd;
  1120.     {-Set block end marker}
  1121.  
  1122.   begin                      {EdBlockEnd}
  1123.     EdSetupBlock(Blockto, Blockfrom);
  1124.   end;                       {EdBlockEnd}
  1125.  
  1126.   procedure EdBlockWord;
  1127.     {-Mark the current word as a block}
  1128.   var
  1129.     C, Len : Integer;
  1130.  
  1131.   begin                      {EdBlockWord}
  1132.  
  1133.     with CurWin^ do begin
  1134.  
  1135.       Len := EdTextLength(CurLine);
  1136.       if Len = 0 then
  1137.         {Line is empty, don't change anything}
  1138.         Exit;
  1139.  
  1140.       {Set marker to this line}
  1141.       Blockto.Line := CurLine;
  1142.       Blockfrom.Line := CurLine;
  1143.  
  1144.       C := ColNo;
  1145.  
  1146.       with CurLine^ do begin
  1147.  
  1148.         if C > Len then
  1149.           {Cursor past end of line, mark word to left}
  1150.           C := Len;
  1151.  
  1152.         if (Txt^[C] in Alphas) then begin
  1153.           {In a word, scan to left edge of word}
  1154.           while (C > 0) and (Txt^[C] in Alphas) do
  1155.             Dec(C);
  1156.           Inc(C);
  1157.         end else
  1158.           {In white space, scan right to next word}
  1159.           while not(Txt^[C] in Alphas) do
  1160.             Inc(C);
  1161.  
  1162.         {From marker starts at left edge of word}
  1163.         Blockfrom.Col := C;
  1164.  
  1165.         {Scan right past end of word}
  1166.         while (C <= Len) and (Txt^[C] in Alphas) do
  1167.           Inc(C);
  1168.  
  1169.         {To marker is just past right edge of word}
  1170.         Blockto.Col := C;
  1171.  
  1172.       end;
  1173.     end;
  1174.  
  1175.     EdOffblock;
  1176.     Blockhide := False;
  1177.  
  1178.   end;                       {EdBlockWord}
  1179.  
  1180. begin
  1181.   NextStream := 0;           {Initialize text stream identifier}
  1182. end.
  1183.