📄 zipbuilder.cpp
字号:
// Do we still have enough free space on this disk.
if(FFreeOnDisk < MinFreeVolumeSize)
{ // No, too bad...
FileClose(FOutFileHandle);
DeleteFile(DiskFile);
FOutFileHandle = -1;
if(Unattended) throw EZipBuilder(DS_NoUnattSpan);
if(FOnStatusDisk)
{ // v1.60L
int DiskSeq;
if(FSpanOptions.Contains(spNoVolumeName)) DiskSeq = FDiskNr + 1;
else DiskSeq = StrToIntDef(FVolumeName.SubString(9, 3), 1);
FZipDiskAction = zdaOk; // The default action
FZipDiskStatus = TZipDiskStatus() << zdsNotEnoughSpace;
OnStatusDisk(this, DiskSeq, DiskFile, FZipDiskStatus, FZipDiskAction);
switch(FZipDiskAction)
{
case zdaCancel: Res = IDCANCEL; break;
case zdaOk:
case zdaErase:
case zdaReject: Res = IDRETRY;
}
}
else
{
MsgQ = LoadZipStr(DS_NoDiskSpace, "This disk has not enough free space available");
Res = Application->MessageBox(MsgQ.c_str(), Application->Title.c_str(),
MB_RETRYCANCEL | MB_ICONERROR );
}
if(!Res) throw EZipBuilder(DS_NoMem);
if(Res != IDRETRY ) throw EZipAbort();
FDiskWritten = 0;
FNewDisk = true;
continue;
}
// Set the volume label of this disk if it is not a fixed one.
if(!FDriveFixed && !FSpanOptions.Contains(spNoVolumeName))
{
FVolumeName = "PKBACK# " + IntToStr(1001 + FDiskNr).SubString(2, 3);
if(!::SetVolumeLabel(FDrive.c_str(), FVolumeName.c_str()))
throw EZipBuilder(DS_NoVolume);
}
}
// Check if we have at least MinSize available on this disk,
// headers are not allowed to cross disk boundaries. ( if zero than don't care.)
if(MinSize && MinSize > FFreeOnDisk)
{
FileSetDate(FOutFileHandle, FDateStamp);
FileClose(FOutFileHandle);
FOutFileHandle = -1;
FDiskWritten = 0;
FDiskNr++;
FNewDisk = true;
continue;
}
// Don't try to write more bytes than allowed on this disk.
MaxLen = min(Len, FFreeOnDisk);
Res = FileWrite(FOutFileHandle, Buf, MaxLen);
// Give some progress info while writing.
// While processing the central header we don't want messages.
if(FShowProgress) CallBack(zacProgress, 0,"",MaxLen);
if(Res == -1) throw EZipBuilder(DS_NoWrite); // A write error (disk removed?).
FDiskWritten += Res;
FFreeOnDisk -= MaxLen;
if(MaxLen == Len) break;
// We still have some data left, we need a new disk.
FileSetDate(FOutFileHandle, FDateStamp);
FileClose(FOutFileHandle);
FOutFileHandle = -1;
FFreeOnDisk = FDiskWritten = 0;
FDiskNr++;
FNewDisk = true;
Buf += MaxLen;
Len -= MaxLen;
}
}
// TZipBuilder::WriteSplit
#endif
// TZipBuilder::CopyZippedFiles-------------------------------------------------
// 1.73 1 Oct 2003 RA corrected slashes in central dir
// 1.73 8 August 2003 RA close InFileHandle after error, set to -1 in non-error case
// 1.73 (31 July 2003) RA close InFileHandle for Spanerror
// 1.73 (24 July 2003) RA init OutFileHandle
// 1.73 (12 July 2003) RP string extra data
// 1.73 (2 June 2003) RA Use of ConvertOEM
// Function to copy one or more zipped files from the zip archive to another zip archive
// FSpecArgs in source is used to hold the filename(s) to be copied.
// When this function is ready FSpecArgs contains the file(s) that where not copied.
// Return values:
// 0 All Ok.
// -6 CopyZippedFiles Busy
// -7 CopyZippedFiles errors. See ZipMsgXX.rc
// -8 Memory allocation error.
// -9 General unknown CopyZippedFiles error.
int __fastcall TZipBuilder::CopyZippedFiles(TZipBuilder *DestZipBuilder, bool DeleteFromSource,
OvrOpts OverwriteDest)
{
ZipEndOfCentral EOC;
ZipCentralHeader CEH;
ZipDirEntry *zde, *pzd;
String OutFilePath;
char *Buffer = NULL;
int Result = 0;
int In2FileHandle = -1; //to avoid external exception in case of error RAEL
TStringList *NotCopiedFiles = NULL;
bool Found;
int DestMemCount;
TMZipDataList* MDZD = NULL;
if(Busy()) return BUSY_ERROR;
StartWaitCursor();
FZipBusy = true;
FShowProgress = false;
FOutFileHandle = -1;
try
{
// Are source and destination different?
if(DestZipBuilder == this || !AnsiStrIComp(ZipFileName.c_str(),
DestZipBuilder->ZipFileName.c_str()))
throw EZipBuilder(CF_SourceIsDest);
//testing for diskspan added RAEL
// The following function a.o. opens the input file no. 1.
OpenEOC(EOC, false);
if(DestZipBuilder->IsSpanned || FIsSpanned)
throw EZipBuilder(CF_NoCopyOnSpan, true);
Buffer = new char[BufSize];
// Now check for every source file if it is in the destination archive and determine what to do.
// we use the three most significant bits from the Flag field from ZipDirEntry to specify the action
// None = 000xxxxx, Destination no change. Action: Copy old Dest to New Dest
// Add = 001xxxxx (New). Action: Copy Source to New Dest
// Overwrite = 010xxxxx (OvrAlways) Action: Copy Source to New Dest
// AskToOverwrite = 011xxxxx (OvrConfirm) Action to perform: Overwrite or NeverOverwrite
// NeverOverwrite = 100xxxxx (OvrNever) Action: Copy old Dest to New Dest
for(int s = 0; s < FSpecArgs->Count; s++)
{
//added R.Aelbrecht to allow drive name in FSpecArg
AnsiString FSpec = FSpecArgs->Strings[s];
RemoveDriveSpec(FSpec);
Found = false;
for(int d = 0; d < DestZipBuilder->Count; d++)
{
zde = (ZipDirEntry *)DestZipBuilder->ZipContents->Items[d];
if(!AnsiStrIComp(FSpec.c_str(), zde->FileName.c_str()))
{
Found = true;
zde->Flag &= 0x1FFF; // Clear the three upper bits.
zde->Flag |= (OverwriteDest == OvrAlways) ? 0x4000ui16 : (OverwriteDest == OvrNever) ? 0x8000ui16 : 0x6000ui16;
break;
}
}
if(!Found)
{ // Add the Filename to the list and set flag
zde = new ZipDirEntry;
DestZipBuilder->FZipContents->Add(zde);
zde->FileName = FSpec;
zde->FileName.Unique();
zde->FileNameLength = (Word)FSpecArgs->Strings[s].Length();
zde->Flag |= 0x2000; // (a new entry)
zde->ExtraData = NULL; //Needed when deleting zde
}
}
// Make a temporary filename like: C:\...\zipxxxx.zip for the new destination
if((OutFilePath = MakeTempFileName()) == "") throw EZipBuilder(DS_NoTempFile);
// Create the output file.
FOutFileHandle = FileCreate(OutFilePath);
if(FOutFileHandle == -1) throw EZipBuilder(DS_NoOutFile);
// Open the second input archive, i.e. the original destination.
if((In2FileHandle = FileOpen(DestZipBuilder->ZipFileName, fmShareDenyWrite | fmOpenRead )) == -1)
throw EZipBuilder(CF_DestFileNoOpen);
// Get the date-time stamp and save for later.
FDateStamp = FileGetDate(In2FileHandle);
// Write the SFX header if present.
if(CopyBuffer(In2FileHandle, FOutFileHandle, DestZipBuilder->SFXOffset))
throw EZipBuilder(CF_SFXCopyError);
NotCopiedFiles = new TStringList();
// Now walk trough the destination, copying and replacing
DestMemCount = DestZipBuilder->FZipContents->Count;
MDZD = new TMZipDataList(DestMemCount); // create class
// Copy the local data and save central header info for later use.
for(int d = 0; d < DestMemCount; d++)
{
zde = (ZipDirEntry *)DestZipBuilder->ZipContents->Items[d];
if((zde->Flag & 0xE000) == 0x6000)
{ // Ask first if we may overwrite.
bool Overwrite = false;
// Do we have a event assigned for this then don't ask.
if(FOnCopyZipOverwrite) FOnCopyZipOverwrite(DestZipBuilder, zde->FileName, Overwrite);
else Overwrite = (Application->MessageBox( Format(LoadZipStr(CF_OverwriteYN, "Overwrite %s in %s ?"),
ARRAYOFCONST((zde->FileName, DestZipBuilder->ZipFileName))).c_str(),
Application->Title.c_str(),
MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2 )== IDYES) ? true : false;
zde->Flag &= 0x1FFF; // Clear the three upper bits.
zde->Flag |= (Overwrite) ? 0x4000ui16 : 0x8000ui16;
}
// Change info for later while writing the central dir in new Dest.
MDZD->Items[d]->RelOffLocal = FileSeek(FOutFileHandle, 0, 1);
if((zde->Flag & 0x6000) == 0x0000)
{ // Copy from original dest to new dest.
// Set the file pointer to the start of the local header.
FileSeek(In2FileHandle, (SeekInt)zde->RelOffLocalHdr, 0);
if(CopyBuffer(In2FileHandle, FOutFileHandle, sizeof(ZipLocalHeader) +
zde->FileNameLength +
zde->ExtraFieldLength +
zde->CompressedSize))
throw EZipBuilder(CF_CopyFailed, DestZipBuilder->ZipFileName, DestZipBuilder->ZipFileName);
if(zde->Flag & 0x8000)
{
NotCopiedFiles->Add(zde->FileName);
// Delete also from FSpecArgs, should not be deleted from source later.
FSpecArgs->Delete(FSpecArgs->IndexOf(zde->FileName));
}
}
else
{ // Copy from source to new dest.
// Find the filename in the source archive and position the file pointer.
for(int s = 0; s < Count; s++)
{
pzd = (ZipDirEntry *)ZipContents->Items[s];
if(!AnsiStrIComp(pzd->FileName.c_str(), zde->FileName.c_str()))
{
FileSeek(FInFileHandle, (SeekInt)pzd->RelOffLocalHdr, 0);
if(CopyBuffer(FInFileHandle, FOutFileHandle, sizeof(ZipLocalHeader) +
pzd->FileNameLength +
pzd->ExtraFieldLength +
pzd->CompressedSize))
throw EZipBuilder(CF_CopyFailed, ZipFileName, DestZipBuilder->ZipFileName);
break;
}
}
}
// Save the file name info in the MDZD structure.
MDZD->Items[d]->FileNameLen = zde->FileNameLength;
MDZD->Items[d]->FileName = new char[zde->FileNameLength + 1];
StrCopy(MDZD->Items[d]->FileName, zde->FileName.c_str());
} // Now we have written al entries.
// Now write the central directory with possibly changed offsets.
// Remember the EOC we are going to use is from the wrong input file!
EOC.CentralSize = 0;
for(int d = 0; d < DestMemCount; d++)
{
zde = (ZipDirEntry *)DestZipBuilder->ZipContents->Items[d];
Found = false;
// Rebuild the CEH structure.
if((zde->Flag & 0x6000) == 0x0000)
{ // Copy from original dest to new dest.
pzd = (ZipDirEntry *)DestZipBuilder->ZipContents->Items[d];
Found = true;
}
else
{ // Copy from source to new dest.
// Find the filename in the source archive and position the file pointer.
for(int s = 0; s < Count; s++)
{
pzd = (ZipDirEntry *)ZipContents->Items[s];
if(!AnsiStrIComp(pzd->FileName.c_str(), zde->FileName.c_str()))
{
Found = true;
break;
}
}
}
if(!Found)
throw EZipBuilder(CF_SourceNotFound, zde->FileName, ZipFileName);
memcpy(&CEH.VersionMadeBy0, pzd, sizeof(ZipCentralHeader )- 4);
CEH.HeaderSig = CentralFileHeaderSig;
CEH.Flag &= 0x1FFF;
CEH.RelOffLocal = MDZD->Items[d]->RelOffLocal;
// Save the first Central directory offset for use in EOC record.
if(!d) EOC.CentralOffset = FileSeek(FOutFileHandle, 0, 1);
EOC.CentralSize += (sizeof(CEH) + CEH.FileNameLen + CEH.ExtraLen + CEH.FileComLen);
// Write this changed central header to disk
WriteJoin(&CEH, sizeof(CEH), DS_CEHBadWrite);
//if filename was converted OEM2ISO then we have to reconvert before copying
FHostNum = CEH.VersionMadeBy1;
FHostVer = CEH.VersionMadeBy0;
// StrCopy(MDZD->Items[d]->FileName,(ConvertOEM(MDZD->Items[d]->FileName, cpdISO2OEM)).c_str());
StrCopy(MDZD->Items[d]->FileName,SetSlash(ConvertOEM(MDZD->Items[d]->FileName, cpdISO2OEM), true).c_str());
// Write to destination the central filename.
WriteJoin(MDZD->Items[d]->FileName, CEH.FileNameLen, DS_CEHBadWrite);
// And the extra field from zde or pzd.
if(CEH.ExtraLen) WriteJoin(pzd->ExtraData.c_str(), CEH.ExtraLen, DS_CEExtraLen);
// And the file comment.
if(CEH.FileComLen) WriteJoin(pzd->FileComment.c_str(), CEH.FileComLen, DS_CECommentLen);
}
EOC.TotalEntries = EOC.CentralEntries = (unsigned short)DestMemCount;
EOC.ZipCommentLen = (unsigned short)DestZipBuilder->ZipComment.Length();
// Write the changed EndOfCentral directory record.
WriteJoin(&EOC, sizeof(EOC), DS_EOCBadWrite);
// And finally the archive comment
FileSeek(In2FileHandle, (SeekInt)DestZipBuilder->ZipEOC + sizeof(EOC), 0);
if(CopyBuffer(In2FileHandle, FOutFileHandle, DestZipBuilder->ZipComment.Length()))
throw EZipBuilder(DS_EOArchComLen);
if(FInFileHandle != -1 ) FileClose(FInFileHandle);
FInFileHandle = -1;
// Now delete all copied files from the source when deletion is wanted.
if(DeleteFromSource && FSpecArgs->Count > 0)
{
FZipBusy = false;
Delete(); // Delete files specified in FSpecArgs and update the contents.
FInFileHandle = -1; // closed by _Delete and _List
}
FSpecArgs->Assign(NotCopiedFiles); // Info for the caller.
}
catch(const EZipBuilder &ers)
{ // All CopyZippedFiles specific errors..
ShowExceptionError(ers);
Result = -7;
}
catch(const MEMEXCEPT &me)
{ // All memory allocation errors.
ShowZipMessage(GE_NoMem);
Result = -8;
}
catch(const Exception &E)
{
ShowZipMessage(DS_ErrorUnknown, "\n" + E.Message);
Result = -9;
}
catch( ... )
{ // The remaining errors, should not occur.
ShowZipMessage(DS_ErrorUnknown);
Result = -9;
}
if(MDZD) delete MDZD;
delete NotCopiedFiles;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -