Guest User

LibTAR

a guest
Jun 2nd, 2013
150
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Delphi 34.51 KB | None | 0 0
  1. (**
  2. ===============================================================================================
  3. Name    : LibTar
  4. ===============================================================================================
  5. Subject : Handling of "tar" files
  6. ===============================================================================================
  7. Author  : Stefan Heymann
  8.           Eschenweg 3
  9.           72076 Tьbingen
  10.           GERMANY
  11.  
  12. Web:      www.destructor.de
  13.  
  14. ===============================================================================================
  15. TTarArchive Usage
  16. -----------------
  17. - Choose a constructor
  18. - Make an instance of TTarArchive                  TA := TTarArchive.Create (Filename);
  19. - Scan through the archive                         TA.Reset;
  20.                                                    WHILE TA.FindNext (DirRec) DO BEGIN
  21. - Evaluate the DirRec for each file                  ListBox.Items.Add (DirRec.Name);
  22. - Read out the current file                          TA.ReadFile (DestFilename);
  23.   (You can ommit this if you want to
  24.   read in the directory only)                        END;      
  25. - You're done                                      TA.Free;
  26.  
  27.  
  28. TTarWriter Usage
  29. ----------------
  30. - Choose a constructor
  31. - Make an instance of TTarWriter                   TW := TTarWriter.Create ('my.tar');
  32. - Add a file to the tar archive                    TW.AddFile ('foobar.txt');
  33. - Add a string as a file                           TW.AddString (SL.Text, 'joe.txt', Now);
  34. - Destroy TarWriter instance                       TW.Free;
  35. - Now your tar file is ready.
  36.  
  37.  
  38. Source, Legals ("Licence")
  39. --------------------------
  40. The official site to get this code is http://www.destructor.de/
  41.  
  42. Usage and Distribution of this Source Code is ruled by the
  43. "Destructor.de Source code Licence" (DSL) which comes with this file or
  44. can be downloaded at http://www.destructor.de/
  45.  
  46. IN SHORT: Usage and distribution of this source code is free.
  47.           You use it completely on your own risk.
  48.  
  49. Donateware
  50. ----------
  51. If you like this code, you are free to donate
  52. http://www.destructor.de/donateware.htm
  53.  
  54. ===============================================================================================
  55. !!!  All parts of this code which are not finished or known to be buggy
  56.      are marked with three exclamation marks
  57. ===============================================================================================
  58. Date        Author Changes
  59. -----------------------------------------------------------------------------------------------
  60. 2001-04-26  HeySt  0.0.1 Start
  61. 2001-04-28  HeySt  1.0.0 First Release
  62. 2001-06-19  HeySt  2.0.0 Finished TTarWriter
  63. 2001-09-06  HeySt  2.0.1 Bugfix in TTarArchive.FindNext: FBytesToGo must sometimes be 0
  64. 2001-10-25  HeySt  2.0.2 Introduced the ClearDirRec procedure
  65. 2001-11-13  HeySt  2.0.3 Bugfix: Take out ClearDirRec call from WriteTarHeader
  66.                          Bug Reported by Tony BenBrahim
  67. 2001-12-25  HeySt  2.0.4 WriteTarHeader: Fill Rec with zero bytes before filling it
  68. 2002-05-18  HeySt  2.0.5 Kylix awareness: Thanks to Kerry L. Davison for the canges
  69. 2005-09-03  HeySt  2.0.6 TTarArchive.FindNext: Don't access SourceStream.Size
  70.                          (for compressed streams, which don't know their .Size)
  71. 2006-03-13  HeySt  2.0.7 Bugfix in ReadFile (Buffer : POINTER)
  72. 2007-05-16  HeySt  2.0.8 Bugfix in TTarWriter.AddFile (Convertfilename in the ELSE branch)
  73.                          Bug Reported by Chris Rorden
  74. 2010-11-29  HeySt  2.1.0 WriteTarHeader: Mode values for ftNormal/ftLink/ftSymbolicLink/ftDirectory
  75.                          Thanks to Iouri Kharon for the fix.
  76.                          Still no support for filenames > 100 bytes. Sorry.
  77.                          Support for Unicode Delphi versions (2009, 2010, XE, etc.)
  78. 2011-05-23  HeySt  2.1.1 New IFDEF WIN32 in the USES clause
  79. *)
  80.  
  81. UNIT LibTar;
  82.  
  83. INTERFACE
  84.  
  85. USES
  86. (*$IFDEF LINUX*)
  87.    Libc,
  88. (*$ENDIF *)
  89. {$IFDEF WIN32}
  90.   {$DEFINE MSWINDOWS} // predefined for D6+/BCB6+    // because in Delphi 5  MSWINDOWS is not defined
  91. {$ENDIF}
  92. (*$IFDEF MSWINDOWS *)
  93.    Windows,
  94. (*$ENDIF *)
  95.   SysUtils, Classes;
  96.  
  97. TYPE
  98.   (*$IFNDEF UNICODE *)
  99.   RawByteString = AnsiString;
  100.   (*$ENDIF *)
  101.  
  102.   // --- File Access Permissions
  103.   TTarPermission  = (tpReadByOwner, tpWriteByOwner, tpExecuteByOwner,
  104.                      tpReadByGroup, tpWriteByGroup, tpExecuteByGroup,
  105.                      tpReadByOther, tpWriteByOther, tpExecuteByOther);
  106.   TTarPermissions = SET OF TTarPermission;
  107.  
  108.   // --- Type of File
  109.   TFileType = (ftNormal,          // Regular file
  110.                ftLink,            // Link to another, previously archived, file (LinkName)
  111.                ftSymbolicLink,    // Symbolic link to another file              (LinkName)
  112.                ftCharacter,       // Character special files
  113.                ftBlock,           // Block special files
  114.                ftDirectory,       // Directory entry. Size is zero (unlimited) or max. number of bytes
  115.                ftFifo,            // FIFO special file. No data stored in the archive.
  116.                ftContiguous,      // Contiguous file, if supported by OS
  117.                ftDumpDir,         // List of files
  118.                ftMultiVolume,     // Multi-volume file part
  119.                ftVolumeHeader);   // Volume header. Can appear only as first record in the archive
  120.  
  121.   // --- Mode
  122.   TTarMode  = (tmSetUid, tmSetGid, tmSaveText);
  123.   TTarModes = SET OF TTarMode;
  124.  
  125.   // --- Record for a Directory Entry
  126.   //     Adjust the ClearDirRec procedure when this record changes!
  127.   TTarDirRec  = RECORD
  128.                   Name        : AnsiString;        // File path and name
  129.                   Size        : INT64;             // File size in Bytes
  130.                   DateTime    : TDateTime;         // Last modification date and time
  131.                   Permissions : TTarPermissions;   // Access permissions
  132.                   FileType    : TFileType;         // Type of file
  133.                   LinkName    : AnsiString;        // Name of linked file (for ftLink, ftSymbolicLink)
  134.                   UID         : INTEGER;           // User ID
  135.                   GID         : INTEGER;           // Group ID
  136.                   UserName    : AnsiString;        // User name
  137.                   GroupName   : AnsiString;        // Group name
  138.                   ChecksumOK  : BOOLEAN;           // Checksum was OK
  139.                   Mode        : TTarModes;         // Mode
  140.                   Magic       : AnsiString;        // Contents of the "Magic" field
  141.                   MajorDevNo  : INTEGER;           // Major Device No. for ftCharacter and ftBlock
  142.                   MinorDevNo  : INTEGER;           // Minor Device No. for ftCharacter and ftBlock
  143.                   FilePos     : INT64;             // Position in TAR file
  144.                 END;
  145.  
  146.   // --- The TAR Archive CLASS
  147.   TTarArchive = CLASS
  148.                 PROTECTED
  149.                   FStream     : TStream;   // Internal Stream
  150.                   FOwnsStream : BOOLEAN;   // True if FStream is owned by the TTarArchive instance
  151.                   FBytesToGo  : INT64;     // Bytes until the next Header Record
  152.                 PUBLIC
  153.                   CONSTRUCTOR Create (Stream   : TStream);                                OVERLOAD;
  154.                   CONSTRUCTOR Create (Filename : STRING;
  155.                                       FileMode : WORD = fmOpenRead OR fmShareDenyWrite);  OVERLOAD;
  156.                   DESTRUCTOR Destroy;                                       OVERRIDE;
  157.                   PROCEDURE Reset;                                         // Reset File Pointer
  158.                   FUNCTION  FindNext (VAR DirRec : TTarDirRec) : BOOLEAN;  // Reads next Directory Info Record. FALSE if EOF reached
  159.                   PROCEDURE ReadFile (Buffer   : POINTER); OVERLOAD;       // Reads file data for last Directory Record
  160.                   PROCEDURE ReadFile (Stream   : TStream); OVERLOAD;       // -;-
  161.                   PROCEDURE ReadFile (Filename : STRING);  OVERLOAD;       // -;-
  162.                   FUNCTION  ReadFile : RawByteString;      OVERLOAD;       // -;-
  163.  
  164.                   PROCEDURE GetFilePos (VAR Current, Size : INT64);        // Current File Position
  165.                   PROCEDURE SetFilePos (NewPos : INT64);                   // Set new Current File Position
  166.                 END;
  167.  
  168.   // --- The TAR Archive Writer CLASS
  169.   TTarWriter = CLASS
  170.                PROTECTED
  171.                  FStream      : TStream;
  172.                  FOwnsStream  : BOOLEAN;
  173.                  FFinalized   : BOOLEAN;
  174.                                                    // --- Used at the next "Add" method call: ---
  175.                  FPermissions : TTarPermissions;   // Access permissions
  176.                  FUID         : INTEGER;           // User ID
  177.                  FGID         : INTEGER;           // Group ID
  178.                  FUserName    : AnsiString;        // User name
  179.                  FGroupName   : AnsiString;        // Group name
  180.                  FMode        : TTarModes;         // Mode
  181.                  FMagic       : AnsiString;        // Contents of the "Magic" field
  182.                  CONSTRUCTOR CreateEmpty;
  183.                PUBLIC
  184.                  CONSTRUCTOR Create (TargetStream   : TStream);                            OVERLOAD;
  185.                  CONSTRUCTOR Create (TargetFilename : STRING; Mode : INTEGER = fmCreate);  OVERLOAD;
  186.                  DESTRUCTOR Destroy; OVERRIDE;                   // Writes End-Of-File Tag
  187.                  PROCEDURE AddFile   (Filename : STRING;        TarFilename : AnsiString = '');
  188.                  PROCEDURE AddStream (Stream   : TStream;       TarFilename : AnsiString; FileDateGmt : TDateTime);
  189.                  PROCEDURE AddString (Contents : RawByteString; TarFilename : AnsiString; FileDateGmt : TDateTime);
  190.                  PROCEDURE AddDir          (Dirname            : AnsiString; DateGmt : TDateTime; MaxDirSize : INT64 = 0);
  191.                  PROCEDURE AddSymbolicLink (Filename, Linkname : AnsiString; DateGmt : TDateTime);
  192.                  PROCEDURE AddLink         (Filename, Linkname : AnsiString; DateGmt : TDateTime);
  193.                  PROCEDURE AddVolumeHeader (VolumeId           : AnsiString; DateGmt : TDateTime);
  194.                  PROCEDURE Finalize;
  195.                  PROPERTY Permissions : TTarPermissions READ FPermissions WRITE FPermissions;   // Access permissions
  196.                  PROPERTY UID         : INTEGER         READ FUID         WRITE FUID;           // User ID
  197.                  PROPERTY GID         : INTEGER         READ FGID         WRITE FGID;           // Group ID
  198.                  PROPERTY UserName    : AnsiString      READ FUserName    WRITE FUserName;      // User name
  199.                  PROPERTY GroupName   : AnsiString      READ FGroupName   WRITE FGroupName;     // Group name
  200.                  PROPERTY Mode        : TTarModes       READ FMode        WRITE FMode;          // Mode
  201.                  PROPERTY Magic       : AnsiString      READ FMagic       WRITE FMagic;         // Contents of the "Magic" field
  202.                END;
  203.  
  204. // --- Some useful constants
  205. CONST
  206.   FILETYPE_NAME : ARRAY [TFileType] OF STRING =
  207.                   ('Regular', 'Link', 'Symbolic Link', 'Char File', 'Block File',
  208.                    'Directory', 'FIFO File', 'Contiguous', 'Dir Dump', 'Multivol', 'Volume Header');
  209.  
  210.   ALL_PERMISSIONS     = [tpReadByOwner, tpWriteByOwner, tpExecuteByOwner,
  211.                          tpReadByGroup, tpWriteByGroup, tpExecuteByGroup,
  212.                          tpReadByOther, tpWriteByOther, tpExecuteByOther];
  213.   READ_PERMISSIONS    = [tpReadByOwner, tpReadByGroup,  tpReadByOther];
  214.   WRITE_PERMISSIONS   = [tpWriteByOwner, tpWriteByGroup, tpWriteByOther];
  215.   EXECUTE_PERMISSIONS = [tpExecuteByOwner, tpExecuteByGroup, tpExecuteByOther];
  216.  
  217.  
  218. FUNCTION  PermissionString      (Permissions : TTarPermissions) : STRING;
  219. FUNCTION  ConvertFilename       (Filename    : STRING)          : STRING;
  220. FUNCTION  FileTimeGMT           (FileName    : STRING)          : TDateTime;  OVERLOAD;
  221. FUNCTION  FileTimeGMT           (SearchRec   : TSearchRec)      : TDateTime;  OVERLOAD;
  222. PROCEDURE ClearDirRec           (VAR DirRec  : TTarDirRec);
  223.  
  224.  
  225. (*
  226. ===============================================================================================
  227. IMPLEMENTATION
  228. ===============================================================================================
  229. *)
  230.  
  231. IMPLEMENTATION
  232.  
  233. FUNCTION PermissionString (Permissions : TTarPermissions) : STRING;
  234. BEGIN
  235.   Result := '';
  236.   IF tpReadByOwner    IN Permissions THEN Result := Result + 'r' ELSE Result := Result + '-';
  237.   IF tpWriteByOwner   IN Permissions THEN Result := Result + 'w' ELSE Result := Result + '-';
  238.   IF tpExecuteByOwner IN Permissions THEN Result := Result + 'x' ELSE Result := Result + '-';
  239.   IF tpReadByGroup    IN Permissions THEN Result := Result + 'r' ELSE Result := Result + '-';
  240.   IF tpWriteByGroup   IN Permissions THEN Result := Result + 'w' ELSE Result := Result + '-';
  241.   IF tpExecuteByGroup IN Permissions THEN Result := Result + 'x' ELSE Result := Result + '-';
  242.   IF tpReadByOther    IN Permissions THEN Result := Result + 'r' ELSE Result := Result + '-';
  243.   IF tpWriteByOther   IN Permissions THEN Result := Result + 'w' ELSE Result := Result + '-';
  244.   IF tpExecuteByOther IN Permissions THEN Result := Result + 'x' ELSE Result := Result + '-';
  245. END;
  246.  
  247.  
  248. FUNCTION ConvertFilename  (Filename : STRING) : STRING;
  249.          // Converts the filename to Unix conventions
  250. BEGIN
  251.   (*$IFDEF LINUX *)
  252.   Result := Filename;
  253.   (*$ELSE *)
  254.   Result := StringReplace (Filename, '\', '/', [rfReplaceAll]);
  255.   (*$ENDIF *)
  256. END;
  257.  
  258.  
  259. FUNCTION FileTimeGMT (FileName: STRING): TDateTime;
  260.          // Returns the Date and Time of the last modification of the given File
  261.          // The Result is zero if the file could not be found
  262.          // The Result is given in UTC (GMT) time zone
  263. VAR
  264.   SR : TSearchRec;
  265. BEGIN
  266.   Result := 0.0;
  267.   IF FindFirst (FileName, faAnyFile, SR) = 0 THEN
  268.     Result := FileTimeGMT (SR);
  269.   FindClose (SR);
  270. END;
  271.  
  272.  
  273. FUNCTION FileTimeGMT (SearchRec : TSearchRec) : TDateTime;
  274. (*$IFDEF MSWINDOWS *)
  275. VAR
  276.   SystemFileTime: TSystemTime;
  277. (*$ENDIF *)
  278. (*$IFDEF LINUX *)
  279. VAR
  280.   TimeVal  : TTimeVal;
  281.   TimeZone : TTimeZone;
  282. (*$ENDIF *)
  283. BEGIN
  284.   Result := 0.0;
  285.   (*$IFDEF MSWINDOWS *) (*$WARNINGS OFF *)
  286.     IF (SearchRec.FindData.dwFileAttributes AND faDirectory) = 0 THEN
  287.       IF FileTimeToSystemTime (SearchRec.FindData.ftLastWriteTime, SystemFileTime) THEN
  288.         Result := EncodeDate (SystemFileTime.wYear, SystemFileTime.wMonth, SystemFileTime.wDay)
  289.                 + EncodeTime (SystemFileTime.wHour, SystemFileTime.wMinute, SystemFileTime.wSecond, SystemFileTime.wMilliseconds);
  290.   (*$ENDIF *) (*$WARNINGS ON *)
  291.   (*$IFDEF LINUX *)
  292.      IF SearchRec.Attr AND faDirectory = 0 THEN BEGIN
  293.        Result := FileDateToDateTime (SearchRec.Time);
  294.        GetTimeOfDay (TimeVal, TimeZone);
  295.        Result := Result + TimeZone.tz_minuteswest / (60 * 24);
  296.        END;
  297.   (*$ENDIF *)
  298. end;
  299.  
  300.  
  301. PROCEDURE ClearDirRec (VAR DirRec : TTarDirRec);
  302.           // This is included because a FillChar (DirRec, SizeOf (DirRec), 0)
  303.           // will destroy the long string pointers, leading to strange bugs
  304. BEGIN
  305.   WITH DirRec DO BEGIN
  306.     Name        := '';
  307.     Size        := 0;
  308.     DateTime    := 0.0;
  309.     Permissions := [];
  310.     FileType    := TFileType (0);
  311.     LinkName    := '';
  312.     UID         := 0;
  313.     GID         := 0;
  314.     UserName    := '';
  315.     GroupName   := '';
  316.     ChecksumOK  := FALSE;
  317.     Mode        := [];
  318.     Magic       := '';
  319.     MajorDevNo  := 0;
  320.     MinorDevNo  := 0;
  321.     FilePos     := 0;
  322.     END;
  323. END;
  324.  
  325. (*
  326. ===============================================================================================
  327. TAR format
  328. ===============================================================================================
  329. *)
  330.  
  331. CONST
  332.   RECORDSIZE = 512;
  333.   NAMSIZ     = 100;
  334.   TUNMLEN    =  32;
  335.   TGNMLEN    =  32;
  336.   CHKBLANKS  = #32#32#32#32#32#32#32#32;
  337.  
  338. TYPE
  339.   TTarHeader = PACKED RECORD
  340.                  Name     : ARRAY [0..NAMSIZ-1] OF AnsiChar;
  341.                  Mode     : ARRAY [0..7]  OF AnsiChar;
  342.                  UID      : ARRAY [0..7]  OF AnsiChar;
  343.                  GID      : ARRAY [0..7]  OF AnsiChar;
  344.                  Size     : ARRAY [0..11] OF AnsiChar;
  345.                  MTime    : ARRAY [0..11] OF AnsiChar;
  346.                  ChkSum   : ARRAY [0..7]  OF AnsiChar;
  347.                  LinkFlag : AnsiChar;
  348.                  LinkName : ARRAY [0..NAMSIZ-1] OF AnsiChar;
  349.                  Magic    : ARRAY [0..7] OF AnsiChar;
  350.                  UName    : ARRAY [0..TUNMLEN-1] OF AnsiChar;
  351.                  GName    : ARRAY [0..TGNMLEN-1] OF AnsiChar;
  352.                  DevMajor : ARRAY [0..7] OF AnsiChar;
  353.                  DevMinor : ARRAY [0..7] OF AnsiChar;
  354.                END;
  355.  
  356. FUNCTION ExtractText (P : PAnsiChar) : AnsiString;
  357. BEGIN
  358.   Result := AnsiString (P);
  359. END;
  360.  
  361.  
  362. FUNCTION ExtractNumber (P : PAnsiChar) : INTEGER; OVERLOAD;
  363. VAR
  364.   Strg : AnsiString;
  365. BEGIN
  366.   Strg := AnsiString (Trim (string (P)));
  367.   P := PAnsiChar (Strg);
  368.   Result := 0;
  369.   WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN
  370.     Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3);
  371.     INC (P);
  372.     END;
  373. END;
  374.  
  375.  
  376. FUNCTION ExtractNumber64 (P : PAnsiChar) : INT64; OVERLOAD;
  377. VAR
  378.   Strg : AnsiString;
  379. BEGIN
  380.   Strg := AnsiString (Trim (string (P)));
  381.   P := PAnsiChar (Strg);
  382.   Result := 0;
  383.   WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN
  384.     Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3);
  385.     INC (P);
  386.     END;
  387. END;
  388.  
  389.  
  390.  
  391. FUNCTION ExtractNumber (P : PAnsiChar; MaxLen : INTEGER) : INTEGER; OVERLOAD;
  392. VAR
  393.   S0   : ARRAY [0..255] OF AnsiChar;
  394.   Strg : AnsiString;
  395. BEGIN
  396.   StrLCopy (S0, P, MaxLen);
  397.   Strg := AnsiString (Trim (string (S0)));
  398.   P := PAnsiChar (Strg);
  399.   Result := 0;
  400.   WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN
  401.     Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3);
  402.     INC (P);
  403.     END;
  404. END;
  405.  
  406.  
  407. FUNCTION ExtractNumber64 (P : PAnsiChar; MaxLen : INTEGER) : INT64; OVERLOAD;
  408. VAR
  409.   S0   : ARRAY [0..255] OF AnsiChar;
  410.   Strg : AnsiString;
  411. BEGIN
  412.   StrLCopy (S0, P, MaxLen);
  413.   Strg := AnsiString (Trim (string (S0)));
  414.   P := PAnsiChar (Strg);
  415.   Result := 0;
  416.   WHILE (P^ <> #32) AND (P^ <> #0) DO BEGIN
  417.     Result := (ORD (P^) - ORD ('0')) OR (Result SHL 3);
  418.     INC (P);
  419.     END;
  420. END;
  421.  
  422.  
  423. FUNCTION Records (Bytes : INT64) : INT64;
  424. BEGIN
  425.   Result := Bytes DIV RECORDSIZE;
  426.   IF Bytes MOD RECORDSIZE > 0 THEN
  427.     INC (Result);
  428. END;
  429.  
  430.  
  431. PROCEDURE Octal (N : INTEGER; P : PAnsiChar; Len : INTEGER);
  432.          // Makes a string of octal digits
  433.          // The string will always be "Len" characters long
  434. VAR
  435.   I : INTEGER;
  436. BEGIN
  437.   FOR I := Len-2 DOWNTO 0 DO BEGIN
  438.     (P+I)^ := AnsiChar (ORD ('0') + ORD (N AND $07));
  439.     N := N SHR 3;
  440.     END;
  441.   FOR I := 0 TO Len-3 DO
  442.     IF (P+I)^ = '0'
  443.       THEN (P+I)^ := #32
  444.       ELSE BREAK;
  445.   (P+Len-1)^ := #32;
  446. END;
  447.  
  448.  
  449. PROCEDURE Octal64 (N : INT64; P : PAnsiChar; Len : INTEGER);
  450.          // Makes a string of octal digits
  451.          // The string will always be "Len" characters long
  452. VAR
  453.   I     : INTEGER;
  454. BEGIN
  455.   FOR I := Len-2 DOWNTO 0 DO BEGIN
  456.     (P+I)^ := AnsiChar (ORD ('0') + ORD (N AND $07));
  457.     N := N SHR 3;
  458.     END;
  459.   FOR I := 0 TO Len-3 DO
  460.     IF (P+I)^ = '0'
  461.       THEN (P+I)^ := #32
  462.       ELSE BREAK;
  463.   (P+Len-1)^ := #32;
  464. END;
  465.  
  466.  
  467. PROCEDURE OctalN (N : INTEGER; P : PAnsiChar; Len : INTEGER);
  468. BEGIN
  469.   Octal (N, P, Len-1);
  470.   (P+Len-1)^ := #0;
  471. END;
  472.  
  473.  
  474. PROCEDURE WriteTarHeader (Dest : TStream; DirRec : TTarDirRec);
  475. VAR
  476.   Rec      : ARRAY [0..RECORDSIZE-1] OF AnsiChar;
  477.   TH       : TTarHeader ABSOLUTE Rec;
  478.   Mode     : INTEGER;
  479.   NullDate : TDateTime;
  480.   Checksum : CARDINAL;
  481.   I        : INTEGER;
  482. BEGIN
  483.   FillChar (Rec, RECORDSIZE, 0);
  484.   StrLCopy (TH.Name, PAnsiChar (DirRec.Name), NAMSIZ);
  485.   CASE DirRec.FileType OF
  486.     ftNormal, ftLink  : Mode := $08000;
  487.     ftSymbolicLink    : Mode := $0A000;
  488.     ftDirectory       : Mode := $04000;
  489.     ELSE                Mode := 0;
  490.     END;
  491.   IF tmSaveText IN DirRec.Mode THEN Mode := Mode OR $0200;
  492.   IF tmSetGid   IN DirRec.Mode THEN Mode := Mode OR $0400;
  493.   IF tmSetUid   IN DirRec.Mode THEN Mode := Mode OR $0800;
  494.   IF tpReadByOwner    IN DirRec.Permissions THEN Mode := Mode OR $0100;
  495.   IF tpWriteByOwner   IN DirRec.Permissions THEN Mode := Mode OR $0080;
  496.   IF tpExecuteByOwner IN DirRec.Permissions THEN Mode := Mode OR $0040;
  497.   IF tpReadByGroup    IN DirRec.Permissions THEN Mode := Mode OR $0020;
  498.   IF tpWriteByGroup   IN DirRec.Permissions THEN Mode := Mode OR $0010;
  499.   IF tpExecuteByGroup IN DirRec.Permissions THEN Mode := Mode OR $0008;
  500.   IF tpReadByOther    IN DirRec.Permissions THEN Mode := Mode OR $0004;
  501.   IF tpWriteByOther   IN DirRec.Permissions THEN Mode := Mode OR $0002;
  502.   IF tpExecuteByOther IN DirRec.Permissions THEN Mode := Mode OR $0001;
  503.   OctalN (Mode, @TH.Mode, 8);
  504.   OctalN (DirRec.UID, @TH.UID, 8);
  505.   OctalN (DirRec.GID, @TH.GID, 8);
  506.   Octal64 (DirRec.Size, @TH.Size, 12);
  507.   NullDate := EncodeDate (1970, 1, 1);
  508.   IF DirRec.DateTime >= NullDate
  509.     THEN Octal (Trunc ((DirRec.DateTime - NullDate) * 86400.0), @TH.MTime, 12)
  510.     ELSE Octal (Trunc (                   NullDate  * 86400.0), @TH.MTime, 12);
  511.   CASE DirRec.FileType OF
  512.     ftNormal       : TH.LinkFlag := '0';
  513.     ftLink         : TH.LinkFlag := '1';
  514.     ftSymbolicLink : TH.LinkFlag := '2';
  515.     ftCharacter    : TH.LinkFlag := '3';
  516.     ftBlock        : TH.LinkFlag := '4';
  517.     ftDirectory    : TH.LinkFlag := '5';
  518.     ftFifo         : TH.LinkFlag := '6';
  519.     ftContiguous   : TH.LinkFlag := '7';
  520.     ftDumpDir      : TH.LinkFlag := 'D';
  521.     ftMultiVolume  : TH.LinkFlag := 'M';
  522.     ftVolumeHeader : TH.LinkFlag := 'V';
  523.     END;
  524.   StrLCopy (TH.LinkName, PAnsiChar (DirRec.LinkName), NAMSIZ);
  525.   StrLCopy (TH.Magic, PAnsiChar (DirRec.Magic + #32#32#32#32#32#32#32#32), 8);
  526.   StrLCopy (TH.UName, PAnsiChar (DirRec.UserName), TUNMLEN);
  527.   StrLCopy (TH.GName, PAnsiChar (DirRec.GroupName), TGNMLEN);
  528.   OctalN (DirRec.MajorDevNo, @TH.DevMajor, 8);
  529.   OctalN (DirRec.MinorDevNo, @TH.DevMinor, 8);
  530.   StrMove (TH.ChkSum, CHKBLANKS, 8);
  531.  
  532.   CheckSum := 0;
  533.   FOR I := 0 TO SizeOf (TTarHeader)-1 DO
  534.     INC (CheckSum, INTEGER (ORD (Rec [I])));
  535.   OctalN (CheckSum, @TH.ChkSum, 8);
  536.  
  537.   Dest.Write (TH, RECORDSIZE);
  538. END;
  539.  
  540.  
  541.  
  542. (*
  543. ===============================================================================================
  544. TTarArchive
  545. ===============================================================================================
  546. *)
  547.  
  548. CONSTRUCTOR TTarArchive.Create (Stream : TStream);
  549. BEGIN
  550.   INHERITED Create;
  551.   FStream     := Stream;
  552.   FOwnsStream := FALSE;
  553.   Reset;
  554. END;
  555.  
  556.  
  557. CONSTRUCTOR TTarArchive.Create (Filename : STRING; FileMode : WORD);
  558. BEGIN
  559.   INHERITED Create;
  560.   FStream     := TFileStream.Create (Filename, FileMode);
  561.   FOwnsStream := TRUE;
  562.   Reset;
  563. END;
  564.  
  565.  
  566. DESTRUCTOR TTarArchive.Destroy;
  567. BEGIN
  568.   IF FOwnsStream THEN
  569.     FStream.Free;
  570.   INHERITED Destroy;
  571. END;
  572.  
  573.  
  574. PROCEDURE TTarArchive.Reset;
  575.           // Reset File Pointer
  576. BEGIN
  577.   FStream.Position := 0;
  578.   FBytesToGo       := 0;
  579. END;
  580.  
  581.  
  582. FUNCTION  TTarArchive.FindNext (VAR DirRec : TTarDirRec) : BOOLEAN;
  583.           // Reads next Directory Info Record
  584.           // The Stream pointer must point to the first byte of the tar header
  585. VAR
  586.   Rec          : ARRAY [0..RECORDSIZE-1] OF CHAR;
  587.   CurFilePos   : INTEGER;
  588.   Header       : TTarHeader ABSOLUTE Rec;
  589.   I            : INTEGER;
  590.   HeaderChkSum : WORD;
  591.   Checksum     : CARDINAL;
  592. BEGIN
  593.   // --- Scan until next pointer
  594.   IF FBytesToGo > 0 THEN
  595.     FStream.Seek (Records (FBytesToGo) * RECORDSIZE, soFromCurrent);
  596.  
  597.   // --- EOF reached?
  598.   Result := FALSE;
  599.   CurFilePos := FStream.Position;
  600.   TRY
  601.     FStream.ReadBuffer (Rec, RECORDSIZE);
  602.     if Rec [0] = #0 THEN EXIT;   // EOF reached
  603.   EXCEPT
  604.     EXIT;   // EOF reached, too
  605.     END;
  606.   Result := TRUE;
  607.  
  608.   ClearDirRec (DirRec);
  609.  
  610.   DirRec.FilePos := CurFilePos;
  611.   DirRec.Name := ExtractText (Header.Name);
  612.   DirRec.Size := ExtractNumber64 (@Header.Size, 12);
  613.   DirRec.DateTime := EncodeDate (1970, 1, 1) + (ExtractNumber (@Header.MTime, 12) / 86400.0);
  614.   I := ExtractNumber (@Header.Mode);
  615.   IF I AND $0100 <> 0 THEN Include (DirRec.Permissions, tpReadByOwner);
  616.   IF I AND $0080 <> 0 THEN Include (DirRec.Permissions, tpWriteByOwner);
  617.   IF I AND $0040 <> 0 THEN Include (DirRec.Permissions, tpExecuteByOwner);
  618.   IF I AND $0020 <> 0 THEN Include (DirRec.Permissions, tpReadByGroup);
  619.   IF I AND $0010 <> 0 THEN Include (DirRec.Permissions, tpWriteByGroup);
  620.   IF I AND $0008 <> 0 THEN Include (DirRec.Permissions, tpExecuteByGroup);
  621.   IF I AND $0004 <> 0 THEN Include (DirRec.Permissions, tpReadByOther);
  622.   IF I AND $0002 <> 0 THEN Include (DirRec.Permissions, tpWriteByOther);
  623.   IF I AND $0001 <> 0 THEN Include (DirRec.Permissions, tpExecuteByOther);
  624.   IF I AND $0200 <> 0 THEN Include (DirRec.Mode, tmSaveText);
  625.   IF I AND $0400 <> 0 THEN Include (DirRec.Mode, tmSetGid);
  626.   IF I AND $0800 <> 0 THEN Include (DirRec.Mode, tmSetUid);
  627.   CASE Header.LinkFlag OF
  628.     #0, '0' : DirRec.FileType := ftNormal;
  629.     '1'     : DirRec.FileType := ftLink;
  630.     '2'     : DirRec.FileType := ftSymbolicLink;
  631.     '3'     : DirRec.FileType := ftCharacter;
  632.     '4'     : DirRec.FileType := ftBlock;
  633.     '5'     : DirRec.FileType := ftDirectory;
  634.     '6'     : DirRec.FileType := ftFifo;
  635.     '7'     : DirRec.FileType := ftContiguous;
  636.     'D'     : DirRec.FileType := ftDumpDir;
  637.     'M'     : DirRec.FileType := ftMultiVolume;
  638.     'V'     : DirRec.FileType := ftVolumeHeader;
  639.     END;
  640.   DirRec.LinkName   := ExtractText (Header.LinkName);
  641.   DirRec.UID        := ExtractNumber (@Header.UID);
  642.   DirRec.GID        := ExtractNumber (@Header.GID);
  643.   DirRec.UserName   := ExtractText (Header.UName);
  644.   DirRec.GroupName  := ExtractText (Header.GName);
  645.   DirRec.Magic      := AnsiString (Trim (string (Header.Magic)));
  646.   DirRec.MajorDevNo := ExtractNumber (@Header.DevMajor);
  647.   DirRec.MinorDevNo := ExtractNumber (@Header.DevMinor);
  648.  
  649.   HeaderChkSum := ExtractNumber (@Header.ChkSum);   // Calc Checksum
  650.   CheckSum := 0;
  651.   StrMove (Header.ChkSum, CHKBLANKS, 8);
  652.   FOR I := 0 TO SizeOf (TTarHeader)-1 DO
  653.     INC (CheckSum, INTEGER (ORD (Rec [I])));
  654.   DirRec.CheckSumOK := WORD (CheckSum) = WORD (HeaderChkSum);
  655.  
  656.   IF DirRec.FileType in [ftLink, ftSymbolicLink, ftDirectory, ftFifo, ftVolumeHeader]
  657.     THEN FBytesToGo := 0
  658.     ELSE FBytesToGo := DirRec.Size;
  659. END;
  660.  
  661.  
  662. PROCEDURE TTarArchive.ReadFile (Buffer : POINTER);
  663.           // Reads file data for the last Directory Record. The entire file is read into the buffer.
  664.           // The buffer must be large enough to take up the whole file.
  665. VAR
  666.   RestBytes : INTEGER;
  667. BEGIN
  668.   IF FBytesToGo = 0 THEN EXIT;
  669.   RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo;
  670.   FStream.ReadBuffer (Buffer^, FBytesToGo);
  671.   FStream.Seek (RestBytes, soFromCurrent);
  672.   FBytesToGo := 0;
  673. END;
  674.  
  675.  
  676. PROCEDURE TTarArchive.ReadFile (Stream : TStream);
  677.           // Reads file data for the last Directory Record.
  678.           // The entire file is written out to the stream.
  679.           // The stream is left at its current position prior to writing
  680. VAR
  681.   RestBytes : INTEGER;
  682. BEGIN
  683.   IF FBytesToGo = 0 THEN EXIT;
  684.   RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo;
  685.   Stream.CopyFrom (FStream, FBytesToGo);
  686.   FStream.Seek (RestBytes, soFromCurrent);
  687.   FBytesToGo := 0;
  688. END;
  689.  
  690.  
  691. PROCEDURE TTarArchive.ReadFile (Filename : STRING);
  692.           // Reads file data for the last Directory Record.
  693.           // The entire file is saved in the given Filename
  694. VAR
  695.   FS : TFileStream;
  696. BEGIN
  697.   FS := TFileStream.Create (Filename, fmCreate);
  698.   TRY
  699.     ReadFile (FS);
  700.   FINALLY
  701.     FS.Free;
  702.     END;
  703. END;
  704.  
  705.  
  706. FUNCTION  TTarArchive.ReadFile : RawByteString;
  707.           // Reads file data for the last Directory Record. The entire file is returned
  708.           // as a large ANSI string.
  709. VAR
  710.   RestBytes : INTEGER;
  711. BEGIN
  712.   IF FBytesToGo = 0 THEN EXIT;
  713.   RestBytes := Records (FBytesToGo) * RECORDSIZE - FBytesToGo;
  714.   SetLength (Result, FBytesToGo);
  715.   FStream.ReadBuffer (PAnsiChar (Result)^, FBytesToGo);
  716.   FStream.Seek (RestBytes, soFromCurrent);
  717.   FBytesToGo := 0;
  718. END;
  719.  
  720.  
  721. PROCEDURE TTarArchive.GetFilePos (VAR Current, Size : INT64);
  722.           // Returns the Current Position in the TAR stream
  723. BEGIN
  724.   Current := FStream.Position;
  725.   Size    := FStream.Size;
  726. END;
  727.  
  728.  
  729. PROCEDURE TTarArchive.SetFilePos (NewPos : INT64);                   // Set new Current File Position
  730. BEGIN
  731.   IF NewPos < FStream.Size THEN
  732.     FStream.Seek (NewPos, soFromBeginning);
  733. END;
  734.  
  735.  
  736. (*
  737. ===============================================================================================
  738. TTarWriter
  739. ===============================================================================================
  740. *)
  741.  
  742.  
  743. CONSTRUCTOR TTarWriter.CreateEmpty;
  744. VAR
  745.   TP : TTarPermission;
  746. BEGIN
  747.   INHERITED Create;
  748.   FOwnsStream  := FALSE;
  749.   FFinalized   := FALSE;
  750.   FPermissions := [];
  751.   FOR TP := Low (TP) TO High (TP) DO
  752.     Include (FPermissions, TP);
  753.   FUID       := 0;
  754.   FGID       := 0;
  755.   FUserName  := '';
  756.   FGroupName := '';
  757.   FMode      := [];
  758.   FMagic     := 'ustar';
  759. END;
  760.  
  761. CONSTRUCTOR TTarWriter.Create (TargetStream   : TStream);
  762. BEGIN
  763.   CreateEmpty;
  764.   FStream     := TargetStream;
  765.   FOwnsStream := FALSE;
  766. END;
  767.  
  768.  
  769. CONSTRUCTOR TTarWriter.Create (TargetFilename : STRING; Mode : INTEGER = fmCreate);
  770. BEGIN
  771.   CreateEmpty;
  772.   FStream     := TFileStream.Create (TargetFilename, Mode);
  773.   FOwnsStream := TRUE;
  774. END;
  775.  
  776.  
  777. DESTRUCTOR TTarWriter.Destroy;
  778. BEGIN
  779.   IF NOT FFinalized THEN BEGIN
  780.     Finalize;
  781.     FFinalized := TRUE;
  782.     END;
  783.   IF FOwnsStream THEN
  784.     FStream.Free;
  785.   INHERITED Destroy;
  786. END;
  787.  
  788.  
  789. PROCEDURE TTarWriter.AddFile   (Filename : STRING;  TarFilename : AnsiString = '');
  790. VAR
  791.   S    : TFileStream;
  792.   Date : TDateTime;
  793. BEGIN
  794.   Date := FileTimeGMT (Filename);
  795.   IF TarFilename = ''
  796.     THEN TarFilename := AnsiString (ConvertFilename (Filename))
  797.     ELSE TarFilename := AnsiString (ConvertFilename (string (TarFilename)));
  798.   S := TFileStream.Create (Filename, fmOpenRead OR fmShareDenyWrite);
  799.   TRY
  800.     AddStream (S, TarFilename, Date);
  801.   FINALLY
  802.     S.Free
  803.     END;
  804. END;
  805.  
  806.  
  807. PROCEDURE TTarWriter.AddStream (Stream : TStream; TarFilename : AnsiString; FileDateGmt : TDateTime);
  808. VAR
  809.   DirRec      : TTarDirRec;
  810.   Rec         : ARRAY [0..RECORDSIZE-1] OF CHAR;
  811.   BytesToRead : INT64;      // Bytes to read from the Source Stream
  812.   BlockSize   : INT64;      // Bytes to write out for the current record
  813. BEGIN
  814.   ClearDirRec (DirRec);
  815.   DirRec.Name        := TarFilename;
  816.   DirRec.Size        := Stream.Size - Stream.Position;
  817.   DirRec.DateTime    := FileDateGmt;
  818.   DirRec.Permissions := FPermissions;
  819.   DirRec.FileType    := ftNormal;
  820.   DirRec.LinkName    := '';
  821.   DirRec.UID         := FUID;
  822.   DirRec.GID         := FGID;
  823.   DirRec.UserName    := FUserName;
  824.   DirRec.GroupName   := FGroupName;
  825.   DirRec.ChecksumOK  := TRUE;
  826.   DirRec.Mode        := FMode;
  827.   DirRec.Magic       := FMagic;
  828.   DirRec.MajorDevNo  := 0;
  829.   DirRec.MinorDevNo  := 0;
  830.  
  831.   WriteTarHeader (FStream, DirRec);
  832.   BytesToRead := DirRec.Size;
  833.   WHILE BytesToRead > 0 DO BEGIN
  834.     BlockSize := BytesToRead;
  835.     IF BlockSize > RECORDSIZE THEN BlockSize := RECORDSIZE;
  836.     FillChar (Rec, RECORDSIZE, 0);
  837.     Stream.Read (Rec, BlockSize);
  838.     FStream.Write (Rec, RECORDSIZE);
  839.     DEC (BytesToRead, BlockSize);
  840.     END;
  841. END;
  842.  
  843.  
  844. PROCEDURE TTarWriter.AddString (Contents : RawByteString; TarFilename : AnsiString; FileDateGmt : TDateTime);
  845. VAR
  846.   S : TStringStream;
  847. BEGIN
  848.   S := TStringStream.Create (Contents);
  849.   TRY
  850.     AddStream (S, TarFilename, FileDateGmt);
  851.   FINALLY
  852.     S.Free
  853.     END
  854. END;
  855.  
  856.  
  857. PROCEDURE TTarWriter.AddDir (Dirname : AnsiString; DateGmt : TDateTime; MaxDirSize : INT64 = 0);
  858. VAR
  859.   DirRec      : TTarDirRec;
  860. BEGIN
  861.   ClearDirRec (DirRec);
  862.   DirRec.Name        := Dirname;
  863.   DirRec.Size        := MaxDirSize;
  864.   DirRec.DateTime    := DateGmt;
  865.   DirRec.Permissions := FPermissions;
  866.   DirRec.FileType    := ftDirectory;
  867.   DirRec.LinkName    := '';
  868.   DirRec.UID         := FUID;
  869.   DirRec.GID         := FGID;
  870.   DirRec.UserName    := FUserName;
  871.   DirRec.GroupName   := FGroupName;
  872.   DirRec.ChecksumOK  := TRUE;
  873.   DirRec.Mode        := FMode;
  874.   DirRec.Magic       := FMagic;
  875.   DirRec.MajorDevNo  := 0;
  876.   DirRec.MinorDevNo  := 0;
  877.  
  878.   WriteTarHeader (FStream, DirRec);
  879. END;
  880.  
  881.  
  882. PROCEDURE TTarWriter.AddSymbolicLink (Filename, Linkname : AnsiString; DateGmt : TDateTime);
  883. VAR
  884.   DirRec : TTarDirRec;
  885. BEGIN
  886.   ClearDirRec (DirRec);
  887.   DirRec.Name        := Filename;
  888.   DirRec.Size        := 0;
  889.   DirRec.DateTime    := DateGmt;
  890.   DirRec.Permissions := FPermissions;
  891.   DirRec.FileType    := ftSymbolicLink;
  892.   DirRec.LinkName    := Linkname;
  893.   DirRec.UID         := FUID;
  894.   DirRec.GID         := FGID;
  895.   DirRec.UserName    := FUserName;
  896.   DirRec.GroupName   := FGroupName;
  897.   DirRec.ChecksumOK  := TRUE;
  898.   DirRec.Mode        := FMode;
  899.   DirRec.Magic       := FMagic;
  900.   DirRec.MajorDevNo  := 0;
  901.   DirRec.MinorDevNo  := 0;
  902.  
  903.   WriteTarHeader (FStream, DirRec);
  904. END;
  905.  
  906.  
  907. PROCEDURE TTarWriter.AddLink (Filename, Linkname : AnsiString; DateGmt : TDateTime);
  908. VAR
  909.   DirRec : TTarDirRec;
  910. BEGIN
  911.   ClearDirRec (DirRec);
  912.   DirRec.Name        := Filename;
  913.   DirRec.Size        := 0;
  914.   DirRec.DateTime    := DateGmt;
  915.   DirRec.Permissions := FPermissions;
  916.   DirRec.FileType    := ftLink;
  917.   DirRec.LinkName    := Linkname;
  918.   DirRec.UID         := FUID;
  919.   DirRec.GID         := FGID;
  920.   DirRec.UserName    := FUserName;
  921.   DirRec.GroupName   := FGroupName;
  922.   DirRec.ChecksumOK  := TRUE;
  923.   DirRec.Mode        := FMode;
  924.   DirRec.Magic       := FMagic;
  925.   DirRec.MajorDevNo  := 0;
  926.   DirRec.MinorDevNo  := 0;
  927.  
  928.   WriteTarHeader (FStream, DirRec);
  929. END;
  930.  
  931.  
  932. PROCEDURE TTarWriter.AddVolumeHeader (VolumeId : AnsiString; DateGmt : TDateTime);
  933. VAR
  934.   DirRec : TTarDirRec;
  935. BEGIN
  936.   ClearDirRec (DirRec);
  937.   DirRec.Name        := VolumeId;
  938.   DirRec.Size        := 0;
  939.   DirRec.DateTime    := DateGmt;
  940.   DirRec.Permissions := FPermissions;
  941.   DirRec.FileType    := ftVolumeHeader;
  942.   DirRec.LinkName    := '';
  943.   DirRec.UID         := FUID;
  944.   DirRec.GID         := FGID;
  945.   DirRec.UserName    := FUserName;
  946.   DirRec.GroupName   := FGroupName;
  947.   DirRec.ChecksumOK  := TRUE;
  948.   DirRec.Mode        := FMode;
  949.   DirRec.Magic       := FMagic;
  950.   DirRec.MajorDevNo  := 0;
  951.   DirRec.MinorDevNo  := 0;
  952.  
  953.   WriteTarHeader (FStream, DirRec);
  954. END;
  955.  
  956.  
  957. PROCEDURE TTarWriter.Finalize;
  958.           // Writes the End-Of-File Tag
  959.           // Data after this tag will be ignored
  960.           // The destructor calls this automatically if you didn't do it before
  961. VAR
  962.   Rec : ARRAY [0..RECORDSIZE-1] OF CHAR;
  963. BEGIN
  964.   FillChar (Rec, SizeOf (Rec), 0);
  965.   FStream.Write (Rec, RECORDSIZE);
  966.   FFinalized := TRUE;
  967. END;
  968.  
  969.  
  970. END.
Advertisement
Add Comment
Please, Sign In to add comment