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

  1. {                          EDFILE.PAS
  2.                              ED 4.0
  3.              Copyright (c) 1985, 87 by Borland International, Inc.            }
  4.  
  5. {$I eddirect.inc}
  6.  
  7. unit EdFile;
  8.   {-Perform FirstEd file operations}
  9.  
  10. interface
  11.  
  12. uses
  13.   crt,                       {Basic video}
  14.   Dos,                       {DOS calls - standard unit}
  15.   Errors,                    {Runtime error handler}
  16.   EdVars,                    {Global types and declarations}
  17.   EdScrn1,                   {Fast screen writing routines}
  18.   EdString,                  {String primitives}
  19.   EdPtrOp,                   {Primitive pointer operations}
  20.   EdCmds,                    {Maps keystrokes to commands}
  21.   int24,                     {DOS critical error handler}
  22.   Message,                   {Message system}
  23.   EdUser,                    {User keyboard input, line editing and error reporting}
  24.   EdMemOp,                   {Text buffer allocation and deallocation}
  25.   EdBack,                    {Background processes}
  26.   EdScrn2,                   {Editor screen updating}
  27.   EdEdit;                    {Basic editing commands}
  28.  
  29. function EdExistFile(Fname : Filepath) : Boolean;
  30.   {-return true if file exists, false if non-existent or a device}
  31.  
  32. procedure EdGetDefaultExtension;
  33.   {-Get a new default file extension}
  34.  
  35. procedure EdShutWindow(ExitEditor : Boolean);
  36.   {-Shut the current window, set Rundown true if last one and ExitEditor true}
  37.  
  38. procedure EdFileWrite(Fname : Filepath; Quitting : Boolean);
  39.   {-Save current text stream to specified file}
  40.  
  41. procedure EdLogDrive(NewPath : Filepath);
  42.   {-Select a new drive or directory}
  43.  
  44. procedure EdAbandonFile(ExitEditor : Boolean);
  45.   {-Close file without saving}
  46.  
  47. function EdGetFileName(Prompt, DefExt : VarString; Attr : Byte; var LastFname : Filepath) : Filepath;
  48.   {-Return a file name to use}
  49.  
  50. procedure EdReadtextfile(Fname : Filepath; ReadingBlock : Boolean);
  51.   {-Read text file into current window}
  52.  
  53. procedure EdReadFile(Fname : Filepath);
  54.   {-Check and open a text file for editing}
  55.  
  56. procedure EdPromptWriteBlock;
  57.   {-Prompt for and write a block to a file}
  58.  
  59.   {==========================================================================}
  60.  
  61. implementation
  62.  
  63. const
  64.   EofWrite : string[1] = ^Z; {Written at end of file}
  65.   EofMark : string[1] = ^Z;  {Indicates end of file}
  66.   BakFileExt : ExtString = 'BAK'; {Extension given to backup files}
  67.  
  68.   function EdIsDevice(Fname : Filepath) : Boolean;
  69.     {-return true if fname is a DOS device}
  70.   var
  71.     F : file;
  72.     Handle : Word absolute F;
  73.     regs : registers;
  74.  
  75.   begin                      {EdIsDevice}
  76.     Assign(F, Fname);
  77.     Reset(F);
  78.     if EdINT24Result <> 0 then
  79.       {Probably a file}
  80.       EdIsDevice := False
  81.     else
  82.       with regs do begin
  83.         ax := $4400;
  84.         Bx := Handle;
  85.         intr($21, regs);
  86.         EdIsDevice := (Dx and $80 <> 0);
  87.       end;
  88.     Close(F);
  89.   end;                       {EdIsDevice}
  90.  
  91.   function EdExistFile(Fname : Filepath) : Boolean;
  92.     {-return true if file exists, false if non-existent or a device}
  93.   var
  94.     F : file;
  95.     I : Word;
  96.  
  97.   begin                      {EdExistFile}
  98.     if EdIsDevice(Fname) then
  99.       {Return false so we don't try to back up devices or get their file size}
  100.       EdExistFile := False
  101.     else begin
  102.       Assign(F, Fname);
  103.       Reset(F);
  104.       EdExistFile := (EdINT24Result = 0);
  105.       Close(F);
  106.       {Clear IOresult}
  107.       I := EdINT24Result;
  108.     end;
  109.   end;                       {EdExistfile}
  110.  
  111.   function EdFileerror : Boolean;
  112.     {-Report error during file operation}
  113.   var
  114.     Code : Word;
  115.  
  116.   begin                      {EdFileerror}
  117.     Code := EdINT24Result;
  118.     if hi(Code) <> 0 then
  119.       EdErrormsg(128)
  120.     else if Code <> 0 then
  121.       EdErrormsg(Code);
  122.     EdFileerror := (Code <> 0);
  123.   end;                       {EdFileerror}
  124.  
  125.   procedure EdBlockWrite(var F : file; var Buf; Num : Word);
  126.     {-Write a block and check for errors}
  127.   var
  128.     BytesWritten : Word;
  129.  
  130.   begin                      {EdBlockWrite}
  131.     BlockWrite(F, Buf, Num, BytesWritten);
  132.     if EdFileerror then
  133.       Exit;
  134.     if BytesWritten <> Num then
  135.       EdErrormsg(240);
  136.   end;                       {EdBlockWrite}
  137.  
  138.   procedure EdBlockRead(var F : file; var Buf; Num : Word; var BytesRead);
  139.     {-Read a block and check for errors}
  140.   var
  141.     BR : Word absolute BytesRead;
  142.  
  143.   begin                      {EdBlockRead}
  144.     BlockRead(F, Buf, Num, BR);
  145.     if EdFileerror then
  146.       ;
  147.   end;                       {EdBlockRead}
  148.  
  149.   procedure EdGetDefaultExtension;
  150.     {-Get a new default file extension}
  151.   var
  152.     DefExt : string[4];
  153.     Done : Boolean;
  154.     I : Word;
  155.  
  156.   begin                      {EdGetDefaultExtension}
  157.     repeat
  158.       Done := True;
  159.       DefExt := DefExtension;
  160.       EdAskfor(EdGetMessage(377), 10, 20, 6, DefExt);
  161.       if not(Abortcmd) then
  162.         {Do some error checking}
  163.         if Pos('*', DefExt)+Pos('?', DefExt) <> 0 then begin
  164.           EdErrormsg(49);
  165.           Done := False;
  166.         end else begin
  167.           if Copy(DefExt, 1, 1) = Period then
  168.             Delete(DefExt, 1, 1);
  169.           for I := 1 to Length(DefExt) do
  170.             DefExt[I] := Upcase(DefExt[I]);
  171.           DefExtension := DefExt;
  172.         end;
  173.     until Done;
  174.   end;                       {EdGetDefaultExtension}
  175.  
  176.   {***}
  177.   procedure EdReadtextfile(Fname : Filepath; ReadingBlock : Boolean);
  178.     {-Read text file into current window}
  179.   var
  180.     InFile : file;
  181.     TopSave, CurSave : PlineDesc;
  182.     EdError, EndOfFile, GotEol : Boolean;
  183.     colSave, BufOfs, BufPos, BufCnt, EolPos, EofPos : Integer;
  184.  
  185.     function EdInsertbuffer(Ncols : Integer) : Boolean;
  186.       {-Insert buffer into text stream - after current line}
  187.     var
  188.       P : PlineDesc;
  189.       Len : Integer;
  190.  
  191.     begin                    {EdInsertbuffer}
  192.       with CurWin^ do begin
  193.  
  194.         {Calculate appropriate buffer size}
  195.         Len := EdBufferSize(Ncols);
  196.  
  197.         if not(EdMemAvail(Len+SizeOf(LineDesc), FreeListSpace)) then begin
  198.           {Margin for stack not available}
  199.           EdErrormsg(35);
  200.           EdInsertbuffer := False;
  201.           Exit;
  202.         end;
  203.  
  204.         {Make new text buffer}
  205.         GetMem(P, SizeOf(LineDesc));
  206.  
  207.         with P^ do begin
  208.           GetMem(Txt, Len);
  209.           {Don't include font descriptor byte in size of text buffer}
  210.           Bufflen := Pred(Len);
  211.  
  212.           {Initialize flags}
  213.           Flags := 0;
  214.         end;
  215.  
  216.         {Link new buffer into stream}
  217.         EdLinkbuffer(CurLine, P);
  218.  
  219.         {Advance cursor to next line}
  220.         CurLine := P;
  221.  
  222.         EdInsertbuffer := True;
  223.       end;
  224.     end;                     {EdInsertbuffer}
  225.  
  226.   begin                      {EdReadtextfile}
  227.  
  228.     if Abortcmd or EdStringEmpty(Fname) then
  229.       Exit;
  230.  
  231.     EdWritePromptLine(EdGetMessage(325));
  232.  
  233.     Assign(InFile, Fname);
  234.     Reset(InFile, 1);
  235.     if EdFileerror then
  236.       Exit;
  237.  
  238.     with CurWin^ do begin
  239.  
  240.       {Reduce available memory so we have something left to link with}
  241.       FreeListSpace := FreeListPerm+(MaxLineLength shl 1);
  242.  
  243.       {Start inserting text at current cursor}
  244.       EdInsertLine;
  245.       if GotError then begin
  246.         Close(InFile);
  247.         Exit;
  248.       end;
  249.  
  250.       {Save line position, which will be starting line of newly read text}
  251.       TopSave := TopLine;
  252.       CurSave := CurLine;
  253.       colSave := ColNo;
  254.  
  255.       EdError := False;
  256.       BufOfs := 0;
  257.       ExactAllocation := True;
  258.  
  259.       if ReadingBlock then
  260.         Modified := True
  261.       else begin
  262.         {A new file}
  263.         Modified := False;
  264.         Clineno := 1;
  265.       end;
  266.  
  267.       repeat
  268.  
  269.         {Check keyboard for abort}
  270.         EdBreathe;
  271.         EdError := EdError or Abortcmd;
  272.  
  273.         if not(EdError) then begin
  274.           {Get a new buffer full of characters}
  275.           EdBlockRead(InFile, WorkBuf[Succ(BufOfs)], Bufsize-BufOfs, BufCnt);
  276.           Inc(BufCnt, BufOfs);
  277.           {Bufcnt now holds count of characters in buffer}
  278.  
  279.           EdError := EdError or GotError;
  280.  
  281.           {Adjust bufcnt for first ^Z found in buffer}
  282.           if BufCnt <> 0 then begin
  283.             EofPos := EdLongPosFwd(WorkBuf, 1, BufCnt, EofMark);
  284.             if EofPos <> 0 then
  285.               BufCnt := Pred(EofPos);
  286.           end;
  287.         end;
  288.  
  289.         {End of file if no active characters in buffer}
  290.         EndOfFile := EdError or (BufCnt <= 0);
  291.  
  292.         if not(EndOfFile) then begin
  293.  
  294.           {Scan the buffer, breaking it into <CR><LF> delimited lines}
  295.           BufOfs := 0;
  296.           BufPos := 1;
  297.  
  298.           repeat
  299.  
  300.             {Find next EOL in the buffer}
  301.             EolPos := EdLongPosFwd(WorkBuf, BufPos, BufCnt, EolMark);
  302.  
  303.             if (EolPos = 0) and (BufPos+MaxLineLength >= Bufsize) then begin
  304.  
  305.               {Eolmark not found in buffer}
  306.  
  307.               {Partial line, continue line into next buffer}
  308.               BufOfs := Succ(BufCnt-BufPos);
  309.               Move(WorkBuf[BufPos], WorkBuf[1], BufOfs);
  310.               {Force loop exit}
  311.               BufPos := Succ(BufCnt);
  312.  
  313.             end else begin
  314.  
  315.               {Eolmark found or linebreak forced}
  316.  
  317.               if (EolPos = 0) or ((EolPos-BufPos) >= MaxLineLength) then begin
  318.                 {Linebreak forced without finding a <CR><LF>}
  319.                 {Always leave at least one blank at end of line}
  320.                 EolPos := Pred(BufPos+MaxLineLength);
  321.                 if EolPos > BufCnt then
  322.                   EolPos := Succ(BufCnt);
  323.                 GotEol := False;
  324.               end else
  325.                 GotEol := True;
  326.  
  327.               if EolPos > BufPos then begin
  328.                 {Nonempty line, store it}
  329.                 if EdInsertbuffer(Succ(EolPos-BufPos)) then
  330.                   with CurLine^ do begin
  331.                     Move(WorkBuf[BufPos], Txt^[1], EolPos-BufPos);
  332.                     FillChar(Txt^[Succ(EolPos-BufPos)], Bufflen-EolPos+BufPos, Blank);
  333.                   end
  334.                 else begin
  335.                   EdError := True;
  336.                   GotError := False;
  337.                   Modified := True;
  338.                 end;
  339.               end else begin
  340.                 {Empty line}
  341.                 if EdInsertbuffer(1) then
  342.                   with CurLine^ do
  343.                     {Initialize buffer with blanks}
  344.                     FillChar(Txt^[1], Bufflen, Blank)
  345.                 else begin
  346.                   EdError := True;
  347.                   GotError := False;
  348.                   Modified := True;
  349.                 end;
  350.               end;
  351.  
  352.               if GotEol then
  353.                 {Skip over <CR><LF>}
  354.                 BufPos := EolPos+Length(EolMark)
  355.               else
  356.                 {Start immediately after break}
  357.                 BufPos := EolPos;
  358.  
  359.             end;             {Eolmark found}
  360.           until EdError or (BufPos > BufCnt);
  361.         end;                 {Not endoffile}
  362.       until EdError or EndOfFile;
  363.  
  364.       Close(InFile);
  365.  
  366.       {Restore stack margin}
  367.       FreeListSpace := FreeListPerm;
  368.  
  369.       if ReadingBlock then begin
  370.         {Set block markers around what we read in}
  371.         EdRightLine;
  372.         if GotEol then begin
  373.           EdDownLine;
  374.           ColNo := 1;
  375.         end else
  376.           EdJoinline;
  377.         with Blockto do begin
  378.           Line := CurLine;
  379.           Col := ColNo;
  380.         end;
  381.         with Blockfrom do begin
  382.           Line := CurSave;
  383.           Col := colSave;
  384.         end;
  385.         {Turn off old block marks}
  386.         EdOffblock;
  387.         {Prepare to display new ones}
  388.         Blockhide := False;
  389.       end;
  390.  
  391.       if CurLine <> CurSave then begin
  392.         {Restore original line position}
  393.         TopLine := TopSave;
  394.         CurLine := CurSave;
  395.         ColNo := colSave;
  396.         {Rejoin the left half of the line we split}
  397.         EdJoinline;
  398.       end;
  399.  
  400.       ExactAllocation := False;
  401.  
  402.       EdRealign;
  403.       UpdateScreen := True;
  404.       EdBufferCurrentLine;
  405.  
  406.     end;                     {With Curwin^}
  407.  
  408.     EdZapPromptLine;
  409.   end;                       {EdReadtextfile}
  410.  
  411.   procedure EdMakeBakFile(Fname : Filepath);
  412.     {-Create a backup file based on fname}
  413.   var
  414.     I : Integer;
  415.     Bname : Filepath;
  416.     F : file;
  417.  
  418.   begin                      {EdMakeBakFile}
  419.  
  420.     if not(MakeBackups) then
  421.       Exit;
  422.  
  423.     {Build backup name}
  424.     if EdFileHasExtension(Fname, I) then
  425.       Bname := Copy(Fname, 1, I)+BakFileExt
  426.     else
  427.       Bname := Fname+Period+BakFileExt;
  428.  
  429.     {Erase existing backup}
  430.     if EdExistFile(Bname) then begin
  431.       Assign(F, Bname);
  432.       Erase(F);
  433.       if EdINT24Result <> 0 then begin
  434.         EdErrormsg(103);
  435.         Exit;
  436.       end;
  437.     end;
  438.  
  439.     {Rename existing file to backup}
  440.     Assign(F, Fname);
  441.     Rename(F, Bname);
  442.     if EdINT24Result <> 0 then
  443.       EdErrormsg(104);
  444.   end;                       {EdMakebakfile}
  445.  
  446.   {*** ExitEditor ignored}
  447.   procedure EdShutWindow(ExitEditor : Boolean);
  448.     {-Shut the current window, set Rundown true if last one and ExitEditor true}
  449.  
  450.   begin                      {EdShutWindow}
  451.     {See if there is another window open}
  452.     if WindowCount <= 1 then
  453.       {Exit the editor}
  454.       EdFlagExit
  455.     else begin
  456.       {Delete current window and return in other one}
  457.       EdWindowUp;
  458.       EdWindowDelete(Succ(EdWindowNumber));
  459.     end;
  460.     if WindowCount > 0 then
  461.       Dec(WindowCount);
  462.   end;                       {EdShutWindow}
  463.  
  464.   procedure EdFileWrite(Fname : Filepath; Quitting : Boolean);
  465.     {-Save current text stream to specified file}
  466.   var
  467.     OutFile : file;
  468.     P : PlineDesc;
  469.     W : PwinDesc;
  470.     Len, BufPos, BufSiz : Integer;
  471.  
  472.   begin                      {EdFileWrite}
  473.     AbortEnable := True;
  474.     EdWait;
  475.  
  476.     if EdExistFile(Fname) then begin
  477.       {Create a .BAK file}
  478.       EdMakeBakFile(Fname);
  479.       if GotError then
  480.         Exit;
  481.     end;
  482.  
  483.     Assign(OutFile, Fname);
  484.     Rewrite(OutFile, 1);
  485.     if EdFileerror then
  486.       Exit;
  487.  
  488.     {Find top of stream}
  489.     P := EdTopofStream(CurWin);
  490.  
  491.     BufPos := 0;
  492.     BufSiz := Bufsize-Length(EolMark);
  493.  
  494.     with CurWin^ do
  495.       repeat
  496.  
  497.         {Check for abort}
  498.         if not(Quitting) then
  499.           {if leaving the editor, let DOS buffer keystrokes}
  500.           EdBreathe;
  501.  
  502.         Len := EdTextLength(P);
  503.  
  504.         if BufPos+Len > BufSiz then begin
  505.           {Flush write buffer}
  506.           EdBlockWrite(OutFile, WorkBuf[1], BufPos);
  507.           BufPos := 0;
  508.         end;
  509.  
  510.         if Len <> 0 then begin
  511.           Move(P^.Txt^[1], WorkBuf[Succ(BufPos)], Len);
  512.           Inc(BufPos, Len);
  513.         end;
  514.  
  515.         EdFwdPtr(P);
  516.         if EdPtrNotNil(P) then begin
  517.           {Add end of line marker}
  518.           Move(EolMark[1], WorkBuf[Succ(BufPos)], Length(EolMark));
  519.           Inc(BufPos, Length(EolMark));
  520.         end;
  521.  
  522.       until Abortcmd or GotError or EdPtrIsNil(P);
  523.  
  524.     if EdPtrIsNil(P) and (BufPos <> 0) then
  525.       {Flush the final chunk}
  526.       EdBlockWrite(OutFile, WorkBuf[1], BufPos);
  527.  
  528.     if EdPtrIsNil(P) and not(EdIsDevice(Fname)) then begin
  529.       {Write EOF marker}
  530.       if not(EdStringEmpty(EofWrite)) then
  531.         EdBlockWrite(OutFile, EofWrite[1], Length(EofWrite));
  532.  
  533.       if not(GotError) then begin
  534.         {Indicate that window and any linked to it are now saved completely}
  535.         W := CurWin;
  536.         repeat
  537.           if W^.Stream = CurWin^.Stream then
  538.             W^.Modified := False;
  539.           EdFwdPtr(W);
  540.         until W = CurWin;
  541.       end;
  542.  
  543.     end;
  544.  
  545.     Close(OutFile);
  546.     if EdFileerror then
  547.       ;
  548.   end;                       {EdFileWrite}
  549.  
  550.   procedure EdLogDrive(NewPath : Filepath);
  551.     {-Select a new drive or directory}
  552.  
  553.   begin                      {EdLogdrive}
  554.     if Abortcmd or EdStringEmpty(NewPath) then
  555.       Exit;
  556.     if (Length(NewPath) > 1)
  557.     and (NewPath[Length(NewPath)] = '\')
  558.     and (NewPath[Pred(Length(NewPath))] <> ':') then
  559.       {Remove trailing backslash}
  560.       Delete(NewPath, Length(NewPath), 1);
  561.     ChDir(NewPath);
  562.     if EdINT24Result <> 0 then
  563.       {Invalid directory}
  564.       EdErrormsg(122);
  565.   end;                       {EdLogdrive}
  566.  
  567.   {***}
  568.   function CheckCurwinModified : boolean;
  569.     {-See if current window is modified, and if so, prompt to save it}
  570.   var
  571.     SaveFirst : Boolean;
  572.  
  573.   begin                        {CheckCurwinModified}
  574.     CheckCurwinModified := false;
  575.     with Curwin^ do
  576.       if Modified then
  577.         {Prompt user to avoid loss of edits}
  578.         if EdLinkedWindow(Curwin) then
  579.           {Assure other modified flags are set, but abandon this window}
  580.           EdCloneModifiedFlags
  581.         else begin
  582.           {See if user wants to save it before quitting window}
  583.           SaveFirst := EdYesNo(Blank+EdEndOfPath(Filename)+EdGetMessage(306));
  584.           if Abortcmd then
  585.             Exit;
  586.           if SaveFirst then
  587.             EdFileWrite(Filename, False);
  588.         end;
  589.     CheckCurwinModified := true;
  590.   end;                         {CheckCurwinModified}
  591.  
  592.   procedure EdAbandonFile(ExitEditor : Boolean);
  593.     {-Close file without saving}
  594.  
  595.   begin                      {EdAbandonFile}
  596.     if WindowCount >= 1 then
  597.       {If current window is modified, prompt to save it}
  598.       if not CheckCurwinModified then
  599.         {Exit if AbortCmd or <Esc> was entered}
  600.         exit;
  601.  
  602.     {Clearing the text stream from memory takes a little while}
  603.     EdWait;
  604.     EdShutWindow(ExitEditor);
  605.   end;                       {EdAbandonFile}
  606.  
  607.   {***}
  608.   function EdGetFileName(Prompt, DefExt : VarString; Attr : Byte;
  609.                          var LastFname : Filepath) : Filepath;
  610.     {-Return a file name to use}
  611.   var
  612.     Fname : Filepath;
  613.  
  614.   begin                      {EdGetFileName}
  615.     EdGetFileName := '';
  616.  
  617.     {Initialize default value}
  618.     Abortcmd := False;
  619.     Fname := LastFname;
  620.     EdAskfor(Prompt, 1, 1, 66, Fname);
  621.     if Abortcmd or EdStringEmpty(Fname) then
  622.       Exit;
  623.     EdCleanFileName(Fname);
  624.     if Attr = 0 then
  625.       EdDefaultExtension(DefExt, Fname);
  626.     {Store response for later use}
  627.     LastFname := Fname;
  628.  
  629.     EdGetFileName := Fname;
  630.   end;                       {EdGetfilename}
  631.  
  632.   {***}
  633.   procedure EdReadFile(Fname : Filepath);
  634.     {-Check and open a text file for editing}
  635.   var
  636.     Code : Word;
  637.     F : file;
  638.  
  639.     function EdWindowLinked(Fname : Filepath) : Boolean;
  640.       {-Return true if and when window has been linked to another window}
  641.     var
  642.       IsLinked : Boolean;
  643.       Wthis, Wnext : Integer;
  644.       W : PwinDesc;
  645.  
  646.       procedure EdWindowLink(Wto : Byte; Wfrom : Byte);
  647.         {-Link one window to another}
  648.       var
  649.         Pto, Pfrom : PwinDesc;
  650.  
  651.       begin                  {EdWindowLink}
  652.  
  653.         Pto := EdFindWindesc(Wto);
  654.         Pfrom := EdFindWindesc(Wfrom);
  655.  
  656.         with Pfrom^ do begin
  657.  
  658.           {Clean out source window's text if no other windows point to it}
  659.           if not(EdLinkedWindow(Pfrom)) then
  660.             EdDeleteAllText(Pfrom);
  661.  
  662.           {Match streams}
  663.           Stream := Pto^.Stream;
  664.  
  665.           {Now equate the two}
  666.           Filename := Pto^.Filename;
  667.           TopLine := Pto^.TopLine;
  668.           CurLine := Pto^.TopLine;
  669.           Lmargin := Pto^.Lmargin;
  670.           Wmargin := Pto^.Wmargin;
  671.           Rmargin := Pto^.Rmargin;
  672.           Modified := Pto^.Modified;
  673.           LeftEdge := Pto^.LeftEdge;
  674.           LineNo := 1;
  675.           ColNo := 1;
  676.         end;
  677.  
  678.       end;                   {EdWindowLink}
  679.  
  680.     begin                    {EdWindowLinked}
  681.       IsLinked := False;
  682.       {If more than one window, then see if we should link to other window}
  683.       if (Fname <> NoFile) and (WindowCount > 0) then begin
  684.         {Get number of this window}
  685.         Wthis := EdWindowNumber;
  686.         Wnext := Wthis;
  687.         W := CurWin;
  688.         repeat
  689.           EdFwdPtr(W);
  690.           Inc(Wnext);
  691.           if Fname = W^.Filename then begin
  692.             IsLinked := True;
  693.             EdWindowLink(Wnext, Wthis);
  694.           end;
  695.         until IsLinked or (W = CurWin);
  696.       end;
  697.       EdWindowLinked := IsLinked;
  698.     end;                     {EdWindowLinked}
  699.  
  700.   begin                      {EdReadfile}
  701.  
  702.     if Abortcmd then begin
  703.       GotError := True;
  704.       Exit;
  705.     end;
  706.  
  707.     if EdStringEmpty(Fname) then
  708.       {Support editing of as yet unnamed files}
  709.       Fname := NoFile
  710.     else if EdIsDevice(Fname) then begin
  711.       {Can't read from a device}
  712.       EdErrormsg(34);
  713.       Exit;
  714.     end;
  715.  
  716.     {Link text stream to existing window if appropriate}
  717.     if not(EdWindowLinked(Fname)) then begin
  718.  
  719.       if Fname <> NoFile then begin
  720.         {Try to open existing file}
  721.         Assign(F, Fname);
  722.         Reset(F);
  723.         Code := EdINT24Result;
  724.         if hi(Code) <> 0 then begin
  725.           {Drive not ready}
  726.           EdErrormsg(128);
  727.           Exit;
  728.         end;
  729.       end else
  730.         Code := 1;
  731.  
  732.       CurWin^.Filename := Fname;
  733.  
  734.       if Code <> 0 then begin
  735.  
  736.         if Fname <> NoFile then begin
  737.           {File was not found. See if illegal name, or just new file}
  738.           Rewrite(F);
  739.           if EdINT24Result <> 0 then begin
  740.             {Illegal file name}
  741.             EdErrormsg(5);
  742.             Exit;
  743.           end;
  744.           Close(F);
  745.           Erase(F);
  746.         end;
  747.  
  748.         {New file}
  749.         EdZapPromptLine;
  750.         EdAppPromptLine(EdGetMessage(301));
  751.         CurWin^.Modified := False;
  752.         IntrFlag := NoInterr;
  753.         EdUpdateScreen;
  754.         EdInterruptibleDelay(1500);
  755.  
  756.       end else begin
  757.  
  758.         {Update the screen while the file is read}
  759.         IntrFlag := NoInterr;
  760.         EdUpdateScreen;
  761.         {Read in existing file}
  762.         Close(F);
  763.         EdReadtextfile(Fname, False);
  764.  
  765.       end;
  766.  
  767.     end;
  768.     EdZapPromptLine;
  769.     UpdateScreen := True;
  770.     UpdateCursor := True;
  771.  
  772.   end;                       {EdReadfile}
  773.  
  774.   procedure EdPromptWriteBlock;
  775.     {-Prompt for and write a block to a file}
  776.   var
  777.     Fname : Filepath;
  778.     Exists : Boolean;
  779.  
  780.     {***}
  781.     procedure EdWriteBlock(Fname : Filepath);
  782.       {-Write marked block to file}
  783.     var
  784.       OutFile : file;
  785.       Stop, Start, Len : Integer;
  786.       P : PlineDesc;
  787.       BufPos, BufSiz : Integer;
  788.  
  789.     begin                    {EdWriteBlock}
  790.  
  791.       {Append wait signal to command line}
  792.       EdWait;
  793.  
  794.       Assign(OutFile, Fname);
  795.       Rewrite(OutFile, 1);
  796.       if EdFileerror then
  797.         Exit;
  798.  
  799.       P := Blockfrom.Line;
  800.  
  801.       BufPos := 0;
  802.       BufSiz := Bufsize-Length(EolMark);
  803.  
  804.       repeat
  805.  
  806.         {Enable typeahead and abort during writing}
  807.         EdBreathe;
  808.  
  809.         if P = Blockfrom.Line then
  810.           Start := Blockfrom.Col
  811.         else
  812.           Start := 1;
  813.  
  814.         if P = Blockto.Line then
  815.           Stop := Pred(Blockto.Col)
  816.         else
  817.           Stop := EdTextLength(P);
  818.  
  819.         Len := Succ(Stop-Start);
  820.  
  821.         if BufPos+Len > BufSiz then begin
  822.           {Flush write buffer}
  823.           EdBlockWrite(OutFile, WorkBuf[1], BufPos);
  824.           BufPos := 0;
  825.         end;
  826.  
  827.         if Len > 0 then begin
  828.           Move(P^.Txt^[Start], WorkBuf[Succ(BufPos)], Len);
  829.           Inc(BufPos, Len);
  830.         end;
  831.  
  832.         if P = Blockto.Line then
  833.           EdSetPtrNil(P)
  834.         else begin
  835.           EdFwdPtr(P);
  836.           Move(EolMark[1], WorkBuf[Succ(BufPos)], Length(EolMark));
  837.           Inc(BufPos, Length(EolMark));
  838.         end;
  839.  
  840.       until Abortcmd or GotError or EdPtrIsNil(P);
  841.  
  842.       if EdPtrIsNil(P) and (BufPos <> 0) then
  843.         {Flush the final chunk}
  844.         EdBlockWrite(OutFile, WorkBuf[1], BufPos);
  845.  
  846.       if EdPtrIsNil(P) and not(EdIsDevice(Fname)) then
  847.         {Write EOF marker}
  848.         if not(EdStringEmpty(EofWrite)) then
  849.           EdBlockWrite(OutFile, EofWrite[1], Length(EofWrite));
  850.  
  851.       Close(OutFile);
  852.       if EdFileerror then
  853.         ;
  854.  
  855.     end;                     {EdWriteBlock}
  856.  
  857.   begin                      {EdPromptWriteBlock}
  858.     {Make sure a block is marked}
  859.     if EdNoBlock then begin
  860.       EdErrormsg(26);
  861.       Exit;
  862.     end;
  863.  
  864.     {Get a file name to write to}
  865.     Fname := EdGetFileName(EdGetMessage(310), DefExtension, 0, LastBlockWrite);
  866.     if Abortcmd or EdStringEmpty(Fname) then
  867.       Exit;
  868.  
  869.     {See if file already exists}
  870.     Exists := EdExistFile(Fname);
  871.     if GotError then
  872.       Exit;
  873.  
  874.     if Exists then
  875.       {Prompt to overwrite}
  876.       if not(EdYesNo(EdGetMessage(319))) then
  877.         Exit;
  878.  
  879.     {Write the block}
  880.     EdWriteBlock(Fname);
  881.  
  882.   end;                       {EdPromptWriteBlock}
  883.  
  884. end.
  885.