402 lines
10 KiB
Plaintext
402 lines
10 KiB
Plaintext
{************************************************}
|
|
{ }
|
|
{ Turbo Vision File Manager Demo }
|
|
{ Copyright (c) 1992 by Borland International }
|
|
{ }
|
|
{************************************************}
|
|
|
|
{$X+,V-,R-,S-}
|
|
|
|
unit FileCopy;
|
|
|
|
interface
|
|
|
|
uses Objects;
|
|
|
|
const
|
|
|
|
{ I/O error constants }
|
|
erWriteOpen = -1; { error opening for Write }
|
|
erReadOpen = -2; { error opening for read }
|
|
erDiskFull = -3; { error writing to file }
|
|
erLostFile = -4; { file never finished }
|
|
erNoFile = -5; { file not found }
|
|
erRename = -6; { Unable to rename }
|
|
erResetAFlag = -7; { Unable to reset archive flag on original file }
|
|
|
|
{ Internal error constants }
|
|
erOutOfMemory = -99; { could not allocate more memory }
|
|
|
|
{ copy options }
|
|
coNormal = $0000; { normal copy }
|
|
coCopyAOnly = $0001; { copy file only if Archive bit is set }
|
|
coResetAFlag = $0002; { reset Archive bit after succesful copy }
|
|
|
|
type
|
|
|
|
erAction = (erAbort, erRetry);
|
|
|
|
PFileCopy = ^TFileCopy;
|
|
TFileCopy = object(TObject)
|
|
Incomplete: Boolean;
|
|
IsNewFile: Boolean;
|
|
Offset: Longint;
|
|
Base: Longint;
|
|
FileList: PCollection;
|
|
Result: Integer;
|
|
Action: erAction;
|
|
|
|
constructor Init(MaxFiles: Integer);
|
|
destructor Done; virtual;
|
|
|
|
{ reporting methods }
|
|
procedure ReadMsg(const FName: FNameStr; Progress: Longint); virtual;
|
|
procedure WriteMsg(const FName: FNameStr; Progress: Longint); virtual;
|
|
procedure ReportError(S: String); virtual;
|
|
|
|
{ copy and support methods }
|
|
function CopyFile(const SourceName, TargetName: FNameStr; Options: Word): Boolean;
|
|
procedure FlushBuffers;
|
|
procedure EraseByName(const FName: FNameStr);
|
|
|
|
{ error methods }
|
|
function IOError(const FName: FNameStr; ECode:Integer) : erAction; virtual;
|
|
function InternalError(ECode: Integer) : erAction; virtual;
|
|
function ErrorMsg(ECode: Integer) : String; virtual;
|
|
end;
|
|
|
|
|
|
implementation
|
|
|
|
uses Dos;
|
|
|
|
const
|
|
fmReadOnly = 0;
|
|
fmReadWrite = 2;
|
|
MaxXFerSize = $F000; { largest block to read from disk }
|
|
|
|
type
|
|
String10 = String[10];
|
|
|
|
{ TPtrCollection implements a collection of pointers. Instead of each }
|
|
{ entry in the collection pointing to a pointer, the entries themselves }
|
|
{ are the actual pointers. }
|
|
|
|
PPtrCollection = ^TPtrCollection;
|
|
TPtrCollection = object(TCollection)
|
|
function GetItem(var S:TStream) : Pointer; virtual;
|
|
procedure PutItem(var S: TStream; Item: Pointer); virtual;
|
|
procedure FreeItem(Item:Pointer); virtual;
|
|
end;
|
|
|
|
{ PFileRec represents a single file that is being processed. }
|
|
|
|
PFileRec = ^TFileRec;
|
|
TFileRec = object(TObject)
|
|
Filename: PString;
|
|
OrigName: PString;
|
|
FTime: Longint;
|
|
FSize: Longint;
|
|
Buffers: PPtrCollection;
|
|
Offset: Longint;
|
|
Create: Boolean;
|
|
OptFlags: Word;
|
|
constructor Init(OldName, NewName: FNameStr);
|
|
destructor Done; virtual;
|
|
end;
|
|
|
|
|
|
{ TPtrCollection }
|
|
|
|
function TPtrCollection.GetItem(var S: TStream): Pointer;
|
|
var
|
|
P : Pointer;
|
|
begin
|
|
S.Read(P, SizeOf(Pointer));
|
|
GetItem := P;
|
|
end;
|
|
|
|
procedure TPtrCollection.PutItem(var S: TStream; Item: Pointer);
|
|
begin
|
|
S.Write(Item, SizeOf(Pointer));
|
|
end;
|
|
|
|
procedure TPtrCollection.FreeItem(Item:Pointer);
|
|
begin
|
|
{ do nothing }
|
|
end;
|
|
|
|
|
|
{ TFileRec }
|
|
constructor TFileRec.Init(OldName, NewName: FNameStr);
|
|
begin
|
|
inherited Init;
|
|
Filename := NewStr(NewName);
|
|
OrigName := NewStr(OldName);
|
|
end;
|
|
|
|
destructor TFileRec.Done;
|
|
begin
|
|
DisposeStr(Filename);
|
|
DisposeStr(OrigName);
|
|
if Buffers <> nil then Dispose(Buffers, Done);
|
|
inherited Done;
|
|
end;
|
|
|
|
|
|
{ TFileCopy }
|
|
constructor TFileCopy.Init(MaxFiles: Integer);
|
|
begin
|
|
inherited Init;
|
|
FileList := New(PCollection, Init(MaxFiles, MaxFiles div 2));
|
|
if Filelist = nil then
|
|
begin
|
|
ReportError(ErrorMsg(erOutOfMemory));
|
|
Fail;
|
|
end;
|
|
end;
|
|
|
|
destructor TFileCopy.Done;
|
|
begin
|
|
if FileList^.Count <> 0 then FlushBuffers;
|
|
if FileList <> nil then Dispose(FileList, Done);
|
|
inherited Done;
|
|
end;
|
|
|
|
function TFileCopy.IOError(const FName: FNameStr; ECode: Integer): erAction;
|
|
begin
|
|
ReportError(ErrorMsg(ECode));
|
|
IOError := erAbort;
|
|
end;
|
|
|
|
function TFileCopy.InternalError(ECode: Integer): erAction;
|
|
begin
|
|
ReportError(ErrorMsg(ECode));
|
|
InternalError := erAbort;
|
|
end;
|
|
|
|
procedure TFileCopy.EraseByName(const FName: FNameStr);
|
|
var
|
|
F: File;
|
|
I: Integer;
|
|
begin
|
|
Assign(F, FName);
|
|
{$I-}
|
|
Reset(F);
|
|
{$I+}
|
|
if IOResult = 0 then Erase(F);
|
|
I := IOResult; { read this so we don't leave any unused value there }
|
|
end;
|
|
|
|
procedure TFileCopy.FlushBuffers;
|
|
var
|
|
Leave : Integer;
|
|
|
|
procedure FlushFile(CurFile: PFileRec); far;
|
|
var
|
|
BufAddr: Pointer;
|
|
BytesToXFer: Word;
|
|
BytesRead: Word;
|
|
TargetFile: File;
|
|
RemainingBytes: Longint;
|
|
Attr: Word;
|
|
begin
|
|
FileMode := fmReadWrite;
|
|
|
|
Action := erRetry;
|
|
Result := 1;
|
|
while (Action <> erAbort) and (Result <> 0) do
|
|
begin
|
|
Assign(TargetFile, CurFile^.FileName^);
|
|
{$I-}
|
|
if CurFile^.Create then Rewrite(TargetFile, 1)
|
|
else Reset(TargetFile, 1);
|
|
{$I+}
|
|
Result := IOResult;
|
|
if Result <> 0 then
|
|
begin
|
|
Action := IOError(CurFile^.FileName^, erWriteOpen);
|
|
if Action = erAbort then Exit;
|
|
end;
|
|
end;
|
|
|
|
Seek(TargetFile, CurFile^.Offset);
|
|
|
|
if (FileList^.IndexOf(CurFile) = FileList^.Count-1) and Incomplete
|
|
then Inc(Offset, CurFile^.FSize);
|
|
|
|
RemainingBytes := CurFile^.FSize;
|
|
|
|
repeat
|
|
if RemainingBytes > MaxXFerSize then BytesToXFer := MaxXFerSize
|
|
else BytesToXFer := RemainingBytes;
|
|
BufAddr := CurFile^.Buffers^.At(0); { get first address }
|
|
BlockWrite(TargetFile, BufAddr^, BytesToXFer, BytesRead);
|
|
WriteMsg(CurFile^.Filename^, BytesRead);
|
|
Dec(RemainingBytes, BytesRead);
|
|
FreeMem(BufAddr, BytesToXFer);
|
|
CurFile^.Buffers^.AtDelete(0);
|
|
until RemainingBytes = 0;
|
|
|
|
if not Incomplete then SetFTime(TargetFile, CurFile^.FTime);
|
|
Close(TargetFile);
|
|
|
|
if ((FileList^.IndexOf(CurFile) <> FileList^.Count-1) or
|
|
(not Incomplete)) then
|
|
begin
|
|
if CurFile^.OptFlags and coResetAFlag <> 0 then
|
|
begin
|
|
Assign(TargetFile, CurFile^.OrigName^);
|
|
GetFAttr(TargetFile, Attr);
|
|
Attr := Attr and (not Archive);
|
|
SetFAttr(TargetFile, Attr);
|
|
if DosError <> 0 then IOError(CurFile^.OrigName^, erResetAFlag);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
FileList^.ForEach(@FlushFile);
|
|
if Incomplete then Leave := 1 else Leave := 0;
|
|
while FileList^.Count <> Leave do
|
|
FileList^.AtFree(0);
|
|
end;
|
|
|
|
function TFileCopy.CopyFile(const SourceName, TargetName: FNameStr;
|
|
Options: Word): Boolean;
|
|
const
|
|
Safety = 4096;
|
|
var
|
|
Flush: Boolean;
|
|
SourceFile: File;
|
|
TargetFile: File;
|
|
CurFile: PFileRec;
|
|
RemainingBytes: Longint;
|
|
BytesToXFer: Word;
|
|
DidXFer: Word;
|
|
BufAddr: Pointer;
|
|
CurMaxAvail: Longint;
|
|
SRec: SearchRec;
|
|
begin
|
|
CopyFile := False;
|
|
|
|
{ validate the coCopyAOnly flag }
|
|
if Options and coCopyAOnly <> 0 then
|
|
begin
|
|
FindFirst(SourceName, AnyFile, SRec);
|
|
if (DosError = 0) and ((SRec.Attr and Archive) = 0) then Exit
|
|
else IOError(SourceName, erNoFile);
|
|
end;
|
|
|
|
CurFile := New(PFileRec, Init(SourceName, TargetName));
|
|
CurFile^.Buffers := New(PPtrCollection, Init(5,2));
|
|
if (CurFile = nil) or (CurFile^.Buffers = nil) then
|
|
begin
|
|
InternalError(erOutOfMemory);
|
|
Exit;
|
|
end;
|
|
CurFile^.OptFlags := Options;
|
|
CurFile^.Offset := 0;
|
|
CurFile^.Create := True;
|
|
FileList^.Insert(CurFile);
|
|
|
|
Offset := 0;
|
|
Incomplete := False;
|
|
IsNewFile := False;
|
|
Base := 0;
|
|
|
|
repeat
|
|
Flush := False;
|
|
|
|
FileMode := fmReadOnly;
|
|
Assign(SourceFile, SourceName);
|
|
|
|
Action := erRetry;
|
|
Result := 1;
|
|
while (Action <> erAbort) and (Result <> 0) do
|
|
begin
|
|
{$I-}
|
|
Reset(SourceFile,1);
|
|
{$I+}
|
|
Result := IOResult;
|
|
if Result <> 0 then
|
|
begin
|
|
if IOError(SourceName, erReadOpen) = erAbort then
|
|
begin
|
|
FileList^.Free(CurFile);
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
if Incomplete then
|
|
begin
|
|
Seek(SourceFile, Offset-Base);
|
|
CurFile^.Offset := Offset;
|
|
if Incomplete then CurFile^.Create := False;
|
|
end;
|
|
|
|
Incomplete := False;
|
|
|
|
CurFile^.FSize := FileSize(SourceFile) - Offset + Base;
|
|
CurMaxAvail := MaxAvail - Safety;
|
|
|
|
if CurFile^.FSize > CurMaxAvail then
|
|
begin
|
|
CurFile^.FSize := CurMaxAvail;
|
|
Flush := True;
|
|
Incomplete := True;
|
|
CurFile^.Offset := Offset;
|
|
end;
|
|
RemainingBytes := CurFile^.FSize;
|
|
|
|
repeat
|
|
if RemainingBytes > MaxXFerSize then BytesToXFer := MaxXFerSize
|
|
else BytesToXFer := RemainingBytes;
|
|
GetMem(BufAddr, BytesToXFer);
|
|
CurFile^.Buffers^.Insert(BufAddr);
|
|
BlockRead(SourceFile, BufAddr^, BytesToXFer, DidXFer);
|
|
ReadMsg(SourceName, DidXFer);
|
|
Dec(RemainingBytes, DidXFer);
|
|
until RemainingBytes = 0;
|
|
|
|
GetFTime(SourceFile, CurFile^.FTime);
|
|
Close(SourceFile);
|
|
|
|
if Flush then FlushBuffers;
|
|
|
|
until not Incomplete;
|
|
CopyFile := True;
|
|
end;
|
|
|
|
procedure TFileCopy.ReadMsg(const FName: FNameStr; Progress: Longint);
|
|
begin
|
|
Writeln('Reading ', FName);
|
|
end;
|
|
|
|
procedure TFileCopy.WriteMsg(const FName: FNameStr; Progress: Longint);
|
|
begin
|
|
Writeln('Writing ', FName);
|
|
end;
|
|
|
|
procedure TFileCopy.ReportError(S: String);
|
|
begin
|
|
Writeln(S);
|
|
end;
|
|
|
|
function TFileCopy.ErrorMsg(ECode: Integer): String;
|
|
begin
|
|
case ECode of
|
|
erWriteOpen : ErrorMsg := 'Unable to open for write access';
|
|
erReadOpen : ErrorMsg := 'Unable to open for read access';
|
|
erDiskFull : ErrorMsg := 'Unable to write to file. Disk full?';
|
|
erLostFile : ErrorMsg := 'File never flushed from buffers';
|
|
erNoFile : ErrorMsg := 'File not found.';
|
|
erRename : ErrorMsg := 'Unable to rename to final name.';
|
|
erOutOfMemory : ErrorMsg := 'Unable to allocate memory.';
|
|
else ErrorMsg := 'Unknown error.';
|
|
end; { case }
|
|
end;
|
|
|
|
end.
|