ADD: Zip - more accurate time support (fixes #2555)

This commit is contained in:
Alexander Koblov 2025-10-25 15:12:54 +03:00
commit a8e1440aea
8 changed files with 187 additions and 294 deletions

View file

@ -243,9 +243,12 @@ begin
HeaderData.UnpSize := Lo(UncompressedSize);
HeaderData.UnpSizeHigh := Hi(UncompressedSize);
HeaderData.FileCRC := CRC32;
HeaderData.FileTime := NativeLastModFileTime;
HeaderData.FileAttr := NativeFileAttributes;
HeaderData.MfileTime := DateTimeToWinFileTime(LastModTimeAsDateTime);
HeaderData.MfileTime := LastWriteTime;
if (HeaderData.MfileTime = 0) then begin
HeaderData.FileTime := NativeLastModFileTime;
end;
if IsEncrypted then begin
HeaderData.Flags := RHDF_ENCRYPTED;

View file

@ -41,7 +41,8 @@ uses
{$ENDIF MSWINDOWS}
Classes,
Types,
AbUtils;
AbUtils,
DCBasicTypes;
{ ===== TAbArchiveItem ====================================================== }
type
@ -55,8 +56,7 @@ type
FExternalFileAttributes : LongWord;
FFileName : string;
FIsEncrypted : Boolean;
FLastModFileTime : Word;
FLastModFileDate : Word;
FLastWriteTime : TWinFileTime;
FTagged : Boolean;
FUncompressedSize : Int64;
@ -68,8 +68,7 @@ type
function GetFileName : string; virtual;
function GetIsDirectory: Boolean; virtual;
function GetIsEncrypted : Boolean; virtual;
function GetLastModFileDate : Word; virtual;
function GetLastModFileTime : Word; virtual;
function GetLastWriteTime: TWinFileTime; virtual;
{ This depends on in what format the attributes are stored in the archive,
to which system they refer (MS-DOS, Unix, etc.) and what system
we're running on (compile time). }
@ -85,8 +84,7 @@ type
procedure SetExternalFileAttributes( Value : LongWord ); virtual;
procedure SetFileName(const Value : string); virtual;
procedure SetIsEncrypted(Value : Boolean); virtual;
procedure SetLastModFileDate(const Value : Word); virtual;
procedure SetLastModFileTime(const Value : Word); virtual;
procedure SetLastWriteTime(AValue: TWinFileTime); virtual;
procedure SetUncompressedSize(const Value : Int64); virtual;
function GetLastModTimeAsDateTime: TDateTime; virtual;
procedure SetLastModTimeAsDateTime(const Value: TDateTime); virtual;
@ -125,12 +123,9 @@ type
property IsEncrypted : Boolean
read GetIsEncrypted
write SetIsEncrypted;
property LastModFileDate : Word
read GetLastModFileDate
write SetLastModFileDate;
property LastModFileTime : Word
read GetLastModFileTime
write SetLastModFileTime;
property LastWriteTime : TWinFileTime
read GetLastWriteTime
write SetLastWriteTime;
property NativeFileAttributes : LongInt
read GetNativeFileAttributes;
property NativeLastModFileTime : Longint
@ -613,7 +608,8 @@ uses
AbConst,
AbResString,
DCOSUtils,
DCClassesUtf8;
DCClassesUtf8,
DCDateTimeUtils;
{ TAbArchiveItem implementation ============================================ }
@ -625,8 +621,6 @@ begin
FUncompressedSize := 0;
FFileName := '';
FAction := aaNone;
FLastModFileTime := 0;
FLastModFileDate := 0;
end;
{ -------------------------------------------------------------------------- }
destructor TAbArchiveItem.Destroy;
@ -669,14 +663,9 @@ begin
Result := FIsEncrypted;
end;
{ -------------------------------------------------------------------------- }
function TAbArchiveItem.GetLastModFileTime : Word;
function TAbArchiveItem.GetLastWriteTime: TWinFileTime;
begin
Result := FLastModFileTime;
end;
{ -------------------------------------------------------------------------- }
function TAbArchiveItem.GetLastModFileDate : Word;
begin
Result := FLastModFileDate;
Result := FLastWriteTime;
end;
{ -------------------------------------------------------------------------- }
function TAbArchiveItem.GetNativeFileAttributes : LongInt;
@ -697,8 +686,7 @@ end;
{ -------------------------------------------------------------------------- }
function TAbArchiveItem.GetNativeLastModFileTime : Longint;
begin
LongRec(Result).Hi := LastModFileDate;
LongRec(Result).Lo := LastModFileTime;
Result := 0;
end;
{ -------------------------------------------------------------------------- }
function TAbArchiveItem.GetStoredPath : string;
@ -781,33 +769,24 @@ begin
FIsEncrypted := Value;
end;
{ -------------------------------------------------------------------------- }
procedure TAbArchiveItem.SetLastModFileDate(const Value : Word);
procedure TAbArchiveItem.SetLastWriteTime(AValue: TWinFileTime);
begin
FLastModFileDate := Value;
FLastWriteTime := AValue;
end;
{ -------------------------------------------------------------------------- }
procedure TAbArchiveItem.SetLastModFileTime(const Value : Word);
begin
FLastModFileTime := Value;
end;
{ -------------------------------------------------------------------------- }
procedure TAbArchiveItem.SetUnCompressedSize(const Value : Int64);
procedure TAbArchiveItem.SetUnCompressedSize(const Value: Int64);
begin
FUnCompressedSize := Value;
end;
{ -------------------------------------------------------------------------- }
function TAbArchiveItem.GetLastModTimeAsDateTime: TDateTime;
begin
Result := AbDosFileDateToDateTime(LastModFileDate, LastModFileTime);
Result := WinFileTimeToDateTime(FLastWriteTime);
end;
{ -------------------------------------------------------------------------- }
procedure TAbArchiveItem.SetLastModTimeAsDateTime(const Value: TDateTime);
var
FileDate : Integer;
begin
FileDate := AbDateTimeToDosFileDate(Value);
LastModFileTime := LongRec(FileDate).Lo;
LastModFileDate := LongRec(FileDate).Hi;
SetLastWriteTime(DateTimeToWinFileTime(Value));
end;
{ -------------------------------------------------------------------------- }
@ -1705,29 +1684,22 @@ end;
{ -------------------------------------------------------------------------- }
function TAbArchive.FreshenRequired(Item : TAbArchiveItem) : Boolean;
var
FS : TFileStreamEx;
DateTime : LongInt;
FileTime : Word;
FileDate : Word;
Matched : Boolean;
SaveDir : string;
SaveDir : String;
Matched : Boolean;
Attr : TAbAttrExRec;
FileTime : TWinFileTime;
begin
GetDir(0, SaveDir);
if BaseDirectory <> '' then
ChDir(BaseDirectory);
try
FS := TFileStreamEx.Create(Item.DiskFileName,
fmOpenRead or fmShareDenyWrite);
try
DateTime := FileGetDate(FS.Handle);
FileTime := LongRec(DateTime).Lo;
FileDate := LongRec(DateTime).Hi;
Matched := (Item.LastModFileDate = FileDate) and
(Item.LastModFileTime = FileTime) and
(Item.UncompressedSize = FS.Size);
if not AbFileGetAttrEx(Item.DiskFileName, Attr) then
Result := False
else begin
FileTime := FileTimeExToWinFileTime(Attr.Time);
Matched := (Item.LastWriteTime = FileTime) and
(Item.UncompressedSize = Attr.Size);
Result := not Matched;
finally
FS.Free;
end;
finally
if BaseDirectory <> '' then

View file

@ -46,7 +46,7 @@ unit AbGzTyp;
interface
uses
Classes, AbUtils, AbArcTyp, AbTarTyp;
Classes, AbUtils, AbArcTyp, AbTarTyp, DCBasicTypes;
type
{ pre-defined "operating system" (really more FILE system)
@ -138,16 +138,14 @@ type
function GetExternalFileAttributes : LongWord; override;
function GetIsEncrypted : Boolean; override;
function GetLastModFileDate : Word; override;
function GetLastModFileTime : Word; override;
function GetLastWriteTime: TWinFileTime; override;
function GetLastModTimeAsDateTime: TDateTime; override;
function GetNativeLastModFileTime: Longint; override;
procedure SetExternalFileAttributes( Value : LongWord ); override;
procedure SetFileName(const Value : string); override;
procedure SetIsEncrypted(Value : Boolean); override;
procedure SetLastModFileDate(const Value : Word); override;
procedure SetLastModFileTime(const Value : Word); override;
procedure SetLastWriteTime(AValue: TWinFileTime); override;
procedure SetLastModTimeAsDateTime(const Value: TDateTime); override;
procedure SaveGzHeaderToStream(AStream : TStream);
@ -295,7 +293,7 @@ implementation
uses
SysUtils, BufStream,
AbBitBkt, AbGz, AbZlibPrc, AbExcept, AbResString, AbProgress,
AbVMStrm, DCOSUtils, DCClassesUtf8, DCConvertEncoding;
AbVMStrm, DCOSUtils, DCClassesUtf8, DCConvertEncoding, DCDateTimeUtils;
const
{ Header Signature Values}
@ -700,16 +698,9 @@ begin
Result := (FGZHeader.Flags and AB_GZ_FLAG_FTEXT) = AB_GZ_FLAG_FTEXT;
end;
function TAbGzipItem.GetLastModFileDate: Word;
function TAbGzipItem.GetLastWriteTime: TWinFileTime;
begin
{ convert to local DOS file Date }
Result := LongRec(AbDateTimeToDosFileDate(LastModTimeAsDateTime)).Hi;
end;
function TAbGzipItem.GetLastModFileTime: Word;
begin
{ convert to local DOS file Time }
Result := LongRec(AbDateTimeToDosFileDate(LastModTimeAsDateTime)).Lo;
Result := UnixFileTimeToWinTime(TUnixFileTime(FGZHeader.ModTime));
end;
function TAbGzipItem.GetLastModTimeAsDateTime: TDateTime;
@ -718,16 +709,10 @@ begin
end;
function TAbGzipItem.GetNativeLastModFileTime: Longint;
{$IFDEF MSWINDOWS}
var
DateTime: TDateTime;
{$ENDIF}
begin
Result := FGZHeader.ModTime;
{$IFDEF MSWINDOWS}
DateTime := AbUnixTimeToLocalDateTime(Result);
Result := AbDateTimeToDosFileDate(DateTime);
Result := UnixFileTimeToDosTime(Result);
{$ENDIF}
end;
@ -857,26 +842,18 @@ begin
FGzHeader.Flags := FGzHeader.Flags and not AB_GZ_FLAG_FTEXT;
end;
procedure TAbGzipItem.SetLastModFileDate(const Value: Word);
procedure TAbGzipItem.SetLastWriteTime(AValue: TWinFileTime);
var
ATime: TUnixFileTime;
begin
{ replace date, keep existing time }
LastModTimeAsDateTime :=
EncodeDate(
Value shr 9 + 1980,
Value shr 5 and 15,
Value and 31) +
Frac(LastModTimeAsDateTime);
end;
procedure TAbGzipItem.SetLastModFileTime(const Value: Word);
begin
{ keep current date, replace time }
LastModTimeAsDateTime :=
Trunc(LastModTimeAsDateTime) +
EncodeTime(
Value shr 11,
Value shr 5 and 63,
Value and 31 shl 1, 0);
ATime:= WinFileTimeToUnixTime(AValue);
if ATime < Low(LongInt) then
FGZHeader.ModTime := Low(LongInt)
else if ATime > High(LongInt) then
FGZHeader.ModTime := High(LongInt)
else begin
FGZHeader.ModTime := LongInt(ATime);
end;
end;
procedure TAbGzipItem.SetLastModTimeAsDateTime(const Value: TDateTime);
@ -970,7 +947,7 @@ begin
finally {OutStream}
OutStream.Free;
end; {OutStream}
AbSetFileTime(UseName, CurItem.LastModTimeAsDateTime);
AbSetFileTime(UseName, CurItem.LastWriteTime);
AbSetFileAttr(UseName, CurItem.NativeFileAttributes);
except
on E : EAbUserAbort do begin
@ -1235,7 +1212,7 @@ begin
OutGzHelp.WriteArchiveTail;
end
else begin
CurItem.LastModTimeAsDateTime := AbGetFileTime(CurItem.DiskFileName);
CurItem.LastWriteTime := AbGetFileTime(CurItem.DiskFileName);
UncompressedStream := TFileStreamEx.Create(CurItem.DiskFileName,
fmOpenRead or fmShareDenyWrite);

View file

@ -42,7 +42,7 @@ interface
uses
Classes,
AbUtils, AbArcTyp;
AbUtils, AbArcTyp, DCBasicTypes;
const
AB_TAR_RECORDSIZE = 512; {Note: SizeOf(TAbTarHeaderRec) = AB_TAR_RECORDSIZE}
@ -317,8 +317,7 @@ type
function GetFileName : string; override;
function GetIsDirectory: Boolean; override;
function GetIsEncrypted : Boolean; override;
function GetLastModFileDate : Word; override;
function GetLastModFileTime : Word; override;
function GetLastWriteTime: TWinFileTime; override;
function GetLastModTimeAsDateTime: TDateTime; override;
function GetNativeFileAttributes : LongInt; override;
function GetNativeLastModFileTime: Longint; override;
@ -328,8 +327,7 @@ type
procedure SetExternalFileAttributes( Value : LongWord ); override;
procedure SetFileName(const Value : string); override; { Extended Headers }
procedure SetIsEncrypted(Value : Boolean); override;
procedure SetLastModFileDate(const Value : Word); override; { Extended Headers }
procedure SetLastModFileTime(const Value : Word); override; { Extended Headers }
procedure SetLastWriteTime(AValue: TWinFileTime); override;
procedure SetLastModTimeAsDateTime(const Value: TDateTime); override;
procedure SetUncompressedSize(const Value : Int64); override; { Extended Headers }
@ -474,7 +472,7 @@ implementation
uses
Math, RTLConsts, SysUtils, AbVMStrm, AbExcept, AbProgress,
DCOSUtils, DCClassesUtf8, DCConvertEncoding, DCStrUtils;
DCOSUtils, DCClassesUtf8, DCConvertEncoding, DCStrUtils, DCDateTimeUtils;
{ ****************** Helper functions Not from Classes Above ***************** }
function OctalToInt(const Oct : PAnsiChar; aLen : integer): Int64;
@ -788,16 +786,9 @@ begin
Result := False;
end;
function TAbTarItem.GetLastModFileDate: Word;
function TAbTarItem.GetLastWriteTime: TWinFileTime;
begin
{ convert to local DOS file Date }
Result := LongRec(AbDateTimeToDosFileDate(LastModTimeAsDateTime)).Hi;
end;
function TAbTarItem.GetLastModFileTime: Word;
begin
{ convert to local DOS file Time }
Result := LongRec(AbDateTimeToDosFileDate(LastModTimeAsDateTime)).Lo;
Result := UnixFileTimeToWinTime(FTarItem.ModTime);
end;
function TAbTarItem.GetLastModTimeAsDateTime: TDateTime;
@ -1721,26 +1712,9 @@ begin
{ do nothing, TAR has no native encryption }
end;
procedure TAbTarItem.SetLastModFileDate(const Value: Word);
procedure TAbTarItem.SetLastWriteTime(AValue: TWinFileTime);
begin
{ replace date, keep existing time }
LastModTimeAsDateTime :=
EncodeDate(
Value shr 9 + 1980,
Value shr 5 and 15,
Value and 31) +
Frac(LastModTimeAsDateTime);
end;
procedure TAbTarItem.SetLastModFileTime(const Value: Word);
begin
{ keep current date, replace time }
LastModTimeAsDateTime :=
Trunc(LastModTimeAsDateTime) +
EncodeTime(
Value shr 11,
Value shr 5 and 63,
Value and 31 shl 1, 0);
SetModTime(WinFileTimeToUnixTime(AValue));
end;
procedure TAbTarItem.SetLastModTimeAsDateTime(const Value: TDateTime);
@ -2299,7 +2273,7 @@ begin
end;
if (CurItem.Mode and $F000) <> AB_FMODE_FILELINK then
begin
AbSetFileTime(UseName, CurItem.LastModTimeAsDateTime);
AbSetFileTime(UseName, CurItem.LastWriteTime);
AbSetFileAttr(UseName, CurItem.NativeFileAttributes);
end;
end;
@ -2500,7 +2474,7 @@ begin
if not AbFileGetAttrEx(CurItem.DiskFileName, AttrEx, False) then
Raise EAbFileNotFound.Create;
CurItem.ExternalFileAttributes := AttrEx.Mode;
CurItem.LastModTimeAsDateTime := AttrEx.Time;
CurItem.LastWriteTime := FileTimeExToWinFileTime(AttrEx.Time);
{ TODO: uid, gid, uname, gname should be added here }
{ TODO: Add support for different types of files here }
case (AttrEx.Mode and $F000) of

View file

@ -1265,7 +1265,7 @@ begin
end;
if not FPS_ISLNK(Item.NativeFileAttributes) then
begin
AbSetFileTime(UseName, Item.LastModTimeAsDateTime);
AbSetFileTime(UseName, Item.LastWriteTime);
AbSetFileAttr(UseName, Item.NativeFileAttributes);
end;
end;

View file

@ -63,6 +63,7 @@ uses
{$IFDEF UNIX}
DCClassesUtf8,
{$ENDIF}
DCBasicTypes,
DateUtils,
SysUtils,
Classes;
@ -298,9 +299,9 @@ const
type
TAbAttrExRec = record
Time: TDateTime;
Size: Int64;
Attr: Integer;
Time: TFileTimeEx;
Mode: {$IFDEF UNIX}mode_t{$ELSE}Cardinal{$ENDIF};
end;
@ -329,8 +330,8 @@ const
function AbDosFileDateToDateTime(FileDate, FileTime : Word) : TDateTime;
function AbDateTimeToDosFileDate(Value : TDateTime) : LongInt;
function AbGetFileTime(const aFileName: string): TDateTime;
function AbSetFileTime(const aFileName: string; aValue: TDateTime): Boolean;
function AbGetFileTime(const aFileName: string): TWinFileTime;
function AbSetFileTime(const aFileName: string; aValue: TWinFileTime): Boolean;
{ file attributes }
function AbDOS2UnixFileAttributes(Attr: LongInt): LongInt;
@ -378,7 +379,6 @@ uses
AbExcept,
DCOSUtils,
DCStrUtils,
DCBasicTypes,
DCDateTimeUtils;
(*
@ -1041,55 +1041,16 @@ Result := Result * SecondsInMinute;
end;
{$ENDIF}
{ -------------------------------------------------------------------------- }
function AbUnixTimeToLocalDateTime(UnixTime : Int64) : TDateTime;
function AbUnixTimeToLocalDateTime(UnixTime : Int64) : TDateTime; inline;
{ convert UTC unix date to Delphi TDateTime in local timezone }
{$IFDEF MSWINDOWS}
var
Hrs, Mins, Secs : Word;
TodaysSecs : LongInt;
Time: TDateTime;
begin
UnixTime := UnixTime - AbOffsetFromUTC;
TodaysSecs := UnixTime mod SecondsInDay;
Hrs := TodaysSecs div SecondsInHour;
TodaysSecs := TodaysSecs - (Hrs * SecondsInHour);
Mins := TodaysSecs div SecondsInMinute;
Secs := TodaysSecs - (Mins * SecondsInMinute);
if TryEncodeTime(Hrs, Mins, Secs, 0, Time) then
Result := Unix0Date + (UnixTime div SecondsInDay) + Time
else
Result := 0;
{$ENDIF}
{$IFDEF UNIX}
begin
Result := UnixFileTimeToDateTime(TUnixFileTime(UnixTime));
{$ENDIF}
end;
{ -------------------------------------------------------------------------- }
function AbLocalDateTimeToUnixTime(DateTime : TDateTime) : Int64;
function AbLocalDateTimeToUnixTime(DateTime : TDateTime) : Int64; inline;
{ convert local Delphi TDateTime to UTC unix date }
{$IFDEF MSWINDOWS}
var
Hrs, Mins, Secs, MSecs : Word;
Dt, Tm : TDateTime;
begin
Dt := Trunc(DateTime);
Tm := DateTime - Dt;
if Dt < Unix0Date then
Result := 0
else
Result := Trunc(Dt - Unix0Date) * SecondsInDay;
DecodeTime(Tm, Hrs, Mins, Secs, MSecs);
Result := Result + (Hrs * SecondsInHour) + (Mins * SecondsInMinute) + Secs;
Result := Result + AbOffsetFromUTC;
{$ENDIF}
{$IFDEF UNIX}
begin
Result := Int64(DateTimeToUnixFileTime(DateTime));
{$ENDIF}
Result := DateTimeToUnixFileTime(DateTime);
end;
{ -------------------------------------------------------------------------- }
function AbDosFileDateToDateTime(FileDate, FileTime : Word) : TDateTime;
@ -1144,17 +1105,17 @@ end;
{ -------------------------------------------------------------------------- }
function AbGetFileTime(const aFileName: string): TDateTime;
function AbGetFileTime(const aFileName: string): TWinFileTime;
var
Attr: TAbAttrExRec;
begin
AbFileGetAttrEx(aFileName, Attr);
Result := Attr.Time;
Result := FileTimeExToWinFileTime(Attr.Time);
end;
function AbSetFileTime(const aFileName: string; aValue: TDateTime): Boolean;
function AbSetFileTime(const aFileName: string; aValue: TWinFileTime): Boolean;
begin
Result:= mbFileSetTime(aFileName, DateTimeToFileTime(aValue));
Result:= mbFileSetTimeEx(aFileName, WinFileTimeToFileTimeEx(aValue), TFileTimeExNull, TFileTimeExNull);
end;
{ -------------------------------------------------------------------------- }
@ -1243,65 +1204,49 @@ end;
{ -------------------------------------------------------------------------- }
function AbFileGetAttrEx(const aFileName: string; out aAttr: TAbAttrExRec; FollowLinks: Boolean = True) : Boolean;
var
{$IFDEF MSWINDOWS}
{$IF DEFINED(MSWINDOWS)}
FileDate: LongRec;
FindData: TWin32FindDataW;
LocalFileTime: Windows.TFileTime;
{$ENDIF}
{$IFDEF FPCUnixAPI}
{$ELSEIF DEFINED(UNIX)}
StatBuf: stat;
{$ENDIF}
{$IFDEF LibcAPI}
StatBuf: TStatBuf64;
{$ENDIF}
{$IFDEF PosixAPI}
StatBuf: _stat;
{$ENDIF}
begin
aAttr.Time := 0;
aAttr.Size := -1;
aAttr.Attr := -1;
aAttr.Mode := 0;
{$IFDEF MSWINDOWS}
aAttr.Time := TFileTimeExNull;
{$IF DEFINED(MSWINDOWS)}
Result := GetFileAttributesExW(PWideChar(UTF16LongName(aFileName)), GetFileExInfoStandard, @FindData);
if Result then begin
aAttr.Time := WinFileTimeToDateTime(FindData.ftLastWriteTime);
if Result then
begin
aAttr.Attr := FindData.dwFileAttributes;
LARGE_INTEGER(aAttr.Size).LowPart := FindData.nFileSizeLow;
LARGE_INTEGER(aAttr.Size).HighPart := FindData.nFileSizeHigh;
aAttr.Attr := FindData.dwFileAttributes;
aAttr.Mode := AbDOS2UnixFileAttributes(FindData.dwFileAttributes);
aAttr.Time := DCBasicTypes.TFileTimeEx(FindData.ftLastWriteTime);
end;
{$ENDIF}
{$IFDEF UNIX}
{$IFDEF FPCUnixAPI}
{$ELSEIF DEFINED(UNIX)}
if FollowLinks then
Result := (FpStat(UTF8ToSys(aFileName), StatBuf) = 0)
else
else begin
Result := (FpLStat(UTF8ToSys(aFileName), StatBuf) = 0);
{$ENDIF}
{$IFDEF LibcAPI}
// Work around Kylix QC#2761: Stat64, et al., are defined incorrectly
Result := (__lxstat64(_STAT_VER, PAnsiChar(aFileName), StatBuf) = 0);
{$ENDIF}
{$IFDEF PosixAPI}
Result := (stat(PAnsiChar(AbSysString(aFileName)), StatBuf) = 0);
{$ENDIF}
if Result then begin
aAttr.Time := FileDateToDateTime(StatBuf.st_mtime);
end;
if Result then
begin
aAttr.Size := StatBuf.st_size;
aAttr.Attr := AbUnix2DosFileAttributes(StatBuf.st_mode);
aAttr.Mode := StatBuf.st_mode;
aAttr.Attr := AbUnix2DosFileAttributes(StatBuf.st_mode);
aAttr.Time := TFileTimeEx.Create(Int64(StatBuf.st_mtime), Int64(StatBuf.st_mtime_nsec));
end;
{$ENDIF UNIX}
end;
const
MAX_VOL_LABEL = 16;
function AbGetVolumeLabel(Drive : Char) : string;
{-Get the volume label for the specified drive.}
{$IFDEF MSWINDOWS}
const
MAX_VOL_LABEL = 16;
var
Root : WideString;
Flags, MaxLength : DWORD;

View file

@ -57,7 +57,8 @@ uses
AbZlibPrc,
AbZipxPrc,
DCcrc32,
DCClassesUtf8;
DCClassesUtf8,
DCDateTimeUtils;
{ ========================================================================== }
@ -281,14 +282,10 @@ end;
{ -------------------------------------------------------------------------- }
procedure AbZipFromStream(Sender : TAbZipArchive; Item : TAbZipItem;
OutStream, InStream : TStream);
var
FileTimeStamp : LongInt;
begin
// Set item properties for non-file streams
Item.ExternalFileAttributes := 0;
FileTimeStamp := DateTimeToFileDate(SysUtils.Now);
Item.LastModFileTime := LongRec(FileTimeStamp).Lo;
Item.LastModFileDate := LongRec(FileTimeStamp).Hi;
Item.LastWriteTime := DateTimeToWinFileTime(SysUtils.Now);
DoZipFromStream(Sender, Item, OutStream, InStream);
end;
@ -297,21 +294,13 @@ procedure AbZip( Sender : TAbZipArchive; Item : TAbZipItem;
OutStream : TStream );
var
UncompressedStream : TStream;
AttrEx : TAbAttrExRec;
begin
if not AbFileGetAttrEx(Item.DiskFileName, AttrEx) then
Raise EAbFileNotFound.Create;
if ((AttrEx.Attr and faDirectory) <> 0) then
if ((Item.ExternalFileAttributes and faDirectory) <> 0) then
UncompressedStream := TMemoryStream.Create
else
else begin
UncompressedStream := TFileStreamEx.Create(Item.DiskFileName, fmOpenRead or fmShareDenyWrite);
end;
try {UncompressedStream}
{$IFDEF UNIX}
Item.ExternalFileAttributes := LongWord(AttrEx.Mode) shl 16 + LongWord(AttrEx.Attr);
{$ELSE}
Item.ExternalFileAttributes := AttrEx.Attr;
{$ENDIF}
Item.LastModTimeAsDateTime := AttrEx.Time;
DoZipFromStream(Sender, Item, OutStream, UncompressedStream);
finally {UncompressedStream}
UncompressedStream.Free;

View file

@ -39,7 +39,7 @@ unit AbZipTyp;
interface
uses
Classes, AbArcTyp, AbUtils, AbSpanSt;
Classes, AbArcTyp, AbUtils, AbSpanSt, DCBasicTypes;
const
{ note #$50 = 'P', #$4B = 'K'}
@ -387,10 +387,10 @@ type
TAbZipItem = class( TAbArchiveItem )
protected {private}
FItemInfo : TAbZipDirectoryFileHeader;
FLastWriteDosTime : TDosFileTime;
FDiskNumberStart : LongWord;
FLFHExtraField : TAbExtraField;
FRelativeOffset : Int64;
FDateTime : TDateTime;
protected {methods}
function GetCompressionMethod : TAbZipCompressionMethod;
@ -406,6 +406,8 @@ type
function GetShannonFanoTreeCount : Byte;
function GetVersionMadeBy : Word;
function GetVersionNeededToExtract : Word;
function GetLastModFileDate : Word;
function GetLastModFileTime : Word;
procedure SaveCDHToStream( Stream : TStream );
procedure SaveDDToStream( Stream : TStream );
procedure SaveLFHToStream( Stream : TStream );
@ -426,8 +428,7 @@ type
function GetExternalFileAttributes : LongWord; override;
function GetIsDirectory: Boolean; override;
function GetIsEncrypted : Boolean; override;
function GetLastModFileDate : Word; override;
function GetLastModFileTime : Word; override;
function GetLastWriteTime: TWinFileTime; override;
function GetNativeFileAttributes : LongInt; override;
function GetNativeLastModFileTime: Longint; override;
function GetLastModTimeAsDateTime: TDateTime; override;
@ -435,10 +436,8 @@ type
procedure SetCRC32( const Value : Longint ); override;
procedure SetExternalFileAttributes( Value : LongWord ); override;
procedure SetFileName(const Value : string ); override;
procedure SetLastModFileDate(const Value : Word ); override;
procedure SetLastModFileTime(const Value : Word ); override;
procedure SetLastWriteTime(AValue: TWinFileTime); override;
procedure SetUncompressedSize( const Value : Int64 ); override;
procedure SetLastModTimeAsDateTime(const Value: TDateTime); override;
public {methods}
constructor Create;
@ -472,6 +471,10 @@ type
property GeneralPurposeBitFlag : Word
read GetGeneralPurposeBitFlag
write SetGeneralPurposeBitFlag;
property LastModFileDate : Word
read GetLastModFileDate;
property LastModFileTime : Word
read GetLastModFileTime;
property LFHExtraField : TAbExtraField
read FLFHExtraField;
property RawFileName : AnsiString
@ -652,7 +655,6 @@ uses
LazUTF8,
DCOSUtils,
DCStrUtils,
DCBasicTypes,
DCClassesUtf8,
DCDateTimeUtils,
DCConvertEncoding;
@ -1421,14 +1423,13 @@ begin
Result := FItemInfo.IsEncrypted;
end;
{ -------------------------------------------------------------------------- }
function TAbZipItem.GetLastModFileDate : Word;
function TAbZipItem.GetLastWriteTime: TWinFileTime;
begin
Result := FItemInfo.LastModFileDate;
end;
{ -------------------------------------------------------------------------- }
function TAbZipItem.GetLastModFileTime : Word;
begin
Result := FItemInfo.LastModFileTime;
if FLastWriteTime <> 0 then
Result := FLastWriteTime
else begin
Result := DosTimeToWinFileTime(FLastWriteDosTime);
end;
end;
{ -------------------------------------------------------------------------- }
function TAbZipItem.GetNativeFileAttributes : LongInt;
@ -1460,35 +1461,29 @@ begin
end;
{ -------------------------------------------------------------------------- }
function TAbZipItem.GetNativeLastModFileTime: Longint;
{$IFDEF UNIX}
var
DateTime: TDateTime;
{$ENDIF}
begin
// Zip stores MS-DOS date/time.
{$IFDEF UNIX}
if (FDateTime <> 0) then
DateTime := FDateTime
if (FLastWriteTime <> 0) then
Result := Longint(WinFileTimeToUnixTime(FLastWriteTime))
else begin
DateTime := AbDosFileDateToDateTime(LastModFileDate, LastModFileTime);
Result := Longint(DosTimeToUnixFileTime(FLastWriteDosTime));
end;
Result := DateTimeToUnixFileTime(DateTime);
{$ELSE}
if (FDateTime <> 0) then
Result := DateTimeToDosFileTime(FDateTime)
if (FLastWriteTime <> 0) then
Result := Longint(WinFileTimeToDosTime(FLastWriteTime))
else begin
LongRec(Result).Hi := LastModFileDate;
LongRec(Result).Lo := LastModFileTime;
Result := Longint(FLastWriteDosTime);
end;
{$ENDIF}
end;
{ -------------------------------------------------------------------------- }
function TAbZipItem.GetLastModTimeAsDateTime: TDateTime;
begin
if (FDateTime <> 0) then
Result := FDateTime
if (FLastWriteTime <> 0) then
Result := WinFileTimeToDateTime(FLastWriteTime)
else
Result := AbDosFileDateToDateTime(FItemInfo.LastModFileDate, FItemInfo.LastModFileTime);
Result := DosFileTimeToDateTime(FLastWriteDosTime);
end;
{ -------------------------------------------------------------------------- }
function TAbZipItem.GetShannonFanoTreeCount : Byte;
@ -1506,6 +1501,16 @@ begin
Result := FItemInfo.VersionNeededToExtract;
end;
{ -------------------------------------------------------------------------- }
function TAbZipItem.GetLastModFileDate: Word;
begin
Result := LongRec(FLastWriteDosTime).Hi;
end;
{ -------------------------------------------------------------------------- }
function TAbZipItem.GetLastModFileTime: Word;
begin
Result := LongRec(FLastWriteDosTime).Lo;
end;
{ -------------------------------------------------------------------------- }
procedure TAbZipItem.LoadFromStream( Stream : TStream );
var
Tag, TagSize,
@ -1566,8 +1571,8 @@ begin
FieldStream.Free;
end;
LastModFileTime := FItemInfo.LastModFileTime;
LastModFileDate := FItemInfo.LastModFileDate;
LongRec(FLastWriteDosTime).Lo := FItemInfo.LastModFileTime;
LongRec(FLastWriteDosTime).Hi := FItemInfo.LastModFileDate;
// NTFS Extra Field
if FItemInfo.ExtraField.GetStream(Ab_NTFSSubfieldID, FieldStream) then
try
@ -1585,7 +1590,7 @@ begin
TagSize:= Min(TagSize, FieldSize);
if (Tag = $0001) and (TagSize >= 24) then
begin
FDateTime:= WinFileTimeToDateTime(TWinFileTime(FieldStream.ReadQWord));
FLastWriteTime:= TWinFileTime(FieldStream.ReadQWord);
Break;
end;
Dec(FieldSize, TagSize);
@ -1602,7 +1607,7 @@ begin
begin
Tag:= FieldStream.ReadByte;
if (Tag and $01 <> 0) then
FDateTime:= UnixFileTimeToDateTime(TUnixFileTime(FieldStream.ReadDWord));
FLastWriteTime:= UnixFileTimeToWinTime(TUnixFileTime(FieldStream.ReadDWord));
end;
finally
FieldStream.Free;
@ -1630,6 +1635,7 @@ begin
LFH.ExtraField.Assign(LFHExtraField);
LFH.ExtraField.CloneFrom(ExtraField, Ab_InfoZipUnicodePathSubfieldID);
LFH.ExtraField.CloneFrom(ExtraField, Ab_XceedUnicodePathSubfieldID);
LFH.ExtraField.CloneFrom(ExtraField, Ab_InfoZipTimestampSubfieldID);
{ Write ZIP64 local header when file size > 3 GB to speed up archive creation }
if (UncompressedSize > $C0000000) then
begin
@ -1798,16 +1804,6 @@ begin
FItemInfo.InternalFileAttributes := Value;
end;
{ -------------------------------------------------------------------------- }
procedure TAbZipItem.SetLastModFileDate( const Value : Word );
begin
FItemInfo.LastModFileDate := Value;
end;
{ -------------------------------------------------------------------------- }
procedure TAbZipItem.SetLastModFileTime( const Value : Word );
begin
FItemInfo.LastModFileTime := Value;
end;
{ -------------------------------------------------------------------------- }
procedure TAbZipItem.SetRelativeOffset( Value : Int64 );
begin
FRelativeOffset := Value;
@ -1815,20 +1811,20 @@ begin
UpdateZip64ExtraHeader;
end;
{ -------------------------------------------------------------------------- }
procedure TAbZipItem.SetUncompressedSize( const Value : Int64 );
begin
FUncompressedSize := Value;
FItemInfo.UncompressedSize:= Min(Value, $FFFFFFFF);
UpdateZip64ExtraHeader;
end;
procedure TAbZipItem.SetLastModTimeAsDateTime(const Value: TDateTime);
procedure TAbZipItem.SetLastWriteTime(AValue: TWinFileTime);
var
DataSize: Word;
AUnixTime: TUnixFileTime;
ANtfsTime: PNtfsTimeField;
AInfoZipTime: PInfoZipTimeField;
begin
inherited SetLastModTimeAsDateTime(Value);
inherited SetLastWriteTime(AValue);
FLastWriteDosTime:= WinFileTimeToDosTime(AValue);
// Update central directory header fields
FItemInfo.LastModFileTime:= LastModFileTime;
FItemInfo.LastModFileDate:= LastModFileDate;
// Update time extra fields
if FItemInfo.ExtraField.Get(Ab_NTFSSubfieldID, ANtfsTime, DataSize) then
@ -1837,7 +1833,7 @@ begin
begin
if ANtfsTime^.Tag = $0001 then
begin
ANtfsTime^.Mtime := DateTimeToWinFileTime(Value);
ANtfsTime^.Mtime := AValue;
end;
end;
end
@ -1847,12 +1843,37 @@ begin
begin
if (AInfoZipTime^.Tag and $01 <> 0) then
begin
AInfoZipTime^.Mtime := UInt32(DateTimeToUnixFileTime(Value));
AUnixTime:= WinFileTimeToUnixTime(AValue);
if (AUnixTime >= 0) and (AUnixTime <= High(UInt32)) then
AInfoZipTime^.Mtime := UInt32(AUnixTime)
else begin
FItemInfo.ExtraField.Delete(Ab_InfoZipTimestampSubfieldID);
end
end;
end;
end
else begin
AUnixTime:= WinFileTimeToUnixTime(AValue);
if (AUnixTime >= 0) and (AUnixTime <= High(UInt32)) then
begin
New(AInfoZipTime);
try
AInfoZipTime^.Tag:= $01;
AInfoZipTime^.Mtime := UInt32(AUnixTime);
FItemInfo.ExtraField.Put(Ab_InfoZipTimestampSubfieldID, AInfoZipTime^, SizeOf(TInfoZipTimeField));
finally
Dispose(AInfoZipTime);
end;
end;
end;
end;
{ -------------------------------------------------------------------------- }
procedure TAbZipItem.SetUncompressedSize( const Value : Int64 );
begin
FUncompressedSize := Value;
FItemInfo.UncompressedSize:= Min(Value, $FFFFFFFF);
UpdateZip64ExtraHeader;
end;
{ -------------------------------------------------------------------------- }
procedure TAbZipItem.SetVersionMadeBy( Value : Word );
begin
@ -2266,6 +2287,7 @@ var
Progress : Byte;
ATempName : String;
CreateArchive : Boolean;
AttrEx : TAbAttrExRec;
begin
if Count = 0 then
Exit;
@ -2343,6 +2365,17 @@ begin
aaAdd, aaFreshen, aaReplace, aaStreamAdd: begin
{compress the file and add it to new stream}
try
if not AbFileGetAttrEx(CurrItem.DiskFileName, AttrEx) then
Raise EAbFileNotFound.Create;
{$IFDEF UNIX}
CurrItem.ExternalFileAttributes := LongWord(AttrEx.Mode) shl 16 + LongWord(AttrEx.Attr);
{$ELSE}
CurrItem.ExternalFileAttributes := AttrEx.Attr;
{$ENDIF}
CurrItem.LastWriteTime := FileTimeExToWinFileTime(AttrEx.Time);
if NewStream is TAbSpanWriteStream then
begin
WorkingStream := TAbVirtualMemoryStream.Create;