home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / perqb / pq2fil.pas < prev    next >
Pascal/Delphi Source File  |  2020-01-01  |  36KB  |  1,082 lines

  1. module KermitFile;
  2.  
  3.  
  4. { Abstract: 
  5. {   This module implements a 'KermitFile' abstract datatype.
  6. {
  7. {   A 'KermitFile' consists of two sets of files, with one-to-one
  8. {   mapping between the two.  The sets of files and the 
  9. {   mapping are defined by two patterns, SourcePat and DestPat.
  10. {   SourcePat defines the name space of the source files (all files
  11. {   on local or remote machine that matches the pattern).  DestPat then
  12. {   gives the translation into the name space of the destination files.
  13. {
  14. {   The routines SetReadFile and SetWriteFile defines the name spaces,
  15. {   when the source file is on the Perq and on the remote machine, 
  16. {   respectively.  Then NextReadFile and NextWriteFile will step 
  17. {   through all files in the name spaces.
  18. {
  19. {   When reading, FillBuffer will read one data packet from the file. 
  20. {   At end-of-file, a EOF (Z) packet will be generated instead of a
  21. {   data packet.  EndFile may always be called to test for an end-of-file
  22. {   condition.  No special termination will need to be done when a
  23. {   entire file group is transferred, calling NextReadFile iteratively
  24. {   until it returns FALSE (no next file).
  25. {
  26. {   When writing, EmptyBuffer will write one data packet to the file.
  27. {   To keep the file, call KeepFile after all data has been written; 
  28. {   otherwise DiscardFile may be called at any time.  In that case, 
  29. {   all file operations after the last NextWriteFile will be undone.
  30. {
  31. {   If unsure of the state, FileIdle will always reset the module to the
  32. {   idle state.
  33. {         
  34. {============================} EXPORTS {======================================}
  35.  
  36.     imports KermitGlobals from KermitGlobals;
  37.  
  38. CONST
  39.     TempName = '$Kermit$Temp$';
  40.  
  41. TYPE
  42.     Byte8        =   0..255;
  43.  
  44.     Byte8File    =   packed file of Byte8;
  45.  
  46.     FileErrs   =   (    { Fatal errors - aborts one file  }
  47.                           { or the whole batch }
  48.   
  49.                        FReadErr,    { Disk read error }
  50.                        FWriteErr,   { Disk write error }
  51.                        FNoSpace,    { No more space to write file into }
  52.                        FNoReadPriv, { Not read access to file }
  53.                        FNoWritePriv,{ Not write access to file }
  54.                        FCantOpen,   { Cannot open file }
  55.                        FNotRenamed, { Could not rename }
  56.                        FNoFile,     { No file of this name }
  57.                        FBadNames,   { Bad filenames or wildcard matching }
  58.                        FInternalErr,{ Internal error (program logic) } 
  59.  
  60.                        FNoError,    { Idle code }
  61.  
  62.                           { Informational } 
  63.                        FRenamed,    { Renamed files when FileWarning on }
  64.                        FEndDir,     { No more matching files when wildcards }
  65.                        FAtEof);     { File is already at EOF }
  66.  
  67. {----------------------------------------------------------------------------}
  68.  
  69.  
  70. { -- File Open/Close routines: (Pascal files)
  71.     These routines are not to be used for the transferred files }
  72.  
  73.     function OpenRead (     VAR ReadFile    :   Byte8File ;
  74.                             VAR FileName    :   FNameType ) : FileErrs;
  75.  
  76.     function OpenWrite (    VAR WriteFile   :   Byte8File ;
  77.                             VAR FileName    :   FNameType ) : FileErrs;
  78.  
  79.     function CloseFile(     VAR FileToClose : Byte8File ) : FileErrs;
  80.  
  81. { -- Filename manipulation routines }
  82.  
  83.     procedure ParseArgs( VAR Args, Arg1, Arg2 : String );
  84.  
  85.     procedure ReadFName   ( Var FileName    :   FNameType   );
  86.  
  87.     procedure PutFileName ( VAR FileN       :   FNameType;
  88.                             VAR Pack        :   Packet      );
  89.  
  90.     procedure GetFileName ( VAR FileN       :   FNameType;
  91.                             VAR Pack        :   Packet      );
  92.  
  93. { -- KermitFile manipulation }
  94.  
  95.     function SetReadFile( VAR SourcePat, DestPat : String ) : FileErrs;
  96.  
  97.     function NextReadFile( VAR FileName : String ) : FileErrs;
  98.  
  99.     function EndFile : Boolean;
  100.  
  101.     function SetWriteFile( VAR SourcePat, DestPat : String ) : FileErrs;
  102.    
  103.     function NextWriteFile( VAR FileName : String ) : FileErrs;
  104.  
  105.     procedure WriteScreen;
  106.  
  107.     function FillBuffer ( Var Data : Packet ) : FileErrs;
  108.  
  109.     function EmptyBuffer( Var Data : Packet ) : FileErrs;
  110.  
  111.     function FileIdle : FileErrs;
  112.  
  113.     function DiscardFile : FileErrs;
  114.  
  115.     function KeepFile : FileErrs;
  116.     
  117.     procedure FileAbort;
  118.  
  119. { -- Error message generator }
  120.  
  121.     procedure  FileError (       FileName : FNameType; 
  122.                                  ErrCode  : FileErrs;
  123.                              Var Message  : String );
  124.  
  125.     procedure InitFile;
  126.  
  127. {============================} PRIVATE {======================================}
  128.  
  129.     imports KermitParameters from KermitParameters;
  130.     imports FileSystem from FileSystem;
  131.     imports FileUtils from FileUtils;
  132.     imports CmdParse from CmdParse;
  133.     imports Perq_String from Perq_String;
  134.     imports PMatch from PMatch;
  135.     imports Stream from Stream;
  136.  
  137. {----------------------------------------------------------------------------}
  138.  
  139. CONST
  140.  
  141.     NoFile      = '?No such file to open: ';
  142.     NoSetRead   = '?Internal error: NextReadFile without SetReadFile';
  143.     NoSetWrite  = '?Internal error: NextWriteFile without SetWriteFile';
  144.     NotReading  = '?Internal error: FillBuffer when not reading';
  145.     NotWriting  = '?Internal error: EmptyBuffer when not writing';
  146.  
  147. {----------------------------------------------------------------------------}
  148.  
  149. TYPE
  150.  
  151.     ModuleState = ( Idling, Writing, WritingScreen, Reading );
  152.  
  153. {----------------------------------------------------------------------------}
  154.  
  155. VAR
  156.     RemoteFName, LocalFName : FNameType;  { Rem. & loc. names of current file }
  157.  
  158.     SourcePat, DestPat :  String; { Matching patterns of file names }
  159.     ScanPtr     :   ptrScanRecord;
  160.     DataFile    :   Byte8File;    { File to receive to/send from }
  161.     FileIsOpen  :   Boolean;      { True if DataFile is open }
  162.     FileState   :   ModuleState;  { What we're doing now }
  163.     FileNoPatt  :   Boolean;      { Wildcard filename }
  164.  
  165. {----------------------------------------------------------------------------}
  166.  
  167. procedure InitFile;
  168. begin
  169.     FileIsOpen := FALSE;
  170.     FileState := Idling;
  171. end;
  172.  
  173. {----------------------------------------------------------------------------}
  174.  
  175. procedure ConvLower( VAR S : PString );
  176. var i : integer;
  177. begin
  178.     for i := 1 to length( s ) do
  179.         if S[i] in ['a'..'z'] then
  180.             S[i] := chr( Ord(S[i]) - (ord('a')-ord('A')) );
  181. end;
  182.     
  183. {----------------------------------------------------------------------------}
  184.  
  185. function ReleaseFName( VAR FileName : FNameType ) : FileErrs;
  186. { -- Assumes a file of name FileName exists.  Free this name by
  187.         renaming existing files.  }
  188. var Renamed : FNameType;
  189.     B1, B2  : Integer;
  190.     Dummy   : FileErrs;
  191. begin
  192.     Renamed := FileName;
  193.     AppendChar( Renamed, '$' );
  194.     if 0<>FSLocalLookUp( Renamed, B1, B2 ) then
  195.         Dummy := ReleaseFName( Renamed );
  196.     FSRename( FileName, Renamed );
  197.     ReleaseFName := FRenamed;
  198. end;
  199.  
  200. {----------------------------------------------------------------------------}
  201.  
  202. procedure ReadFName(    Var FileName    :   FNameType );
  203. {  Abstract :  Reads filename from terminal (standard input).
  204.                 Skips blanks before filename.
  205.                 Skips over rest of line until EOLN.
  206.                 No check of correct syntax is at present
  207.                 performed.  }
  208. var first : char;
  209.     Fstr  : string[1];
  210. begin
  211.     read( first );  { read at least one character }
  212.     while (not EOLN) and (first=' ') do
  213.         read( first );
  214.     read( FileName );
  215.     adjust( Fstr, 1);
  216.     FStr[1] := first;
  217.     if first<>' ' then
  218.         FileName := Concat( FStr, FileName );
  219. end;
  220.  
  221. {----------------------------------------------------------------------------}
  222.  
  223. function OpenRead (     VAR ReadFile    :   Byte8File ;
  224.                         VAR FileName    :   FNameType ) : FileErrs;
  225. { Abstract : Opens ReadFile for Read 
  226.               Does a RESET of the file
  227.  
  228.               Returns FNoError if Open was successful,
  229.                    i.e. file existed and read access of file was granted.
  230.               Returns FNoFile if file did not exist.
  231.  }
  232.  
  233. var     Ostat   : FileErrs;
  234.         B1,B2   : integer;
  235. begin
  236.     if 0=FSLookUp( FileName, B1, B2 ) then
  237.        Ostat := FNoFile
  238.     else begin
  239.         Ostat := FNoError;
  240.         reset( ReadFile, FileName );
  241.     end;
  242.     OpenRead := Ostat;
  243. end;
  244.  
  245. {----------------------------------------------------------------------------}
  246.  
  247. function OpenWrite (    VAR WriteFile   :   Byte8File ;
  248.                         VAR FileName    :   FNameType ) : FileErrs;
  249. { Abstract:  Opens WriteFile for Write
  250.               Does a REWRITE of the file
  251.               Returns  FNoFile: If Open was NOT successful.
  252.                        FNoError: If Open was immediately successful, i.e.
  253.                          new file or write access granted to existing
  254.                          file, provided FileWarning OFF.
  255.                        FRenamed: If Open was successful after renaming files,
  256.                          i.e. Kermit was able to create the new file       }
  257.  
  258. const   MaxTries = 5;
  259.  
  260. var     B1, B2    : integer;
  261.  
  262. begin
  263.     if NOT FileWarning then begin  { don't worry about existing file }
  264.         rewrite( WriteFile, FileName );
  265.         OpenWrite := FNoError;
  266.  
  267.     end else    { we have to check if file already exists }
  268.         if 0 = FSLocalLookUp( FileName, B1, B2 ) then begin
  269.             rewrite( WriteFile, FileName );
  270.             OpenWrite := FNoError;
  271.         end 
  272.         else begin
  273.             if ReleaseFName( FileName )=FRenamed then begin
  274.                 Rewrite( WriteFile, Filename );
  275.                 OpenWrite := FRenamed;
  276.             end else
  277.                 OpenWrite := FNoWritePriv;
  278.         end;
  279. end;
  280.  
  281. {----------------------------------------------------------------------------}
  282.  
  283.  
  284. function CloseFile( VAR FileToClose     : Byte8File ) : FileErrs;
  285. {  Abstract:   Do any actions necessary when closing file }
  286. begin
  287.     Close( FileToClose );
  288.     CloseFile := FNoError;
  289. end;
  290.  
  291. {----------------------------------------------------------------------------}
  292.  
  293. function KeepFile : FileErrs;
  294. { -- Close a file after writing, keep file }
  295. var B1, B2 : Integer;
  296.     OldWin : WinType;
  297.     RetCode: FileErrs;
  298.  
  299.     handler RenToExist( FileName : PathName );
  300.     begin
  301.         raise RenError( 'Attempted rename to existing name:', FileName );
  302.     end;
  303.      
  304.     handler RenError( Msg : String; FileName : PathName );
  305.     begin
  306.         writeln( '**', Msg, FileName );
  307.         FileAbort;
  308.         KeepFile := FNotRenamed;
  309.         Exit( KeepFile );
  310.     end;
  311.  
  312. begin
  313.     CurrentWindow( OldWin );
  314.     SwitchWindow( MainWindow );
  315.     RetCode := FNoError;
  316.  
  317.     if (FileState=Writing) and FileIsOpen then begin
  318.         Close( DataFile );
  319.  
  320.         if 0 <> FSLocalLookUp( LocalFName, B1, B2 ) then 
  321.             if FileWarning then
  322.                 RetCode := ReleaseFName( LocalFName )
  323.             else
  324.                 FSDelete( LocalFName );
  325.  
  326.         FSRename( TempName, LocalFName );
  327.         writeln( 'Completed:     ', RemoteFName, ' --> ', LocalFName );
  328.  
  329.         FileIsOpen := FALSE;
  330.     end;
  331.  
  332.     SwitchWindow( OldWin );
  333.     KeepFile := RetCode;
  334. end;
  335.               
  336. {----------------------------------------------------------------------------}
  337.  
  338. function DiscardFile : FileErrs;
  339. { -- Close a file after writing, discard file }
  340. VAR OldWin : WinType;
  341. begin
  342.     CurrentWindow( OldWin );
  343.     SwitchWindow( MainWindow );
  344.     DiscardFile := FNoError;
  345.  
  346.     if (FileState=Writing) and FileIsOpen then begin
  347.         Close( DataFile );
  348.         FSDelete( TempName );
  349.         FileIsOpen := FALSE;
  350.         writeln( '**Discarded**: ', RemoteFName, ' --> ', LocalFName );
  351.     end;
  352.  
  353.     SwitchWindow( OldWin );
  354. end;
  355.  
  356. {----------------------------------------------------------------------------}
  357.  
  358. procedure FileAbort;
  359. VAR OldWin : WinType;
  360. begin
  361.     CurrentWindow( OldWin );
  362.     SwitchWindow( MainWindow );
  363.     write( '**Aborted**:   ' );
  364.     if Reading=FileState then begin
  365.         writeln( LocalFName, ' --> ', RemoteFName );
  366.     end else if Writing=FileState then begin
  367.         writeln( RemoteFName, ' --> ', LocalFName );
  368.     end;
  369.     SwitchWindow( OldWin );
  370. end;
  371.  
  372. {----------------------------------------------------------------------------}
  373.  
  374. procedure CloseReading;
  375. var OldWin : WinType;
  376. begin
  377.     if EOF(DataFile) then begin
  378.         CurrentWindow( OldWin );
  379.         SwitchWindow( MainWindow );
  380.         writeln( 'Completed:     ', LocalFName, 
  381.                              ' --> ', RemoteFName );
  382.         SwitchWindow( OldWin );
  383.     end else
  384.         FileAbort;
  385.     Close( DataFile );
  386. end;    { CloseReading }
  387.  
  388. {----------------------------------------------------------------------------}
  389.  
  390. function CheckPatterns( VAR S, D : String ) : FileErrs;
  391. { -- Verify that patterns S and D are valid }
  392. VAR InS, OutS       : String;
  393.     Dummy           : Boolean;
  394.     
  395.     handler BadPatterns;
  396.     begin
  397.         CheckPatterns := FBadNames;
  398.         exit( CheckPatterns );
  399.     end;
  400.  
  401. begin
  402.  
  403.     InS := '';
  404.     OutS := '';
  405.     CheckPatterns := FNoError;
  406.     if IsPattern( S ) then begin
  407.         FileNoPatt := FALSE;
  408.         dummy := PattMap ( InS, S, D, OutS, Translate=TransUpper );
  409.     end else
  410.         FileNoPatt := TRUE;
  411. end;
  412.  
  413. {----------------------------------------------------------------------------}
  414.  
  415. procedure ParseArgs( VAR Args, Arg1, Arg2 : String );
  416. var DelPos : integer;
  417.     
  418.     procedure LeadingBlanks( VAR Arg : String );
  419.     var i, l : integer;
  420.     begin
  421.         i := 1;
  422.         L := Length(Arg);
  423.         if L<>0 then
  424.             while (Arg[i]=' ') and (i<L) do i := i+1;
  425.  
  426.         if i>=L then   { All spaces }
  427.             Arg := ''
  428.         else begin
  429.             if Arg[i]<>' ' then
  430.                i := i-1;
  431.             Delete( Arg, 1, i );
  432.         end;
  433.     end;
  434.  
  435. begin
  436.     LeadingBlanks( Args );
  437.     DelPos := PosC( Args, ' ');
  438.     if DelPos=0 then
  439.         DelPos := PosC( Args, ',' );
  440.  
  441.     if DelPos=0 then begin
  442.         Arg1 := Args;
  443.         Arg2 := '';
  444.     end else begin
  445.         Arg1 := SubStr( Args, 1, DelPos -1 );
  446.  
  447.         Delete( Args, 1, DelPos );
  448.         LeadingBlanks( Args );
  449.         DelPos := PosC( Args, ' ' );
  450.         if DelPos = 0 then
  451.             DelPos := PosC( Args, ',' );
  452.         if DelPos <> 0 then
  453.             Args := SubStr( Args, 1, DelPos -1 );
  454.         Arg2 := Args;
  455.     end;
  456. end;
  457.  
  458. {----------------------------------------------------------------------------}
  459.  
  460. function SetPatterns( VAR S, D : String ) : FileErrs;
  461. { -- Set the module local pattern names }
  462. begin
  463.     if (S='') and (D='') then begin
  464.         SourcePat := '';
  465.         DestPat := '';
  466.     end else begin 
  467.         if S = '' then 
  468.             SourcePat := D
  469.         else
  470.             SourcePat := S;
  471.     
  472.         if D = '' then
  473.             DestPat := S
  474.         else
  475.             DestPat := D;
  476.     end;
  477.  
  478.     SetPatterns := CheckPatterns( SourcePat, DestPat );
  479. end;
  480.  
  481. {----------------------------------------------------------------------------}
  482.  
  483. function SetReadFile( VAR SourcePat, DestPat : String ) : FileErrs;
  484. { -- Setup for read of multiple files.  S contains Perq filename  }
  485. {    to match, D is name to transmit file under.                  }
  486. var Dummy : FileErrs;
  487. begin
  488.     if FileIsOpen then 
  489.         Dummy := FileIdle;
  490.     
  491.     SetReadFile := SetPatterns( SourcePat, DestPat );
  492.  
  493.     new( ScanPtr );
  494.     ScanPtr^.InitialCall := TRUE;
  495.     ScanPtr^.DirName := FSDirPrefix;
  496.  
  497.     FileState := Reading;
  498.     FileIsOpen := False;
  499. end;
  500.  
  501. {----------------------------------------------------------------------------}
  502.  
  503. procedure ConvExt( VAR FileN : String );
  504. {  Abstract: Converts a filename to external form }
  505. var  FD, LD, PD, TI, L, T : Integer;
  506. begin
  507.         { Pathname is always stripped }
  508.     L := RevPosC( FileN, '>' );
  509.     if (Length( FileN )-L) > MaxString then
  510.         Adjust( FileN, MaxString+L );
  511.     FileN := SubStr( FileN, L+1, Length( FileN )-L );
  512.  
  513.     if Nord then begin                  { Apply NORD transformation } 
  514.  
  515.         LD := RevPosC( FileN, '.' );    { find last dot of file name  }
  516.         FD := PosC   ( FileN, '.' );    { find first dot of file name }
  517.         while LD<>FD do begin           { substitute until last dot   }
  518.             FileN[FD] := '-';           { if no dots: LD=FD=0         }
  519.             FD := PosC( FileN, '.' );   { find next dot               }
  520.         end;
  521.  
  522.     end else
  523.  
  524.         if NumTrunc>0 then begin            { Do TRUNCATE transformation  }
  525.             LD := RevPosC( FileN, '.' );
  526.  
  527.             if (LD=0) or (NumTrunc=1) then  { ONE part, truncate according}
  528.             begin                           { to first entry of list      }
  529.                 T := TruncList[1];
  530.                 if Length(FileN) < T then   { See where to chop off name: }
  531.                     T := Length(FileN);     { Minimum of length, trunc    }
  532.                 if LD<>0 then begin         { and position of dot         }
  533.                     FD := PosC(FileN,'.')-1;{ Guaranteed to find a dot    }
  534.                     if FD<T then 
  535.                         T := FD;
  536.                 end;
  537.                 Adjust( FileN, T );
  538.             end else begin
  539.  
  540.                 L := Length( FileN )-LD;        { length of last part }
  541.                 if L>TruncList[NumTrunc] then   { truncate last part  }
  542.                     Delete( FileN, LD+TruncList[NumTrunc]+1,
  543.                                    L-TruncList[NumTrunc] );
  544.  
  545.                 TI := 1;
  546.                 PD := 0;
  547.                 FD := PosC( FileN, '.' );   { where does next part end??  }
  548.  
  549.                 while (FD<>0) do begin      { Move it until no next part  }
  550.  
  551.                     if TI>=NumTrunc then    { Part with no matching entry }
  552.                         T := -1             { Delete everything, dot too  }
  553.                     else
  554.                         T := TruncList[TI]; { Keep as much as list tell   }
  555.  
  556.                     TI := TI + 1;
  557.                     L := FD-PD-1-T;         { Num. chars to delete        }
  558.  
  559.                     if L>0 then begin
  560.                         Delete( FileN, PD+T+2, L );
  561.                         LD := LD - L;       { Last dot has been moved     }
  562.                         PD := FD - L;       { So has the delimiting one - }
  563.                     end else
  564.                         PD := FD;
  565.  
  566.                     FileN[PD] := '>';       { don't find it again         }
  567.                     FD := PosC( FileN, '.' );
  568.                 end;
  569.  
  570.                 FD := PosC( FileN, '>' );
  571.                 while FD<>0 do begin        { Restore dots }
  572.                     FileN[FD] := '.';
  573.                     FD := PosC( FileN, '>' );
  574.                 end;
  575.      
  576.             end;  { Two parts }
  577.         end;    { TRUNCATE }
  578.  
  579.     if Nord or (Translate=TransUpper) then
  580.         ConvUpper( FileN )
  581.     else if (Translate=TransLower) then
  582.         ConvLower( FileN );
  583.  
  584. end;
  585.  
  586. {----------------------------------------------------------------------------}
  587.  
  588. function NextReadFile( VAR FileName : String ) : FileErrs;
  589. { -- Open next file }
  590. var id                  : FileId;
  591.     NewFile, Matched    : Boolean;
  592.     B1, B2              : integer;
  593.  
  594.     handler ResetError( FName : PathName );
  595.     begin
  596.         NextReadFile := FCantOpen;
  597.         FileName := FName;
  598.         exit( NextReadFile );
  599.     end;
  600.     
  601. begin
  602.     if FileState<>Reading then begin
  603.         NextReadFile := FInternalErr;
  604.         Writeln( NoSetRead );
  605.     end else begin
  606.  
  607.         if FileNoPatt then begin
  608.             if not FileIsOpen then begin   { First time }
  609.                 LocalFName := SourcePat;
  610.                 NewFile := 0 <> FSLocalLookUp( SourcePat, B1, B2 );
  611.                 Matched := True;
  612.                 if Not NewFile then begin
  613.                     NextReadFile := FNoFile;
  614.                 end else begin
  615.                     NextReadFile := FNoError;
  616.                     if DestPat<>'' then
  617.                         RemoteFName := DestPat
  618.                     else
  619.                         RemoteFName := SourcePat;
  620.                 end;    
  621.             end else begin
  622.                 NextReadFile := FEndDir;
  623.                 NewFile := False;
  624.                 CloseReading;
  625.                 FileIsOpen := False;
  626.             end;
  627.  
  628.         end else begin
  629.  
  630.             if FileIsOpen then
  631.                 CloseReading;
  632.               
  633.             repeat
  634.                 NewFile := FSScan( ScanPtr, LocalFName, ID );
  635.                 if NewFile then
  636.                     Matched := 
  637.                         PattMap( LocalFName, SourcePat, DestPat, RemoteFName, 
  638.                                  Translate=TransUpper );
  639.             until Matched or ( NOT NewFile );
  640.  
  641.             if not NewFile then 
  642.                 NextReadFile := FEndDir;
  643.             
  644.         end;
  645.  
  646.         if NOT NewFile then begin
  647.  
  648.             Dispose( ScanPtr );
  649.             FileState := Idling;
  650.             FileIsOpen := False;
  651.             FileName := SourcePat;   { To be able to report name in err.mess.}
  652.  
  653.         end else begin
  654.  
  655.             NextReadFile := FNoError;
  656.             ConvExt( RemoteFName );
  657.             ShowSRFile( True, RemoteFName, LocalFName );
  658.             FileIsOpen := TRUE;
  659.             FileName := RemoteFName;    { To put into FileHeader packet }
  660.             Reset( DataFile, LocalFName );
  661.  
  662.         end;
  663.  
  664.     end;
  665. end;
  666.  
  667. {----------------------------------------------------------------------------}
  668.  
  669. function EndFile : Boolean;
  670. begin
  671.     if (FileState=Reading) and FileIsOpen then
  672.         EndFile := EOF( DataFile )
  673.     else
  674.         EndFile := TRUE;
  675. end;
  676.  
  677. {----------------------------------------------------------------------------}
  678.  
  679. function SetWriteFile( VAR SourcePat, DestPat : String ) : FileErrs;
  680. { -- Setup for write to file }
  681. var Dummy : FileErrs;   
  682. begin
  683.     if FileIsOpen then
  684.         Dummy := FileIdle;
  685.     SetWriteFile := SetPatterns( SourcePat, DestPat );
  686.     FileState := Writing;
  687.     FileIsOpen := False;
  688. end;
  689.  
  690. {----------------------------------------------------------------------------}
  691.  
  692. procedure WriteScreen;
  693. { -- Setup to write to screen instead of file }
  694. var Dummy : FileErrs;   
  695. begin
  696.     if FileIsOpen then
  697.         Dummy := FileIdle;
  698.     FileState := WritingScreen;
  699. end;
  700.  
  701. {----------------------------------------------------------------------------}
  702.  
  703. procedure ConvInt(  VAR FileN : FNameType );
  704. {  Abstract:    Converts a file name 
  705.                 to internal format in FileN, including 
  706.                 any necessary transformations of file name }
  707. var FD              : integer;
  708.     T               : PString; 
  709.     Sep             : char;
  710.     IsSwitch        : boolean;
  711. begin
  712.     { We expect DEC-10, -20, CP/M and MP/M style filenames, <name>.<typ>
  713.        Acceptable to PERQ! }
  714.  
  715.     if Nord then begin
  716.         FD := PosC( FileN, '-' );       { Apply reverse NORD transf. }
  717.         while FD<>0 do begin
  718.             FileN[FD] := '.';
  719.             FD := PosC( FileN, '-' );
  720.         end;
  721.     end;
  722. end;
  723.  
  724. {----------------------------------------------------------------------------}
  725.  
  726. function NextWriteFile( VAR FileName : String ) : FileErrs;
  727. { -- Open next file to write. }
  728. var Matched : boolean;
  729.     RetCode : FileErrs;   
  730. begin
  731.     if FileState<>Writing then begin
  732.         if FileState<>WritingScreen then begin
  733.             Writeln( NoSetWrite );
  734.             RetCode := FInternalErr;
  735.         end;    
  736.     end else begin
  737.         RetCode := FNoError;
  738.  
  739.         if FileIsOpen then
  740.             RetCode := KeepFile;
  741.  
  742.         if RetCode>=FNoError then begin
  743.             
  744.             RemoteFName := FileName;
  745.             ConvInt( FileName );
  746.  
  747.             if FileNoPatt then begin
  748.                 if (SourcePat=FileName) and (DestPat<>'') then
  749.                     LocalFName := DestPat       { Two file names given: }
  750.                 else                            { Rename intended, but only }
  751.                     LocalFName := FileName;     { if equal to the first one }
  752.             end else begin
  753.                 Matched := 
  754.                     PattMap( FileName, SourcePat, DestPat, LocalFName, 
  755.                              Translate=TransUpper );
  756.                 if not Matched then
  757.                     LocalFName := FileName;     { Store with no translation }
  758.             end;
  759.  
  760.             rewrite( DataFile, TempName );
  761.             FileIsOpen := TRUE;
  762.             ShowSRFile( False, RemoteFName, LocalFName );
  763.         end
  764.            { else NextWriteFile should be retried };
  765.  
  766.     end;
  767.     NextWriteFile := RetCode;
  768. end;
  769.  
  770. {----------------------------------------------------------------------------}
  771.  
  772. function FileIdle : FileErrs;
  773. { -- Reset the module to idle state }
  774. var OldWin : WinType;
  775. begin
  776.     FileIdle := FNoError;
  777.     if FileIsOpen then begin
  778.  
  779.         if FileState = Writing then
  780.             FileIdle := DiscardFile
  781.         else if FileState = Reading then
  782.             CloseReading;
  783.     end;
  784.  
  785.     FileIsOpen := False;
  786.     FileState := Idling;
  787. end;
  788.  
  789. {----------------------------------------------------------------------------}
  790.  
  791. function   FillBuffer ( var data : Packet ) : FileErrs;
  792.  
  793. { -- Read a packet from the file }
  794.  
  795. const   PackHead = 4;       { Number of characters in packet header }
  796. var
  797.         NextB                                               : Byte8;
  798.         i, j, RepCnt, NextBSz, Needed                       : integer;
  799.         GoForNext, Quote8, CtrlChar, eofi, WillRepeat       : boolean;
  800.  
  801.     {--------------------------------------------------------------------}
  802.  
  803.     procedure CharInPack;
  804.     begin
  805.         With data do
  806.         begin                           { Put character into the packet }
  807.             if Quote8 then
  808.             begin
  809.                 data[i] := Bit8Quote;           { Quote for 8'th bit    }
  810.                 i := i + 1;
  811.                 NextB := Land ( NextB, 127 );   { Mask 8'th bit         }
  812.             end;                                
  813.             if CtrlChar then
  814.             begin                               { Real control character?}
  815.                 if    ( Land( NextB, 127) < ord ( ' ' ) )
  816.                    or ( Land( NextB, 127) = 127 ) then      { De-       }
  817.                     NextB := ord ( ctl ( chr ( NextB ) ) ); { controlify}
  818.                 data[i] := SendQuote;
  819.                 i := i + 1;
  820.             end;
  821.             data[i] := chr ( NextB );
  822.             i := i + 1;
  823.         end;
  824.     end;
  825.  
  826.     {--------------------------------------------------------------------}
  827.  
  828.     procedure FetchNext;
  829.     begin
  830.         NextB := DataFile^;   { Retreive next character from file buffer. }
  831.                             { How will it have to be quoted?            }
  832.         Quote8 := ( NextB >= 128 ) and NowUse8Quote;
  833.  
  834.         if Parity<>NOKParity then           { Test for quotes with char.}
  835.             NextB := LAnd( NextB, 127 );    { as it will arrive at rcvr.}
  836.  
  837.         CtrlChar := ( Land( NextB,127) < ord ( ' ' )               ) or
  838.                     ( Land( NextB,127) = 127                       ) or
  839.                     (   chr( NextB ) = SendQuote                   ) or
  840.                     ( ( chr( NextB ) = Bit8Quote) and NowUse8Quote ) or
  841.                     ( ( chr( NextB ) = RepFix   ) and NowUseRepFix );
  842.  
  843.         NextBSz := 1;       { How much packet space will it need?       }
  844.         if Quote8 then NextBSz := NextBSz + 1;      { Adjust for the    }
  845.         if CtrlChar then NextBSz := NextBSz + 1;    { quotes!           }
  846.     end;
  847.  
  848.     {--------------------------------------------------------------------}
  849.  
  850.     Procedure PutLookAhead;
  851.     var PutIt : boolean;
  852.     begin
  853.         if not eofi then        { We've decided to use the character in }
  854.             get( DataFile );      { file buffer.   Advance file window so }
  855.         eofi := eof( DataFile );  { we may test against next character.   }
  856.                                 { Remember DataFile^ is undef. if at EOF  }
  857.         if not NowUseRepFix then begin  
  858.             CharInPack;         { Don't use prefixing - assert RepCnt=1 }
  859.             Needed := 0;
  860.         end else begin          { Do we have to put out the lookahead   }
  861.             if eofi or (NextB<>DataFile^) or (RepCnt>=94) then    { char? }
  862.             begin
  863.                 if not WillRepeat then
  864.                     for j := 1 to RepCnt do     { Too few occurrences - }
  865.                         CharInPack              { put it out literally  }
  866.                 else 
  867.                 with Data do begin              { We will gain  -       }
  868.                     Data[i] := RepFix;                  { put prefix,   }
  869.                     Data[i+1] := ToChar(chr(RepCnt));   { RepCnt,       }
  870.                     i := i+2;
  871.                     CharInPack;                 { the character itself  }
  872.                 end;
  873.                 RepCnt := 1;
  874.                 Needed := 0;            { What space we're committed to }
  875.                 WillRepeat := false;    { Not decided to repeat yet!    }
  876.             end
  877.             else 
  878.             begin
  879.                 RepCnt := RepCnt + 1;         { just count occurrences  }
  880.                 if not WillRepeat then
  881.                     if Needed+NextBSz<=2 then       { Committing our-   }
  882.                         Needed := Needed + NextBSz  { selves to use     }
  883.                     else begin                      { more space!       }
  884.                         Needed := NextBSz+2;        { Else: limit has   }
  885.                         WillRepeat := true;         { been reached,     }
  886.                     end;                    { will not need more space. }
  887.             end;
  888.         end;
  889.         
  890.         if eofi then            { No character to go next.              }
  891.             GoForNext := false  { Last one has already been put.        }
  892.         else begin 
  893.             FetchNext;          { Look at the next character, decide    }
  894.                                 { whether it too wil go into packet.    }
  895.             if WillRepeat then  { Next char won't use additional space. }  
  896.                 GoForNext := true
  897.             else                { Is there space for NextB?             }
  898.                 GoForNext := SendPSize >= (i+PackHead+Needed+NextBSz);
  899.         end;
  900.     end;
  901.  
  902.     {--------------------------------------------------------------------}
  903.  
  904. begin
  905.  
  906.     FillBuffer := FNoError;
  907.  
  908.     if (FileState<>Reading) or (Not FileIsOpen) then 
  909.         Writeln( NotReading )
  910.     else               
  911.       with data do begin
  912.         if not eof ( DataFile ) then
  913.         begin
  914.             RepCnt := 1;        { #Times DataFile^ is to be put into packet.}
  915.             i := 1;             { Where will the character go?            }
  916.             adjust( Data, 100 );
  917.             eofi := false; 
  918.             FetchNext;          { Establish lookahead. }
  919.             WillRepeat := false;
  920.             
  921.             repeat              { NOT EOF => At least one character to put}
  922.                 PutLookAhead;
  923.             until not GoForNext;
  924.  
  925.             if (RepCnt>1) then begin        { Don't forget it if last   }
  926.                 for j := 1 to RepCnt do     { char. was repeated.       }
  927.                     CharInPack;             { ASSERT not WillRepeat     }
  928.                 if not eofi then get( DataFile );
  929.             end;
  930.             
  931.                 { Put count field = len of data + 3, i = len of data +1 }
  932.             count := ToChar ( chr ( i + 2 ) );
  933.             ptype := PackToCh( DataPack );
  934.             adjust( Data, i );
  935.         end
  936.         else begin
  937.             count := ToChar ( chr ( 3 ) );
  938.             Ptype := PackToCh( EOFPack );
  939.             FillBuffer := FAtEOF;
  940.         end;    
  941.  
  942.     end;
  943. end;
  944.  
  945. {----------------------------------------------------------------------------}
  946.  
  947. function   EmptyBuffer ( var data  : Packet ) : FileErrs;
  948.  
  949. { -- Write a data packet to file }
  950.  
  951. var     i,j,scr,rep         : integer;
  952.         CtrlChar, Quote8    : boolean;
  953.         ch                  : char;
  954. begin
  955.     EmptyBuffer := FNoError;
  956.     if (FileState<>WritingScreen) and 
  957.       ((FileState<>Writing) or (Not FileIsOpen)) then
  958.         Writeln( NotWriting )
  959.     else begin
  960.         i := 1;
  961.         with data do
  962.  
  963.         while i <= ( ord ( UnChar( count ) ) - 3 ) do
  964.         begin
  965.             ch := data[i];
  966.  
  967.             if NowUseRepFix and ( ch = RepFix ) then begin
  968.                 i := i + 1;
  969.                 ch := data[i];
  970.                 rep := ord( UnChar( ch ) );
  971.                 i := i + 1;
  972.                 ch := data[i];
  973.             end else
  974.                 rep := 1;
  975.  
  976.             Quote8 := NowUse8Quote and ( ch = Bit8Quote );
  977.             if Quote8 then
  978.             begin
  979.                 i := i + 1;
  980.                 ch := data[i];
  981.             end;
  982.  
  983.             CtrlChar := ch = RecQuote;
  984.             if CtrlChar then
  985.             begin
  986.                 i := i + 1;
  987.                 ch := data[i];
  988.                 if ch in CtlMapping then
  989.                     ch := ctl ( ch );
  990.                 { else character is a quoted quote(!) }
  991.             end;
  992.  
  993.             if Quote8 then
  994.                 Scr := Lor ( ord ( ch ) , 128 )
  995.             else
  996.                 Scr := ord ( ch );
  997.  
  998.             if FileState=WritingScreen then
  999.                 for j := 1 to rep do
  1000.                     write( chr( Land(Scr,127) ) )
  1001.             else    
  1002.                 for j := 1 to rep do begin
  1003.                     DataFile^ := Scr;
  1004.                     put( DataFile );
  1005.                 end;
  1006.  
  1007.             i := i + 1;
  1008.         end;
  1009.     end;
  1010. end;
  1011.  
  1012. {----------------------------------------------------------------------------}
  1013.  
  1014. procedure PutFileName(  VAR FileN   :   FNameType;
  1015.                         VAR Pack    :   Packet );
  1016. {  Abstract:   Puts a file name corresponding to internal format
  1017.                 in FileN into a FileHeader packet (Pack). }
  1018. begin
  1019.         Pack.Data := Concat( FileN, ' ' );
  1020.         Pack.Count := ToChar( chr( Length( Pack.Data ) + 2 ) );
  1021. end;
  1022.  
  1023. {----------------------------------------------------------------------------}
  1024.  
  1025. procedure GetFileName(  VAR FileN   :   FNameType;
  1026.                         VAR Pack    :   Packet );
  1027. {  Abstract:   Gets a file name from a FileHeader packet and converts
  1028.                 to internal format in FileN, including 
  1029.                 any necessary transformations of file name }
  1030. var FD              : integer;
  1031.     T               : PString; 
  1032.     Sep             : char;
  1033.     IsSwitch        : boolean;
  1034. begin
  1035.     with Pack do begin
  1036.         if ( Ptype<>PackToCh( FHeadPack ) ) and Debug then begin
  1037.             DbgWrite(' Attempts GetFileName from non-FileHeader packet!');
  1038.             DbgNL;
  1039.         end;
  1040.     { We expect DEC-10, -20, CP/M and MP/M style filenames, <name>.<typ>
  1041.        Acceptable to PERQ! }
  1042.             { remember not to include the checksum byte!! }
  1043.         T := SubStr( Data, 1, Length( Data )-1 );
  1044.             { Also: be sure there are no trailing separator characters }
  1045.         Sep := NextIDString( T, FileN, isSwitch );
  1046.     end;
  1047. end;
  1048.  
  1049. {----------------------------------------------------------------------------}
  1050.  
  1051. procedure  FileError (       FileName : FNameType; ErrCode : FileErrs;
  1052.                          Var Message  : String );
  1053. { -- Generate File error messages }
  1054. begin
  1055.     case ErrCode of
  1056.  
  1057.        FReadErr:    Message := 'Disk read error';
  1058.        FWriteErr:   Message := 'Disk write error';
  1059.        FNoSpace:    Message := 'No more space to write file into';
  1060.        FNoReadPriv: Message := 'Not granted read access to file';
  1061.        FNoWritePriv:Message := 'Not granted write access to file';
  1062.        FCantOpen:   Message := 'Cannot open file';
  1063.        FNotRenamed: Message := 'Could not rename file';
  1064.        FNoFile:     Message := 'No file of this name';
  1065.        FBadNames:   Message := 'Bad filenames or wildcard matching';
  1066.        FInternalErr:Message := 'Kermit internal error';
  1067.  
  1068.        FNoError:    Message := 'File operation successful';
  1069.  
  1070.        FRenamed:    Message := 'Filename conflict, renamed files';
  1071.        FEndDir:     Message := 'No more matching names in directory';
  1072.        FAtEof:      Message := 'At end-of-file';
  1073.     end;
  1074.  
  1075.     Message := Concat( Message, ' for file : ' );
  1076.     if FileName<>'' then
  1077.         Message := Concat( Message, FileName )
  1078.     else
  1079.         Message := Concat( Message, LocalFName );
  1080. end
  1081. .
  1082.