module KermitFile; { Abstract: { This module implements a 'KermitFile' abstract datatype. { { A 'KermitFile' consists of two sets of files, with one-to-one { mapping between the two. The sets of files and the { mapping are defined by two patterns, SourcePat and DestPat. { SourcePat defines the name space of the source files (all files { on local or remote machine that matches the pattern). DestPat then { gives the translation into the name space of the destination files. { { The routines SetReadFile and SetWriteFile defines the name spaces, { when the source file is on the Perq and on the remote machine, { respectively. Then NextReadFile and NextWriteFile will step { through all files in the name spaces. { { When reading, FillBuffer will read one data packet from the file. { At end-of-file, a EOF (Z) packet will be generated instead of a { data packet. EndFile may always be called to test for an end-of-file { condition. No special termination will need to be done when a { entire file group is transferred, calling NextReadFile iteratively { until it returns FALSE (no next file). { { When writing, EmptyBuffer will write one data packet to the file. { To keep the file, call KeepFile after all data has been written; { otherwise DiscardFile may be called at any time. In that case, { all file operations after the last NextWriteFile will be undone. { { If unsure of the state, FileIdle will always reset the module to the { idle state. { {============================} EXPORTS {======================================} imports KermitGlobals from KermitGlobals; CONST TempName = '$Kermit$Temp$'; TYPE Byte8 = 0..255; Byte8File = packed file of Byte8; FileErrs = ( { Fatal errors - aborts one file } { or the whole batch } FReadErr, { Disk read error } FWriteErr, { Disk write error } FNoSpace, { No more space to write file into } FNoReadPriv, { Not read access to file } FNoWritePriv,{ Not write access to file } FCantOpen, { Cannot open file } FNotRenamed, { Could not rename } FNoFile, { No file of this name } FBadNames, { Bad filenames or wildcard matching } FInternalErr,{ Internal error (program logic) } FNoError, { Idle code } { Informational } FRenamed, { Renamed files when FileWarning on } FEndDir, { No more matching files when wildcards } FAtEof); { File is already at EOF } {----------------------------------------------------------------------------} { -- File Open/Close routines: (Pascal files) These routines are not to be used for the transferred files } function OpenRead ( VAR ReadFile : Byte8File ; VAR FileName : FNameType ) : FileErrs; function OpenWrite ( VAR WriteFile : Byte8File ; VAR FileName : FNameType ) : FileErrs; function CloseFile( VAR FileToClose : Byte8File ) : FileErrs; { -- Filename manipulation routines } procedure ParseArgs( VAR Args, Arg1, Arg2 : String ); procedure ReadFName ( Var FileName : FNameType ); procedure PutFileName ( VAR FileN : FNameType; VAR Pack : Packet ); procedure GetFileName ( VAR FileN : FNameType; VAR Pack : Packet ); { -- KermitFile manipulation } function SetReadFile( VAR SourcePat, DestPat : String ) : FileErrs; function NextReadFile( VAR FileName : String ) : FileErrs; function EndFile : Boolean; function SetWriteFile( VAR SourcePat, DestPat : String ) : FileErrs; function NextWriteFile( VAR FileName : String ) : FileErrs; procedure WriteScreen; function FillBuffer ( Var Data : Packet ) : FileErrs; function EmptyBuffer( Var Data : Packet ) : FileErrs; function FileIdle : FileErrs; function DiscardFile : FileErrs; function KeepFile : FileErrs; procedure FileAbort; { -- Error message generator } procedure FileError ( FileName : FNameType; ErrCode : FileErrs; Var Message : String ); procedure InitFile; {============================} PRIVATE {======================================} imports KermitParameters from KermitParameters; imports FileSystem from FileSystem; imports FileUtils from FileUtils; imports CmdParse from CmdParse; imports Perq_String from Perq_String; imports PMatch from PMatch; imports Stream from Stream; {----------------------------------------------------------------------------} CONST NoFile = '?No such file to open: '; NoSetRead = '?Internal error: NextReadFile without SetReadFile'; NoSetWrite = '?Internal error: NextWriteFile without SetWriteFile'; NotReading = '?Internal error: FillBuffer when not reading'; NotWriting = '?Internal error: EmptyBuffer when not writing'; {----------------------------------------------------------------------------} TYPE ModuleState = ( Idling, Writing, WritingScreen, Reading ); {----------------------------------------------------------------------------} VAR RemoteFName, LocalFName : FNameType; { Rem. & loc. names of current file } SourcePat, DestPat : String; { Matching patterns of file names } ScanPtr : ptrScanRecord; DataFile : Byte8File; { File to receive to/send from } FileIsOpen : Boolean; { True if DataFile is open } FileState : ModuleState; { What we're doing now } FileNoPatt : Boolean; { Wildcard filename } {----------------------------------------------------------------------------} procedure InitFile; begin FileIsOpen := FALSE; FileState := Idling; end; {----------------------------------------------------------------------------} procedure ConvLower( VAR S : PString ); var i : integer; begin for i := 1 to length( s ) do if S[i] in ['a'..'z'] then S[i] := chr( Ord(S[i]) - (ord('a')-ord('A')) ); end; {----------------------------------------------------------------------------} function ReleaseFName( VAR FileName : FNameType ) : FileErrs; { -- Assumes a file of name FileName exists. Free this name by renaming existing files. } var Renamed : FNameType; B1, B2 : Integer; Dummy : FileErrs; begin Renamed := FileName; AppendChar( Renamed, '$' ); if 0<>FSLocalLookUp( Renamed, B1, B2 ) then Dummy := ReleaseFName( Renamed ); FSRename( FileName, Renamed ); ReleaseFName := FRenamed; end; {----------------------------------------------------------------------------} procedure ReadFName( Var FileName : FNameType ); { Abstract : Reads filename from terminal (standard input). Skips blanks before filename. Skips over rest of line until EOLN. No check of correct syntax is at present performed. } var first : char; Fstr : string[1]; begin read( first ); { read at least one character } while (not EOLN) and (first=' ') do read( first ); read( FileName ); adjust( Fstr, 1); FStr[1] := first; if first<>' ' then FileName := Concat( FStr, FileName ); end; {----------------------------------------------------------------------------} function OpenRead ( VAR ReadFile : Byte8File ; VAR FileName : FNameType ) : FileErrs; { Abstract : Opens ReadFile for Read Does a RESET of the file Returns FNoError if Open was successful, i.e. file existed and read access of file was granted. Returns FNoFile if file did not exist. } var Ostat : FileErrs; B1,B2 : integer; begin if 0=FSLookUp( FileName, B1, B2 ) then Ostat := FNoFile else begin Ostat := FNoError; reset( ReadFile, FileName ); end; OpenRead := Ostat; end; {----------------------------------------------------------------------------} function OpenWrite ( VAR WriteFile : Byte8File ; VAR FileName : FNameType ) : FileErrs; { Abstract: Opens WriteFile for Write Does a REWRITE of the file Returns FNoFile: If Open was NOT successful. FNoError: If Open was immediately successful, i.e. new file or write access granted to existing file, provided FileWarning OFF. FRenamed: If Open was successful after renaming files, i.e. Kermit was able to create the new file } const MaxTries = 5; var B1, B2 : integer; begin if NOT FileWarning then begin { don't worry about existing file } rewrite( WriteFile, FileName ); OpenWrite := FNoError; end else { we have to check if file already exists } if 0 = FSLocalLookUp( FileName, B1, B2 ) then begin rewrite( WriteFile, FileName ); OpenWrite := FNoError; end else begin if ReleaseFName( FileName )=FRenamed then begin Rewrite( WriteFile, Filename ); OpenWrite := FRenamed; end else OpenWrite := FNoWritePriv; end; end; {----------------------------------------------------------------------------} function CloseFile( VAR FileToClose : Byte8File ) : FileErrs; { Abstract: Do any actions necessary when closing file } begin Close( FileToClose ); CloseFile := FNoError; end; {----------------------------------------------------------------------------} function KeepFile : FileErrs; { -- Close a file after writing, keep file } var B1, B2 : Integer; OldWin : WinType; RetCode: FileErrs; handler RenToExist( FileName : PathName ); begin raise RenError( 'Attempted rename to existing name:', FileName ); end; handler RenError( Msg : String; FileName : PathName ); begin writeln( '**', Msg, FileName ); FileAbort; KeepFile := FNotRenamed; Exit( KeepFile ); end; begin CurrentWindow( OldWin ); SwitchWindow( MainWindow ); RetCode := FNoError; if (FileState=Writing) and FileIsOpen then begin Close( DataFile ); if 0 <> FSLocalLookUp( LocalFName, B1, B2 ) then if FileWarning then RetCode := ReleaseFName( LocalFName ) else FSDelete( LocalFName ); FSRename( TempName, LocalFName ); writeln( 'Completed: ', RemoteFName, ' --> ', LocalFName ); FileIsOpen := FALSE; end; SwitchWindow( OldWin ); KeepFile := RetCode; end; {----------------------------------------------------------------------------} function DiscardFile : FileErrs; { -- Close a file after writing, discard file } VAR OldWin : WinType; begin CurrentWindow( OldWin ); SwitchWindow( MainWindow ); DiscardFile := FNoError; if (FileState=Writing) and FileIsOpen then begin Close( DataFile ); FSDelete( TempName ); FileIsOpen := FALSE; writeln( '**Discarded**: ', RemoteFName, ' --> ', LocalFName ); end; SwitchWindow( OldWin ); end; {----------------------------------------------------------------------------} procedure FileAbort; VAR OldWin : WinType; begin CurrentWindow( OldWin ); SwitchWindow( MainWindow ); write( '**Aborted**: ' ); if Reading=FileState then begin writeln( LocalFName, ' --> ', RemoteFName ); end else if Writing=FileState then begin writeln( RemoteFName, ' --> ', LocalFName ); end; SwitchWindow( OldWin ); end; {----------------------------------------------------------------------------} procedure CloseReading; var OldWin : WinType; begin if EOF(DataFile) then begin CurrentWindow( OldWin ); SwitchWindow( MainWindow ); writeln( 'Completed: ', LocalFName, ' --> ', RemoteFName ); SwitchWindow( OldWin ); end else FileAbort; Close( DataFile ); end; { CloseReading } {----------------------------------------------------------------------------} function CheckPatterns( VAR S, D : String ) : FileErrs; { -- Verify that patterns S and D are valid } VAR InS, OutS : String; Dummy : Boolean; handler BadPatterns; begin CheckPatterns := FBadNames; exit( CheckPatterns ); end; begin InS := ''; OutS := ''; CheckPatterns := FNoError; if IsPattern( S ) then begin FileNoPatt := FALSE; dummy := PattMap ( InS, S, D, OutS, Translate=TransUpper ); end else FileNoPatt := TRUE; end; {----------------------------------------------------------------------------} procedure ParseArgs( VAR Args, Arg1, Arg2 : String ); var DelPos : integer; procedure LeadingBlanks( VAR Arg : String ); var i, l : integer; begin i := 1; L := Length(Arg); if L<>0 then while (Arg[i]=' ') and (i=L then { All spaces } Arg := '' else begin if Arg[i]<>' ' then i := i-1; Delete( Arg, 1, i ); end; end; begin LeadingBlanks( Args ); DelPos := PosC( Args, ' '); if DelPos=0 then DelPos := PosC( Args, ',' ); if DelPos=0 then begin Arg1 := Args; Arg2 := ''; end else begin Arg1 := SubStr( Args, 1, DelPos -1 ); Delete( Args, 1, DelPos ); LeadingBlanks( Args ); DelPos := PosC( Args, ' ' ); if DelPos = 0 then DelPos := PosC( Args, ',' ); if DelPos <> 0 then Args := SubStr( Args, 1, DelPos -1 ); Arg2 := Args; end; end; {----------------------------------------------------------------------------} function SetPatterns( VAR S, D : String ) : FileErrs; { -- Set the module local pattern names } begin if (S='') and (D='') then begin SourcePat := ''; DestPat := ''; end else begin if S = '' then SourcePat := D else SourcePat := S; if D = '' then DestPat := S else DestPat := D; end; SetPatterns := CheckPatterns( SourcePat, DestPat ); end; {----------------------------------------------------------------------------} function SetReadFile( VAR SourcePat, DestPat : String ) : FileErrs; { -- Setup for read of multiple files. S contains Perq filename } { to match, D is name to transmit file under. } var Dummy : FileErrs; begin if FileIsOpen then Dummy := FileIdle; SetReadFile := SetPatterns( SourcePat, DestPat ); new( ScanPtr ); ScanPtr^.InitialCall := TRUE; ScanPtr^.DirName := FSDirPrefix; FileState := Reading; FileIsOpen := False; end; {----------------------------------------------------------------------------} procedure ConvExt( VAR FileN : String ); { Abstract: Converts a filename to external form } var FD, LD, PD, TI, L, T : Integer; begin { Pathname is always stripped } L := RevPosC( FileN, '>' ); if (Length( FileN )-L) > MaxString then Adjust( FileN, MaxString+L ); FileN := SubStr( FileN, L+1, Length( FileN )-L ); if Nord then begin { Apply NORD transformation } LD := RevPosC( FileN, '.' ); { find last dot of file name } FD := PosC ( FileN, '.' ); { find first dot of file name } while LD<>FD do begin { substitute until last dot } FileN[FD] := '-'; { if no dots: LD=FD=0 } FD := PosC( FileN, '.' ); { find next dot } end; end else if NumTrunc>0 then begin { Do TRUNCATE transformation } LD := RevPosC( FileN, '.' ); if (LD=0) or (NumTrunc=1) then { ONE part, truncate according} begin { to first entry of list } T := TruncList[1]; if Length(FileN) < T then { See where to chop off name: } T := Length(FileN); { Minimum of length, trunc } if LD<>0 then begin { and position of dot } FD := PosC(FileN,'.')-1;{ Guaranteed to find a dot } if FDTruncList[NumTrunc] then { truncate last part } Delete( FileN, LD+TruncList[NumTrunc]+1, L-TruncList[NumTrunc] ); TI := 1; PD := 0; FD := PosC( FileN, '.' ); { where does next part end?? } while (FD<>0) do begin { Move it until no next part } if TI>=NumTrunc then { Part with no matching entry } T := -1 { Delete everything, dot too } else T := TruncList[TI]; { Keep as much as list tell } TI := TI + 1; L := FD-PD-1-T; { Num. chars to delete } if L>0 then begin Delete( FileN, PD+T+2, L ); LD := LD - L; { Last dot has been moved } PD := FD - L; { So has the delimiting one - } end else PD := FD; FileN[PD] := '>'; { don't find it again } FD := PosC( FileN, '.' ); end; FD := PosC( FileN, '>' ); while FD<>0 do begin { Restore dots } FileN[FD] := '.'; FD := PosC( FileN, '>' ); end; end; { Two parts } end; { TRUNCATE } if Nord or (Translate=TransUpper) then ConvUpper( FileN ) else if (Translate=TransLower) then ConvLower( FileN ); end; {----------------------------------------------------------------------------} function NextReadFile( VAR FileName : String ) : FileErrs; { -- Open next file } var id : FileId; NewFile, Matched : Boolean; B1, B2 : integer; handler ResetError( FName : PathName ); begin NextReadFile := FCantOpen; FileName := FName; exit( NextReadFile ); end; begin if FileState<>Reading then begin NextReadFile := FInternalErr; Writeln( NoSetRead ); end else begin if FileNoPatt then begin if not FileIsOpen then begin { First time } LocalFName := SourcePat; NewFile := 0 <> FSLocalLookUp( SourcePat, B1, B2 ); Matched := True; if Not NewFile then begin NextReadFile := FNoFile; end else begin NextReadFile := FNoError; if DestPat<>'' then RemoteFName := DestPat else RemoteFName := SourcePat; end; end else begin NextReadFile := FEndDir; NewFile := False; CloseReading; FileIsOpen := False; end; end else begin if FileIsOpen then CloseReading; repeat NewFile := FSScan( ScanPtr, LocalFName, ID ); if NewFile then Matched := PattMap( LocalFName, SourcePat, DestPat, RemoteFName, Translate=TransUpper ); until Matched or ( NOT NewFile ); if not NewFile then NextReadFile := FEndDir; end; if NOT NewFile then begin Dispose( ScanPtr ); FileState := Idling; FileIsOpen := False; FileName := SourcePat; { To be able to report name in err.mess.} end else begin NextReadFile := FNoError; ConvExt( RemoteFName ); ShowSRFile( True, RemoteFName, LocalFName ); FileIsOpen := TRUE; FileName := RemoteFName; { To put into FileHeader packet } Reset( DataFile, LocalFName ); end; end; end; {----------------------------------------------------------------------------} function EndFile : Boolean; begin if (FileState=Reading) and FileIsOpen then EndFile := EOF( DataFile ) else EndFile := TRUE; end; {----------------------------------------------------------------------------} function SetWriteFile( VAR SourcePat, DestPat : String ) : FileErrs; { -- Setup for write to file } var Dummy : FileErrs; begin if FileIsOpen then Dummy := FileIdle; SetWriteFile := SetPatterns( SourcePat, DestPat ); FileState := Writing; FileIsOpen := False; end; {----------------------------------------------------------------------------} procedure WriteScreen; { -- Setup to write to screen instead of file } var Dummy : FileErrs; begin if FileIsOpen then Dummy := FileIdle; FileState := WritingScreen; end; {----------------------------------------------------------------------------} procedure ConvInt( VAR FileN : FNameType ); { Abstract: Converts a file name to internal format in FileN, including any necessary transformations of file name } var FD : integer; T : PString; Sep : char; IsSwitch : boolean; begin { We expect DEC-10, -20, CP/M and MP/M style filenames, . Acceptable to PERQ! } if Nord then begin FD := PosC( FileN, '-' ); { Apply reverse NORD transf. } while FD<>0 do begin FileN[FD] := '.'; FD := PosC( FileN, '-' ); end; end; end; {----------------------------------------------------------------------------} function NextWriteFile( VAR FileName : String ) : FileErrs; { -- Open next file to write. } var Matched : boolean; RetCode : FileErrs; begin if FileState<>Writing then begin if FileState<>WritingScreen then begin Writeln( NoSetWrite ); RetCode := FInternalErr; end; end else begin RetCode := FNoError; if FileIsOpen then RetCode := KeepFile; if RetCode>=FNoError then begin RemoteFName := FileName; ConvInt( FileName ); if FileNoPatt then begin if (SourcePat=FileName) and (DestPat<>'') then LocalFName := DestPat { Two file names given: } else { Rename intended, but only } LocalFName := FileName; { if equal to the first one } end else begin Matched := PattMap( FileName, SourcePat, DestPat, LocalFName, Translate=TransUpper ); if not Matched then LocalFName := FileName; { Store with no translation } end; rewrite( DataFile, TempName ); FileIsOpen := TRUE; ShowSRFile( False, RemoteFName, LocalFName ); end { else NextWriteFile should be retried }; end; NextWriteFile := RetCode; end; {----------------------------------------------------------------------------} function FileIdle : FileErrs; { -- Reset the module to idle state } var OldWin : WinType; begin FileIdle := FNoError; if FileIsOpen then begin if FileState = Writing then FileIdle := DiscardFile else if FileState = Reading then CloseReading; end; FileIsOpen := False; FileState := Idling; end; {----------------------------------------------------------------------------} function FillBuffer ( var data : Packet ) : FileErrs; { -- Read a packet from the file } const PackHead = 4; { Number of characters in packet header } var NextB : Byte8; i, j, RepCnt, NextBSz, Needed : integer; GoForNext, Quote8, CtrlChar, eofi, WillRepeat : boolean; {--------------------------------------------------------------------} procedure CharInPack; begin With data do begin { Put character into the packet } if Quote8 then begin data[i] := Bit8Quote; { Quote for 8'th bit } i := i + 1; NextB := Land ( NextB, 127 ); { Mask 8'th bit } end; if CtrlChar then begin { Real control character?} if ( Land( NextB, 127) < ord ( ' ' ) ) or ( Land( NextB, 127) = 127 ) then { De- } NextB := ord ( ctl ( chr ( NextB ) ) ); { controlify} data[i] := SendQuote; i := i + 1; end; data[i] := chr ( NextB ); i := i + 1; end; end; {--------------------------------------------------------------------} procedure FetchNext; begin NextB := DataFile^; { Retreive next character from file buffer. } { How will it have to be quoted? } Quote8 := ( NextB >= 128 ) and NowUse8Quote; if Parity<>NOKParity then { Test for quotes with char.} NextB := LAnd( NextB, 127 ); { as it will arrive at rcvr.} CtrlChar := ( Land( NextB,127) < ord ( ' ' ) ) or ( Land( NextB,127) = 127 ) or ( chr( NextB ) = SendQuote ) or ( ( chr( NextB ) = Bit8Quote) and NowUse8Quote ) or ( ( chr( NextB ) = RepFix ) and NowUseRepFix ); NextBSz := 1; { How much packet space will it need? } if Quote8 then NextBSz := NextBSz + 1; { Adjust for the } if CtrlChar then NextBSz := NextBSz + 1; { quotes! } end; {--------------------------------------------------------------------} Procedure PutLookAhead; var PutIt : boolean; begin if not eofi then { We've decided to use the character in } get( DataFile ); { file buffer. Advance file window so } eofi := eof( DataFile ); { we may test against next character. } { Remember DataFile^ is undef. if at EOF } if not NowUseRepFix then begin CharInPack; { Don't use prefixing - assert RepCnt=1 } Needed := 0; end else begin { Do we have to put out the lookahead } if eofi or (NextB<>DataFile^) or (RepCnt>=94) then { char? } begin if not WillRepeat then for j := 1 to RepCnt do { Too few occurrences - } CharInPack { put it out literally } else with Data do begin { We will gain - } Data[i] := RepFix; { put prefix, } Data[i+1] := ToChar(chr(RepCnt)); { RepCnt, } i := i+2; CharInPack; { the character itself } end; RepCnt := 1; Needed := 0; { What space we're committed to } WillRepeat := false; { Not decided to repeat yet! } end else begin RepCnt := RepCnt + 1; { just count occurrences } if not WillRepeat then if Needed+NextBSz<=2 then { Committing our- } Needed := Needed + NextBSz { selves to use } else begin { more space! } Needed := NextBSz+2; { Else: limit has } WillRepeat := true; { been reached, } end; { will not need more space. } end; end; if eofi then { No character to go next. } GoForNext := false { Last one has already been put. } else begin FetchNext; { Look at the next character, decide } { whether it too wil go into packet. } if WillRepeat then { Next char won't use additional space. } GoForNext := true else { Is there space for NextB? } GoForNext := SendPSize >= (i+PackHead+Needed+NextBSz); end; end; {--------------------------------------------------------------------} begin FillBuffer := FNoError; if (FileState<>Reading) or (Not FileIsOpen) then Writeln( NotReading ) else with data do begin if not eof ( DataFile ) then begin RepCnt := 1; { #Times DataFile^ is to be put into packet.} i := 1; { Where will the character go? } adjust( Data, 100 ); eofi := false; FetchNext; { Establish lookahead. } WillRepeat := false; repeat { NOT EOF => At least one character to put} PutLookAhead; until not GoForNext; if (RepCnt>1) then begin { Don't forget it if last } for j := 1 to RepCnt do { char. was repeated. } CharInPack; { ASSERT not WillRepeat } if not eofi then get( DataFile ); end; { Put count field = len of data + 3, i = len of data +1 } count := ToChar ( chr ( i + 2 ) ); ptype := PackToCh( DataPack ); adjust( Data, i ); end else begin count := ToChar ( chr ( 3 ) ); Ptype := PackToCh( EOFPack ); FillBuffer := FAtEOF; end; end; end; {----------------------------------------------------------------------------} function EmptyBuffer ( var data : Packet ) : FileErrs; { -- Write a data packet to file } var i,j,scr,rep : integer; CtrlChar, Quote8 : boolean; ch : char; begin EmptyBuffer := FNoError; if (FileState<>WritingScreen) and ((FileState<>Writing) or (Not FileIsOpen)) then Writeln( NotWriting ) else begin i := 1; with data do while i <= ( ord ( UnChar( count ) ) - 3 ) do begin ch := data[i]; if NowUseRepFix and ( ch = RepFix ) then begin i := i + 1; ch := data[i]; rep := ord( UnChar( ch ) ); i := i + 1; ch := data[i]; end else rep := 1; Quote8 := NowUse8Quote and ( ch = Bit8Quote ); if Quote8 then begin i := i + 1; ch := data[i]; end; CtrlChar := ch = RecQuote; if CtrlChar then begin i := i + 1; ch := data[i]; if ch in CtlMapping then ch := ctl ( ch ); { else character is a quoted quote(!) } end; if Quote8 then Scr := Lor ( ord ( ch ) , 128 ) else Scr := ord ( ch ); if FileState=WritingScreen then for j := 1 to rep do write( chr( Land(Scr,127) ) ) else for j := 1 to rep do begin DataFile^ := Scr; put( DataFile ); end; i := i + 1; end; end; end; {----------------------------------------------------------------------------} procedure PutFileName( VAR FileN : FNameType; VAR Pack : Packet ); { Abstract: Puts a file name corresponding to internal format in FileN into a FileHeader packet (Pack). } begin Pack.Data := Concat( FileN, ' ' ); Pack.Count := ToChar( chr( Length( Pack.Data ) + 2 ) ); end; {----------------------------------------------------------------------------} procedure GetFileName( VAR FileN : FNameType; VAR Pack : Packet ); { Abstract: Gets a file name from a FileHeader packet and converts to internal format in FileN, including any necessary transformations of file name } var FD : integer; T : PString; Sep : char; IsSwitch : boolean; begin with Pack do begin if ( Ptype<>PackToCh( FHeadPack ) ) and Debug then begin DbgWrite(' Attempts GetFileName from non-FileHeader packet!'); DbgNL; end; { We expect DEC-10, -20, CP/M and MP/M style filenames, . Acceptable to PERQ! } { remember not to include the checksum byte!! } T := SubStr( Data, 1, Length( Data )-1 ); { Also: be sure there are no trailing separator characters } Sep := NextIDString( T, FileN, isSwitch ); end; end; {----------------------------------------------------------------------------} procedure FileError ( FileName : FNameType; ErrCode : FileErrs; Var Message : String ); { -- Generate File error messages } begin case ErrCode of FReadErr: Message := 'Disk read error'; FWriteErr: Message := 'Disk write error'; FNoSpace: Message := 'No more space to write file into'; FNoReadPriv: Message := 'Not granted read access to file'; FNoWritePriv:Message := 'Not granted write access to file'; FCantOpen: Message := 'Cannot open file'; FNotRenamed: Message := 'Could not rename file'; FNoFile: Message := 'No file of this name'; FBadNames: Message := 'Bad filenames or wildcard matching'; FInternalErr:Message := 'Kermit internal error'; FNoError: Message := 'File operation successful'; FRenamed: Message := 'Filename conflict, renamed files'; FEndDir: Message := 'No more matching names in directory'; FAtEof: Message := 'At end-of-file'; end; Message := Concat( Message, ' for file : ' ); if FileName<>'' then Message := Concat( Message, FileName ) else Message := Concat( Message, LocalFName ); end .