src/packkit@ar.erl
-module(packkit@ar).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/packkit/ar.gleam").
-export([format/0, new/0, decode_with_limits/2, decode/1, encode/1]).
-export_type([special_kind/0, parsed_record/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Unix `ar` archive encoder and decoder.\n"
"\n"
" The encoder emits the BSD long-name variant (`#1/N`) so it can\n"
" carry archive entries whose paths exceed 16 bytes or contain\n"
" spaces. The decoder additionally accepts the GNU long-name\n"
" variant (a leading `//` string-table member plus `/<offset>`\n"
" references in entry headers), which is the form produced by\n"
" `binutils` ar and present in nearly every `.deb` / `.a` on\n"
" Linux. GNU symbol-table members (named `/`) are skipped\n"
" transparently so they do not show up as user-visible entries.\n"
"\n"
" Only regular file entries are supported - `ar` does not represent\n"
" directories or symbolic links.\n"
).
-type special_kind() :: regular_entry | symbol_table | string_table.
-type parsed_record() :: {parsed_record,
binary(),
integer(),
gleam@option:option(integer()),
special_kind(),
integer(),
integer(),
integer(),
integer(),
integer()}.
-file("src/packkit/ar.gleam", 38).
?DOC(" AR archive format marker.\n").
-spec format() -> packkit@archive:archive_format().
format() ->
packkit@archive:ar().
-file("src/packkit/ar.gleam", 43).
?DOC(" Create an empty AR archive value.\n").
-spec new() -> packkit@archive:archive().
new() ->
packkit@archive:new(format()).
-file("src/packkit/ar.gleam", 61).
-spec reject_comment(packkit@archive:archive()) -> {ok, nil} |
{error, packkit@error:archive_error()}.
reject_comment(Archive_value) ->
case packkit@archive:comment(Archive_value) of
none ->
{ok, nil};
{some, _} ->
{error, {archive_comment_unsupported, <<"ar"/utf8>>}}
end.
-file("src/packkit/ar.gleam", 260).
-spec find_gnu_terminator(bitstring(), integer(), integer()) -> integer().
find_gnu_terminator(Bytes, Pos, Len) ->
case Pos >= Len of
true ->
Len;
false ->
case gleam_stdlib:bit_array_slice(Bytes, Pos, 1) of
{ok, <<16#2F>>} ->
Pos;
{ok, <<16#00>>} ->
Pos;
{ok, <<16#0A>>} ->
Pos;
_ ->
find_gnu_terminator(Bytes, Pos + 1, Len)
end
end.
-file("src/packkit/ar.gleam", 274).
-spec check_member_limit(integer(), packkit@limit:limits()) -> {ok, nil} |
{error, packkit@error:archive_error()}.
check_member_limit(Count, Limits) ->
case Count > packkit@limit:max_members(Limits) of
true ->
{error, {archive_limit_exceeded, <<"max_members"/utf8>>, Count}};
false ->
{ok, nil}
end.
-file("src/packkit/ar.gleam", 535).
-spec pow_int_loop(integer(), integer(), integer()) -> integer().
pow_int_loop(Base, Exponent, Acc) ->
case Exponent of
0 ->
Acc;
_ ->
pow_int_loop(Base, Exponent - 1, Acc * Base)
end.
-file("src/packkit/ar.gleam", 531).
-spec pow_int(integer(), integer()) -> integer().
pow_int(Base, Exponent) ->
pow_int_loop(Base, Exponent, 1).
-file("src/packkit/ar.gleam", 563).
-spec byte_repeat_loop(integer(), integer(), bitstring()) -> bitstring().
byte_repeat_loop(Byte, Count, Acc) ->
case Count of
0 ->
Acc;
_ ->
byte_repeat_loop(Byte, Count - 1, <<Acc/bitstring, Byte>>)
end.
-file("src/packkit/ar.gleam", 559).
-spec byte_repeat(integer(), integer()) -> bitstring().
byte_repeat(Byte, Count) ->
byte_repeat_loop(Byte, Count, <<>>).
-file("src/packkit/ar.gleam", 548).
-spec right_pad(bitstring(), integer(), integer()) -> bitstring().
right_pad(Value, Width, Fill) ->
Size = erlang:byte_size(Value),
case Size >= Width of
true ->
Slice@1 = case gleam_stdlib:bit_array_slice(Value, 0, Width) of
{ok, Slice} -> Slice;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/ar"/utf8>>,
function => <<"right_pad"/utf8>>,
line => 552,
value => _assert_fail,
start => 16047,
'end' => 16102,
pattern_start => 16058,
pattern_end => 16067})
end,
Slice@1;
false ->
gleam_stdlib:bit_array_concat(
[Value, byte_repeat(Fill, Width - Size)]
)
end.
-file("src/packkit/ar.gleam", 480).
-spec text_field(binary(), integer()) -> bitstring().
text_field(Value, Width) ->
Bytes = gleam_stdlib:identity(Value),
right_pad(Bytes, Width, 16#20).
-file("src/packkit/ar.gleam", 485).
-spec decimal_field(integer(), integer()) -> bitstring().
decimal_field(Value, Width) ->
Raw = erlang:integer_to_binary(Value),
Raw_bits = gleam_stdlib:identity(Raw),
right_pad(Raw_bits, Width, 16#20).
-file("src/packkit/ar.gleam", 491).
-spec checked_decimal_field(integer(), integer(), binary()) -> {ok, bitstring()} |
{error, packkit@error:archive_error()}.
checked_decimal_field(Value, Width, Field) ->
Raw = erlang:integer_to_binary(Value),
Raw_size = erlang:byte_size(Raw),
case (Value < 0) orelse (Raw_size > Width) of
true ->
Max_value = pow_int(10, Width) - 1,
{error,
{archive_field_overflow,
<<"ar "/utf8, Field/binary>>,
Value,
Max_value}};
false ->
{ok, decimal_field(Value, Width)}
end.
-file("src/packkit/ar.gleam", 542).
-spec octal_field(integer(), integer()) -> bitstring().
octal_field(Value, Width) ->
Raw = gleam@int:to_base8(Value),
Raw_bits = gleam_stdlib:identity(Raw),
right_pad(Raw_bits, Width, 16#20).
-file("src/packkit/ar.gleam", 511).
-spec checked_octal_field(integer(), integer(), binary()) -> {ok, bitstring()} |
{error, packkit@error:archive_error()}.
checked_octal_field(Value, Width, Field) ->
Raw = gleam@int:to_base8(Value),
Raw_size = erlang:byte_size(Raw),
case (Value < 0) orelse (Raw_size > Width) of
true ->
Max_value = pow_int(8, Width) - 1,
{error,
{archive_field_overflow,
<<"ar "/utf8, Field/binary>>,
Value,
Max_value}};
false ->
{ok, octal_field(Value, Width)}
end.
-file("src/packkit/ar.gleam", 608).
-spec slice_or_error(bitstring(), integer(), integer()) -> {ok, bitstring()} |
{error, packkit@error:archive_error()}.
slice_or_error(Block, Offset, Width) ->
case gleam_stdlib:bit_array_slice(Block, Offset, Width) of
{ok, Value} ->
{ok, Value};
{error, _} ->
{error, {archive_invalid, <<"ar header field out of bounds"/utf8>>}}
end.
-file("src/packkit/ar.gleam", 620).
-spec bytes_to_string(bitstring()) -> {ok, binary()} |
{error, packkit@error:archive_error()}.
bytes_to_string(Bytes) ->
case gleam@bit_array:to_string(Bytes) of
{ok, Value} ->
{ok, Value};
{error, _} ->
{error, {archive_invalid, <<"non-UTF-8 ar header string"/utf8>>}}
end.
-file("src/packkit/ar.gleam", 241).
-spec gnu_string_at(bitstring(), integer()) -> {ok, binary()} |
{error, packkit@error:archive_error()}.
gnu_string_at(Table, Offset) ->
Size = erlang:byte_size(Table),
case (Offset < 0) orelse (Offset >= Size) of
true ->
{error,
{archive_invalid,
<<"ar GNU string table offset out of range"/utf8>>}};
false ->
Tail@1 = case gleam_stdlib:bit_array_slice(
Table,
Offset,
Size - Offset
) of
{ok, Tail} -> Tail;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/ar"/utf8>>,
function => <<"gnu_string_at"/utf8>>,
line => 252,
value => _assert_fail,
start => 7860,
'end' => 7927,
pattern_start => 7871,
pattern_end => 7879})
end,
End_index = find_gnu_terminator(Tail@1, 0, Size - Offset),
Name_bits@1 = case gleam_stdlib:bit_array_slice(
Tail@1,
0,
End_index
) of
{ok, Name_bits} -> Name_bits;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/ar"/utf8>>,
function => <<"gnu_string_at"/utf8>>,
line => 254,
value => _assert_fail@1,
start => 8000,
'end' => 8062,
pattern_start => 8011,
pattern_end => 8024})
end,
bytes_to_string(Name_bits@1)
end.
-file("src/packkit/ar.gleam", 570).
-spec read_decimal_field(bitstring(), integer(), integer()) -> {ok, integer()} |
{error, packkit@error:archive_error()}.
read_decimal_field(Block, Offset, Width) ->
gleam@result:'try'(
slice_or_error(Block, Offset, Width),
fun(Slice) ->
gleam@result:'try'(
bytes_to_string(Slice),
fun(Raw) ->
Trimmed = gleam@string:trim(Raw),
case Trimmed of
<<""/utf8>> ->
{ok, 0};
_ ->
case gleam_stdlib:parse_int(Trimmed) of
{ok, Value} ->
{ok, Value};
{error, _} ->
{error,
{archive_invalid,
<<"invalid decimal ar field"/utf8>>}}
end
end
end
)
end
).
-file("src/packkit/ar.gleam", 589).
-spec read_octal_field(bitstring(), integer(), integer()) -> {ok, integer()} |
{error, packkit@error:archive_error()}.
read_octal_field(Block, Offset, Width) ->
gleam@result:'try'(
slice_or_error(Block, Offset, Width),
fun(Slice) ->
gleam@result:'try'(
bytes_to_string(Slice),
fun(Raw) ->
Trimmed = gleam@string:trim(Raw),
case Trimmed of
<<""/utf8>> ->
{ok, 0};
_ ->
case gleam@int:base_parse(Trimmed, 8) of
{ok, Value} ->
{ok, Value};
{error, _} ->
{error,
{archive_invalid,
<<"invalid octal ar field"/utf8>>}}
end
end
end
)
end
).
-file("src/packkit/ar.gleam", 628).
-spec entry_error_to_archive_error(packkit@entry:entry_error(), binary()) -> packkit@error:archive_error().
entry_error_to_archive_error(Err, Path) ->
case Err of
empty_path ->
{archive_entry_rejected, Path, <<"empty path"/utf8>>};
{absolute_path, _} ->
{archive_entry_rejected, Path, <<"absolute path"/utf8>>};
{path_traversal, _} ->
{archive_entry_rejected, Path, <<"path traversal"/utf8>>};
{windows_path, _} ->
{archive_entry_rejected, Path, <<"windows path"/utf8>>};
{empty_segment, _} ->
{archive_entry_rejected, Path, <<"empty segment"/utf8>>};
{dot_segment, _} ->
{archive_entry_rejected, Path, <<"dot segment"/utf8>>};
{contains_nul, _} ->
{archive_entry_rejected, Path, <<"nul byte"/utf8>>}
end.
-file("src/packkit/ar.gleam", 650).
-spec strip_trailing_slash(binary()) -> binary().
strip_trailing_slash(Value) ->
case gleam_stdlib:string_ends_with(Value, <<"/"/utf8>>) of
true ->
gleam@string:drop_end(Value, 1);
false ->
Value
end.
-file("src/packkit/ar.gleam", 662).
-spec strip_trailing_nul_loop(bitstring(), integer()) -> bitstring().
strip_trailing_nul_loop(Bytes, Size) ->
case Size of
0 ->
<<>>;
_ ->
case gleam_stdlib:bit_array_slice(Bytes, Size - 1, 1) of
{ok, <<0>>} ->
strip_trailing_nul_loop(Bytes, Size - 1);
_ ->
Slice@1 = case gleam_stdlib:bit_array_slice(Bytes, 0, Size) of
{ok, Slice} -> Slice;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/ar"/utf8>>,
function => <<"strip_trailing_nul_loop"/utf8>>,
line => 669,
value => _assert_fail,
start => 19250,
'end' => 19304,
pattern_start => 19261,
pattern_end => 19270})
end,
Slice@1
end
end.
-file("src/packkit/ar.gleam", 657).
-spec strip_trailing_nul(bitstring()) -> bitstring().
strip_trailing_nul(Bytes) ->
Size = erlang:byte_size(Bytes),
strip_trailing_nul_loop(Bytes, Size).
-file("src/packkit/ar.gleam", 212).
-spec resolve_entry_name(
parsed_record(),
bitstring(),
integer(),
gleam@option:option(bitstring())
) -> {ok, binary()} | {error, packkit@error:archive_error()}.
resolve_entry_name(Record, Bytes, After_header_offset, String_table) ->
case {erlang:element(3, Record), erlang:element(4, Record)} of
{N, _} when N > 0 ->
gleam@result:'try'(
slice_or_error(
Bytes,
After_header_offset,
erlang:element(3, Record)
),
fun(Ext_bits) ->
bytes_to_string(strip_trailing_nul(Ext_bits))
end
);
{_, {some, Offset}} ->
case String_table of
none ->
{error,
{archive_invalid,
<<"ar entry references missing GNU string table"/utf8>>}};
{some, Table} ->
gnu_string_at(Table, Offset)
end;
{_, none} ->
{ok, erlang:element(2, Record)}
end.
-file("src/packkit/ar.gleam", 676).
-spec min(integer(), integer()) -> integer().
min(A, B) ->
case A < B of
true ->
A;
false ->
B
end.
-file("src/packkit/ar.gleam", 305).
-spec parse_header(bitstring()) -> {ok, parsed_record()} |
{error, packkit@error:archive_error()}.
parse_header(Block) ->
gleam@result:'try'(
slice_or_error(Block, 0, 16),
fun(Raw_name_bits) ->
gleam@result:'try'(
bytes_to_string(Raw_name_bits),
fun(Raw_name) ->
Trimmed_name = gleam@string:trim_end(Raw_name),
gleam@result:'try'(
read_decimal_field(Block, 16, 12),
fun(Mtime) ->
gleam@result:'try'(
read_decimal_field(Block, 28, 6),
fun(Uid) ->
gleam@result:'try'(
read_decimal_field(Block, 34, 6),
fun(Gid) ->
gleam@result:'try'(
read_octal_field(Block, 40, 8),
fun(Mode) ->
gleam@result:'try'(
read_decimal_field(
Block,
48,
10
),
fun(Size) ->
gleam@result:'try'(
slice_or_error(
Block,
58,
2
),
fun(Ending_bits) ->
gleam@bool:guard(
Ending_bits
/= <<16#60,
16#0A>>,
{error,
{archive_invalid,
<<"ar header marker missing"/utf8>>}},
fun() ->
case Trimmed_name of
<<"/"/utf8>> ->
{ok,
{parsed_record,
Trimmed_name,
0,
none,
symbol_table,
Mtime,
Uid,
Gid,
Mode,
Size}};
<<"/SYM64/"/utf8>> ->
{ok,
{parsed_record,
Trimmed_name,
0,
none,
symbol_table,
Mtime,
Uid,
Gid,
Mode,
Size}};
<<"//"/utf8>> ->
{ok,
{parsed_record,
Trimmed_name,
0,
none,
string_table,
Mtime,
Uid,
Gid,
Mode,
Size}};
<<"ARFILENAMES/"/utf8>> ->
{ok,
{parsed_record,
Trimmed_name,
0,
none,
string_table,
Mtime,
Uid,
Gid,
Mode,
Size}};
_ ->
case gleam_stdlib:string_starts_with(
Trimmed_name,
<<"#1/"/utf8>>
) of
true ->
Len_string = gleam@string:drop_start(
Trimmed_name,
3
),
case gleam_stdlib:parse_int(
Len_string
) of
{ok,
Len} ->
{ok,
{parsed_record,
<<""/utf8>>,
Len,
none,
regular_entry,
Mtime,
Uid,
Gid,
Mode,
Size}};
{error,
_} ->
{error,
{archive_invalid,
<<"invalid BSD long name length in ar header"/utf8>>}}
end;
false ->
case gleam_stdlib:string_starts_with(
Trimmed_name,
<<"/"/utf8>>
) of
true ->
Offset_string = gleam@string:drop_start(
Trimmed_name,
1
),
case gleam_stdlib:parse_int(
Offset_string
) of
{ok,
Offset} ->
{ok,
{parsed_record,
<<""/utf8>>,
0,
{some,
Offset},
regular_entry,
Mtime,
Uid,
Gid,
Mode,
Size}};
{error,
_} ->
{error,
{archive_invalid,
<<"invalid GNU long name offset in ar header"/utf8>>}}
end;
false ->
{ok,
{parsed_record,
strip_trailing_slash(
Trimmed_name
),
0,
none,
regular_entry,
Mtime,
Uid,
Gid,
Mode,
Size}}
end
end
end
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/packkit/ar.gleam", 104).
-spec decode_loop(
bitstring(),
list(packkit@entry:entry()),
integer(),
gleam@option:option(bitstring()),
packkit@limit:limits()
) -> {ok, list(packkit@entry:entry())} | {error, packkit@error:archive_error()}.
decode_loop(Bytes, Acc, Count, String_table, Limits) ->
case erlang:byte_size(Bytes) of
0 ->
{ok, Acc};
_ ->
case erlang:byte_size(Bytes) < 60 of
true ->
{error,
{archive_invalid, <<"ar entry header truncated"/utf8>>}};
false ->
Header_bits@1 = case gleam_stdlib:bit_array_slice(
Bytes,
0,
60
) of
{ok, Header_bits} -> Header_bits;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/ar"/utf8>>,
function => <<"decode_loop"/utf8>>,
line => 118,
value => _assert_fail,
start => 3520,
'end' => 3587,
pattern_start => 3531,
pattern_end => 3546})
end,
gleam@result:'try'(
parse_header(Header_bits@1),
fun(Record) ->
After_header_offset = 60,
Payload_offset = After_header_offset + erlang:element(
3,
Record
),
Payload_size = erlang:element(10, Record) - erlang:element(
3,
Record
),
Total_size = 60 + erlang:element(10, Record),
Padded_size = case Total_size rem 2 of
0 ->
Total_size;
_ ->
Total_size + 1
end,
gleam@bool:guard(
erlang:byte_size(Bytes) < (Payload_offset + Payload_size),
{error,
{archive_invalid,
<<"ar entry truncated"/utf8>>}},
fun() ->
Advance = min(
Padded_size,
erlang:byte_size(Bytes)
),
Remaining@1 = case gleam_stdlib:bit_array_slice(
Bytes,
Advance,
erlang:byte_size(Bytes) - Advance
) of
{ok, Remaining} -> Remaining;
_assert_fail@1 ->
erlang:error(
#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/ar"/utf8>>,
function => <<"decode_loop"/utf8>>,
line => 136,
value => _assert_fail@1,
start => 4294,
'end' => 4459,
pattern_start => 4305,
pattern_end => 4318}
)
end,
case erlang:element(5, Record) of
symbol_table ->
decode_loop(
Remaining@1,
Acc,
Count,
String_table,
Limits
);
string_table ->
Table_body@1 = case gleam_stdlib:bit_array_slice(
Bytes,
Payload_offset,
Payload_size
) of
{ok, Table_body} -> Table_body;
_assert_fail@2 ->
erlang:error(
#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/ar"/utf8>>,
function => <<"decode_loop"/utf8>>,
line => 147,
value => _assert_fail@2,
start => 4634,
'end' => 4730,
pattern_start => 4645,
pattern_end => 4659}
)
end,
decode_loop(
Remaining@1,
Acc,
Count,
{some, Table_body@1},
Limits
);
regular_entry ->
gleam@result:'try'(
check_member_limit(
Count + 1,
Limits
),
fun(_) ->
gleam@result:'try'(
resolve_entry_name(
Record,
Bytes,
After_header_offset,
String_table
),
fun(Name) ->
gleam@bool:guard(
erlang:byte_size(
Name
)
> packkit@limit:max_entry_name_bytes(
Limits
),
{error,
{archive_limit_exceeded,
<<"max_entry_name_bytes"/utf8>>,
erlang:byte_size(
Name
)}},
fun() ->
Body@1 = case gleam_stdlib:bit_array_slice(
Bytes,
Payload_offset,
Payload_size
) of
{ok,
Body} -> Body;
_assert_fail@3 ->
erlang:error(
#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/ar"/utf8>>,
function => <<"decode_loop"/utf8>>,
line => 176,
value => _assert_fail@3,
start => 5559,
'end' => 5649,
pattern_start => 5570,
pattern_end => 5578}
)
end,
gleam@result:'try'(
begin
_pipe = packkit@entry:file_checked(
Name,
Body@1
),
gleam@result:map_error(
_pipe,
fun(
_capture
) ->
entry_error_to_archive_error(
_capture,
Name
)
end
)
end,
fun(
Base_entry
) ->
Depth = packkit@entry:depth(
packkit@entry:path(
Base_entry
)
),
gleam@bool:guard(
Depth
> packkit@limit:max_entry_depth(
Limits
),
{error,
{archive_limit_exceeded,
<<"max_entry_depth"/utf8>>,
Depth}},
fun(
) ->
Entry_value = begin
_pipe@1 = Base_entry,
_pipe@2 = packkit@entry:with_mode(
_pipe@1,
erlang:element(
9,
Record
)
),
_pipe@3 = packkit@entry:with_owner(
_pipe@2,
erlang:element(
7,
Record
),
erlang:element(
8,
Record
)
),
packkit@entry:with_modified_at(
_pipe@3,
erlang:element(
6,
Record
)
)
end,
decode_loop(
Remaining@1,
[Entry_value |
Acc],
Count
+ 1,
String_table,
Limits
)
end
)
end
)
end
)
end
)
end
)
end
end
)
end
)
end
end.
-file("src/packkit/ar.gleam", 78).
?DOC(" Decode an `ar` byte stream using explicit limits.\n").
-spec decode_with_limits(bitstring(), packkit@limit:limits()) -> {ok,
packkit@archive:archive()} |
{error, packkit@error:archive_error()}.
decode_with_limits(Bytes, Limits) ->
gleam@bool:guard(
erlang:byte_size(Bytes) > packkit@limit:max_input_bytes(Limits),
{error,
{archive_limit_exceeded,
<<"max_input_bytes"/utf8>>,
erlang:byte_size(Bytes)}},
fun() ->
gleam@result:'try'(
slice_or_error(Bytes, 0, 8),
fun(Header_bits) ->
gleam@result:'try'(
bytes_to_string(Header_bits),
fun(Header_string) ->
gleam@bool:guard(
Header_string /= <<"!<arch>\n"/utf8>>,
{error,
{archive_invalid,
<<"missing !<arch> magic"/utf8>>}},
fun() ->
Body_size = erlang:byte_size(Bytes) - 8,
Rest@1 = case gleam_stdlib:bit_array_slice(
Bytes,
8,
Body_size
) of
{ok, Rest} -> Rest;
_assert_fail ->
erlang:error(
#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"packkit/ar"/utf8>>,
function => <<"decode_with_limits"/utf8>>,
line => 98,
value => _assert_fail,
start => 2872,
'end' => 2939,
pattern_start => 2883,
pattern_end => 2891}
)
end,
_pipe = decode_loop(
Rest@1,
[],
0,
none,
Limits
),
_pipe@1 = gleam@result:map(
_pipe,
fun lists:reverse/1
),
gleam@result:map(
_pipe@1,
fun(_capture) ->
packkit@archive:from_entries(
format(),
_capture
)
end
)
end
)
end
)
end
)
end
).
-file("src/packkit/ar.gleam", 71).
?DOC(" Decode an `ar` byte stream using default limits.\n").
-spec decode(bitstring()) -> {ok, packkit@archive:archive()} |
{error, packkit@error:archive_error()}.
decode(Bytes) ->
decode_with_limits(Bytes, packkit@limit:default()).
-file("src/packkit/ar.gleam", 409).
-spec encode_entry(packkit@entry:entry()) -> {ok, bitstring()} |
{error, packkit@error:archive_error()}.
encode_entry(Value) ->
Kind = packkit@entry:kind(Value),
gleam@bool:guard(
Kind /= file,
{error,
{archive_entry_rejected,
packkit@entry:to_string(packkit@entry:path(Value)),
<<"ar only supports regular file entries"/utf8>>}},
fun() ->
Path = packkit@entry:to_string(packkit@entry:path(Value)),
Metadata = packkit@entry:metadata(Value),
Body = packkit@entry:body(Value),
Body_size = erlang:byte_size(Body),
Name_bytes = gleam_stdlib:identity(Path),
Name_size = erlang:byte_size(Name_bytes),
Needs_long_name = ((Name_size > 16) orelse gleam_stdlib:contains_string(
Path,
<<" "/utf8>>
))
orelse gleam_stdlib:contains_string(Path, <<"/"/utf8>>),
{Name_field, Name_extension_bytes, Total_size} = case Needs_long_name of
true ->
Extension_size = Name_size,
Label = <<"#1/"/utf8,
(erlang:integer_to_binary(Extension_size))/binary>>,
{text_field(Label, 16),
Name_bytes,
Body_size + Extension_size};
false ->
{text_field(Path, 16), <<>>, Body_size}
end,
gleam@result:'try'(
checked_decimal_field(
packkit@entry:modified_at_unix(Metadata),
12,
<<"mtime"/utf8>>
),
fun(Mtime_field) ->
gleam@result:'try'(
checked_decimal_field(
packkit@entry:user_id(Metadata),
6,
<<"uid"/utf8>>
),
fun(Uid_field) ->
gleam@result:'try'(
checked_decimal_field(
packkit@entry:group_id(Metadata),
6,
<<"gid"/utf8>>
),
fun(Gid_field) ->
gleam@result:'try'(
checked_octal_field(
packkit@entry:mode(Metadata),
8,
<<"mode"/utf8>>
),
fun(Mode_field) ->
gleam@result:'try'(
checked_decimal_field(
Total_size,
10,
<<"size"/utf8>>
),
fun(Size_field) ->
Header = gleam_stdlib:bit_array_concat(
[Name_field,
Mtime_field,
Uid_field,
Gid_field,
Mode_field,
Size_field,
<<16#60, 16#0A>>]
),
Combined = gleam_stdlib:bit_array_concat(
[Header,
Name_extension_bytes,
Body]
),
Padded = case Total_size rem 2 of
0 ->
Combined;
_ ->
gleam_stdlib:bit_array_concat(
[Combined,
<<16#0A>>]
)
end,
{ok, Padded}
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/packkit/ar.gleam", 48).
?DOC(" Encode the logical archive to an `ar` byte stream.\n").
-spec encode(packkit@archive:archive()) -> {ok, bitstring()} |
{error, packkit@error:archive_error()}.
encode(Archive_value) ->
gleam@result:'try'(
reject_comment(Archive_value),
fun(_) -> _pipe = Archive_value,
_pipe@1 = packkit@archive:entries(_pipe),
_pipe@2 = gleam@list:try_map(_pipe@1, fun encode_entry/1),
gleam@result:map(
_pipe@2,
fun(Records) ->
_pipe@3 = [gleam_stdlib:identity(<<"!<arch>\n"/utf8>>),
gleam_stdlib:bit_array_concat(Records)],
gleam_stdlib:bit_array_concat(_pipe@3)
end
) end
).