| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971 | (** Copyright (c) 2000-2006 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          GERMANYE-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 donatehttp://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 Start2001-04-28  HeySt  1.0.0 First Release2001-06-19  HeySt  2.0.0 Finished TTarWriter2001-09-06  HeySt  2.0.1 Bugfix in TTarArchive.FindNext: FBytesToGo must sometimes be 02001-10-25  HeySt  2.0.2 Introduced the ClearDirRec procedure2001-11-13  HeySt  2.0.3 Bugfix: Take out ClearDirRec call from WriteTarHeader                         Bug Reported by Tony BenBrahim2001-12-25  HeySt  2.0.4 WriteTarHeader: Fill Rec with zero bytes before filling it2002-05-18  HeySt  2.0.5 Kylix awareness: Thanks to Kerry L. Davison for the canges2005-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.*)UNIT LibTar;INTERFACE{$IFDEF FPC} {$MODE Delphi}{$ELSE}  {$IFDEF LINUX}     {$DEFINE Kylix}     {$DEFINE LIBCUNIT}  {$ENDIF}{$ENDIF} USES{$IFDEF LIBCUNIT}   Libc,		// MvdV: Nothing is used from this???{$ENDIF}{$ifdef Unix}   BaseUnix, Unix,{$endif}(*$IFDEF MSWINDOWS *)   Windows,(*$ENDIF *)  SysUtils, Classes;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        : STRING;            // 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    : STRING;            // Name of linked file (for ftLink, ftSymbolicLink)                  UID         : INTEGER;           // User ID                  GID         : INTEGER;           // Group ID                  UserName    : STRING;            // User name                  GroupName   : STRING;            // Group name                  ChecksumOK  : BOOLEAN;           // Checksum was OK                  Mode        : TTarModes;         // Mode                  Magic       : STRING;            // 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 : STRING;           OVERLOAD;         // -;-                  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    : STRING;            // User name                 FGroupName   : STRING;            // Group name                 FMode        : TTarModes;         // Mode                 FMagic       : STRING;            // 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 : STRING = '');                 PROCEDURE AddStream (Stream   : TStream; TarFilename : STRING; FileDateGmt : TDateTime);                 PROCEDURE AddString (Contents : STRING;  TarFilename : STRING; FileDateGmt : TDateTime);                 PROCEDURE AddDir          (Dirname            : STRING; DateGmt : TDateTime; MaxDirSize : INT64 = 0);                 PROCEDURE AddSymbolicLink (Filename, Linkname : STRING; DateGmt : TDateTime);                 PROCEDURE AddLink         (Filename, Linkname : STRING; DateGmt : TDateTime);                 PROCEDURE AddVolumeHeader (VolumeId           : STRING; 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    : STRING          READ FUserName    WRITE FUserName;      // User name                 PROPERTY GroupName   : STRING          READ FGroupName   WRITE FGroupName;     // Group name                 PROPERTY Mode        : TTarModes       READ FMode        WRITE FMode;          // Mode                 PROPERTY Magic       : STRING          READ FMagic       WRITE FMagic;         // Contents of the "Magic" field               END;// --- Some useful constantsCONST  FILETYPE_NAME : ARRAY [TFileType] OF STRING =                  ('Regular', 'Link', 'Symbolic Link', 'Char 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===============================================================================================*)IMPLEMENTATIONFUNCTION 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 zoneVAR  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       Result := FileDateToDateTime (SearchRec.Time);       {$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 bugsBEGIN  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 CHAR;                 Mode     : ARRAY [0..7] OF CHAR;                 UID      : ARRAY [0..7] OF CHAR;                 GID      : ARRAY [0..7] OF CHAR;                 Size     : ARRAY [0..11] OF CHAR;                 MTime    : ARRAY [0..11] OF CHAR;                 ChkSum   : ARRAY [0..7] OF CHAR;                 LinkFlag : CHAR;                 LinkName : ARRAY [0..NAMSIZ-1] OF CHAR;                 Magic    : ARRAY [0..7] OF CHAR;                 UName    : ARRAY [0..TUNMLEN-1] OF CHAR;                 GName    : ARRAY [0..TGNMLEN-1] OF CHAR;                 DevMajor : ARRAY [0..7] OF CHAR;                 DevMinor : ARRAY [0..7] OF CHAR;               END;FUNCTION ExtractText (P : PChar) : STRING;BEGIN  Result := STRING (P);END;FUNCTION ExtractNumber (P : PChar) : INTEGER; OVERLOAD;VAR  Strg : STRING;BEGIN  Strg := Trim (StrPas (P));  P := PChar (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 : PChar) : INT64; OVERLOAD;VAR  Strg : STRING;BEGIN  Strg := Trim (StrPas (P));  P := PChar (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 : PChar; MaxLen : INTEGER) : INTEGER; OVERLOAD;VAR  S0   : ARRAY [0..255] OF CHAR;  Strg : STRING;BEGIN  StrLCopy (S0, P, MaxLen);  Strg := Trim (StrPas (S0));  P := PChar (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 : PChar; MaxLen : INTEGER) : INT64; OVERLOAD;VAR  S0   : ARRAY [0..255] OF CHAR;  Strg : STRING;BEGIN  StrLCopy (S0, P, MaxLen);  Strg := Trim (StrPas (S0));  P := PChar (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 : PChar; Len : INTEGER);         // Makes a string of octal digits         // The string will always be "Len" characters longVAR  I     : INTEGER;BEGIN  FOR I := Len-2 DOWNTO 0 DO BEGIN    (P+I)^ := CHR (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 : PChar; Len : INTEGER);         // Makes a string of octal digits         // The string will always be "Len" characters longVAR  I     : INTEGER;BEGIN  FOR I := Len-2 DOWNTO 0 DO BEGIN    (P+I)^ := CHR (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 : PChar; 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 CHAR;  TH       : TTarHeader ABSOLUTE Rec;  Mode     : INTEGER;  NullDate : TDateTime;  Checksum : CARDINAL;  I        : INTEGER;BEGIN  FillChar (Rec, RECORDSIZE, 0);  StrLCopy (TH.Name, PChar (DirRec.Name), NAMSIZ);  Mode := 0;  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, PChar (DirRec.LinkName), NAMSIZ);  StrLCopy (TH.Magic, PChar (DirRec.Magic + #32#32#32#32#32#32#32#32), 8);  StrLCopy (TH.UName, PChar (DirRec.UserName), TUNMLEN);  StrLCopy (TH.GName, PChar (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 PointerBEGIN  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 headerVAR  Rec          : ARRAY [0..RECORDSIZE-1] OF CHAR;  CurFilePos   : INTEGER;  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 writingVAR  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 FilenameVAR  FS : TFileStream;BEGIN  FS := TFileStream.Create (Filename, fmCreate);  TRY    ReadFile (FS);  FINALLY    FS.Free;    END;END;FUNCTION  TTarArchive.ReadFile : STRING;          // Reads file data for the last Directory Record. The entire file is returned          // as a large ANSI string.VAR  RestBytes : INTEGER;BEGIN  IF FBytesToGo = 0 THEN EXIT;  RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo;  SetLength (Result, FBytesToGo);  FStream.ReadBuffer (PChar (Result)^, FBytesToGo);  FStream.Seek (RestBytes, soFromCurrent);  FBytesToGo := 0;END;PROCEDURE TTarArchive.GetFilePos (VAR Current, Size : INT64);          // Returns the Current Position in the TAR streamBEGIN  Current := FStream.Position;  Size    := FStream.Size;END;PROCEDURE TTarArchive.SetFilePos (NewPos : INT64);                   // Set new Current File PositionBEGIN  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 : STRING = '');VAR  S    : TFileStream;  Date : TDateTime;BEGIN  Date := FileTimeGMT (Filename);  IF TarFilename = '' THEN    TarFilename := ConvertFilename (Filename);  S := TFileStream.Create (Filename, fmOpenRead OR fmShareDenyWrite);  TRY    AddStream (S, TarFilename, Date);  FINALLY    S.Free    END;END;PROCEDURE TTarWriter.AddStream (Stream : TStream; TarFilename : STRING; FileDateGmt : TDateTime);VAR  DirRec      : TTarDirRec;  Rec         : ARRAY [0..RECORDSIZE-1] OF CHAR;  BytesToRead : INT64;      // Bytes to read from the Source Stream  BlockSize   : INT64;      // Bytes to write out for the current recordBEGIN  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 : STRING; TarFilename : STRING; FileDateGmt : TDateTime);VAR  S : TStringStream;BEGIN  S := TStringStream.Create (Contents);  TRY    AddStream (S, TarFilename, FileDateGmt);  FINALLY    S.Free    ENDEND;PROCEDURE TTarWriter.AddDir (Dirname : STRING; 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 : STRING; 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 : STRING; 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           : STRING; 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 beforeVAR  Rec : ARRAY [0..RECORDSIZE-1] OF CHAR;BEGIN  FillChar (Rec, SizeOf (Rec), 0);  FStream.Write (Rec, RECORDSIZE);  FFinalized := TRUE;END;END.
 |