| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013 |
- (**
- Copyright (c) 2000-2010 by Stefan Heymann
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- ===============================================================================================
- Name : LibTar
- ===============================================================================================
- Subject : Handling of "tar" files
- ===============================================================================================
- Author : Stefan Heymann
- Eschenweg 3
- 72076 T�bingen
- GERMANY
- E-Mail: [email protected]
- Web: www.destructor.de
- ===============================================================================================
- TTarArchive Usage
- -----------------
- - Choose a constructor
- - Make an instance of TTarArchive TA := TTarArchive.Create (Filename);
- - Scan through the archive TA.Reset;
- WHILE TA.FindNext (DirRec) DO BEGIN
- - Evaluate the DirRec for each file ListBox.Items.Add (DirRec.Name);
- - Read out the current file TA.ReadFile (DestFilename);
- (You can ommit this if you want to
- read in the directory only) END;
- - You're done TA.Free;
- TTarWriter Usage
- ----------------
- - Choose a constructor
- - Make an instance of TTarWriter TW := TTarWriter.Create ('my.tar');
- - Add a file to the tar archive TW.AddFile ('foobar.txt');
- - Add a string as a file TW.AddString (SL.Text, 'joe.txt', Now);
- - Destroy TarWriter instance TW.Free;
- - Now your tar file is ready.
- Source
- --------------------------
- The official site to get this code is http://www.destructor.de/
- Donateware
- ----------
- If you like this code, you are free to donate
- http://www.destructor.de/donateware.htm
- ===============================================================================================
- !!! All parts of this code which are not finished or known to be buggy
- are marked with three exclamation marks
- ===============================================================================================
- Date Author Changes
- -----------------------------------------------------------------------------------------------
- 2001-04-26 HeySt 0.0.1 Start
- 2001-04-28 HeySt 1.0.0 First Release
- 2001-06-19 HeySt 2.0.0 Finished TTarWriter
- 2001-09-06 HeySt 2.0.1 Bugfix in TTarArchive.FindNext: FBytesToGo must sometimes be 0
- 2001-10-25 HeySt 2.0.2 Introduced the ClearDirRec procedure
- 2001-11-13 HeySt 2.0.3 Bugfix: Take out ClearDirRec call from WriteTarHeader
- Bug Reported by Tony BenBrahim
- 2001-12-25 HeySt 2.0.4 WriteTarHeader: Fill Rec with zero bytes before filling it
- 2002-05-18 HeySt 2.0.5 Kylix awareness: Thanks to Kerry L. Davison for the canges
- 2005-09-03 HeySt 2.0.6 TTarArchive.FindNext: Don't access SourceStream.Size
- (for compressed streams, which don't know their .Size)
- 2006-03-13 HeySt 2.0.7 Bugfix in ReadFile (Buffer : POINTER)
- 2006-09-20 MvdV 2.0.7.1 Small fixes for FPC.
- 2007-05-16 HeySt 2.0.8 Bugfix in TTarWriter.AddFile (Convertfilename in the ELSE branch)
- Bug Reported by Chris Rorden
- 2010-11-29 HeySt 2.1.0 WriteTarHeader: Mode values for ftNormal/ftLink/ftSymbolicLink/ftDirectory
- Thanks to Iouri Kharon for the fix.
- Still no support for filenames > 100 bytes. Sorry.
- Support for Unicode Delphi versions (2009, 2010, XE, etc.)
- MvdV 2.1.0 notes : not all of the Unicode changes have been made, decisions on this subject still pending on the FPC side.
- Mostly rawbytestring and a couple of more hary typecasts.
- *)
- {$IFNDEF FPC_DOTTEDUNITS}
- UNIT libtar;
- {$ENDIF FPC_DOTTEDUNITS}
- INTERFACE
- {$IFDEF FPC}
- {$MODE Delphi}
- {$ELSE}
- {$IFDEF LINUX}
- {$DEFINE Kylix}
- {$DEFINE LIBCUNIT}
- {$ENDIF}
- {$ENDIF}
- {$IFDEF FPC_DOTTEDUNITS}
- USES
- {$IFDEF LIBCUNIT}
- Api.Libc, // MvdV: Nothing is used from this???
- {$ENDIF}
- {$ifdef Unix}
- UnixApi.Types, UnixApi.Base, UnixApi.Unix,
- {$endif}
- (*$IFDEF MSWINDOWS *)
- WinApi.Windows,
- (*$ENDIF *)
- System.SysUtils, System.Classes;
- {$ELSE FPC_DOTTEDUNITS}
- USES
- {$IFDEF LIBCUNIT}
- Libc, // MvdV: Nothing is used from this???
- {$ENDIF}
- {$ifdef Unix}
- UnixType, BaseUnix, Unix,
- {$endif}
- (*$IFDEF MSWINDOWS *)
- Windows,
- (*$ENDIF *)
- SysUtils, Classes;
- {$ENDIF FPC_DOTTEDUNITS}
- TYPE
- // --- File Access Permissions
- TTarPermission = (tpReadByOwner, tpWriteByOwner, tpExecuteByOwner,
- tpReadByGroup, tpWriteByGroup, tpExecuteByGroup,
- tpReadByOther, tpWriteByOther, tpExecuteByOther);
- TTarPermissions = SET OF TTarPermission;
- // --- Type of File
- TFileType = (ftNormal, // Regular file
- ftLink, // Link to another, previously archived, file (LinkName)
- ftSymbolicLink, // Symbolic link to another file (LinkName)
- ftCharacter, // Character special files
- ftBlock, // Block special files
- ftDirectory, // Directory entry. Size is zero (unlimited) or max. number of bytes
- ftFifo, // FIFO special file. No data stored in the archive.
- ftContiguous, // Contiguous file, if supported by OS
- ftDumpDir, // List of files
- ftMultiVolume, // Multi-volume file part
- ftVolumeHeader); // Volume header. Can appear only as first record in the archive
- // --- Mode
- TTarMode = (tmSetUid, tmSetGid, tmSaveText);
- TTarModes = SET OF TTarMode;
- // --- Record for a Directory Entry
- // Adjust the ClearDirRec procedure when this record changes!
- TTarDirRec = RECORD
- Name : AnsiString; // File path and name
- Size : INT64; // File size in Bytes
- DateTime : TDateTime; // Last modification date and time
- Permissions : TTarPermissions; // Access permissions
- FileType : TFileType; // Type of file
- LinkName : AnsiString; // Name of linked file (for ftLink, ftSymbolicLink)
- UID : INTEGER; // User ID
- GID : INTEGER; // Group ID
- UserName : AnsiString; // User name
- GroupName : AnsiString; // Group name
- ChecksumOK : BOOLEAN; // Checksum was OK
- Mode : TTarModes; // Mode
- Magic : AnsiString; // Contents of the "Magic" field
- MajorDevNo : INTEGER; // Major Device No. for ftCharacter and ftBlock
- MinorDevNo : INTEGER; // Minor Device No. for ftCharacter and ftBlock
- FilePos : INT64; // Position in TAR file
- END;
- // --- The TAR Archive CLASS
- TTarArchive = CLASS
- PROTECTED
- FStream : TStream; // Internal Stream
- FOwnsStream : BOOLEAN; // True if FStream is owned by the TTarArchive instance
- FBytesToGo : INT64; // Bytes until the next Header Record
- PUBLIC
- CONSTRUCTOR Create (Stream : TStream); OVERLOAD;
- CONSTRUCTOR Create (Filename : STRING;
- FileMode : WORD = fmOpenRead OR fmShareDenyWrite); OVERLOAD;
- DESTRUCTOR Destroy; OVERRIDE;
- PROCEDURE Reset; // Reset File Pointer
- FUNCTION FindNext (VAR DirRec : TTarDirRec) : BOOLEAN; // Reads next Directory Info Record. FALSE if EOF reached
- PROCEDURE ReadFile (Buffer : POINTER); OVERLOAD; // Reads file data for last Directory Record
- PROCEDURE ReadFile (Stream : TStream); OVERLOAD; // -;-
- PROCEDURE ReadFile (Filename : STRING); OVERLOAD; // -;-
- FUNCTION ReadFile : RawByteString; OVERLOAD; // -;- RawByteString in D2009+. Not active due to FPC unicode architecture not being finalized
- PROCEDURE GetFilePos (VAR Current, Size : INT64); // Current File Position
- PROCEDURE SetFilePos (NewPos : INT64); // Set new Current File Position
- END;
- // --- The TAR Archive Writer CLASS
- TTarWriter = CLASS
- PROTECTED
- FStream : TStream;
- FOwnsStream : BOOLEAN;
- FFinalized : BOOLEAN;
- // --- Used at the next "Add" method call: ---
- FPermissions : TTarPermissions; // Access permissions
- FUID : INTEGER; // User ID
- FGID : INTEGER; // Group ID
- FUserName : AnsiString; // User name
- FGroupName : AnsiString; // Group name
- FMode : TTarModes; // Mode
- FMagic : AnsiString; // Contents of the "Magic" field
- CONSTRUCTOR CreateEmpty;
- PUBLIC
- CONSTRUCTOR Create (TargetStream : TStream); OVERLOAD;
- CONSTRUCTOR Create (TargetFilename : STRING; Mode : INTEGER = fmCreate); OVERLOAD;
- DESTRUCTOR Destroy; OVERRIDE; // Writes End-Of-File Tag
- PROCEDURE AddFile (Filename : STRING; TarFilename : AnsiString = '');
- PROCEDURE AddStream (Stream : TStream; TarFilename : AnsiString; FileDateGmt : TDateTime);
- PROCEDURE AddString (Contents : Ansistring; TarFilename : AnsiString; FileDateGmt : TDateTime); // RawByteString
- PROCEDURE AddDir (Dirname : AnsiString; DateGmt : TDateTime; MaxDirSize : INT64 = 0);
- PROCEDURE AddSymbolicLink (Filename, Linkname : AnsiString; DateGmt : TDateTime);
- PROCEDURE AddLink (Filename, Linkname : AnsiString; DateGmt : TDateTime);
- PROCEDURE AddVolumeHeader (VolumeId : AnsiString; DateGmt : TDateTime);
- PROCEDURE Finalize;
- PROPERTY Permissions : TTarPermissions READ FPermissions WRITE FPermissions; // Access permissions
- PROPERTY UID : INTEGER READ FUID WRITE FUID; // User ID
- PROPERTY GID : INTEGER READ FGID WRITE FGID; // Group ID
- PROPERTY UserName : AnsiString READ FUserName WRITE FUserName; // User name
- PROPERTY GroupName : AnsiString READ FGroupName WRITE FGroupName; // Group name
- PROPERTY Mode : TTarModes READ FMode WRITE FMode; // Mode
- PROPERTY Magic : AnsiString READ FMagic WRITE FMagic; // Contents of the "Magic" field
- END;
- // --- Some useful constants
- CONST
- FILETYPE_NAME : ARRAY [TFileType] OF STRING =
- ('Regular', 'Link', 'Symbolic Link', 'AnsiChar File', 'Block File',
- 'Directory', 'FIFO File', 'Contiguous', 'Dir Dump', 'Multivol', 'Volume Header');
- ALL_PERMISSIONS = [tpReadByOwner, tpWriteByOwner, tpExecuteByOwner,
- tpReadByGroup, tpWriteByGroup, tpExecuteByGroup,
- tpReadByOther, tpWriteByOther, tpExecuteByOther];
- READ_PERMISSIONS = [tpReadByOwner, tpReadByGroup, tpReadByOther];
- WRITE_PERMISSIONS = [tpWriteByOwner, tpWriteByGroup, tpWriteByOther];
- EXECUTE_PERMISSIONS = [tpExecuteByOwner, tpExecuteByGroup, tpExecuteByOther];
- FUNCTION PermissionString (Permissions : TTarPermissions) : STRING;
- FUNCTION ConvertFilename (Filename : STRING) : STRING;
- FUNCTION FileTimeGMT (FileName : STRING) : TDateTime; OVERLOAD;
- FUNCTION FileTimeGMT (SearchRec : TSearchRec) : TDateTime; OVERLOAD;
- PROCEDURE ClearDirRec (VAR DirRec : TTarDirRec);
- (*
- ===============================================================================================
- IMPLEMENTATION
- ===============================================================================================
- *)
- IMPLEMENTATION
- FUNCTION PermissionString (Permissions : TTarPermissions) : STRING;
- BEGIN
- Result := '';
- IF tpReadByOwner IN Permissions THEN Result := Result + 'r' ELSE Result := Result + '-';
- IF tpWriteByOwner IN Permissions THEN Result := Result + 'w' ELSE Result := Result + '-';
- IF tpExecuteByOwner IN Permissions THEN Result := Result + 'x' ELSE Result := Result + '-';
- IF tpReadByGroup IN Permissions THEN Result := Result + 'r' ELSE Result := Result + '-';
- IF tpWriteByGroup IN Permissions THEN Result := Result + 'w' ELSE Result := Result + '-';
- IF tpExecuteByGroup IN Permissions THEN Result := Result + 'x' ELSE Result := Result + '-';
- IF tpReadByOther IN Permissions THEN Result := Result + 'r' ELSE Result := Result + '-';
- IF tpWriteByOther IN Permissions THEN Result := Result + 'w' ELSE Result := Result + '-';
- IF tpExecuteByOther IN Permissions THEN Result := Result + 'x' ELSE Result := Result + '-';
- END;
- FUNCTION ConvertFilename (Filename : STRING) : STRING;
- // Converts the filename to Unix conventions
- // could be empty and inlined away for FPC. FPC I/O should be
- // forward/backward slash safe.
- BEGIN
- (*$IFDEF Unix *)
- Result := Filename;
- (*$ELSE *)
- Result := StringReplace (Filename, '\', '/', [rfReplaceAll]);
- (*$ENDIF *)
- END;
- FUNCTION FileTimeGMT (FileName: STRING): TDateTime;
- // Returns the Date and Time of the last modification of the given File
- // The Result is zero if the file could not be found
- // The Result is given in UTC (GMT) time zone
- VAR
- SR : TSearchRec;
- BEGIN
- Result := 0.0;
- IF FindFirst (FileName, faAnyFile, SR) = 0 THEN
- Result := FileTimeGMT (SR);
- FindClose (SR);
- END;
- FUNCTION FileTimeGMT (SearchRec : TSearchRec) : TDateTime;
- (*$IFDEF MSWINDOWS *)
- VAR
- SystemFileTime: TSystemTime;
- (*$ENDIF *)
- (*$IFDEF Unix *)
- VAR
- TimeVal : TTimeVal;
- TimeZone : TTimeZone;
- (*$ENDIF *)
- BEGIN
- Result := 0.0;
- (*$IFDEF MSWINDOWS *) (*$WARNINGS OFF *)
- IF (SearchRec.FindData.dwFileAttributes AND faDirectory) = 0 THEN
- IF FileTimeToSystemTime (SearchRec.FindData.ftLastWriteTime, SystemFileTime) THEN
- Result := EncodeDate (SystemFileTime.wYear, SystemFileTime.wMonth, SystemFileTime.wDay)
- + EncodeTime (SystemFileTime.wHour, SystemFileTime.wMinute, SystemFileTime.wSecond, SystemFileTime.wMilliseconds);
- (*$ENDIF *) (*$WARNINGS ON *)
- (*$IFDEF Unix *)
- IF SearchRec.Attr AND faDirectory = 0 THEN BEGIN
- FillChar(TimeVal, SizeOf(TimeVal), #0);
- FillChar(TimeZone, SizeOf(TimeZone), #0);
- Result := SearchRec.TimeStamp;
- {$IFDEF Kylix}
- GetTimeOfDay (TimeVal, TimeZone);
- {$ELSE}
- fpGetTimeOfDay (@TimeVal, @TimeZone);
- {$ENDIF}
- Result := Result + TimeZone.tz_minuteswest / (60 * 24);
- END;
- (*$ENDIF *)
- end;
- PROCEDURE ClearDirRec (VAR DirRec : TTarDirRec);
- // This is included because a FillChar (DirRec, SizeOf (DirRec), 0)
- // will destroy the long string pointers, leading to strange bugs
- BEGIN
- WITH DirRec DO BEGIN
- Name := '';
- Size := 0;
- DateTime := 0.0;
- Permissions := [];
- FileType := TFileType (0);
- LinkName := '';
- UID := 0;
- GID := 0;
- UserName := '';
- GroupName := '';
- ChecksumOK := FALSE;
- Mode := [];
- Magic := '';
- MajorDevNo := 0;
- MinorDevNo := 0;
- FilePos := 0;
- END;
- END;
- (*
- ===============================================================================================
- TAR format
- ===============================================================================================
- *)
- CONST
- RECORDSIZE = 512;
- NAMSIZ = 100;
- TUNMLEN = 32;
- TGNMLEN = 32;
- CHKBLANKS = #32#32#32#32#32#32#32#32;
- TYPE
- TTarHeader = PACKED RECORD
- Name : ARRAY [0..NAMSIZ-1] OF AnsiChar;
- Mode : ARRAY [0..7] OF AnsiChar;
- UID : ARRAY [0..7] OF AnsiChar;
- GID : ARRAY [0..7] OF AnsiChar;
- Size : ARRAY [0..11] OF AnsiChar;
- MTime : ARRAY [0..11] OF AnsiChar;
- ChkSum : ARRAY [0..7] OF AnsiChar;
- LinkFlag : AnsiChar;
- LinkName : ARRAY [0..NAMSIZ-1] OF AnsiChar;
- Magic : ARRAY [0..7] OF AnsiChar;
- UName : ARRAY [0..TUNMLEN-1] OF AnsiChar;
- GName : ARRAY [0..TGNMLEN-1] OF AnsiChar;
- DevMajor : ARRAY [0..7] OF AnsiChar;
- DevMinor : ARRAY [0..7] OF AnsiChar;
- END;
- FUNCTION ExtractText (P : PAnsiChar) : AnsiString;
- BEGIN
- Result := AnsiString(P);
- END;
- FUNCTION ExtractNumber (P : PAnsiChar) : INTEGER; OVERLOAD;
- VAR
- Strg : AnsiString;
- BEGIN
- Strg := Trim (StrPas (P));
- P := PAnsiChar (Strg);
- Result := 0;
- WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN
- Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3);
- INC (P);
- END;
- END;
- FUNCTION ExtractNumber64 (P : PAnsiChar) : INT64; OVERLOAD;
- VAR
- Strg : AnsiString;
- BEGIN
- Strg := Trim (StrPas (P));
- P := PAnsiChar (Strg);
- Result := 0;
- WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN
- Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3);
- INC (P);
- END;
- END;
- FUNCTION ExtractNumber (P : PAnsiChar; MaxLen : INTEGER) : INTEGER; OVERLOAD;
- VAR
- S0 : ARRAY [0..255] OF AnsiChar;
- Strg : AnsiString;
- BEGIN
- StrLCopy (S0, P, MaxLen);
- Strg := Trim (StrPas (S0));
- P := PAnsiChar (Strg);
- Result := 0;
- WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN
- Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3);
- INC (P);
- END;
- END;
- FUNCTION ExtractNumber64 (P : PAnsiChar; MaxLen : INTEGER) : INT64; OVERLOAD;
- VAR
- S0 : ARRAY [0..255] OF AnsiChar;
- Strg : AnsiString;
- BEGIN
- StrLCopy (S0, P, MaxLen);
- Strg := Trim (StrPas (S0));
- P := PAnsiChar (Strg);
- Result := 0;
- WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN
- Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3);
- INC (P);
- END;
- END;
- FUNCTION Records (Bytes : INT64) : INT64;
- BEGIN
- Result := Bytes DIV RECORDSIZE;
- IF Bytes MOD RECORDSIZE > 0 THEN
- INC (Result);
- END;
- PROCEDURE Octal (N : INTEGER; P : PAnsiChar; Len : INTEGER);
- // Makes a string of octal digits
- // The string will always be "Len" characters long
- VAR
- I : INTEGER;
- BEGIN
- FOR I := Len-2 DOWNTO 0 DO BEGIN
- (P+I)^ := AnsiChar (ORD ('0') + ORD (N AND $07));
- N := N SHR 3;
- END;
- FOR I := 0 TO Len-3 DO
- IF (P+I)^ = '0'
- THEN (P+I)^ := #32
- ELSE BREAK;
- (P+Len-1)^ := #32;
- END;
- PROCEDURE Octal64 (N : INT64; P : PAnsiChar; Len : INTEGER);
- // Makes a string of octal digits
- // The string will always be "Len" characters long
- VAR
- I : INTEGER;
- BEGIN
- FOR I := Len-2 DOWNTO 0 DO BEGIN
- (P+I)^ := AnsiChar (ORD ('0') + ORD (N AND $07));
- N := N SHR 3;
- END;
- FOR I := 0 TO Len-3 DO
- IF (P+I)^ = '0'
- THEN (P+I)^ := #32
- ELSE BREAK;
- (P+Len-1)^ := #32;
- END;
- PROCEDURE OctalN (N : INTEGER; P : PAnsiChar; Len : INTEGER);
- BEGIN
- Octal (N, P, Len-1);
- (P+Len-1)^ := #0;
- END;
- PROCEDURE WriteTarHeader (Dest : TStream; DirRec : TTarDirRec);
- VAR
- Rec : ARRAY [0..RECORDSIZE-1] OF AnsiChar;
- TH : TTarHeader ABSOLUTE Rec;
- Mode : INTEGER;
- NullDate : TDateTime;
- Checksum : CARDINAL;
- I : INTEGER;
- BEGIN
- FillChar (Rec, RECORDSIZE, 0);
- StrLCopy (TH.Name, PAnsiChar (DirRec.Name), NAMSIZ);
- { INTEGER type can be 16-bit wide, for instance on msdos OS,
- add explicit typecast to avoid range check error in such cases }
- CASE DirRec.FileType OF
- ftNormal, ftLink : Mode := INTEGER($08000);
- ftSymbolicLink : Mode := INTEGER($0A000);
- ftDirectory : Mode := INTEGER($04000);
- ELSE Mode := 0;
- END;
- IF tmSaveText IN DirRec.Mode THEN Mode := Mode OR $0200;
- IF tmSetGid IN DirRec.Mode THEN Mode := Mode OR $0400;
- IF tmSetUid IN DirRec.Mode THEN Mode := Mode OR $0800;
- IF tpReadByOwner IN DirRec.Permissions THEN Mode := Mode OR $0100;
- IF tpWriteByOwner IN DirRec.Permissions THEN Mode := Mode OR $0080;
- IF tpExecuteByOwner IN DirRec.Permissions THEN Mode := Mode OR $0040;
- IF tpReadByGroup IN DirRec.Permissions THEN Mode := Mode OR $0020;
- IF tpWriteByGroup IN DirRec.Permissions THEN Mode := Mode OR $0010;
- IF tpExecuteByGroup IN DirRec.Permissions THEN Mode := Mode OR $0008;
- IF tpReadByOther IN DirRec.Permissions THEN Mode := Mode OR $0004;
- IF tpWriteByOther IN DirRec.Permissions THEN Mode := Mode OR $0002;
- IF tpExecuteByOther IN DirRec.Permissions THEN Mode := Mode OR $0001;
- OctalN (Mode, @TH.Mode, 8);
- OctalN (DirRec.UID, @TH.UID, 8);
- OctalN (DirRec.GID, @TH.GID, 8);
- Octal64 (DirRec.Size, @TH.Size, 12);
- NullDate := EncodeDate (1970, 1, 1);
- IF DirRec.DateTime >= NullDate
- THEN Octal (Trunc ((DirRec.DateTime - NullDate) * 86400.0), @TH.MTime, 12)
- ELSE Octal (Trunc ( NullDate * 86400.0), @TH.MTime, 12);
- CASE DirRec.FileType OF
- ftNormal : TH.LinkFlag := '0';
- ftLink : TH.LinkFlag := '1';
- ftSymbolicLink : TH.LinkFlag := '2';
- ftCharacter : TH.LinkFlag := '3';
- ftBlock : TH.LinkFlag := '4';
- ftDirectory : TH.LinkFlag := '5';
- ftFifo : TH.LinkFlag := '6';
- ftContiguous : TH.LinkFlag := '7';
- ftDumpDir : TH.LinkFlag := 'D';
- ftMultiVolume : TH.LinkFlag := 'M';
- ftVolumeHeader : TH.LinkFlag := 'V';
- END;
- StrLCopy (TH.LinkName, PAnsiChar (DirRec.LinkName), NAMSIZ);
- StrLCopy (TH.Magic, PAnsiChar (DirRec.Magic + #32#32#32#32#32#32#32#32), 7);
- StrLCopy (TH.UName, PAnsiChar (DirRec.UserName), TUNMLEN);
- StrLCopy (TH.GName, PAnsiChar (DirRec.GroupName), TGNMLEN);
- OctalN (DirRec.MajorDevNo, @TH.DevMajor, 8);
- OctalN (DirRec.MinorDevNo, @TH.DevMinor, 8);
- StrMove (TH.ChkSum, CHKBLANKS, 8);
- CheckSum := 0;
- FOR I := 0 TO SizeOf (TTarHeader)-1 DO
- INC (CheckSum, INTEGER (ORD (Rec [I])));
- OctalN (CheckSum, @TH.ChkSum, 8);
- Dest.Write (TH, RECORDSIZE);
- END;
- (*
- ===============================================================================================
- TTarArchive
- ===============================================================================================
- *)
- CONSTRUCTOR TTarArchive.Create (Stream : TStream);
- BEGIN
- INHERITED Create;
- FStream := Stream;
- FOwnsStream := FALSE;
- Reset;
- END;
- CONSTRUCTOR TTarArchive.Create (Filename : STRING; FileMode : WORD);
- BEGIN
- INHERITED Create;
- FStream := TFileStream.Create (Filename, FileMode);
- FOwnsStream := TRUE;
- Reset;
- END;
- DESTRUCTOR TTarArchive.Destroy;
- BEGIN
- IF FOwnsStream THEN
- FStream.Free;
- INHERITED Destroy;
- END;
- PROCEDURE TTarArchive.Reset;
- // Reset File Pointer
- BEGIN
- FStream.Position := 0;
- FBytesToGo := 0;
- END;
- FUNCTION TTarArchive.FindNext (VAR DirRec : TTarDirRec) : BOOLEAN;
- // Reads next Directory Info Record
- // The Stream pointer must point to the first byte of the tar header
- VAR
- Rec : ARRAY [0..RECORDSIZE-1] OF AnsiChar;
- CurFilePos : int64;
- Header : TTarHeader ABSOLUTE Rec;
- I : INTEGER;
- HeaderChkSum : WORD;
- Checksum : CARDINAL;
- BEGIN
- // --- Scan until next pointer
- IF FBytesToGo > 0 THEN
- FStream.Seek (Records (FBytesToGo) * RECORDSIZE, soFromCurrent);
- // --- EOF reached?
- Result := FALSE;
- CurFilePos := FStream.Position;
- TRY
- FStream.ReadBuffer (Rec, RECORDSIZE);
- if Rec [0] = #0 THEN EXIT; // EOF reached
- EXCEPT
- EXIT; // EOF reached, too
- END;
- Result := TRUE;
- ClearDirRec (DirRec);
- DirRec.FilePos := CurFilePos;
- DirRec.Name := ExtractText (Header.Name);
- DirRec.Size := ExtractNumber64 (@Header.Size, 12);
- DirRec.DateTime := EncodeDate (1970, 1, 1) + (ExtractNumber (@Header.MTime, 12) / 86400.0);
- I := ExtractNumber (@Header.Mode);
- IF I AND $0100 <> 0 THEN Include (DirRec.Permissions, tpReadByOwner);
- IF I AND $0080 <> 0 THEN Include (DirRec.Permissions, tpWriteByOwner);
- IF I AND $0040 <> 0 THEN Include (DirRec.Permissions, tpExecuteByOwner);
- IF I AND $0020 <> 0 THEN Include (DirRec.Permissions, tpReadByGroup);
- IF I AND $0010 <> 0 THEN Include (DirRec.Permissions, tpWriteByGroup);
- IF I AND $0008 <> 0 THEN Include (DirRec.Permissions, tpExecuteByGroup);
- IF I AND $0004 <> 0 THEN Include (DirRec.Permissions, tpReadByOther);
- IF I AND $0002 <> 0 THEN Include (DirRec.Permissions, tpWriteByOther);
- IF I AND $0001 <> 0 THEN Include (DirRec.Permissions, tpExecuteByOther);
- IF I AND $0200 <> 0 THEN Include (DirRec.Mode, tmSaveText);
- IF I AND $0400 <> 0 THEN Include (DirRec.Mode, tmSetGid);
- IF I AND $0800 <> 0 THEN Include (DirRec.Mode, tmSetUid);
- CASE Header.LinkFlag OF
- #0, '0' : DirRec.FileType := ftNormal;
- '1' : DirRec.FileType := ftLink;
- '2' : DirRec.FileType := ftSymbolicLink;
- '3' : DirRec.FileType := ftCharacter;
- '4' : DirRec.FileType := ftBlock;
- '5' : DirRec.FileType := ftDirectory;
- '6' : DirRec.FileType := ftFifo;
- '7' : DirRec.FileType := ftContiguous;
- 'D' : DirRec.FileType := ftDumpDir;
- 'M' : DirRec.FileType := ftMultiVolume;
- 'V' : DirRec.FileType := ftVolumeHeader;
- END;
- DirRec.LinkName := ExtractText (Header.LinkName);
- DirRec.UID := ExtractNumber (@Header.UID);
- DirRec.GID := ExtractNumber (@Header.GID);
- DirRec.UserName := ExtractText (Header.UName);
- DirRec.GroupName := ExtractText (Header.GName);
- DirRec.Magic := Trim (ExtractText (Header.Magic));
- DirRec.MajorDevNo := ExtractNumber (@Header.DevMajor);
- DirRec.MinorDevNo := ExtractNumber (@Header.DevMinor);
- HeaderChkSum := ExtractNumber (@Header.ChkSum); // Calc Checksum
- CheckSum := 0;
- StrMove (Header.ChkSum, CHKBLANKS, 8);
- FOR I := 0 TO SizeOf (TTarHeader)-1 DO
- INC (CheckSum, INTEGER (ORD (Rec [I])));
- DirRec.CheckSumOK := WORD (CheckSum) = WORD (HeaderChkSum);
- IF DirRec.FileType in [ftLink, ftSymbolicLink, ftDirectory, ftFifo, ftVolumeHeader]
- THEN FBytesToGo := 0
- ELSE FBytesToGo := DirRec.Size;
- END;
- PROCEDURE TTarArchive.ReadFile (Buffer : POINTER);
- // Reads file data for the last Directory Record. The entire file is read into the buffer.
- // The buffer must be large enough to take up the whole file.
- VAR
- RestBytes : INTEGER;
- BEGIN
- IF FBytesToGo = 0 THEN EXIT;
- RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo;
- FStream.ReadBuffer (Buffer^, FBytesToGo);
- FStream.Seek (RestBytes, soFromCurrent);
- FBytesToGo := 0;
- END;
- PROCEDURE TTarArchive.ReadFile (Stream : TStream);
- // Reads file data for the last Directory Record.
- // The entire file is written out to the stream.
- // The stream is left at its current position prior to writing
- VAR
- RestBytes : INTEGER;
- BEGIN
- IF FBytesToGo = 0 THEN EXIT;
- RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo;
- Stream.CopyFrom (FStream, FBytesToGo);
- FStream.Seek (RestBytes, soFromCurrent);
- FBytesToGo := 0;
- END;
- PROCEDURE TTarArchive.ReadFile (Filename : STRING);
- // Reads file data for the last Directory Record.
- // The entire file is saved in the given Filename
- VAR
- FS : TFileStream;
- BEGIN
- FS := TFileStream.Create (Filename, fmCreate);
- TRY
- ReadFile (FS);
- FINALLY
- FS.Free;
- END;
- END;
- FUNCTION TTarArchive.ReadFile : RawByteSTRING;
- // Reads file data for the last Directory Record. The entire file is returned
- // as a large ANSI Ansistring.
- VAR
- RestBytes : INTEGER;
- BEGIN
- IF FBytesToGo = 0 THEN EXIT;
- RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo;
- SetLength (Result, FBytesToGo);
- FStream.ReadBuffer (PAnsiChar (Result)^, FBytesToGo);
- FStream.Seek (RestBytes, soFromCurrent);
- FBytesToGo := 0;
- END;
- PROCEDURE TTarArchive.GetFilePos (VAR Current, Size : INT64);
- // Returns the Current Position in the TAR stream
- BEGIN
- Current := FStream.Position;
- Size := FStream.Size;
- END;
- PROCEDURE TTarArchive.SetFilePos (NewPos : INT64); // Set new Current File Position
- BEGIN
- IF NewPos < FStream.Size THEN
- FStream.Seek (NewPos, soFromBeginning);
- END;
- (*
- ===============================================================================================
- TTarWriter
- ===============================================================================================
- *)
- CONSTRUCTOR TTarWriter.CreateEmpty;
- VAR
- TP : TTarPermission;
- BEGIN
- INHERITED Create;
- FOwnsStream := FALSE;
- FFinalized := FALSE;
- FPermissions := [];
- FOR TP := Low (TP) TO High (TP) DO
- Include (FPermissions, TP);
- FUID := 0;
- FGID := 0;
- FUserName := '';
- FGroupName := '';
- FMode := [];
- FMagic := 'ustar';
- END;
- CONSTRUCTOR TTarWriter.Create (TargetStream : TStream);
- BEGIN
- CreateEmpty;
- FStream := TargetStream;
- FOwnsStream := FALSE;
- END;
- CONSTRUCTOR TTarWriter.Create (TargetFilename : STRING; Mode : INTEGER = fmCreate);
- BEGIN
- CreateEmpty;
- FStream := TFileStream.Create (TargetFilename, Mode);
- FOwnsStream := TRUE;
- END;
- DESTRUCTOR TTarWriter.Destroy;
- BEGIN
- IF NOT FFinalized THEN BEGIN
- Finalize;
- FFinalized := TRUE;
- END;
- IF FOwnsStream THEN
- FStream.Free;
- INHERITED Destroy;
- END;
- PROCEDURE TTarWriter.AddFile (Filename : STRING; TarFilename : AnsiString = '');
- VAR
- S : TFileStream;
- Date : TDateTime;
- BEGIN
- Date := FileTimeGMT (Filename);
- IF TarFilename = '' THEN
- TarFilename := ConvertFilename (Filename)
- ELSE TarFilename := ConvertFilename (TarFilename);
- S := TFileStream.Create (Filename, fmOpenRead OR fmShareDenyWrite);
- TRY
- AddStream (S, TarFilename, Date);
- FINALLY
- S.Free
- END;
- END;
- PROCEDURE TTarWriter.AddStream (Stream : TStream; TarFilename : AnsiString; FileDateGmt : TDateTime);
- VAR
- DirRec : TTarDirRec;
- Rec : ARRAY [0..RECORDSIZE-1] OF AnsiChar;
- BytesToRead : INT64; // Bytes to read from the Source Stream
- BlockSize : INT64; // Bytes to write out for the current record
- BEGIN
- ClearDirRec (DirRec);
- DirRec.Name := TarFilename;
- DirRec.Size := Stream.Size - Stream.Position;
- DirRec.DateTime := FileDateGmt;
- DirRec.Permissions := FPermissions;
- DirRec.FileType := ftNormal;
- DirRec.LinkName := '';
- DirRec.UID := FUID;
- DirRec.GID := FGID;
- DirRec.UserName := FUserName;
- DirRec.GroupName := FGroupName;
- DirRec.ChecksumOK := TRUE;
- DirRec.Mode := FMode;
- DirRec.Magic := FMagic;
- DirRec.MajorDevNo := 0;
- DirRec.MinorDevNo := 0;
- WriteTarHeader (FStream, DirRec);
- BytesToRead := DirRec.Size;
- WHILE BytesToRead > 0 DO BEGIN
- BlockSize := BytesToRead;
- IF BlockSize > RECORDSIZE THEN BlockSize := RECORDSIZE;
- FillChar (Rec, RECORDSIZE, 0);
- Stream.Read (Rec, BlockSize);
- FStream.Write (Rec, RECORDSIZE);
- DEC (BytesToRead, BlockSize);
- END;
- END;
- PROCEDURE TTarWriter.AddString (Contents : AnsiString; TarFilename : AnsiString; FileDateGmt : TDateTime); // rawbytestring
- VAR
- S : TStringStream;
- BEGIN
- S := TStringStream.Create (Contents);
- TRY
- AddStream (S, TarFilename, FileDateGmt);
- FINALLY
- S.Free
- END
- END;
- PROCEDURE TTarWriter.AddDir (Dirname : AnsiString; DateGmt : TDateTime; MaxDirSize : INT64 = 0);
- VAR
- DirRec : TTarDirRec;
- BEGIN
- ClearDirRec (DirRec);
- DirRec.Name := Dirname;
- DirRec.Size := MaxDirSize;
- DirRec.DateTime := DateGmt;
- DirRec.Permissions := FPermissions;
- DirRec.FileType := ftDirectory;
- DirRec.LinkName := '';
- DirRec.UID := FUID;
- DirRec.GID := FGID;
- DirRec.UserName := FUserName;
- DirRec.GroupName := FGroupName;
- DirRec.ChecksumOK := TRUE;
- DirRec.Mode := FMode;
- DirRec.Magic := FMagic;
- DirRec.MajorDevNo := 0;
- DirRec.MinorDevNo := 0;
- WriteTarHeader (FStream, DirRec);
- END;
- PROCEDURE TTarWriter.AddSymbolicLink (Filename, Linkname : AnsiString; DateGmt : TDateTime);
- VAR
- DirRec : TTarDirRec;
- BEGIN
- ClearDirRec (DirRec);
- DirRec.Name := Filename;
- DirRec.Size := 0;
- DirRec.DateTime := DateGmt;
- DirRec.Permissions := FPermissions;
- DirRec.FileType := ftSymbolicLink;
- DirRec.LinkName := Linkname;
- DirRec.UID := FUID;
- DirRec.GID := FGID;
- DirRec.UserName := FUserName;
- DirRec.GroupName := FGroupName;
- DirRec.ChecksumOK := TRUE;
- DirRec.Mode := FMode;
- DirRec.Magic := FMagic;
- DirRec.MajorDevNo := 0;
- DirRec.MinorDevNo := 0;
- WriteTarHeader (FStream, DirRec);
- END;
- PROCEDURE TTarWriter.AddLink (Filename, Linkname : AnsiString; DateGmt : TDateTime);
- VAR
- DirRec : TTarDirRec;
- BEGIN
- ClearDirRec (DirRec);
- DirRec.Name := Filename;
- DirRec.Size := 0;
- DirRec.DateTime := DateGmt;
- DirRec.Permissions := FPermissions;
- DirRec.FileType := ftLink;
- DirRec.LinkName := Linkname;
- DirRec.UID := FUID;
- DirRec.GID := FGID;
- DirRec.UserName := FUserName;
- DirRec.GroupName := FGroupName;
- DirRec.ChecksumOK := TRUE;
- DirRec.Mode := FMode;
- DirRec.Magic := FMagic;
- DirRec.MajorDevNo := 0;
- DirRec.MinorDevNo := 0;
- WriteTarHeader (FStream, DirRec);
- END;
- PROCEDURE TTarWriter.AddVolumeHeader (VolumeId : AnsiString; DateGmt : TDateTime);
- VAR
- DirRec : TTarDirRec;
- BEGIN
- ClearDirRec (DirRec);
- DirRec.Name := VolumeId;
- DirRec.Size := 0;
- DirRec.DateTime := DateGmt;
- DirRec.Permissions := FPermissions;
- DirRec.FileType := ftVolumeHeader;
- DirRec.LinkName := '';
- DirRec.UID := FUID;
- DirRec.GID := FGID;
- DirRec.UserName := FUserName;
- DirRec.GroupName := FGroupName;
- DirRec.ChecksumOK := TRUE;
- DirRec.Mode := FMode;
- DirRec.Magic := FMagic;
- DirRec.MajorDevNo := 0;
- DirRec.MinorDevNo := 0;
- WriteTarHeader (FStream, DirRec);
- END;
- PROCEDURE TTarWriter.Finalize;
- // Writes the End-Of-File Tag
- // Data after this tag will be ignored
- // The destructor calls this automatically if you didn't do it before
- VAR
- Rec : ARRAY [0..RECORDSIZE-1] OF AnsiChar;
- BEGIN
- FillChar (Rec, SizeOf (Rec), 0);
- FStream.Write (Rec, RECORDSIZE);
- {
- Avoid warning: 'tar: A lone zero block at *'
- The reason for this message is that GNU tar format has been changed
- to require TWO zero blocks marking the end of the archive.
- Thus write a second zero block.
- }
- FStream.Write (Rec, RECORDSIZE);
- FFinalized := TRUE;
- END;
- END.
|