anchor.diff:  -author("Michal Stanisz").
   
   %% API
  --export([prepare_header/5, data_padding/1, ending_marker/0]).
  -
  --type tar_header() :: binary().
  --export_type([tar_header/0]).
  +-export([open_archive/0, open_archive/1]).
  +-export([new_file_entry/6]).
  +-export([append_to_file_content/2, append_to_file_content/3]).
  +-export([close_archive/1]).
  +-export([read_bytes/1]).
   
   -define(BLOCK_SIZE, 512).
   -define(DIRECTORY_TYPE_FLAG, <<"5">>).
  
positive.code: -define(CHECKSUM_PLACEHOLDER, binary:copy(<<32>>, 8)).
  -define(CHECKSUM_ENDING, <<0, 32>>).
  
  %%%===================================================================
  %%% API
  %%%===================================================================
  
  -spec prepare_header(binary(), FileSize :: non_neg_integer(), Mode :: non_neg_integer(), 
      Timestamp :: non_neg_integer(), boolean()) -> tar_header().
  prepare_header(Filename, FileSize, Mode, Timestamp, IsDir) ->
      {Type, FinalFilename} = case IsDir of
          true ->
              {?DIRECTORY_TYPE_FLAG, <<Filename/binary, "/">>};
          false ->
              {?FILE_TYPE_FLAG, Filename}
      end,
      prepare_header(FinalFilename, byte_size(Filename), FileSize, Mode, Timestamp, Type).
  
  
  -spec data_padding(non_neg_integer()) -> binary().
  data_padding(FileSize) when FileSize rem ?BLOCK_SIZE == 0 -> <<>>;
  data_padding(FileSize) ->
      BytesToFill = (?BLOCK_SIZE - FileSize rem ?BLOCK_SIZE),
      binary:copy(?NUL, BytesToFill).
  
  
  -spec ending_marker() -> binary().
  ending_marker() ->
      binary:copy(?NUL, 2 * ?BLOCK_SIZE).
  
  
  %%%===================================================================
  %%% Internal functions
  %%%===================================================================
  
  %% @private
  -spec prepare_header(binary(), FilenameSize :: non_neg_integer(), FileSize :: non_neg_integer(), 
      Mode :: non_neg_integer(), Timestamp :: non_neg_integer(), binary()) -> tar_header().
  prepare_header(Filename, FilenameSize, FileSize, Mode, Timestamp, Type) when FilenameSize > ?MAX_FILENAME_LENGTH ->
      LongLinkHeader = prepare_header(
          ?LONG_LINK_HEADER, byte_size(?LONG_LINK_HEADER), FilenameSize, 0, 0, ?LONG_LINK_TYPE_FLAG),
      TrimmedFilename = binary:part(Filename, 0, ?MAX_FILENAME_LENGTH),
      FileHeader = prepare_header(
          TrimmedFilename, byte_size(TrimmedFilename), FileSize, Mode, Timestamp, Type),
      <<
          LongLinkHeader/binary,
  
negative.code:         FileHeader/binary
      >>;
  
  file_header(Filename, FilenameSize, FileSize, Mode, Timestamp, Type) when FilenameSize =< ?MAX_FILENAME_LENGTH ->
      % use root(0) as owner/group - by default it will be overwritten when untarring
      PaddedOwner = str_utils:pad_left(format_octal(0), 7, ?VALUE_PAD),
      PaddedGroup = str_utils:pad_left(format_octal(0), 7, ?VALUE_PAD),
  